pax_global_header00006660000000000000000000000064145411034220014506gustar00rootroot0000000000000052 comment=3f2aced8c6fd00b0b71da24c790850af2004052b wlroots-0.17.1/000077500000000000000000000000001454110342200133055ustar00rootroot00000000000000wlroots-0.17.1/.builds/000077500000000000000000000000001454110342200146455ustar00rootroot00000000000000wlroots-0.17.1/.builds/alpine.yml000066400000000000000000000015401454110342200166400ustar00rootroot00000000000000image: alpine/edge packages: - eudev-dev - ffmpeg-dev - glslang - libdisplay-info-dev - libinput-dev - libliftoff-dev - libxkbcommon-dev - mesa-dev - meson - pixman-dev - vulkan-headers - vulkan-loader-dev - wayland-dev - wayland-protocols - xcb-util-image-dev - xcb-util-renderutil-dev - xcb-util-wm-dev - xwayland-dev - libseat-dev - hwdata-dev sources: - https://gitlab.freedesktop.org/wlroots/wlroots.git tasks: - setup: | cd wlroots meson setup build --fatal-meson-warnings --default-library=both -Dauto_features=enabled -Dxcb-errors=disabled - build: | cd wlroots ninja -C build sudo ninja -C build install - build-features-disabled: | cd wlroots meson build --reconfigure -Dauto_features=disabled ninja -C build - tinywl: | cd wlroots/tinywl make wlroots-0.17.1/.builds/archlinux.yml000066400000000000000000000024201454110342200173630ustar00rootroot00000000000000image: archlinux packages: - clang - ffmpeg - libinput - libdisplay-info - libliftoff - libxkbcommon - mesa - meson - pixman - wayland - wayland-protocols - xcb-util-errors - xcb-util-image - xcb-util-renderutil - xcb-util-wm - xorg-xwayland - seatd - vulkan-icd-loader - vulkan-headers - glslang - hwdata sources: - https://gitlab.freedesktop.org/wlroots/wlroots.git tasks: - setup: | cd wlroots CC=gcc meson setup build-gcc --fatal-meson-warnings --default-library=both -Dauto_features=enabled --prefix /usr -Db_sanitize=address,undefined CC=clang meson setup build-clang --fatal-meson-warnings -Dauto_features=enabled - gcc: | cd wlroots/build-gcc ninja sudo ninja install cd ../tinywl CFLAGS="-fsanitize=address,undefined -fno-omit-frame-pointer" make - clang: | cd wlroots/build-clang ninja - smoke-test: | cd wlroots/tinywl sudo modprobe vkms udevadm settle export WLR_BACKENDS=drm export WLR_RENDERER=pixman export WLR_DRM_DEVICES=/dev/dri/by-path/platform-vkms-card export UBSAN_OPTIONS=halt_on_error=1 sudo chmod ugo+rw /dev/dri/by-path/platform-vkms-card sudo -E seatd-launch -- ./tinywl -s 'kill $PPID' || [ $? = 143 ] wlroots-0.17.1/.builds/freebsd.yml000066400000000000000000000016271454110342200170100ustar00rootroot00000000000000image: freebsd/latest packages: - devel/evdev-proto - devel/libepoll-shim - devel/libudev-devd - devel/meson # implies ninja - devel/pkgconf - graphics/glslang - graphics/libdrm - graphics/libliftoff - graphics/mesa-libs - graphics/png - graphics/vulkan-headers - graphics/vulkan-loader - graphics/wayland - graphics/wayland-protocols - multimedia/ffmpeg - x11/libX11 - x11/libinput - x11/libxcb - x11/libxkbcommon - x11/pixman - x11/xcb-util-errors - x11/xcb-util-renderutil - x11/xcb-util-wm - x11-servers/xwayland-devel - sysutils/libdisplay-info - sysutils/seatd - gmake - hwdata sources: - https://gitlab.freedesktop.org/wlroots/wlroots.git tasks: - wlroots: | cd wlroots meson setup build --fatal-meson-warnings -Dauto_features=enabled ninja -C build sudo ninja -C build install - tinywl: | cd wlroots/tinywl gmake wlroots-0.17.1/.editorconfig000066400000000000000000000003251454110342200157620ustar00rootroot00000000000000root = true [*] end_of_line = lf insert_final_newline = true charset = utf-8 trim_trailing_whitespace = true indent_style = tab indent_size = 4 max_line_length = 100 [*.xml] indent_style = space indent_size = 2 wlroots-0.17.1/.gitignore000066400000000000000000000000161454110342200152720ustar00rootroot00000000000000/subprojects/ wlroots-0.17.1/.gitlab-ci.yml000066400000000000000000000002451454110342200157420ustar00rootroot00000000000000include: https://git.sr.ht/~emersion/dalligi/blob/master/templates/multi.yml alpine: extends: .dalligi archlinux: extends: .dalligi freebsd: extends: .dalligi wlroots-0.17.1/.gitlab/000077500000000000000000000000001454110342200146255ustar00rootroot00000000000000wlroots-0.17.1/.gitlab/issue_templates/000077500000000000000000000000001454110342200200335ustar00rootroot00000000000000wlroots-0.17.1/.gitlab/issue_templates/Default.md000066400000000000000000000011211454110342200217340ustar00rootroot00000000000000 wlroots-0.17.1/CONTRIBUTING.md000066400000000000000000000374341454110342200155510ustar00rootroot00000000000000# Contributing to wlroots Contributing just involves sending a merge request. You will probably be more successful with your contribution if you visit [#wlroots on Libera Chat] upfront and discuss your plans. Note: rules are made to be broken. Adjust or ignore any/all of these as you see fit, but be prepared to justify it to your peers. ## Forking New GitLab accounts may not have the permission to fork repositories. You will need to [file a user verification request] to get this permission. See the [freedesktop wiki] for more details. The fork must be marked as public to allow CI to run. ## Merge Requests If you already have your own merge request habits, feel free to use them. If you don't, however, allow me to make a suggestion: feature branches pulled from upstream. Try this: 1. Fork wlroots 2. `git clone git@gitlab.freedesktop.org:/wlroots.git && cd wlroots` 3. `git remote add upstream https://gitlab.freedesktop.org/wlroots/wlroots.git` You only need to do this once. You're never going to use your fork's master branch. Instead, when you start working on a feature, do this: 1. `git fetch upstream` 2. `git checkout -b add-so-and-so-feature upstream/master` 3. Add and commit your changes 4. `git push -u origin add-so-and-so-feature` 5. Make a merge request from your feature branch When you submit your merge request, your commit log should do most of the talking when it comes to describing your changes and their motivation. In addition to this, your merge request's comments will ideally include a test plan that the reviewers can use to (1) demonstrate the problem on master, if applicable and (2) verify that the problem no longer exists with your changes applied (or that your new features work correctly). Document all of the edge cases you're aware of so we can adequately test them - then verify the test plan yourself before submitting. ## Commit Log Unlike many projects using GitHub and GitLab, wlroots has a [linear, "recipe" style] history. This means that every commit should be small, digestible, stand-alone, and functional. Rather than a purely chronological commit history like this: ``` doc: final docs for view transforms fix tests when disabled, redo broken doc formatting better transformed-view iteration (thanks Hannah!) try to catch more cases in tests tests: add new spline test fix compilation on splines doc: notes on reticulating splines compositor: add spline reticulation for view transforms ``` We aim to have a clean history which only reflects the final state, broken up into functional groupings: ``` compositor: add spline reticulation for view transforms compositor: new iterator for view transforms tests: add view-transform correctness tests doc: fix formatting for view transforms ``` This ensures that the final patch series only contains the final state, without the changes and missteps taken along the development process. A linear history eases reviewing, cherry-picking and reverting changes. If you aren't comfortable with manipulating the Git history, have a look at [git-rebase.io]. ## Commit Messages Please strive to write good commit messages. Here's some guidelines to follow: The first line should be limited to 50 characters and should be a sentence that completes the thought [When applied, this commit will...] *"Implement cmd_move"* or *"Improve performance of arrange_windows on ARM"* or similar. The subsequent lines should be separated from the subject line by a single blank line, and include optional details. In this you can give justification for the change, [reference issues], or explain some of the subtler details of your patch. This is important because when someone finds a line of code they don't understand later, they can use the `git blame` command to find out what the author was thinking when they wrote it. It's also easier to review your merge requests if they're separated into logical commits that have good commit messages and justify themselves in the extended commit description. As a good rule of thumb, anything you might put into the merge request description on GitLab is probably fair game for going into the extended commit message as well. See [How to Write a Git Commit Message] for more details. ## Code Review When your changes are submitted for review, one or more core committers will look over them. Smaller changes might be merged with little fanfare, but larger changes will typically see review from several people. Be prepared to receive some feedback - you may be asked to make changes to your work. Our code review process is: 1. **Triage** the merge request. Do the commit messages make sense? Is a test plan necessary and/or present? Add anyone as reviewers that you think should be there (using the relevant GitLab feature, if you have the permissions, or with an @mention if necessary). 2. **Review** the code. Look for code style violations, naming convention violations, buffer overflows, memory leaks, logic errors, non-portable code (including GNU-isms), etc. For significant changes to the public API, loop in a couple more people for discussion. 3. **Execute** the test plan, if present. 4. **Merge** the merge request when all reviewers approve. 5. **File** follow-up tickets if appropriate. ## Code of Conduct Note that as a project hosted on freedesktop.org, wlroots follows its [Code of Conduct], based on the Contributor Covenant. Please conduct yourself in a respectful and civilized manner when communicating with community members on IRC and bug tracker. ## Style Reference wlroots is written in C with a style similar to the [kernel style], but with a few notable differences. Try to keep your code conforming to C11 and POSIX as much as possible, and do not use GNU extensions. ### Brackets Brackets always go on the same line, including in functions. Always include brackets for if/while/for, even if it's a single statement. ```c void function(void) { if (condition1) { do_thing1(); } if (condition2) { do_thing2(); } else { do_thing3(); } } ``` ### Indentation Indentations are a single tab. For long lines that need to be broken, the continuation line should be indented with an additional tab. If the line being broken is opening a new block (functions, if, while, etc.), the continuation line should be indented with two tabs, so they can't be misread as being part of the block. ```c really_long_function(argument1, argument2, ..., argument3, argument4); if (condition1 && condition2 && ... condition3 && condition4) { do_thing(); } ``` Try to break the line in the place which you think is the most appropriate. ### Line Length Try to keep your lines under 100 columns, but you can break this rule if it improves readability. Don't break lines indiscriminately, try to find nice breaking points so your code is easy to read. ### Names Global function and type names should be prefixed with `wlr_submodule_` (e.g. `struct wlr_output`, `wlr_output_set_cursor`). For static functions and types local to a file, the names chosen aren't as important. Local function names shouldn't have a `wlr_` prefix. For include guards, use the header's filename relative to include. Uppercase all of the characters, and replace any invalid characters with an underscore. ### Construction/Destruction Functions Functions that are responsible for constructing objects should take one of the two following forms: * `init`: for functions which accept a pointer to a pre-allocated object (e.g. a member of a struct) and initialize it. Such functions must zero out the memory before initializing it to avoid leaving unset fields. * `create`: for functions which allocate the memory for an object, initialize it, and return a pointer. Such functions should allocate the memory with `calloc()` to avoid leaving unset fields. Likewise, functions that are responsible for destructing objects should take one of the two following forms: * `finish`: for functions which accept a pointer to an object and deinitialize it. If a finished object isn't destroyed but kept for future use, it must be reinitialized to be used again. * `destroy`: for functions which accept a pointer to an object, deinitialize it, and free the memory. Such functions should always be able to accept a NULL pointer. ### Error Codes For functions not returning a value, they should return a (stdbool.h) bool to indicate whether they succeeded or not. ### Macros Try to keep the use of macros to a minimum, especially if a function can do the job. If you do need to use them, try to keep them close to where they're being used and `#undef` them after. ### Documentation * Documentation comments for declarations start with `/**` and end with `*/`. * Cross-reference other declarations by ending function names with `()`, and writing `struct`, `union`, `enum` or `typedef` before types. * Document fields which can be NULL with a `// may be NULL` comment, optionally with more details describing when this can happen. * Document the bits of a bitfield with a `// enum bar` comment. * Document the `data` argument of a `struct wl_signal` with a `// struct foo` comment. * Document the contents and container of a `struct wl_list` with a `// content.link` and `// container.list` comment. ### Safety * Avoid string manipulation functions which don't take the size of the destination buffer as input: for instance, prefer `snprintf` over `sprintf`. * Avoid repeating type names in `sizeof()` where possible. For instance, prefer `ptr = calloc(1, sizeof(*ptr))` over `ptr = calloc(1, sizeof(struct foo))`. * Prefer `*ptr = (struct foo){0}` over `memset(ptr, 0, sizeof(*ptr))`. * Prefer `*foo = *bar` over `memcpy(foo, bar, sizeof(*foo))`. ### Example ```c struct wlr_backend *wlr_backend_autocreate(struct wl_display *display) { struct wlr_backend *backend; if (getenv("WAYLAND_DISPLAY") || getenv("_WAYLAND_DISPLAY")) { backend = attempt_wl_backend(display); if (backend) { return backend; } } const char *x11_display = getenv("DISPLAY"); if (x11_display) { return wlr_x11_backend_create(display, x11_display); } // Attempt DRM+libinput struct wlr_session *session = wlr_session_create(display); if (!session) { wlr_log(WLR_ERROR, "Failed to start a DRM session"); return NULL; } int gpu = wlr_session_find_gpu(session); if (gpu == -1) { wlr_log(WLR_ERROR, "Failed to open DRM device"); goto error_session; } backend = wlr_multi_backend_create(session); if (!backend) { goto error_gpu; } struct wlr_backend *libinput = wlr_libinput_backend_create(display, session); if (!libinput) { goto error_multi; } struct wlr_backend *drm = wlr_drm_backend_create(display, session, gpu); if (!drm) { goto error_libinput; } wlr_multi_backend_add(backend, libinput); wlr_multi_backend_add(backend, drm); return backend; error_libinput: wlr_backend_destroy(libinput); error_multi: wlr_backend_destroy(backend); error_gpu: wlr_session_close_file(session, gpu); error_session: wlr_session_destroy(session); return NULL; } ``` ## Wayland protocol implementation Each protocol generally lives in a file with the same name, usually containing at least one struct for each interface in the protocol. For instance, `xdg_shell` lives in `types/wlr_xdg_shell.h` and has a `wlr_xdg_surface` struct. ### Globals Global interfaces generally have public constructors and destructors. Their struct has a field holding the `wl_global` itself, a destroy signal and a `wl_display` destroy listener. Example: ```c struct wlr_compositor { struct wl_global *global; … struct wl_listener display_destroy; struct { struct wl_signal new_surface; struct wl_signal destroy; } events; }; ``` When the destructor is called, it should emit the destroy signal, remove the display destroy listener, destroy the `wl_global` and then destroy the struct. The destructor can assume all clients and resources have been already destroyed. ### Resources Resources are the representation of Wayland objects on the compositor side. They generally have an associated struct, called the _object struct_, stored in their `user_data` field. Object structs can be retrieved from resources via `wl_resource_get_data`. To prevent bad casts, a safe helper function checking the type of the resource is used: ```c static const struct wl_surface_interface surface_impl; struct wlr_surface *wlr_surface_from_resource(struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &wl_surface_interface, &surface_impl)); return wl_resource_get_user_data(resource); } ``` If a pointer to a `wl_resource` is stored, a resource destroy handler needs to be registered to clean it up. libwayland will automatically destroy resources in an arbitrary order when a client is disconnected, the compositor must handle this correctly. ### Destroying resources Object structs should only be destroyed when their resource is destroyed, ie. in the resource destroy handler (set with `wl_resource_set_implementation`). - If the object has a destructor request: the request handler should just call `wl_resource_destroy` and do nothing else. The compositor must not destroy resources on its own outside the destructor request handler. - If the protocol specifies that an object is destroyed when an event is sent: it's the only case where the compositor is allowed to send the event and then call `wl_resource_destroy`. An example of this is `wl_callback`. ### Inert resources Some resources can become inert in situations described in the protocol or when the compositor decides to get rid of them. All requests made to inert resources should be ignored, except the destructor. This is achieved by: 1. When the resource becomes inert: destroy the object struct and call `wl_resource_set_user_data(resource, NULL)`. Do not destroy the resource. 2. For each request made to a resource that can be inert: add a NULL check to ignore the request if the resource is inert. 3. When the client calls the destructor request on the resource: call `wl_resource_destroy(resource)` as usual. 4. When the resource is destroyed, if the resource isn't inert, destroy the object struct. Example: ```c // Handles the destroy request static void subsurface_handle_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } // Handles a regular request static void subsurface_set_position(struct wl_client *client, struct wl_resource *resource, int32_t x, int32_t y) { struct wlr_subsurface *subsurface = subsurface_from_resource(resource); if (subsurface == NULL) { return; } … } // Destroys the wlr_subsurface struct static void subsurface_destroy(struct wlr_subsurface *subsurface) { if (subsurface == NULL) { return; } … wl_resource_set_user_data(subsurface->resource, NULL); free(subsurface); } // Resource destroy listener static void subsurface_handle_resource_destroy(struct wl_resource *resource) { struct wlr_subsurface *subsurface = subsurface_from_resource(resource); subsurface_destroy(subsurface); } // Makes the resource inert static void subsurface_handle_surface_destroy(struct wl_listener *listener, void *data) { struct wlr_subsurface *subsurface = wl_container_of(listener, subsurface, surface_destroy); subsurface_destroy(subsurface); } ``` [#wlroots on Libera Chat]: https://web.libera.chat/gamja/?channels=#wlroots [file a user verification request]: https://gitlab.freedesktop.org/freedesktop/freedesktop/-/issues/new?issuable_template=User%20verification [freedesktop wiki]: https://gitlab.freedesktop.org/freedesktop/freedesktop/-/wikis/home [linear, "recipe" style]: https://www.bitsnbites.eu/git-history-work-log-vs-recipe/ [git-rebase.io]: https://git-rebase.io/ [reference issues]: https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically [Code of Conduct]: https://www.freedesktop.org/wiki/CodeOfConduct/ [How to Write a Git Commit Message]: https://chris.beams.io/posts/git-commit/ [kernel style]: https://www.kernel.org/doc/Documentation/process/coding-style.rst wlroots-0.17.1/LICENSE000066400000000000000000000021641454110342200143150ustar00rootroot00000000000000Copyright (c) 2017, 2018 Drew DeVault Copyright (c) 2014 Jari Vetoniemi Copyright (c) 2023 The wlroots contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. wlroots-0.17.1/README.md000066400000000000000000000066071454110342200145750ustar00rootroot00000000000000# wlroots Pluggable, composable, unopinionated modules for building a [Wayland] compositor; or about 60,000 lines of code you were going to write anyway. - wlroots provides backends that abstract the underlying display and input hardware, including KMS/DRM, libinput, Wayland, X11, and headless backends, plus any custom backends you choose to write, which can all be created or destroyed at runtime and used in concert with each other. - wlroots provides unopinionated, mostly standalone implementations of many Wayland interfaces, both from wayland.xml and various protocol extensions. We also promote the standardization of portable extensions across many compositors. - wlroots provides several powerful, standalone, and optional tools that implement components common to many compositors, such as the arrangement of outputs in physical space. - wlroots provides an Xwayland abstraction that allows you to have excellent Xwayland support without worrying about writing your own X11 window manager on top of writing your compositor. - wlroots provides a renderer abstraction that simple compositors can use to avoid writing GL code directly, but which steps out of the way when your needs demand custom rendering code. wlroots implements a huge variety of Wayland compositor features and implements them *right*, so you can focus on the features that make your compositor unique. By using wlroots, you get high performance, excellent hardware compatibility, broad support for many wayland interfaces, and comfortable development tools - or any subset of these features you like, because all of them work independently of one another and freely compose with anything you want to implement yourself. Check out our [wiki] to get started with wlroots. Join our IRC channel: [#wlroots on Libera Chat]. A variety of [wrapper libraries] are available for using it with your favorite programming language. ## Building Install dependencies: * meson * wayland * wayland-protocols * EGL and GLESv2 (optional, for the GLES2 renderer) * Vulkan loader, headers and glslang (optional, for the Vulkan renderer) * libdrm * GBM (optional, for the GBM allocator) * libinput (optional, for the libinput backend) * xkbcommon * udev (optional, for the session) * pixman * [libseat] (optional, for the session) * [hwdata] (optional, for the DRM backend) * [libdisplay-info] (optional, for the DRM backend) * [libliftoff] (optional, for the DRM backend) If you choose to enable X11 support: * xwayland (build-time only, optional at runtime) * libxcb * libxcb-render-util * libxcb-wm * libxcb-errors (optional, for improved error reporting) Run these commands: meson setup build/ ninja -C build/ Install like so: sudo ninja -C build/ install ## Contributing See [CONTRIBUTING.md]. [Wayland]: https://wayland.freedesktop.org/ [wiki]: https://gitlab.freedesktop.org/wlroots/wlroots/-/wikis/Getting-started [#wlroots on Libera Chat]: https://web.libera.chat/gamja/?channels=#wlroots [wrapper libraries]: https://gitlab.freedesktop.org/wlroots/wlroots/-/wikis/Projects-which-use-wlroots#wrapper-libraries [libseat]: https://git.sr.ht/~kennylevinsen/seatd [hwdata]: https://github.com/vcrhonek/hwdata [libdisplay-info]: https://gitlab.freedesktop.org/emersion/libdisplay-info [libliftoff]: https://gitlab.freedesktop.org/emersion/libliftoff [CONTRIBUTING.md]: https://gitlab.freedesktop.org/wlroots/wlroots/-/blob/master/CONTRIBUTING.md wlroots-0.17.1/backend/000077500000000000000000000000001454110342200146745ustar00rootroot00000000000000wlroots-0.17.1/backend/backend.c000066400000000000000000000226651454110342200164420ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include #include #include #include #include #include #include "backend/backend.h" #include "backend/multi.h" #include "render/allocator/allocator.h" #include "util/env.h" #include "util/time.h" #if WLR_HAS_SESSION #include #endif #if WLR_HAS_DRM_BACKEND #include #include "backend/drm/monitor.h" #endif #if WLR_HAS_LIBINPUT_BACKEND #include #endif #if WLR_HAS_X11_BACKEND #include #endif #define WAIT_SESSION_TIMEOUT 10000 // ms void wlr_backend_init(struct wlr_backend *backend, const struct wlr_backend_impl *impl) { *backend = (struct wlr_backend){ .impl = impl, }; wl_signal_init(&backend->events.destroy); wl_signal_init(&backend->events.new_input); wl_signal_init(&backend->events.new_output); } void wlr_backend_finish(struct wlr_backend *backend) { wl_signal_emit_mutable(&backend->events.destroy, backend); } bool wlr_backend_start(struct wlr_backend *backend) { if (backend->impl->start) { return backend->impl->start(backend); } return true; } void wlr_backend_destroy(struct wlr_backend *backend) { if (!backend) { return; } if (backend->impl && backend->impl->destroy) { backend->impl->destroy(backend); } else { free(backend); } } static struct wlr_session *session_create_and_wait(struct wl_display *disp) { #if WLR_HAS_SESSION struct wlr_session *session = wlr_session_create(disp); if (!session) { wlr_log(WLR_ERROR, "Failed to start a session"); return NULL; } if (!session->active) { wlr_log(WLR_INFO, "Waiting for a session to become active"); int64_t started_at = get_current_time_msec(); int64_t timeout = WAIT_SESSION_TIMEOUT; struct wl_event_loop *event_loop = wl_display_get_event_loop(session->display); while (!session->active) { int ret = wl_event_loop_dispatch(event_loop, (int)timeout); if (ret < 0) { wlr_log_errno(WLR_ERROR, "Failed to wait for session active: " "wl_event_loop_dispatch failed"); return NULL; } int64_t now = get_current_time_msec(); if (now >= started_at + WAIT_SESSION_TIMEOUT) { break; } timeout = started_at + WAIT_SESSION_TIMEOUT - now; } if (!session->active) { wlr_log(WLR_ERROR, "Timeout waiting session to become active"); return NULL; } } return session; #else wlr_log(WLR_ERROR, "Cannot create session: disabled at compile-time"); return NULL; #endif } int wlr_backend_get_drm_fd(struct wlr_backend *backend) { if (!backend->impl->get_drm_fd) { return -1; } return backend->impl->get_drm_fd(backend); } uint32_t backend_get_buffer_caps(struct wlr_backend *backend) { if (!backend->impl->get_buffer_caps) { return 0; } return backend->impl->get_buffer_caps(backend); } static size_t parse_outputs_env(const char *name) { const char *outputs_str = getenv(name); if (outputs_str == NULL) { return 1; } char *end; int outputs = (int)strtol(outputs_str, &end, 10); if (*end || outputs < 0) { wlr_log(WLR_ERROR, "%s specified with invalid integer, ignoring", name); return 1; } return outputs; } static struct wlr_backend *attempt_wl_backend(struct wl_display *display) { struct wlr_backend *backend = wlr_wl_backend_create(display, NULL); if (backend == NULL) { return NULL; } size_t outputs = parse_outputs_env("WLR_WL_OUTPUTS"); for (size_t i = 0; i < outputs; ++i) { wlr_wl_output_create(backend); } return backend; } static struct wlr_backend *attempt_x11_backend(struct wl_display *display, const char *x11_display) { #if WLR_HAS_X11_BACKEND struct wlr_backend *backend = wlr_x11_backend_create(display, x11_display); if (backend == NULL) { return NULL; } size_t outputs = parse_outputs_env("WLR_X11_OUTPUTS"); for (size_t i = 0; i < outputs; ++i) { wlr_x11_output_create(backend); } return backend; #else wlr_log(WLR_ERROR, "Cannot create X11 backend: disabled at compile-time"); return NULL; #endif } static struct wlr_backend *attempt_headless_backend( struct wl_display *display) { struct wlr_backend *backend = wlr_headless_backend_create(display); if (backend == NULL) { return NULL; } size_t outputs = parse_outputs_env("WLR_HEADLESS_OUTPUTS"); for (size_t i = 0; i < outputs; ++i) { wlr_headless_add_output(backend, 1280, 720); } return backend; } static bool attempt_drm_backend(struct wl_display *display, struct wlr_backend *backend, struct wlr_session *session) { #if WLR_HAS_DRM_BACKEND struct wlr_device *gpus[8]; ssize_t num_gpus = wlr_session_find_gpus(session, 8, gpus); if (num_gpus < 0) { wlr_log(WLR_ERROR, "Failed to find GPUs"); return false; } if (num_gpus == 0) { wlr_log(WLR_ERROR, "Found 0 GPUs, cannot create backend"); return false; } else { wlr_log(WLR_INFO, "Found %zu GPUs", num_gpus); } struct wlr_backend *primary_drm = NULL; for (size_t i = 0; i < (size_t)num_gpus; ++i) { struct wlr_backend *drm = wlr_drm_backend_create(display, session, gpus[i], primary_drm); if (!drm) { wlr_log(WLR_ERROR, "Failed to create DRM backend"); continue; } if (!primary_drm) { primary_drm = drm; } wlr_multi_backend_add(backend, drm); } if (!primary_drm) { wlr_log(WLR_ERROR, "Could not successfully create backend on any GPU"); return NULL; } if (getenv("WLR_DRM_DEVICES") == NULL) { drm_backend_monitor_create(backend, primary_drm, session); } return true; #else wlr_log(WLR_ERROR, "Cannot create DRM backend: disabled at compile-time"); return false; #endif } static struct wlr_backend *attempt_libinput_backend(struct wl_display *display, struct wlr_session *session) { #if WLR_HAS_LIBINPUT_BACKEND return wlr_libinput_backend_create(display, session); #else wlr_log(WLR_ERROR, "Cannot create libinput backend: disabled at compile-time"); return NULL; #endif } static bool attempt_backend_by_name(struct wl_display *display, struct wlr_backend *multi, char *name, struct wlr_session **session_ptr) { struct wlr_backend *backend = NULL; if (strcmp(name, "wayland") == 0) { backend = attempt_wl_backend(display); } else if (strcmp(name, "x11") == 0) { backend = attempt_x11_backend(display, NULL); } else if (strcmp(name, "headless") == 0) { backend = attempt_headless_backend(display); } else if (strcmp(name, "drm") == 0 || strcmp(name, "libinput") == 0) { // DRM and libinput need a session if (*session_ptr == NULL) { *session_ptr = session_create_and_wait(display); if (*session_ptr == NULL) { wlr_log(WLR_ERROR, "failed to start a session"); return false; } } if (strcmp(name, "libinput") == 0) { backend = attempt_libinput_backend(display, *session_ptr); } else { // attempt_drm_backend() adds the multi drm backends itself return attempt_drm_backend(display, multi, *session_ptr); } } else { wlr_log(WLR_ERROR, "unrecognized backend '%s'", name); return false; } if (backend == NULL) { return false; } return wlr_multi_backend_add(multi, backend); } struct wlr_backend *wlr_backend_autocreate(struct wl_display *display, struct wlr_session **session_ptr) { if (session_ptr != NULL) { *session_ptr = NULL; } struct wlr_session *session = NULL; struct wlr_backend *multi = wlr_multi_backend_create(display); if (!multi) { wlr_log(WLR_ERROR, "could not allocate multibackend"); return NULL; } char *names = getenv("WLR_BACKENDS"); if (names) { wlr_log(WLR_INFO, "Loading user-specified backends due to WLR_BACKENDS: %s", names); names = strdup(names); if (names == NULL) { wlr_log(WLR_ERROR, "allocation failed"); goto error; } char *saveptr; char *name = strtok_r(names, ",", &saveptr); while (name != NULL) { if (!attempt_backend_by_name(display, multi, name, &session)) { wlr_log(WLR_ERROR, "failed to add backend '%s'", name); free(names); goto error; } name = strtok_r(NULL, ",", &saveptr); } free(names); goto success; } if (getenv("WAYLAND_DISPLAY") || getenv("WAYLAND_SOCKET")) { struct wlr_backend *wl_backend = attempt_wl_backend(display); if (!wl_backend) { goto error; } wlr_multi_backend_add(multi, wl_backend); goto success; } const char *x11_display = getenv("DISPLAY"); if (x11_display) { struct wlr_backend *x11_backend = attempt_x11_backend(display, x11_display); if (!x11_backend) { goto error; } wlr_multi_backend_add(multi, x11_backend); goto success; } // Attempt DRM+libinput session = session_create_and_wait(display); if (!session) { wlr_log(WLR_ERROR, "Failed to start a DRM session"); goto error; } struct wlr_backend *libinput = attempt_libinput_backend(display, session); if (libinput) { wlr_multi_backend_add(multi, libinput); } else if (env_parse_bool("WLR_LIBINPUT_NO_DEVICES")) { wlr_log(WLR_INFO, "WLR_LIBINPUT_NO_DEVICES is set, " "starting without libinput backend"); } else { wlr_log(WLR_ERROR, "Failed to start libinput backend"); wlr_log(WLR_ERROR, "Set WLR_LIBINPUT_NO_DEVICES=1 to skip libinput"); goto error; } if (!attempt_drm_backend(display, multi, session)) { wlr_log(WLR_ERROR, "Failed to open any DRM device"); goto error; } success: if (session_ptr != NULL) { *session_ptr = session; } return multi; error: wlr_backend_destroy(multi); #if WLR_HAS_SESSION wlr_session_destroy(session); #endif return NULL; } wlroots-0.17.1/backend/drm/000077500000000000000000000000001454110342200154565ustar00rootroot00000000000000wlroots-0.17.1/backend/drm/atomic.c000066400000000000000000000254361454110342200171100ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include "backend/drm/drm.h" #include "backend/drm/iface.h" #include "backend/drm/util.h" static char *atomic_commit_flags_str(uint32_t flags) { const char *const l[] = { (flags & DRM_MODE_PAGE_FLIP_EVENT) ? "PAGE_FLIP_EVENT" : NULL, (flags & DRM_MODE_PAGE_FLIP_ASYNC) ? "PAGE_FLIP_ASYNC" : NULL, (flags & DRM_MODE_ATOMIC_TEST_ONLY) ? "ATOMIC_TEST_ONLY" : NULL, (flags & DRM_MODE_ATOMIC_NONBLOCK) ? "ATOMIC_NONBLOCK" : NULL, (flags & DRM_MODE_ATOMIC_ALLOW_MODESET) ? "ATOMIC_ALLOW_MODESET" : NULL, }; char *buf = NULL; size_t size = 0; FILE *f = open_memstream(&buf, &size); if (f == NULL) { return NULL; } for (size_t i = 0; i < sizeof(l) / sizeof(l[0]); i++) { if (l[i] == NULL) { continue; } if (ftell(f) > 0) { fprintf(f, " | "); } fprintf(f, "%s", l[i]); } if (ftell(f) == 0) { fprintf(f, "none"); } fclose(f); return buf; } struct atomic { drmModeAtomicReq *req; bool failed; }; static void atomic_begin(struct atomic *atom) { *atom = (struct atomic){0}; atom->req = drmModeAtomicAlloc(); if (!atom->req) { wlr_log_errno(WLR_ERROR, "Allocation failed"); atom->failed = true; return; } } static bool atomic_commit(struct atomic *atom, struct wlr_drm_connector *conn, struct wlr_drm_page_flip *page_flip, uint32_t flags) { struct wlr_drm_backend *drm = conn->backend; if (atom->failed) { return false; } int ret = drmModeAtomicCommit(drm->fd, atom->req, flags, page_flip); if (ret != 0) { wlr_drm_conn_log_errno(conn, (flags & DRM_MODE_ATOMIC_TEST_ONLY) ? WLR_DEBUG : WLR_ERROR, "Atomic commit failed"); char *flags_str = atomic_commit_flags_str(flags); wlr_log(WLR_DEBUG, "(Atomic commit flags: %s)", flags_str ? flags_str : ""); free(flags_str); return false; } return true; } static void atomic_finish(struct atomic *atom) { drmModeAtomicFree(atom->req); } static void atomic_add(struct atomic *atom, uint32_t id, uint32_t prop, uint64_t val) { if (!atom->failed && drmModeAtomicAddProperty(atom->req, id, prop, val) < 0) { wlr_log_errno(WLR_ERROR, "Failed to add atomic DRM property"); atom->failed = true; } } bool create_mode_blob(struct wlr_drm_backend *drm, struct wlr_drm_connector *conn, const struct wlr_drm_connector_state *state, uint32_t *blob_id) { if (!state->active) { *blob_id = 0; return true; } if (drmModeCreatePropertyBlob(drm->fd, &state->mode, sizeof(drmModeModeInfo), blob_id)) { wlr_log_errno(WLR_ERROR, "Unable to create mode property blob"); return false; } return true; } bool create_gamma_lut_blob(struct wlr_drm_backend *drm, size_t size, const uint16_t *lut, uint32_t *blob_id) { if (size == 0) { *blob_id = 0; return true; } struct drm_color_lut *gamma = malloc(size * sizeof(*gamma)); if (gamma == NULL) { wlr_log(WLR_ERROR, "Failed to allocate gamma table"); return false; } const uint16_t *r = lut; const uint16_t *g = lut + size; const uint16_t *b = lut + 2 * size; for (size_t i = 0; i < size; i++) { gamma[i].red = r[i]; gamma[i].green = g[i]; gamma[i].blue = b[i]; } if (drmModeCreatePropertyBlob(drm->fd, gamma, size * sizeof(*gamma), blob_id) != 0) { wlr_log_errno(WLR_ERROR, "Unable to create gamma LUT property blob"); free(gamma); return false; } free(gamma); return true; } bool create_fb_damage_clips_blob(struct wlr_drm_backend *drm, int width, int height, const pixman_region32_t *damage, uint32_t *blob_id) { if (!pixman_region32_not_empty(damage)) { *blob_id = 0; return true; } pixman_region32_t clipped; pixman_region32_init(&clipped); pixman_region32_intersect_rect(&clipped, damage, 0, 0, width, height); int rects_len; const pixman_box32_t *rects = pixman_region32_rectangles(&clipped, &rects_len); int ret = drmModeCreatePropertyBlob(drm->fd, rects, sizeof(*rects) * rects_len, blob_id); pixman_region32_fini(&clipped); if (ret != 0) { wlr_log_errno(WLR_ERROR, "Failed to create FB_DAMAGE_CLIPS property blob"); return false; } return true; } static uint64_t max_bpc_for_format(uint32_t format) { switch (format) { case DRM_FORMAT_XRGB2101010: case DRM_FORMAT_ARGB2101010: case DRM_FORMAT_XBGR2101010: case DRM_FORMAT_ABGR2101010: return 10; case DRM_FORMAT_XBGR16161616F: case DRM_FORMAT_ABGR16161616F: case DRM_FORMAT_XBGR16161616: case DRM_FORMAT_ABGR16161616: return 16; default: return 8; } } static uint64_t pick_max_bpc(struct wlr_drm_connector *conn, struct wlr_drm_fb *fb) { uint32_t format = DRM_FORMAT_INVALID; struct wlr_dmabuf_attributes attribs = {0}; if (wlr_buffer_get_dmabuf(fb->wlr_buf, &attribs)) { format = attribs.format; } uint64_t target_bpc = max_bpc_for_format(format); if (target_bpc < conn->max_bpc_bounds[0]) { target_bpc = conn->max_bpc_bounds[0]; } if (target_bpc > conn->max_bpc_bounds[1]) { target_bpc = conn->max_bpc_bounds[1]; } return target_bpc; } static void commit_blob(struct wlr_drm_backend *drm, uint32_t *current, uint32_t next) { if (*current == next) { return; } if (*current != 0) { drmModeDestroyPropertyBlob(drm->fd, *current); } *current = next; } static void rollback_blob(struct wlr_drm_backend *drm, uint32_t *current, uint32_t next) { if (*current == next) { return; } if (next != 0) { drmModeDestroyPropertyBlob(drm->fd, next); } } static void plane_disable(struct atomic *atom, struct wlr_drm_plane *plane) { uint32_t id = plane->id; const union wlr_drm_plane_props *props = &plane->props; atomic_add(atom, id, props->fb_id, 0); atomic_add(atom, id, props->crtc_id, 0); } static void set_plane_props(struct atomic *atom, struct wlr_drm_backend *drm, struct wlr_drm_plane *plane, struct wlr_drm_fb *fb, uint32_t crtc_id, int32_t x, int32_t y) { uint32_t id = plane->id; const union wlr_drm_plane_props *props = &plane->props; if (fb == NULL) { wlr_log(WLR_ERROR, "Failed to acquire FB for plane %"PRIu32, plane->id); atom->failed = true; return; } uint32_t width = fb->wlr_buf->width; uint32_t height = fb->wlr_buf->height; // The src_* properties are in 16.16 fixed point atomic_add(atom, id, props->src_x, 0); atomic_add(atom, id, props->src_y, 0); atomic_add(atom, id, props->src_w, (uint64_t)width << 16); atomic_add(atom, id, props->src_h, (uint64_t)height << 16); atomic_add(atom, id, props->crtc_w, width); atomic_add(atom, id, props->crtc_h, height); atomic_add(atom, id, props->fb_id, fb->id); atomic_add(atom, id, props->crtc_id, crtc_id); atomic_add(atom, id, props->crtc_x, (uint64_t)x); atomic_add(atom, id, props->crtc_y, (uint64_t)y); } static bool atomic_crtc_commit(struct wlr_drm_connector *conn, const struct wlr_drm_connector_state *state, struct wlr_drm_page_flip *page_flip, uint32_t flags, bool test_only) { struct wlr_drm_backend *drm = conn->backend; struct wlr_output *output = &conn->output; struct wlr_drm_crtc *crtc = conn->crtc; bool modeset = state->modeset; bool active = state->active; uint32_t mode_id = crtc->mode_id; if (modeset) { if (!create_mode_blob(drm, conn, state, &mode_id)) { return false; } } uint32_t gamma_lut = crtc->gamma_lut; if (state->base->committed & WLR_OUTPUT_STATE_GAMMA_LUT) { // Fallback to legacy gamma interface when gamma properties are not // available (can happen on older Intel GPUs that support gamma but not // degamma). if (crtc->props.gamma_lut == 0) { if (!drm_legacy_crtc_set_gamma(drm, crtc, state->base->gamma_lut_size, state->base->gamma_lut)) { return false; } } else { if (!create_gamma_lut_blob(drm, state->base->gamma_lut_size, state->base->gamma_lut, &gamma_lut)) { return false; } } } uint32_t fb_damage_clips = 0; if ((state->base->committed & WLR_OUTPUT_STATE_DAMAGE) && crtc->primary->props.fb_damage_clips != 0) { create_fb_damage_clips_blob(drm, state->primary_fb->wlr_buf->width, state->primary_fb->wlr_buf->height, &state->base->damage, &fb_damage_clips); } bool prev_vrr_enabled = output->adaptive_sync_status == WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED; bool vrr_enabled = prev_vrr_enabled; if ((state->base->committed & WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED)) { if (!drm_connector_supports_vrr(conn)) { return false; } vrr_enabled = state->base->adaptive_sync_enabled; } if (test_only) { flags |= DRM_MODE_ATOMIC_TEST_ONLY; } if (modeset) { flags |= DRM_MODE_ATOMIC_ALLOW_MODESET; } if (!test_only && state->nonblock) { flags |= DRM_MODE_ATOMIC_NONBLOCK; } struct atomic atom; atomic_begin(&atom); atomic_add(&atom, conn->id, conn->props.crtc_id, active ? crtc->id : 0); if (modeset && active && conn->props.link_status != 0) { atomic_add(&atom, conn->id, conn->props.link_status, DRM_MODE_LINK_STATUS_GOOD); } if (active && conn->props.content_type != 0) { atomic_add(&atom, conn->id, conn->props.content_type, DRM_MODE_CONTENT_TYPE_GRAPHICS); } if (modeset && active && conn->props.max_bpc != 0 && conn->max_bpc_bounds[1] != 0) { atomic_add(&atom, conn->id, conn->props.max_bpc, pick_max_bpc(conn, state->primary_fb)); } atomic_add(&atom, crtc->id, crtc->props.mode_id, mode_id); atomic_add(&atom, crtc->id, crtc->props.active, active); if (active) { if (crtc->props.gamma_lut != 0) { atomic_add(&atom, crtc->id, crtc->props.gamma_lut, gamma_lut); } if (crtc->props.vrr_enabled != 0) { atomic_add(&atom, crtc->id, crtc->props.vrr_enabled, vrr_enabled); } set_plane_props(&atom, drm, crtc->primary, state->primary_fb, crtc->id, 0, 0); if (crtc->primary->props.fb_damage_clips != 0) { atomic_add(&atom, crtc->primary->id, crtc->primary->props.fb_damage_clips, fb_damage_clips); } if (crtc->cursor) { if (drm_connector_is_cursor_visible(conn)) { set_plane_props(&atom, drm, crtc->cursor, get_next_cursor_fb(conn), crtc->id, conn->cursor_x, conn->cursor_y); } else { plane_disable(&atom, crtc->cursor); } } } else { plane_disable(&atom, crtc->primary); if (crtc->cursor) { plane_disable(&atom, crtc->cursor); } } bool ok = atomic_commit(&atom, conn, page_flip, flags); atomic_finish(&atom); if (ok && !test_only) { commit_blob(drm, &crtc->mode_id, mode_id); commit_blob(drm, &crtc->gamma_lut, gamma_lut); if (vrr_enabled != prev_vrr_enabled) { output->adaptive_sync_status = vrr_enabled ? WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED : WLR_OUTPUT_ADAPTIVE_SYNC_DISABLED; wlr_drm_conn_log(conn, WLR_DEBUG, "VRR %s", vrr_enabled ? "enabled" : "disabled"); } } else { rollback_blob(drm, &crtc->mode_id, mode_id); rollback_blob(drm, &crtc->gamma_lut, gamma_lut); } if (fb_damage_clips != 0 && drmModeDestroyPropertyBlob(drm->fd, fb_damage_clips) != 0) { wlr_log_errno(WLR_ERROR, "Failed to destroy FB_DAMAGE_CLIPS property blob"); } return ok; } const struct wlr_drm_interface atomic_iface = { .crtc_commit = atomic_crtc_commit, }; wlroots-0.17.1/backend/drm/backend.c000066400000000000000000000217701454110342200172200ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include "backend/drm/drm.h" struct wlr_drm_backend *get_drm_backend_from_backend( struct wlr_backend *wlr_backend) { assert(wlr_backend_is_drm(wlr_backend)); struct wlr_drm_backend *backend = wl_container_of(wlr_backend, backend, backend); return backend; } static bool backend_start(struct wlr_backend *backend) { struct wlr_drm_backend *drm = get_drm_backend_from_backend(backend); scan_drm_connectors(drm, NULL); return true; } static void backend_destroy(struct wlr_backend *backend) { if (!backend) { return; } struct wlr_drm_backend *drm = get_drm_backend_from_backend(backend); struct wlr_drm_connector *conn, *next; wl_list_for_each_safe(conn, next, &drm->connectors, link) { conn->crtc = NULL; // leave CRTCs on when shutting down destroy_drm_connector(conn); } struct wlr_drm_page_flip *page_flip, *page_flip_tmp; wl_list_for_each_safe(page_flip, page_flip_tmp, &drm->page_flips, link) { drm_page_flip_destroy(page_flip); } wlr_backend_finish(backend); wl_list_remove(&drm->display_destroy.link); wl_list_remove(&drm->session_destroy.link); wl_list_remove(&drm->session_active.link); wl_list_remove(&drm->parent_destroy.link); wl_list_remove(&drm->dev_change.link); wl_list_remove(&drm->dev_remove.link); if (drm->parent) { finish_drm_renderer(&drm->mgpu_renderer); } finish_drm_resources(drm); struct wlr_drm_fb *fb, *fb_tmp; wl_list_for_each_safe(fb, fb_tmp, &drm->fbs, link) { drm_fb_destroy(fb); } free(drm->name); wlr_session_close_file(drm->session, drm->dev); wl_event_source_remove(drm->drm_event); free(drm); } static int backend_get_drm_fd(struct wlr_backend *backend) { struct wlr_drm_backend *drm = get_drm_backend_from_backend(backend); if (drm->parent) { return drm->parent->fd; } else { return drm->fd; } } static uint32_t drm_backend_get_buffer_caps(struct wlr_backend *backend) { return WLR_BUFFER_CAP_DMABUF; } static const struct wlr_backend_impl backend_impl = { .start = backend_start, .destroy = backend_destroy, .get_drm_fd = backend_get_drm_fd, .get_buffer_caps = drm_backend_get_buffer_caps, }; bool wlr_backend_is_drm(struct wlr_backend *b) { return b->impl == &backend_impl; } static void handle_session_active(struct wl_listener *listener, void *data) { struct wlr_drm_backend *drm = wl_container_of(listener, drm, session_active); struct wlr_session *session = drm->session; if (session->active) { wlr_log(WLR_INFO, "DRM fd resumed"); scan_drm_connectors(drm, NULL); // The previous DRM master leaves KMS in an undefined state. We need // to restore out own state, but be careful to avoid invalid // configurations. The connector/CRTC mapping may have changed, so // first disable all CRTCs, then light up the ones we were using // before the VT switch. // TODO: use the atomic API to improve restoration after a VT switch for (size_t i = 0; i < drm->num_crtcs; i++) { struct wlr_drm_crtc *crtc = &drm->crtcs[i]; if (drmModeSetCrtc(drm->fd, crtc->id, 0, 0, 0, NULL, 0, NULL) != 0) { wlr_log_errno(WLR_ERROR, "Failed to disable CRTC %"PRIu32" after VT switch", crtc->id); } } struct wlr_drm_connector *conn; wl_list_for_each(conn, &drm->connectors, link) { bool enabled = conn->status != DRM_MODE_DISCONNECTED && conn->output.enabled; struct wlr_output_state state; wlr_output_state_init(&state); wlr_output_state_set_enabled(&state, enabled); if (enabled) { if (conn->output.current_mode != NULL) { wlr_output_state_set_mode(&state, conn->output.current_mode); } else { wlr_output_state_set_custom_mode(&state, conn->output.width, conn->output.height, conn->output.refresh); } } if (!drm_connector_commit_state(conn, &state)) { wlr_drm_conn_log(conn, WLR_ERROR, "Failed to restore state after VT switch"); } wlr_output_state_finish(&state); } } else { wlr_log(WLR_INFO, "DRM fd paused"); } } static void handle_dev_change(struct wl_listener *listener, void *data) { struct wlr_drm_backend *drm = wl_container_of(listener, drm, dev_change); struct wlr_device_change_event *change = data; if (!drm->session->active) { return; } switch (change->type) { case WLR_DEVICE_HOTPLUG: wlr_log(WLR_DEBUG, "Received hotplug event for %s", drm->name); scan_drm_connectors(drm, &change->hotplug); break; case WLR_DEVICE_LEASE: wlr_log(WLR_DEBUG, "Received lease event for %s", drm->name); scan_drm_leases(drm); break; default: wlr_log(WLR_DEBUG, "Received unknown change event for %s", drm->name); } } static void handle_dev_remove(struct wl_listener *listener, void *data) { struct wlr_drm_backend *drm = wl_container_of(listener, drm, dev_remove); wlr_log(WLR_INFO, "Destroying DRM backend for %s", drm->name); backend_destroy(&drm->backend); } static void handle_session_destroy(struct wl_listener *listener, void *data) { struct wlr_drm_backend *drm = wl_container_of(listener, drm, session_destroy); backend_destroy(&drm->backend); } static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_drm_backend *drm = wl_container_of(listener, drm, display_destroy); backend_destroy(&drm->backend); } static void handle_parent_destroy(struct wl_listener *listener, void *data) { struct wlr_drm_backend *drm = wl_container_of(listener, drm, parent_destroy); backend_destroy(&drm->backend); } struct wlr_backend *wlr_drm_backend_create(struct wl_display *display, struct wlr_session *session, struct wlr_device *dev, struct wlr_backend *parent) { assert(display && session && dev); assert(!parent || wlr_backend_is_drm(parent)); char *name = drmGetDeviceNameFromFd2(dev->fd); drmVersion *version = drmGetVersion(dev->fd); wlr_log(WLR_INFO, "Initializing DRM backend for %s (%s)", name, version->name); drmFreeVersion(version); struct wlr_drm_backend *drm = calloc(1, sizeof(*drm)); if (!drm) { wlr_log_errno(WLR_ERROR, "Allocation failed"); return NULL; } wlr_backend_init(&drm->backend, &backend_impl); drm->session = session; wl_list_init(&drm->fbs); wl_list_init(&drm->connectors); wl_list_init(&drm->page_flips); drm->dev = dev; drm->fd = dev->fd; drm->name = name; if (parent != NULL) { drm->parent = get_drm_backend_from_backend(parent); drm->parent_destroy.notify = handle_parent_destroy; wl_signal_add(&parent->events.destroy, &drm->parent_destroy); } else { wl_list_init(&drm->parent_destroy.link); } drm->dev_change.notify = handle_dev_change; wl_signal_add(&dev->events.change, &drm->dev_change); drm->dev_remove.notify = handle_dev_remove; wl_signal_add(&dev->events.remove, &drm->dev_remove); drm->display = display; struct wl_event_loop *event_loop = wl_display_get_event_loop(display); drm->drm_event = wl_event_loop_add_fd(event_loop, drm->fd, WL_EVENT_READABLE, handle_drm_event, drm); if (!drm->drm_event) { wlr_log(WLR_ERROR, "Failed to create DRM event source"); goto error_fd; } drm->session_active.notify = handle_session_active; wl_signal_add(&session->events.active, &drm->session_active); if (!check_drm_features(drm)) { goto error_event; } if (!init_drm_resources(drm)) { goto error_event; } if (drm->parent) { if (!init_drm_renderer(drm, &drm->mgpu_renderer)) { wlr_log(WLR_ERROR, "Failed to initialize renderer"); goto error_resources; } // We'll perform a multi-GPU copy for all submitted buffers, we need // to be able to texture from them struct wlr_renderer *renderer = drm->mgpu_renderer.wlr_rend; const struct wlr_drm_format_set *texture_formats = wlr_renderer_get_dmabuf_texture_formats(renderer); if (texture_formats == NULL) { wlr_log(WLR_ERROR, "Failed to query renderer texture formats"); goto error_mgpu_renderer; } // Forbid implicit modifiers, because their meaning changes from one // GPU to another. for (size_t i = 0; i < texture_formats->len; i++) { const struct wlr_drm_format *fmt = &texture_formats->formats[i]; for (size_t j = 0; j < fmt->len; j++) { uint64_t mod = fmt->modifiers[j]; if (mod == DRM_FORMAT_MOD_INVALID) { continue; } wlr_drm_format_set_add(&drm->mgpu_formats, fmt->format, mod); } } } drm->session_destroy.notify = handle_session_destroy; wl_signal_add(&session->events.destroy, &drm->session_destroy); drm->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &drm->display_destroy); return &drm->backend; error_mgpu_renderer: finish_drm_renderer(&drm->mgpu_renderer); error_resources: finish_drm_resources(drm); error_event: wl_list_remove(&drm->session_active.link); wl_event_source_remove(drm->drm_event); error_fd: wl_list_remove(&drm->dev_remove.link); wl_list_remove(&drm->dev_change.link); wl_list_remove(&drm->parent_destroy.link); wlr_session_close_file(drm->session, dev); free(drm); return NULL; } wlroots-0.17.1/backend/drm/drm.c000066400000000000000000001474321454110342200164170ustar00rootroot00000000000000#define _XOPEN_SOURCE 700 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "backend/drm/drm.h" #include "backend/drm/iface.h" #include "backend/drm/util.h" #include "render/pixel_format.h" #include "render/drm_format_set.h" #include "render/wlr_renderer.h" #include "util/env.h" #include "config.h" #if HAVE_LIBLIFTOFF #include #endif // Output state which needs a KMS commit to be applied static const uint32_t COMMIT_OUTPUT_STATE = WLR_OUTPUT_STATE_BUFFER | WLR_OUTPUT_STATE_MODE | WLR_OUTPUT_STATE_ENABLED | WLR_OUTPUT_STATE_GAMMA_LUT | WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED | WLR_OUTPUT_STATE_LAYERS; static const uint32_t SUPPORTED_OUTPUT_STATE = WLR_OUTPUT_STATE_BACKEND_OPTIONAL | COMMIT_OUTPUT_STATE; bool check_drm_features(struct wlr_drm_backend *drm) { if (drmGetCap(drm->fd, DRM_CAP_CURSOR_WIDTH, &drm->cursor_width)) { drm->cursor_width = 64; } if (drmGetCap(drm->fd, DRM_CAP_CURSOR_HEIGHT, &drm->cursor_height)) { drm->cursor_height = 64; } uint64_t cap; if (drmGetCap(drm->fd, DRM_CAP_PRIME, &cap) || !(cap & DRM_PRIME_CAP_IMPORT)) { wlr_log(WLR_ERROR, "PRIME import not supported"); return false; } if (drm->parent) { if (drmGetCap(drm->parent->fd, DRM_CAP_PRIME, &cap) || !(cap & DRM_PRIME_CAP_EXPORT)) { wlr_log(WLR_ERROR, "PRIME export not supported on primary GPU"); return false; } } if (drmSetClientCap(drm->fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1)) { wlr_log(WLR_ERROR, "DRM universal planes unsupported"); return false; } if (drmGetCap(drm->fd, DRM_CAP_CRTC_IN_VBLANK_EVENT, &cap) || !cap) { wlr_log(WLR_ERROR, "DRM_CRTC_IN_VBLANK_EVENT unsupported"); return false; } if (drmGetCap(drm->fd, DRM_CAP_TIMESTAMP_MONOTONIC, &cap) || !cap) { wlr_log(WLR_ERROR, "DRM_CAP_TIMESTAMP_MONOTONIC unsupported"); return false; } if (env_parse_bool("WLR_DRM_FORCE_LIBLIFTOFF")) { #if HAVE_LIBLIFTOFF wlr_log(WLR_INFO, "WLR_DRM_FORCE_LIBLIFTOFF set, forcing libliftoff interface"); if (drmSetClientCap(drm->fd, DRM_CLIENT_CAP_ATOMIC, 1) != 0) { wlr_log_errno(WLR_ERROR, "drmSetClientCap(ATOMIC) failed"); return false; } drm->iface = &liftoff_iface; #else wlr_log(WLR_ERROR, "libliftoff interface not available"); return false; #endif } else if (env_parse_bool("WLR_DRM_NO_ATOMIC")) { wlr_log(WLR_DEBUG, "WLR_DRM_NO_ATOMIC set, forcing legacy DRM interface"); drm->iface = &legacy_iface; } else if (drmSetClientCap(drm->fd, DRM_CLIENT_CAP_ATOMIC, 1)) { wlr_log(WLR_DEBUG, "Atomic modesetting unsupported, using legacy DRM interface"); drm->iface = &legacy_iface; } else { wlr_log(WLR_DEBUG, "Using atomic DRM interface"); drm->iface = &atomic_iface; } if (drm->iface == &legacy_iface) { drm->supports_tearing_page_flips = drmGetCap(drm->fd, DRM_CAP_ASYNC_PAGE_FLIP, &cap) == 0 && cap == 1; } if (env_parse_bool("WLR_DRM_NO_MODIFIERS")) { wlr_log(WLR_DEBUG, "WLR_DRM_NO_MODIFIERS set, disabling modifiers"); } else { int ret = drmGetCap(drm->fd, DRM_CAP_ADDFB2_MODIFIERS, &cap); drm->addfb2_modifiers = ret == 0 && cap == 1; wlr_log(WLR_DEBUG, "ADDFB2 modifiers %s", drm->addfb2_modifiers ? "supported" : "unsupported"); } return true; } static bool init_plane(struct wlr_drm_backend *drm, struct wlr_drm_plane *p, const drmModePlane *drm_plane) { uint32_t id = drm_plane->plane_id; union wlr_drm_plane_props props = {0}; if (!get_drm_plane_props(drm->fd, id, &props)) { return false; } uint64_t type; if (!get_drm_prop(drm->fd, id, props.type, &type)) { return false; } p->type = type; p->id = drm_plane->plane_id; p->props = props; p->initial_crtc_id = drm_plane->crtc_id; for (size_t i = 0; i < drm_plane->count_formats; ++i) { // Force a LINEAR layout for the cursor if the driver doesn't support // modifiers wlr_drm_format_set_add(&p->formats, drm_plane->formats[i], DRM_FORMAT_MOD_LINEAR); if (type != DRM_PLANE_TYPE_CURSOR) { wlr_drm_format_set_add(&p->formats, drm_plane->formats[i], DRM_FORMAT_MOD_INVALID); } } if (p->props.in_formats && drm->addfb2_modifiers) { uint64_t blob_id; if (!get_drm_prop(drm->fd, p->id, p->props.in_formats, &blob_id)) { wlr_log(WLR_ERROR, "Failed to read IN_FORMATS property"); goto error; } drmModePropertyBlobRes *blob = drmModeGetPropertyBlob(drm->fd, blob_id); if (!blob) { wlr_log(WLR_ERROR, "Failed to read IN_FORMATS blob"); goto error; } drmModeFormatModifierIterator iter = {0}; while (drmModeFormatModifierBlobIterNext(blob, &iter)) { wlr_drm_format_set_add(&p->formats, iter.fmt, iter.mod); } drmModeFreePropertyBlob(blob); } assert(drm->num_crtcs <= 32); for (size_t j = 0; j < drm->num_crtcs; j++) { uint32_t crtc_bit = 1 << j; if ((drm_plane->possible_crtcs & crtc_bit) == 0) { continue; } struct wlr_drm_crtc *crtc = &drm->crtcs[j]; if (type == DRM_PLANE_TYPE_PRIMARY && !crtc->primary) { crtc->primary = p; break; } if (type == DRM_PLANE_TYPE_CURSOR && !crtc->cursor) { crtc->cursor = p; break; } } return true; error: free(p); return false; } static bool init_planes(struct wlr_drm_backend *drm) { drmModePlaneRes *plane_res = drmModeGetPlaneResources(drm->fd); if (!plane_res) { wlr_log_errno(WLR_ERROR, "Failed to get DRM plane resources"); return false; } wlr_log(WLR_INFO, "Found %"PRIu32" DRM planes", plane_res->count_planes); drm->num_planes = plane_res->count_planes; drm->planes = calloc(drm->num_planes, sizeof(*drm->planes)); if (drm->planes == NULL) { wlr_log_errno(WLR_ERROR, "Allocation failed"); goto error; } for (uint32_t i = 0; i < plane_res->count_planes; ++i) { uint32_t id = plane_res->planes[i]; drmModePlane *drm_plane = drmModeGetPlane(drm->fd, id); if (!drm_plane) { wlr_log_errno(WLR_ERROR, "Failed to get DRM plane"); goto error; } struct wlr_drm_plane *plane = &drm->planes[i]; if (!init_plane(drm, plane, drm_plane)) { goto error; } drmModeFreePlane(drm_plane); } drmModeFreePlaneResources(plane_res); return true; error: free(drm->planes); drmModeFreePlaneResources(plane_res); return false; } bool init_drm_resources(struct wlr_drm_backend *drm) { drmModeRes *res = drmModeGetResources(drm->fd); if (!res) { wlr_log_errno(WLR_ERROR, "Failed to get DRM resources"); return false; } wlr_log(WLR_INFO, "Found %d DRM CRTCs", res->count_crtcs); drm->num_crtcs = res->count_crtcs; if (drm->num_crtcs == 0) { drmModeFreeResources(res); return true; } drm->crtcs = calloc(drm->num_crtcs, sizeof(drm->crtcs[0])); if (!drm->crtcs) { wlr_log_errno(WLR_ERROR, "Allocation failed"); goto error_res; } for (size_t i = 0; i < drm->num_crtcs; ++i) { struct wlr_drm_crtc *crtc = &drm->crtcs[i]; crtc->id = res->crtcs[i]; drmModeCrtc *drm_crtc = drmModeGetCrtc(drm->fd, crtc->id); if (drm_crtc == NULL) { wlr_log_errno(WLR_ERROR, "drmModeGetCrtc failed"); goto error_res; } crtc->legacy_gamma_size = drm_crtc->gamma_size; drmModeFreeCrtc(drm_crtc); if (!get_drm_crtc_props(drm->fd, crtc->id, &crtc->props)) { goto error_crtcs; } wl_list_init(&crtc->layers); } if (!init_planes(drm)) { goto error_crtcs; } if (drm->iface->init != NULL && !drm->iface->init(drm)) { goto error_crtcs; } drmModeFreeResources(res); return true; error_crtcs: free(drm->crtcs); error_res: drmModeFreeResources(res); return false; } void finish_drm_resources(struct wlr_drm_backend *drm) { if (!drm) { return; } if (drm->iface->finish != NULL) { drm->iface->finish(drm); } for (size_t i = 0; i < drm->num_crtcs; ++i) { struct wlr_drm_crtc *crtc = &drm->crtcs[i]; if (crtc->mode_id) { drmModeDestroyPropertyBlob(drm->fd, crtc->mode_id); } if (crtc->gamma_lut) { drmModeDestroyPropertyBlob(drm->fd, crtc->gamma_lut); } } free(drm->crtcs); for (size_t i = 0; i < drm->num_planes; ++i) { struct wlr_drm_plane *plane = &drm->planes[i]; drm_plane_finish_surface(plane); wlr_drm_format_set_finish(&plane->formats); } free(drm->planes); } static struct wlr_drm_connector *get_drm_connector_from_output( struct wlr_output *wlr_output) { assert(wlr_output_is_drm(wlr_output)); struct wlr_drm_connector *conn = wl_container_of(wlr_output, conn, output); return conn; } static void layer_handle_addon_destroy(struct wlr_addon *addon) { struct wlr_drm_layer *layer = wl_container_of(addon, layer, addon); wlr_addon_finish(&layer->addon); wl_list_remove(&layer->link); #if HAVE_LIBLIFTOFF liftoff_layer_destroy(layer->liftoff); #endif drm_fb_clear(&layer->pending_fb); drm_fb_clear(&layer->queued_fb); drm_fb_clear(&layer->current_fb); free(layer->candidate_planes); free(layer); } const struct wlr_addon_interface layer_impl = { .name = "wlr_drm_layer", .destroy = layer_handle_addon_destroy, }; struct wlr_drm_layer *get_drm_layer(struct wlr_drm_backend *drm, struct wlr_output_layer *wlr_layer) { struct wlr_addon *addon = wlr_addon_find(&wlr_layer->addons, drm, &layer_impl); assert(addon != NULL); struct wlr_drm_layer *layer = wl_container_of(addon, layer, addon); return layer; } static struct wlr_drm_layer *get_or_create_layer(struct wlr_drm_backend *drm, struct wlr_drm_crtc *crtc, struct wlr_output_layer *wlr_layer) { struct wlr_drm_layer *layer; struct wlr_addon *addon = wlr_addon_find(&wlr_layer->addons, drm, &layer_impl); if (addon != NULL) { layer = wl_container_of(addon, layer, addon); return layer; } layer = calloc(1, sizeof(*layer)); if (layer == NULL) { return NULL; } layer->wlr = wlr_layer; #if HAVE_LIBLIFTOFF layer->liftoff = liftoff_layer_create(crtc->liftoff); if (layer->liftoff == NULL) { free(layer); return NULL; } #else abort(); // unreachable #endif layer->candidate_planes = calloc(sizeof(bool), drm->num_planes); if (layer->candidate_planes == NULL) { #if HAVE_LIBLIFTOFF liftoff_layer_destroy(layer->liftoff); #endif free(layer); return NULL; } wlr_addon_init(&layer->addon, &wlr_layer->addons, drm, &layer_impl); wl_list_insert(&crtc->layers, &layer->link); return layer; } static void drm_connector_set_pending_page_flip(struct wlr_drm_connector *conn, struct wlr_drm_page_flip *page_flip) { if (conn->pending_page_flip != NULL) { conn->pending_page_flip->conn = NULL; } conn->pending_page_flip = page_flip; } void drm_page_flip_destroy(struct wlr_drm_page_flip *page_flip) { if (!page_flip) { return; } wl_list_remove(&page_flip->link); free(page_flip); } static bool drm_crtc_commit(struct wlr_drm_connector *conn, const struct wlr_drm_connector_state *state, uint32_t flags, bool test_only) { // Disallow atomic-only flags assert((flags & ~DRM_MODE_PAGE_FLIP_FLAGS) == 0); struct wlr_drm_page_flip *page_flip = NULL; if (flags & DRM_MODE_PAGE_FLIP_EVENT) { page_flip = calloc(1, sizeof(*page_flip)); if (page_flip == NULL) { return false; } page_flip->conn = conn; wl_list_insert(&conn->backend->page_flips, &page_flip->link); } struct wlr_drm_backend *drm = conn->backend; struct wlr_drm_crtc *crtc = conn->crtc; bool ok = drm->iface->crtc_commit(conn, state, page_flip, flags, test_only); if (ok && !test_only) { drm_fb_clear(&crtc->primary->queued_fb); if (state->primary_fb != NULL) { crtc->primary->queued_fb = drm_fb_lock(state->primary_fb); } if (crtc->cursor != NULL) { drm_fb_move(&crtc->cursor->queued_fb, &conn->cursor_pending_fb); } struct wlr_drm_layer *layer; wl_list_for_each(layer, &crtc->layers, link) { drm_fb_move(&layer->queued_fb, &layer->pending_fb); } drm_connector_set_pending_page_flip(conn, page_flip); } else { // The set_cursor() hook is a bit special: it's not really synchronized // to commit() or test(). Once set_cursor() returns true, the new // cursor is effectively committed. So don't roll it back here, or we // risk ending up in a state where we don't have a cursor FB but // wlr_drm_connector.cursor_enabled is true. // TODO: fix our output interface to avoid this issue. struct wlr_drm_layer *layer; wl_list_for_each(layer, &crtc->layers, link) { drm_fb_clear(&layer->pending_fb); } drm_page_flip_destroy(page_flip); } return ok; } static void drm_connector_state_init(struct wlr_drm_connector_state *state, struct wlr_drm_connector *conn, const struct wlr_output_state *base) { *state = (struct wlr_drm_connector_state){ .base = base, .modeset = base->allow_reconfiguration, .active = (base->committed & WLR_OUTPUT_STATE_ENABLED) ? base->enabled : conn->output.enabled, // The wlr_output API requires non-modeset commits with a new buffer to // wait for the frame event. However compositors often perform // non-modesets commits without a new buffer without waiting for the // frame event. In that case we need to make the KMS commit blocking, // otherwise the kernel will error out with EBUSY. .nonblock = !base->allow_reconfiguration && (base->committed & WLR_OUTPUT_STATE_BUFFER), }; struct wlr_output_mode *mode = conn->output.current_mode; int32_t width = conn->output.width; int32_t height = conn->output.height; int32_t refresh = conn->output.refresh; if (base->committed & WLR_OUTPUT_STATE_MODE) { switch (base->mode_type) { case WLR_OUTPUT_STATE_MODE_FIXED:; mode = base->mode; break; case WLR_OUTPUT_STATE_MODE_CUSTOM: mode = NULL; width = base->custom_mode.width; height = base->custom_mode.height; refresh = base->custom_mode.refresh; break; } } if (mode) { struct wlr_drm_mode *drm_mode = wl_container_of(mode, drm_mode, wlr_mode); state->mode = drm_mode->drm_mode; } else { generate_cvt_mode(&state->mode, width, height, (float)refresh / 1000); state->mode.type = DRM_MODE_TYPE_USERDEF; } if (conn->crtc != NULL) { struct wlr_drm_plane *primary = conn->crtc->primary; if (primary->queued_fb != NULL) { state->primary_fb = drm_fb_lock(primary->queued_fb); } else if (primary->current_fb != NULL) { state->primary_fb = drm_fb_lock(primary->current_fb); } } } static void drm_connector_state_finish(struct wlr_drm_connector_state *state) { drm_fb_clear(&state->primary_fb); } static bool drm_connector_state_update_primary_fb(struct wlr_drm_connector *conn, struct wlr_drm_connector_state *state) { struct wlr_drm_backend *drm = conn->backend; assert(state->base->committed & WLR_OUTPUT_STATE_BUFFER); struct wlr_drm_crtc *crtc = conn->crtc; assert(crtc != NULL); struct wlr_drm_plane *plane = crtc->primary; struct wlr_buffer *source_buf = state->base->buffer; struct wlr_buffer *local_buf; if (drm->parent) { struct wlr_drm_format format = {0}; if (!drm_plane_pick_render_format(plane, &format, &drm->mgpu_renderer)) { wlr_log(WLR_ERROR, "Failed to pick primary plane format"); return false; } // TODO: fallback to modifier-less buffer allocation bool ok = init_drm_surface(&plane->mgpu_surf, &drm->mgpu_renderer, source_buf->width, source_buf->height, &format); wlr_drm_format_finish(&format); if (!ok) { return false; } local_buf = drm_surface_blit(&plane->mgpu_surf, source_buf); if (local_buf == NULL) { return false; } } else { local_buf = wlr_buffer_lock(source_buf); } bool ok = drm_fb_import(&state->primary_fb, drm, local_buf, &plane->formats); wlr_buffer_unlock(local_buf); if (!ok) { wlr_drm_conn_log(conn, WLR_DEBUG, "Failed to import buffer for scan-out"); return false; } return true; } static bool drm_connector_set_pending_layer_fbs(struct wlr_drm_connector *conn, const struct wlr_output_state *state) { struct wlr_drm_backend *drm = conn->backend; struct wlr_drm_crtc *crtc = conn->crtc; if (!crtc || drm->parent) { return false; } if (!crtc->liftoff) { return true; // libliftoff is disabled } assert(state->committed & WLR_OUTPUT_STATE_LAYERS); for (size_t i = 0; i < state->layers_len; i++) { struct wlr_output_layer_state *layer_state = &state->layers[i]; struct wlr_drm_layer *layer = get_or_create_layer(drm, crtc, layer_state->layer); if (!layer) { return false; } if (layer_state->buffer != NULL) { drm_fb_import(&layer->pending_fb, drm, layer_state->buffer, NULL); } else { drm_fb_clear(&layer->pending_fb); } } return true; } static bool drm_connector_alloc_crtc(struct wlr_drm_connector *conn); static bool drm_connector_test(struct wlr_output *output, const struct wlr_output_state *state) { struct wlr_drm_connector *conn = get_drm_connector_from_output(output); if (!conn->backend->session->active) { return false; } uint32_t unsupported = state->committed & ~SUPPORTED_OUTPUT_STATE; if (unsupported != 0) { wlr_log(WLR_DEBUG, "Unsupported output state fields: 0x%"PRIx32, unsupported); return false; } if ((state->committed & COMMIT_OUTPUT_STATE) == 0) { // This commit doesn't change the KMS state return true; } if ((state->committed & WLR_OUTPUT_STATE_ENABLED) && state->enabled) { if (output->current_mode == NULL && !(state->committed & WLR_OUTPUT_STATE_MODE)) { wlr_drm_conn_log(conn, WLR_DEBUG, "Can't enable an output without a mode"); return false; } } bool ok = false; struct wlr_drm_connector_state pending = {0}; drm_connector_state_init(&pending, conn, state); if (pending.active) { if ((state->committed & (WLR_OUTPUT_STATE_ENABLED | WLR_OUTPUT_STATE_MODE)) && !(state->committed & WLR_OUTPUT_STATE_BUFFER)) { wlr_drm_conn_log(conn, WLR_DEBUG, "Can't enable an output without a buffer"); goto out; } if (!drm_connector_alloc_crtc(conn)) { wlr_drm_conn_log(conn, WLR_DEBUG, "No CRTC available for this connector"); goto out; } } if ((state->committed & WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED) && state->adaptive_sync_enabled && !drm_connector_supports_vrr(conn)) { goto out; } if (conn->backend->parent) { // If we're running as a secondary GPU, we can't perform an atomic // commit without blitting a buffer. ok = true; goto out; } if (!conn->crtc) { // If the output is disabled, we don't have a crtc even after // reallocation ok = true; goto out; } if (state->committed & WLR_OUTPUT_STATE_BUFFER) { if (!drm_connector_state_update_primary_fb(conn, &pending)) { goto out; } if (pending.base->tearing_page_flip && !conn->backend->supports_tearing_page_flips) { wlr_log(WLR_ERROR, "Attempted to submit a tearing page flip to an unsupported backend!"); goto out; } } if (state->committed & WLR_OUTPUT_STATE_LAYERS) { if (!drm_connector_set_pending_layer_fbs(conn, pending.base)) { return false; } } ok = drm_crtc_commit(conn, &pending, 0, true); out: drm_connector_state_finish(&pending); return ok; } bool drm_connector_supports_vrr(struct wlr_drm_connector *conn) { struct wlr_drm_backend *drm = conn->backend; struct wlr_drm_crtc *crtc = conn->crtc; if (!crtc) { return false; } uint64_t vrr_capable; if (conn->props.vrr_capable == 0 || !get_drm_prop(drm->fd, conn->id, conn->props.vrr_capable, &vrr_capable) || !vrr_capable) { wlr_drm_conn_log(conn, WLR_DEBUG, "Failed to enable adaptive sync: " "connector doesn't support VRR"); return false; } if (crtc->props.vrr_enabled == 0) { wlr_drm_conn_log(conn, WLR_DEBUG, "Failed to enable adaptive sync: " "CRTC %"PRIu32" doesn't support VRR", crtc->id); return false; } return true; } bool drm_connector_commit_state(struct wlr_drm_connector *conn, const struct wlr_output_state *base) { struct wlr_drm_backend *drm = conn->backend; if (!drm->session->active) { return false; } bool ok = false; struct wlr_drm_connector_state pending = {0}; drm_connector_state_init(&pending, conn, base); if (!pending.active && conn->crtc == NULL) { // Disabling an already-disabled connector ok = true; goto out; } if (pending.active) { if (!drm_connector_alloc_crtc(conn)) { wlr_drm_conn_log(conn, WLR_ERROR, "No CRTC available for this connector"); goto out; } } if (pending.base->committed & WLR_OUTPUT_STATE_BUFFER) { if (!drm_connector_state_update_primary_fb(conn, &pending)) { goto out; } } if (pending.base->committed & WLR_OUTPUT_STATE_LAYERS) { if (!drm_connector_set_pending_layer_fbs(conn, pending.base)) { return false; } } if (pending.modeset) { if (pending.active) { wlr_drm_conn_log(conn, WLR_INFO, "Modesetting with %dx%d @ %.3f Hz", pending.mode.hdisplay, pending.mode.vdisplay, (float)calculate_refresh_rate(&pending.mode) / 1000); } else { wlr_drm_conn_log(conn, WLR_INFO, "Turning off"); } } // wlr_drm_interface.crtc_commit will perform either a non-blocking // page-flip, either a blocking modeset. When performing a blocking modeset // we'll wait for all queued page-flips to complete, so we don't need this // safeguard. if (pending.nonblock && conn->pending_page_flip != NULL) { wlr_drm_conn_log(conn, WLR_ERROR, "Failed to page-flip output: " "a page-flip is already pending"); goto out; } uint32_t flags = 0; if (pending.active) { flags |= DRM_MODE_PAGE_FLIP_EVENT; } if (pending.base->tearing_page_flip) { flags |= DRM_MODE_PAGE_FLIP_ASYNC; } ok = drm_crtc_commit(conn, &pending, flags, false); if (!ok) { goto out; } if (!pending.active) { drm_plane_finish_surface(conn->crtc->primary); drm_plane_finish_surface(conn->crtc->cursor); drm_fb_clear(&conn->cursor_pending_fb); conn->cursor_enabled = false; conn->crtc = NULL; } out: drm_connector_state_finish(&pending); return ok; } static bool drm_connector_commit(struct wlr_output *output, const struct wlr_output_state *state) { struct wlr_drm_connector *conn = get_drm_connector_from_output(output); if (!drm_connector_test(output, state)) { return false; } return drm_connector_commit_state(conn, state); } size_t drm_crtc_get_gamma_lut_size(struct wlr_drm_backend *drm, struct wlr_drm_crtc *crtc) { if (crtc->props.gamma_lut_size == 0 || drm->iface == &legacy_iface) { return (size_t)crtc->legacy_gamma_size; } uint64_t gamma_lut_size; if (!get_drm_prop(drm->fd, crtc->id, crtc->props.gamma_lut_size, &gamma_lut_size)) { wlr_log(WLR_ERROR, "Unable to get gamma lut size"); return 0; } return gamma_lut_size; } static size_t drm_connector_get_gamma_size(struct wlr_output *output) { struct wlr_drm_connector *conn = get_drm_connector_from_output(output); struct wlr_drm_backend *drm = conn->backend; struct wlr_drm_crtc *crtc = conn->crtc; if (crtc == NULL) { return 0; } return drm_crtc_get_gamma_lut_size(drm, crtc); } struct wlr_drm_fb *get_next_cursor_fb(struct wlr_drm_connector *conn) { if (!conn->cursor_enabled || conn->crtc == NULL) { return NULL; } if (conn->cursor_pending_fb != NULL) { return conn->cursor_pending_fb; } if (conn->crtc->cursor->queued_fb != NULL) { return conn->crtc->cursor->queued_fb; } return conn->crtc->cursor->current_fb; } static void realloc_crtcs(struct wlr_drm_backend *drm, struct wlr_drm_connector *want_conn); static bool drm_connector_alloc_crtc(struct wlr_drm_connector *conn) { if (conn->crtc == NULL) { realloc_crtcs(conn->backend, conn); } bool ok = conn->crtc != NULL; if (!ok) { wlr_drm_conn_log(conn, WLR_DEBUG, "Failed to find free CRTC"); } return ok; } static struct wlr_drm_mode *drm_mode_create(const drmModeModeInfo *modeinfo) { struct wlr_drm_mode *mode = calloc(1, sizeof(*mode)); if (!mode) { return NULL; } mode->drm_mode = *modeinfo; mode->wlr_mode.width = mode->drm_mode.hdisplay; mode->wlr_mode.height = mode->drm_mode.vdisplay; mode->wlr_mode.refresh = calculate_refresh_rate(modeinfo); mode->wlr_mode.picture_aspect_ratio = get_picture_aspect_ratio(modeinfo); if (modeinfo->type & DRM_MODE_TYPE_PREFERRED) { mode->wlr_mode.preferred = true; } return mode; } struct wlr_output_mode *wlr_drm_connector_add_mode(struct wlr_output *output, const drmModeModeInfo *modeinfo) { struct wlr_drm_connector *conn = get_drm_connector_from_output(output); if (modeinfo->type != DRM_MODE_TYPE_USERDEF) { return NULL; } struct wlr_output_mode *wlr_mode; wl_list_for_each(wlr_mode, &conn->output.modes, link) { struct wlr_drm_mode *mode = wl_container_of(wlr_mode, mode, wlr_mode); if (memcmp(&mode->drm_mode, modeinfo, sizeof(*modeinfo)) == 0) { return wlr_mode; } } struct wlr_drm_mode *mode = drm_mode_create(modeinfo); if (!mode) { return NULL; } wl_list_insert(&conn->output.modes, &mode->wlr_mode.link); wlr_drm_conn_log(conn, WLR_INFO, "Registered custom mode " "%"PRId32"x%"PRId32"@%"PRId32, mode->wlr_mode.width, mode->wlr_mode.height, mode->wlr_mode.refresh); return &mode->wlr_mode; } const drmModeModeInfo *wlr_drm_mode_get_info(struct wlr_output_mode *wlr_mode) { const struct wlr_drm_mode *mode = wl_container_of(wlr_mode, mode, wlr_mode); return &mode->drm_mode; } static bool drm_connector_set_cursor(struct wlr_output *output, struct wlr_buffer *buffer, int hotspot_x, int hotspot_y) { struct wlr_drm_connector *conn = get_drm_connector_from_output(output); struct wlr_drm_backend *drm = conn->backend; struct wlr_drm_crtc *crtc = conn->crtc; if (!crtc) { return false; } struct wlr_drm_plane *plane = crtc->cursor; if (plane == NULL) { return false; } if (conn->cursor_hotspot_x != hotspot_x || conn->cursor_hotspot_y != hotspot_y) { // Update cursor hotspot conn->cursor_x -= hotspot_x - conn->cursor_hotspot_x; conn->cursor_y -= hotspot_y - conn->cursor_hotspot_y; conn->cursor_hotspot_x = hotspot_x; conn->cursor_hotspot_y = hotspot_y; } conn->cursor_enabled = false; if (buffer != NULL) { if ((uint64_t)buffer->width != drm->cursor_width || (uint64_t)buffer->height != drm->cursor_height) { wlr_drm_conn_log(conn, WLR_DEBUG, "Cursor buffer size mismatch"); return false; } struct wlr_buffer *local_buf; if (drm->parent) { struct wlr_drm_format format = {0}; if (!drm_plane_pick_render_format(plane, &format, &drm->mgpu_renderer)) { wlr_log(WLR_ERROR, "Failed to pick cursor plane format"); return false; } bool ok = init_drm_surface(&plane->mgpu_surf, &drm->mgpu_renderer, buffer->width, buffer->height, &format); wlr_drm_format_finish(&format); if (!ok) { return false; } local_buf = drm_surface_blit(&plane->mgpu_surf, buffer); if (local_buf == NULL) { return false; } } else { local_buf = wlr_buffer_lock(buffer); } bool ok = drm_fb_import(&conn->cursor_pending_fb, drm, local_buf, &plane->formats); wlr_buffer_unlock(local_buf); if (!ok) { return false; } conn->cursor_enabled = true; conn->cursor_width = buffer->width; conn->cursor_height = buffer->height; } wlr_output_update_needs_frame(output); return true; } static bool drm_connector_move_cursor(struct wlr_output *output, int x, int y) { struct wlr_drm_connector *conn = get_drm_connector_from_output(output); if (!conn->crtc) { return false; } struct wlr_drm_plane *plane = conn->crtc->cursor; if (!plane) { return false; } struct wlr_box box = { .x = x, .y = y }; int width, height; wlr_output_transformed_resolution(output, &width, &height); enum wl_output_transform transform = wlr_output_transform_invert(output->transform); wlr_box_transform(&box, &box, transform, width, height); box.x -= conn->cursor_hotspot_x; box.y -= conn->cursor_hotspot_y; conn->cursor_x = box.x; conn->cursor_y = box.y; wlr_output_update_needs_frame(output); return true; } bool drm_connector_is_cursor_visible(struct wlr_drm_connector *conn) { return conn->cursor_enabled && conn->cursor_x < conn->output.width && conn->cursor_y < conn->output.height && conn->cursor_x + conn->cursor_width >= 0 && conn->cursor_y + conn->cursor_height >= 0; } static void dealloc_crtc(struct wlr_drm_connector *conn); /** * Destroy the compositor-facing part of a connector. * * The connector isn't destroyed when disconnected. Only the compositor-facing * wlr_output interface is cleaned up. */ static void drm_connector_destroy_output(struct wlr_output *output) { struct wlr_drm_connector *conn = get_drm_connector_from_output(output); dealloc_crtc(conn); conn->status = DRM_MODE_DISCONNECTED; drm_connector_set_pending_page_flip(conn, NULL); struct wlr_drm_mode *mode, *mode_tmp; wl_list_for_each_safe(mode, mode_tmp, &conn->output.modes, wlr_mode.link) { wl_list_remove(&mode->wlr_mode.link); free(mode); } conn->output = (struct wlr_output){0}; } static const struct wlr_drm_format_set *drm_connector_get_cursor_formats( struct wlr_output *output, uint32_t buffer_caps) { if (!(buffer_caps & WLR_BUFFER_CAP_DMABUF)) { return NULL; } struct wlr_drm_connector *conn = get_drm_connector_from_output(output); if (!drm_connector_alloc_crtc(conn)) { return NULL; } struct wlr_drm_plane *plane = conn->crtc->cursor; if (!plane) { return NULL; } if (conn->backend->parent) { return &conn->backend->mgpu_formats; } return &plane->formats; } static void drm_connector_get_cursor_size(struct wlr_output *output, int *width, int *height) { struct wlr_drm_backend *drm = get_drm_backend_from_backend(output->backend); *width = (int)drm->cursor_width; *height = (int)drm->cursor_height; } static const struct wlr_drm_format_set *drm_connector_get_primary_formats( struct wlr_output *output, uint32_t buffer_caps) { if (!(buffer_caps & WLR_BUFFER_CAP_DMABUF)) { return NULL; } struct wlr_drm_connector *conn = get_drm_connector_from_output(output); if (!drm_connector_alloc_crtc(conn)) { return NULL; } if (conn->backend->parent) { return &conn->backend->mgpu_formats; } return &conn->crtc->primary->formats; } static const struct wlr_output_impl output_impl = { .set_cursor = drm_connector_set_cursor, .move_cursor = drm_connector_move_cursor, .destroy = drm_connector_destroy_output, .test = drm_connector_test, .commit = drm_connector_commit, .get_gamma_size = drm_connector_get_gamma_size, .get_cursor_formats = drm_connector_get_cursor_formats, .get_cursor_size = drm_connector_get_cursor_size, .get_primary_formats = drm_connector_get_primary_formats, }; bool wlr_output_is_drm(struct wlr_output *output) { return output->impl == &output_impl; } uint32_t wlr_drm_connector_get_id(struct wlr_output *output) { struct wlr_drm_connector *conn = get_drm_connector_from_output(output); return conn->id; } enum wl_output_transform wlr_drm_connector_get_panel_orientation( struct wlr_output *output) { struct wlr_drm_connector *conn = get_drm_connector_from_output(output); if (!conn->props.panel_orientation) { return WL_OUTPUT_TRANSFORM_NORMAL; } char *orientation = get_drm_prop_enum(conn->backend->fd, conn->id, conn->props.panel_orientation); if (orientation == NULL) { return WL_OUTPUT_TRANSFORM_NORMAL; } enum wl_output_transform tr; if (strcmp(orientation, "Normal") == 0) { tr = WL_OUTPUT_TRANSFORM_NORMAL; } else if (strcmp(orientation, "Left Side Up") == 0) { tr = WL_OUTPUT_TRANSFORM_90; } else if (strcmp(orientation, "Upside Down") == 0) { tr = WL_OUTPUT_TRANSFORM_180; } else if (strcmp(orientation, "Right Side Up") == 0) { tr = WL_OUTPUT_TRANSFORM_270; } else { wlr_drm_conn_log(conn, WLR_ERROR, "Unknown panel orientation: %s", orientation); tr = WL_OUTPUT_TRANSFORM_NORMAL; } free(orientation); return tr; } static const int32_t subpixel_map[] = { [DRM_MODE_SUBPIXEL_UNKNOWN] = WL_OUTPUT_SUBPIXEL_UNKNOWN, [DRM_MODE_SUBPIXEL_HORIZONTAL_RGB] = WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB, [DRM_MODE_SUBPIXEL_HORIZONTAL_BGR] = WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR, [DRM_MODE_SUBPIXEL_VERTICAL_RGB] = WL_OUTPUT_SUBPIXEL_VERTICAL_RGB, [DRM_MODE_SUBPIXEL_VERTICAL_BGR] = WL_OUTPUT_SUBPIXEL_VERTICAL_BGR, [DRM_MODE_SUBPIXEL_NONE] = WL_OUTPUT_SUBPIXEL_NONE, }; static void dealloc_crtc(struct wlr_drm_connector *conn) { if (conn->crtc == NULL) { return; } wlr_drm_conn_log(conn, WLR_DEBUG, "De-allocating CRTC %" PRIu32, conn->crtc->id); struct wlr_output_state state; wlr_output_state_init(&state); wlr_output_state_set_enabled(&state, false); if (!drm_connector_commit_state(conn, &state)) { // On GPU unplug, disabling the CRTC can fail with EPERM wlr_drm_conn_log(conn, WLR_ERROR, "Failed to disable CRTC %"PRIu32, conn->crtc->id); } wlr_output_state_finish(&state); } static void realloc_crtcs(struct wlr_drm_backend *drm, struct wlr_drm_connector *want_conn) { assert(drm->num_crtcs > 0); size_t num_connectors = wl_list_length(&drm->connectors); if (num_connectors == 0) { return; } wlr_log(WLR_DEBUG, "Reallocating CRTCs"); struct wlr_drm_connector *connectors[num_connectors]; uint32_t connector_constraints[num_connectors]; uint32_t previous_match[drm->num_crtcs]; uint32_t new_match[drm->num_crtcs]; for (size_t i = 0; i < drm->num_crtcs; ++i) { previous_match[i] = UNMATCHED; } wlr_log(WLR_DEBUG, "State before reallocation:"); size_t i = 0; struct wlr_drm_connector *conn; wl_list_for_each(conn, &drm->connectors, link) { connectors[i] = conn; if (conn->crtc) { previous_match[conn->crtc - drm->crtcs] = i; } // Only request a CRTC if the connected is currently enabled or it's the // connector the user wants to enable bool want_crtc = conn == want_conn || conn->output.enabled; wlr_log(WLR_DEBUG, " '%s': crtc=%d status=%s want_crtc=%d", conn->name, conn->crtc ? (int)(conn->crtc - drm->crtcs) : -1, drm_connector_status_str(conn->status), want_crtc); if (conn->status == DRM_MODE_CONNECTED && want_crtc) { connector_constraints[i] = conn->possible_crtcs; } else { // Will always fail to match anything connector_constraints[i] = 0; } ++i; } match_obj(num_connectors, connector_constraints, drm->num_crtcs, previous_match, new_match); // Converts our crtc=>connector result into a connector=>crtc one. ssize_t connector_match[num_connectors]; for (size_t i = 0 ; i < num_connectors; ++i) { connector_match[i] = -1; } for (size_t i = 0; i < drm->num_crtcs; ++i) { if (new_match[i] != UNMATCHED) { connector_match[new_match[i]] = i; } } // Refuse to remove a CRTC from an enabled connector, and refuse to // change the CRTC of an enabled connector. for (size_t i = 0; i < num_connectors; ++i) { struct wlr_drm_connector *conn = connectors[i]; if (conn->status != DRM_MODE_CONNECTED || !conn->output.enabled) { continue; } if (connector_match[i] == -1) { wlr_log(WLR_DEBUG, "Could not match a CRTC for previously connected output; " "keeping old configuration"); return; } assert(conn->crtc != NULL); if (connector_match[i] != conn->crtc - drm->crtcs) { wlr_log(WLR_DEBUG, "Cannot switch CRTC for enabled output; " "keeping old configuration"); return; } } // Apply new configuration wlr_log(WLR_DEBUG, "State after reallocation:"); for (size_t i = 0; i < num_connectors; ++i) { struct wlr_drm_connector *conn = connectors[i]; wlr_log(WLR_DEBUG, " '%s': crtc=%zd", conn->name, connector_match[i]); if (conn->crtc != NULL && connector_match[i] == conn->crtc - drm->crtcs) { // We don't need to change anything continue; } dealloc_crtc(conn); if (connector_match[i] >= 0) { conn->crtc = &drm->crtcs[connector_match[i]]; } } } static struct wlr_drm_crtc *connector_get_current_crtc( struct wlr_drm_connector *wlr_conn, const drmModeConnector *drm_conn) { struct wlr_drm_backend *drm = wlr_conn->backend; uint32_t crtc_id = 0; if (wlr_conn->props.crtc_id != 0) { uint64_t value; if (!get_drm_prop(drm->fd, wlr_conn->id, wlr_conn->props.crtc_id, &value)) { wlr_drm_conn_log(wlr_conn, WLR_ERROR, "Failed to get CRTC_ID connector property"); return NULL; } crtc_id = (uint32_t)value; } else if (drm_conn->encoder_id != 0) { // Fallback to the legacy API drmModeEncoder *enc = drmModeGetEncoder(drm->fd, drm_conn->encoder_id); if (enc == NULL) { wlr_drm_conn_log(wlr_conn, WLR_ERROR, "drmModeGetEncoder() failed"); return NULL; } crtc_id = enc->crtc_id; drmModeFreeEncoder(enc); } if (crtc_id == 0) { return NULL; } for (size_t i = 0; i < drm->num_crtcs; ++i) { if (drm->crtcs[i].id == crtc_id) { return &drm->crtcs[i]; } } wlr_drm_conn_log(wlr_conn, WLR_ERROR, "Failed to find current CRTC ID %" PRIu32, crtc_id); return NULL; } static struct wlr_drm_connector *create_drm_connector(struct wlr_drm_backend *drm, const drmModeConnector *drm_conn) { struct wlr_drm_connector *wlr_conn = calloc(1, sizeof(*wlr_conn)); if (!wlr_conn) { wlr_log_errno(WLR_ERROR, "Allocation failed"); return NULL; } wlr_conn->backend = drm; wlr_conn->status = DRM_MODE_DISCONNECTED; wlr_conn->id = drm_conn->connector_id; const char *conn_name = drmModeGetConnectorTypeName(drm_conn->connector_type); if (conn_name == NULL) { conn_name = "Unknown"; } snprintf(wlr_conn->name, sizeof(wlr_conn->name), "%s-%"PRIu32, conn_name, drm_conn->connector_type_id); wlr_conn->possible_crtcs = drmModeConnectorGetPossibleCrtcs(drm->fd, drm_conn); if (wlr_conn->possible_crtcs == 0) { wlr_drm_conn_log(wlr_conn, WLR_ERROR, "No CRTC possible"); } wlr_conn->crtc = connector_get_current_crtc(wlr_conn, drm_conn); wl_list_insert(drm->connectors.prev, &wlr_conn->link); return wlr_conn; } static drmModeModeInfo *connector_get_current_mode(struct wlr_drm_connector *wlr_conn) { struct wlr_drm_backend *drm = wlr_conn->backend; if (wlr_conn->crtc == NULL) { return NULL; } if (wlr_conn->crtc->props.mode_id != 0) { size_t size = 0; drmModeModeInfo *mode = get_drm_prop_blob(drm->fd, wlr_conn->crtc->id, wlr_conn->crtc->props.mode_id, &size); assert(mode == NULL || size == sizeof(*mode)); return mode; } else { // Fallback to the legacy API drmModeCrtc *drm_crtc = drmModeGetCrtc(drm->fd, wlr_conn->crtc->id); if (drm_crtc == NULL) { wlr_log_errno(WLR_ERROR, "drmModeGetCrtc failed"); return NULL; } if (!drm_crtc->mode_valid) { drmModeFreeCrtc(drm_crtc); return NULL; } drmModeModeInfo *mode = malloc(sizeof(*mode)); if (mode == NULL) { wlr_log_errno(WLR_ERROR, "Allocation failed"); drmModeFreeCrtc(drm_crtc); return NULL; } *mode = drm_crtc->mode; drmModeFreeCrtc(drm_crtc); return mode; } } static bool connect_drm_connector(struct wlr_drm_connector *wlr_conn, const drmModeConnector *drm_conn) { struct wlr_drm_backend *drm = wlr_conn->backend; struct wlr_output *output = &wlr_conn->output; wlr_log(WLR_DEBUG, "Current CRTC: %d", wlr_conn->crtc ? (int)wlr_conn->crtc->id : -1); // keep track of all the modes ourselves first. We must only fill out // the modes list after wlr_output_init() struct wl_list modes; wl_list_init(&modes); struct wlr_output_state state; wlr_output_state_init(&state); wlr_output_state_set_enabled(&state, wlr_conn->crtc != NULL); drmModeModeInfo *current_modeinfo = connector_get_current_mode(wlr_conn); wlr_log(WLR_INFO, "Detected modes:"); for (int i = 0; i < drm_conn->count_modes; ++i) { if (drm_conn->modes[i].flags & DRM_MODE_FLAG_INTERLACE) { continue; } struct wlr_drm_mode *mode = drm_mode_create(&drm_conn->modes[i]); if (!mode) { wlr_log_errno(WLR_ERROR, "Allocation failed"); wlr_output_state_finish(&state); return false; } // If this is the current mode set on the conn's crtc, // then set it as the conn's output current mode. if (current_modeinfo != NULL && memcmp(&mode->drm_mode, current_modeinfo, sizeof(*current_modeinfo)) == 0) { wlr_output_state_set_mode(&state, &mode->wlr_mode); uint64_t mode_id = 0; get_drm_prop(drm->fd, wlr_conn->crtc->id, wlr_conn->crtc->props.mode_id, &mode_id); wlr_conn->crtc->mode_id = mode_id; } wlr_log(WLR_INFO, " %"PRId32"x%"PRId32" @ %.3f Hz %s", mode->wlr_mode.width, mode->wlr_mode.height, (float)mode->wlr_mode.refresh / 1000, mode->wlr_mode.preferred ? "(preferred)" : ""); wl_list_insert(modes.prev, &mode->wlr_mode.link); } free(current_modeinfo); wlr_output_init(output, &drm->backend, &output_impl, drm->display, &state); wlr_output_state_finish(&state); // fill out the modes wl_list_insert_list(&output->modes, &modes); wlr_output_set_name(output, wlr_conn->name); output->phys_width = drm_conn->mmWidth; output->phys_height = drm_conn->mmHeight; wlr_log(WLR_INFO, "Physical size: %"PRId32"x%"PRId32, output->phys_width, output->phys_height); if (drm_conn->subpixel < sizeof(subpixel_map) / sizeof(subpixel_map[0])) { output->subpixel = subpixel_map[drm_conn->subpixel]; } else { wlr_log(WLR_ERROR, "Unknown subpixel value: %d", (int)drm_conn->subpixel); } if (!get_drm_connector_props(drm->fd, wlr_conn->id, &wlr_conn->props)) { return false; } uint64_t non_desktop; if (get_drm_prop(drm->fd, wlr_conn->id, wlr_conn->props.non_desktop, &non_desktop)) { if (non_desktop == 1) { wlr_log(WLR_INFO, "Non-desktop connector"); } output->non_desktop = non_desktop; } memset(wlr_conn->max_bpc_bounds, 0, sizeof(wlr_conn->max_bpc_bounds)); if (wlr_conn->props.max_bpc != 0) { if (!introspect_drm_prop_range(drm->fd, wlr_conn->props.max_bpc, &wlr_conn->max_bpc_bounds[0], &wlr_conn->max_bpc_bounds[1])) { wlr_log(WLR_ERROR, "Failed to introspect 'max bpc' property"); } } size_t edid_len = 0; uint8_t *edid = get_drm_prop_blob(drm->fd, wlr_conn->id, wlr_conn->props.edid, &edid_len); parse_edid(wlr_conn, edid_len, edid); free(edid); char *subconnector = NULL; if (wlr_conn->props.subconnector) { subconnector = get_drm_prop_enum(drm->fd, wlr_conn->id, wlr_conn->props.subconnector); } if (subconnector && strcmp(subconnector, "Native") == 0) { free(subconnector); subconnector = NULL; } char description[128]; snprintf(description, sizeof(description), "%s %s%s%s (%s%s%s)", output->make, output->model, output->serial ? " " : "", output->serial ? output->serial : "", output->name, subconnector ? " via " : "", subconnector ? subconnector : ""); wlr_output_set_description(output, description); free(subconnector); wlr_conn->status = DRM_MODE_CONNECTED; return true; } static void disconnect_drm_connector(struct wlr_drm_connector *conn); void scan_drm_connectors(struct wlr_drm_backend *drm, struct wlr_device_hotplug_event *event) { if (event != NULL && event->connector_id != 0) { wlr_log(WLR_INFO, "Scanning DRM connector %"PRIu32" on %s", event->connector_id, drm->name); } else { wlr_log(WLR_INFO, "Scanning DRM connectors on %s", drm->name); } drmModeRes *res = drmModeGetResources(drm->fd); if (!res) { wlr_log_errno(WLR_ERROR, "Failed to get DRM resources"); return; } size_t seen_len = wl_list_length(&drm->connectors); // +1 so length can never be 0, which is undefined behaviour. // Last element isn't used. bool seen[seen_len + 1]; memset(seen, false, sizeof(seen)); size_t new_outputs_len = 0; struct wlr_drm_connector *new_outputs[res->count_connectors + 1]; for (int i = 0; i < res->count_connectors; ++i) { uint32_t conn_id = res->connectors[i]; ssize_t index = -1; struct wlr_drm_connector *c, *wlr_conn = NULL; wl_list_for_each(c, &drm->connectors, link) { index++; if (c->id == conn_id) { wlr_conn = c; break; } } // If the hotplug event contains a connector ID, ignore any other // connector. if (event != NULL && event->connector_id != 0 && event->connector_id != conn_id) { if (wlr_conn != NULL) { seen[index] = true; } continue; } drmModeConnector *drm_conn = drmModeGetConnector(drm->fd, conn_id); if (!drm_conn) { wlr_log_errno(WLR_ERROR, "Failed to get DRM connector"); continue; } if (!wlr_conn) { wlr_conn = create_drm_connector(drm, drm_conn); if (wlr_conn == NULL) { continue; } wlr_log(WLR_INFO, "Found connector '%s'", wlr_conn->name); } else { seen[index] = true; } // This can only happen *after* hotplug, since we haven't read the // connector properties yet if (wlr_conn->props.link_status != 0) { uint64_t link_status; if (!get_drm_prop(drm->fd, wlr_conn->id, wlr_conn->props.link_status, &link_status)) { wlr_drm_conn_log(wlr_conn, WLR_ERROR, "Failed to get link status prop"); continue; } if (link_status == DRM_MODE_LINK_STATUS_BAD) { // We need to reload our list of modes and force a modeset wlr_drm_conn_log(wlr_conn, WLR_INFO, "Bad link detected"); disconnect_drm_connector(wlr_conn); } } if (wlr_conn->status == DRM_MODE_DISCONNECTED && drm_conn->connection == DRM_MODE_CONNECTED) { wlr_log(WLR_INFO, "'%s' connected", wlr_conn->name); if (!connect_drm_connector(wlr_conn, drm_conn)) { wlr_drm_conn_log(wlr_conn, WLR_ERROR, "Failed to connect DRM connector"); continue; } new_outputs[new_outputs_len++] = wlr_conn; } else if (wlr_conn->status == DRM_MODE_CONNECTED && drm_conn->connection != DRM_MODE_CONNECTED) { wlr_log(WLR_INFO, "'%s' disconnected", wlr_conn->name); disconnect_drm_connector(wlr_conn); } drmModeFreeConnector(drm_conn); } drmModeFreeResources(res); // Iterate in reverse order because we'll remove items from the list and // still want indices to remain correct. struct wlr_drm_connector *conn, *tmp_conn; size_t index = wl_list_length(&drm->connectors); wl_list_for_each_reverse_safe(conn, tmp_conn, &drm->connectors, link) { index--; if (index >= seen_len || seen[index]) { continue; } wlr_log(WLR_INFO, "'%s' disappeared", conn->name); destroy_drm_connector(conn); } realloc_crtcs(drm, NULL); for (size_t i = 0; i < new_outputs_len; ++i) { struct wlr_drm_connector *conn = new_outputs[i]; wlr_drm_conn_log(conn, WLR_INFO, "Requesting modeset"); wl_signal_emit_mutable(&drm->backend.events.new_output, &conn->output); } } void scan_drm_leases(struct wlr_drm_backend *drm) { drmModeLesseeListRes *list = drmModeListLessees(drm->fd); if (list == NULL) { wlr_log_errno(WLR_ERROR, "drmModeListLessees failed"); return; } struct wlr_drm_connector *conn; wl_list_for_each(conn, &drm->connectors, link) { if (conn->lease == NULL) { continue; } bool found = false; for (size_t i = 0; i < list->count; i++) { if (list->lessees[i] == conn->lease->lessee_id) { found = true; break; } } if (!found) { wlr_log(WLR_DEBUG, "DRM lease %"PRIu32" has been terminated", conn->lease->lessee_id); drm_lease_destroy(conn->lease); } } drmFree(list); } static int mhz_to_nsec(int mhz) { return 1000000000000LL / mhz; } static void handle_page_flip(int fd, unsigned seq, unsigned tv_sec, unsigned tv_usec, unsigned crtc_id, void *data) { struct wlr_drm_page_flip *page_flip = data; struct wlr_drm_connector *conn = page_flip->conn; if (conn != NULL) { conn->pending_page_flip = NULL; } drm_page_flip_destroy(page_flip); if (conn == NULL) { return; } struct wlr_drm_backend *drm = conn->backend; if (conn->status != DRM_MODE_CONNECTED || conn->crtc == NULL) { wlr_drm_conn_log(conn, WLR_DEBUG, "Ignoring page-flip event for disabled connector"); return; } struct wlr_drm_plane *plane = conn->crtc->primary; if (plane->queued_fb) { drm_fb_move(&plane->current_fb, &plane->queued_fb); } if (conn->crtc->cursor && conn->crtc->cursor->queued_fb) { drm_fb_move(&conn->crtc->cursor->current_fb, &conn->crtc->cursor->queued_fb); } struct wlr_drm_layer *layer; wl_list_for_each(layer, &conn->crtc->layers, link) { drm_fb_move(&layer->current_fb, &layer->queued_fb); } uint32_t present_flags = WLR_OUTPUT_PRESENT_VSYNC | WLR_OUTPUT_PRESENT_HW_CLOCK | WLR_OUTPUT_PRESENT_HW_COMPLETION; /* Don't report ZERO_COPY in multi-gpu situations, because we had to copy * data between the GPUs, even if we were using the direct scanout * interface. */ if (!drm->parent) { present_flags |= WLR_OUTPUT_PRESENT_ZERO_COPY; } struct timespec present_time = { .tv_sec = tv_sec, .tv_nsec = tv_usec * 1000, }; struct wlr_output_event_present present_event = { /* The DRM backend guarantees that the presentation event will be for * the last submitted frame. */ .commit_seq = conn->output.commit_seq, .presented = drm->session->active, .when = &present_time, .seq = seq, .refresh = mhz_to_nsec(conn->output.refresh), .flags = present_flags, }; wlr_output_send_present(&conn->output, &present_event); if (drm->session->active) { wlr_output_send_frame(&conn->output); } } int handle_drm_event(int fd, uint32_t mask, void *data) { struct wlr_drm_backend *drm = data; drmEventContext event = { .version = 3, .page_flip_handler2 = handle_page_flip, }; if (drmHandleEvent(fd, &event) != 0) { wlr_log(WLR_ERROR, "drmHandleEvent failed"); wl_display_terminate(drm->display); } return 1; } static void disconnect_drm_connector(struct wlr_drm_connector *conn) { if (conn->status == DRM_MODE_DISCONNECTED) { return; } // This will cleanup the compositor-facing wlr_output, but won't destroy // our wlr_drm_connector. wlr_output_destroy(&conn->output); assert(conn->status == DRM_MODE_DISCONNECTED); } void destroy_drm_connector(struct wlr_drm_connector *conn) { disconnect_drm_connector(conn); wl_list_remove(&conn->link); free(conn); } int wlr_drm_backend_get_non_master_fd(struct wlr_backend *backend) { assert(backend); struct wlr_drm_backend *drm = get_drm_backend_from_backend(backend); char *path = drmGetDeviceNameFromFd2(drm->fd); if (!path) { wlr_log(WLR_ERROR, "Failed to get device name from DRM fd"); return -1; } int fd = open(path, O_RDWR | O_CLOEXEC); if (fd < 0) { wlr_log_errno(WLR_ERROR, "Unable to clone DRM fd for client fd"); free(path); return -1; } if (drmIsMaster(fd) && drmDropMaster(fd) < 0) { wlr_log_errno(WLR_ERROR, "Failed to drop master"); return -1; } return fd; } struct wlr_drm_lease *wlr_drm_create_lease(struct wlr_output **outputs, size_t n_outputs, int *lease_fd_ptr) { assert(outputs); if (n_outputs == 0) { wlr_log(WLR_ERROR, "Can't lease 0 outputs"); return NULL; } struct wlr_drm_backend *drm = get_drm_backend_from_backend(outputs[0]->backend); int n_objects = 0; uint32_t objects[4 * n_outputs + 1]; for (size_t i = 0; i < n_outputs; ++i) { struct wlr_drm_connector *conn = get_drm_connector_from_output(outputs[i]); assert(conn->lease == NULL); if (conn->backend != drm) { wlr_log(WLR_ERROR, "Can't lease output from different backends"); return NULL; } objects[n_objects++] = conn->id; wlr_log(WLR_DEBUG, "Connector %d", conn->id); if (!drm_connector_alloc_crtc(conn)) { wlr_log(WLR_ERROR, "Failled to allocate connector CRTC"); return NULL; } objects[n_objects++] = conn->crtc->id; wlr_log(WLR_DEBUG, "CRTC %d", conn->crtc->id); objects[n_objects++] = conn->crtc->primary->id; wlr_log(WLR_DEBUG, "Primary plane %d", conn->crtc->primary->id); if (conn->crtc->cursor) { wlr_log(WLR_DEBUG, "Cursor plane %d", conn->crtc->cursor->id); objects[n_objects++] = conn->crtc->cursor->id; } } assert(n_objects != 0); struct wlr_drm_lease *lease = calloc(1, sizeof(*lease)); if (lease == NULL) { return NULL; } lease->backend = drm; wl_signal_init(&lease->events.destroy); wlr_log(WLR_DEBUG, "Issuing DRM lease with %d objects", n_objects); int lease_fd = drmModeCreateLease(drm->fd, objects, n_objects, O_CLOEXEC, &lease->lessee_id); if (lease_fd < 0) { free(lease); return NULL; } *lease_fd_ptr = lease_fd; wlr_log(WLR_DEBUG, "Issued DRM lease %"PRIu32, lease->lessee_id); for (size_t i = 0; i < n_outputs; ++i) { struct wlr_drm_connector *conn = get_drm_connector_from_output(outputs[i]); conn->lease = lease; conn->crtc->lease = lease; } return lease; } void wlr_drm_lease_terminate(struct wlr_drm_lease *lease) { struct wlr_drm_backend *drm = lease->backend; wlr_log(WLR_DEBUG, "Terminating DRM lease %d", lease->lessee_id); int ret = drmModeRevokeLease(drm->fd, lease->lessee_id); if (ret < 0) { wlr_log_errno(WLR_ERROR, "Failed to terminate lease"); } drm_lease_destroy(lease); } void drm_lease_destroy(struct wlr_drm_lease *lease) { struct wlr_drm_backend *drm = lease->backend; wl_signal_emit_mutable(&lease->events.destroy, NULL); struct wlr_drm_connector *conn; wl_list_for_each(conn, &drm->connectors, link) { if (conn->lease == lease) { conn->lease = NULL; } } for (size_t i = 0; i < drm->num_crtcs; ++i) { if (drm->crtcs[i].lease == lease) { drm->crtcs[i].lease = NULL; } } free(lease); } wlroots-0.17.1/backend/drm/gen_pnpids.sh000077500000000000000000000010101454110342200201330ustar00rootroot00000000000000#!/bin/sh -eu # # usage: gen_pnpids.sh < pnp.ids > pnpids.c gen_pnps() { while read -r id vendor; do [ "${#id}" = 3 ] || exit 1 printf "\tcase PNP_ID('%c', '%c', '%c'): return \"%s\";\n" \ "$id" "${id#?}" "${id#??}" "$vendor" done } cat << EOF #include "backend/drm/util.h" #define PNP_ID(a, b, c) ((a & 0x1f) << 10) | ((b & 0x1f) << 5) | (c & 0x1f) const char *get_pnp_manufacturer(const char code[static 3]) { switch (PNP_ID(code[0], code[1], code[2])) { $(gen_pnps) } return NULL; } #undef PNP_ID EOF wlroots-0.17.1/backend/drm/legacy.c000066400000000000000000000146751454110342200171030ustar00rootroot00000000000000#include #include #include #include #include #include "backend/drm/drm.h" #include "backend/drm/iface.h" #include "backend/drm/util.h" static bool legacy_fb_props_match(struct wlr_drm_fb *fb1, struct wlr_drm_fb *fb2) { struct wlr_dmabuf_attributes dmabuf1 = {0}, dmabuf2 = {0}; if (!wlr_buffer_get_dmabuf(fb1->wlr_buf, &dmabuf1) || !wlr_buffer_get_dmabuf(fb2->wlr_buf, &dmabuf2)) { return false; } if (dmabuf1.width != dmabuf2.width || dmabuf1.height != dmabuf2.height || dmabuf1.format != dmabuf2.format || dmabuf1.modifier != dmabuf2.modifier || dmabuf1.n_planes != dmabuf2.n_planes) { return false; } for (int i = 0; i < dmabuf1.n_planes; i++) { if (dmabuf1.stride[i] != dmabuf2.stride[i] || dmabuf1.offset[i] != dmabuf2.offset[i]) { return false; } } return true; } static bool legacy_crtc_test(struct wlr_drm_connector *conn, const struct wlr_drm_connector_state *state) { struct wlr_drm_crtc *crtc = conn->crtc; if ((state->base->committed & WLR_OUTPUT_STATE_BUFFER) && !state->modeset) { struct wlr_drm_fb *pending_fb = state->primary_fb; struct wlr_drm_fb *prev_fb = crtc->primary->queued_fb; if (!prev_fb) { prev_fb = crtc->primary->current_fb; } /* Legacy is only guaranteed to be able to display a FB if it's been * allocated the same way as the previous one. */ if (prev_fb != NULL && !legacy_fb_props_match(prev_fb, pending_fb)) { wlr_drm_conn_log(conn, WLR_DEBUG, "Cannot change scan-out buffer parameters with legacy KMS API"); return false; } } return true; } static bool legacy_crtc_commit(struct wlr_drm_connector *conn, const struct wlr_drm_connector_state *state, struct wlr_drm_page_flip *page_flip, uint32_t flags, bool test_only) { if (!legacy_crtc_test(conn, state)) { return false; } if (test_only) { return true; } struct wlr_drm_backend *drm = conn->backend; struct wlr_output *output = &conn->output; struct wlr_drm_crtc *crtc = conn->crtc; struct wlr_drm_plane *cursor = crtc->cursor; uint32_t fb_id = 0; if (state->active) { if (state->primary_fb == NULL) { wlr_log(WLR_ERROR, "%s: failed to acquire primary FB", conn->output.name); return false; } fb_id = state->primary_fb->id; } if (state->modeset) { uint32_t *conns = NULL; size_t conns_len = 0; drmModeModeInfo *mode = NULL; if (state->active) { conns = &conn->id; conns_len = 1; mode = (drmModeModeInfo *)&state->mode; } uint32_t dpms = state->active ? DRM_MODE_DPMS_ON : DRM_MODE_DPMS_OFF; if (drmModeConnectorSetProperty(drm->fd, conn->id, conn->props.dpms, dpms) != 0) { wlr_drm_conn_log_errno(conn, WLR_ERROR, "Failed to set DPMS property"); return false; } if (drmModeSetCrtc(drm->fd, crtc->id, fb_id, 0, 0, conns, conns_len, mode)) { wlr_drm_conn_log_errno(conn, WLR_ERROR, "Failed to set CRTC"); return false; } } if (state->base->committed & WLR_OUTPUT_STATE_GAMMA_LUT) { if (!drm_legacy_crtc_set_gamma(drm, crtc, state->base->gamma_lut_size, state->base->gamma_lut)) { return false; } } if (state->base->committed & WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED) { if (!drm_connector_supports_vrr(conn)) { return false; } if (drmModeObjectSetProperty(drm->fd, crtc->id, DRM_MODE_OBJECT_CRTC, crtc->props.vrr_enabled, state->base->adaptive_sync_enabled) != 0) { wlr_drm_conn_log_errno(conn, WLR_ERROR, "drmModeObjectSetProperty(VRR_ENABLED) failed"); return false; } output->adaptive_sync_status = state->base->adaptive_sync_enabled ? WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED : WLR_OUTPUT_ADAPTIVE_SYNC_DISABLED; wlr_drm_conn_log(conn, WLR_DEBUG, "VRR %s", state->base->adaptive_sync_enabled ? "enabled" : "disabled"); } if (cursor != NULL && drm_connector_is_cursor_visible(conn)) { struct wlr_drm_fb *cursor_fb = get_next_cursor_fb(conn); if (cursor_fb == NULL) { wlr_drm_conn_log(conn, WLR_DEBUG, "Failed to acquire cursor FB"); return false; } drmModeFB *drm_fb = drmModeGetFB(drm->fd, cursor_fb->id); if (drm_fb == NULL) { wlr_drm_conn_log_errno(conn, WLR_DEBUG, "Failed to get cursor " "BO handle: drmModeGetFB failed"); return false; } uint32_t cursor_handle = drm_fb->handle; uint32_t cursor_width = drm_fb->width; uint32_t cursor_height = drm_fb->height; drmModeFreeFB(drm_fb); int ret = drmModeSetCursor(drm->fd, crtc->id, cursor_handle, cursor_width, cursor_height); int set_cursor_errno = errno; if (drmCloseBufferHandle(drm->fd, cursor_handle) != 0) { wlr_log_errno(WLR_ERROR, "drmCloseBufferHandle failed"); } if (ret != 0) { wlr_drm_conn_log(conn, WLR_DEBUG, "drmModeSetCursor failed: %s", strerror(set_cursor_errno)); return false; } if (drmModeMoveCursor(drm->fd, crtc->id, conn->cursor_x, conn->cursor_y) != 0) { wlr_drm_conn_log_errno(conn, WLR_ERROR, "drmModeMoveCursor failed"); return false; } } else { if (drmModeSetCursor(drm->fd, crtc->id, 0, 0, 0)) { wlr_drm_conn_log_errno(conn, WLR_DEBUG, "drmModeSetCursor failed"); return false; } } if (flags & DRM_MODE_PAGE_FLIP_EVENT) { if (drmModePageFlip(drm->fd, crtc->id, fb_id, flags, page_flip)) { wlr_drm_conn_log_errno(conn, WLR_ERROR, "drmModePageFlip failed"); return false; } } return true; } static void fill_empty_gamma_table(size_t size, uint16_t *r, uint16_t *g, uint16_t *b) { assert(0xFFFF < UINT64_MAX / (size - 1)); for (uint32_t i = 0; i < size; ++i) { uint16_t val = (uint64_t)0xFFFF * i / (size - 1); r[i] = g[i] = b[i] = val; } } bool drm_legacy_crtc_set_gamma(struct wlr_drm_backend *drm, struct wlr_drm_crtc *crtc, size_t size, uint16_t *lut) { uint16_t *linear_lut = NULL; if (size == 0) { // The legacy interface doesn't offer a way to reset the gamma LUT size = drm_crtc_get_gamma_lut_size(drm, crtc); if (size == 0) { return false; } linear_lut = malloc(3 * size * sizeof(uint16_t)); if (linear_lut == NULL) { wlr_log_errno(WLR_ERROR, "Allocation failed"); return false; } fill_empty_gamma_table(size, linear_lut, linear_lut + size, linear_lut + 2 * size); lut = linear_lut; } uint16_t *r = lut, *g = lut + size, *b = lut + 2 * size; if (drmModeCrtcSetGamma(drm->fd, crtc->id, size, r, g, b) != 0) { wlr_log_errno(WLR_ERROR, "Failed to set gamma LUT on CRTC %"PRIu32, crtc->id); free(linear_lut); return false; } free(linear_lut); return true; } const struct wlr_drm_interface legacy_iface = { .crtc_commit = legacy_crtc_commit, }; wlroots-0.17.1/backend/drm/libliftoff.c000066400000000000000000000355671454110342200177620ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include "backend/drm/drm.h" #include "backend/drm/iface.h" static bool init(struct wlr_drm_backend *drm) { // TODO: lower log level liftoff_log_set_priority(LIFTOFF_DEBUG); int drm_fd = fcntl(drm->fd, F_DUPFD_CLOEXEC, 0); if (drm_fd < 0) { wlr_log_errno(WLR_ERROR, "fcntl(F_DUPFD_CLOEXEC) failed"); return false; } drm->liftoff = liftoff_device_create(drm_fd); if (!drm->liftoff) { wlr_log(WLR_ERROR, "Failed to create liftoff device"); close(drm_fd); return false; } for (size_t i = 0; i < drm->num_planes; i++) { struct wlr_drm_plane *plane = &drm->planes[i]; if (plane->initial_crtc_id != 0) { continue; } plane->liftoff = liftoff_plane_create(drm->liftoff, plane->id); if (plane->liftoff == NULL) { wlr_log(WLR_ERROR, "Failed to create liftoff plane"); return false; } } for (size_t i = 0; i < drm->num_crtcs; i++) { struct wlr_drm_crtc *crtc = &drm->crtcs[i]; crtc->liftoff = liftoff_output_create(drm->liftoff, crtc->id); if (!crtc->liftoff) { wlr_log(WLR_ERROR, "Failed to create liftoff output"); return false; } crtc->liftoff_composition_layer = liftoff_layer_create(crtc->liftoff); if (!crtc->liftoff_composition_layer) { wlr_log(WLR_ERROR, "Failed to create liftoff composition layer"); return false; } liftoff_output_set_composition_layer(crtc->liftoff, crtc->liftoff_composition_layer); if (crtc->primary) { crtc->primary->liftoff_layer = liftoff_layer_create(crtc->liftoff); if (!crtc->primary->liftoff_layer) { wlr_log(WLR_ERROR, "Failed to create liftoff layer for primary plane"); return false; } } if (crtc->cursor) { crtc->cursor->liftoff_layer = liftoff_layer_create(crtc->liftoff); if (!crtc->cursor->liftoff_layer) { wlr_log(WLR_ERROR, "Failed to create liftoff layer for cursor plane"); return false; } } } return true; } static bool register_planes_for_crtc(struct wlr_drm_backend *drm, struct wlr_drm_crtc *crtc) { // When performing the first modeset on a CRTC, we need to be a bit careful // when it comes to planes: we don't want to allow libliftoff to make use // of planes currently already in-use on another CRTC. We need to wait for // a modeset to happen on the other CRTC before being able to use these. for (size_t i = 0; i < drm->num_planes; i++) { struct wlr_drm_plane *plane = &drm->planes[i]; if (plane->liftoff != NULL || plane->initial_crtc_id != crtc->id) { continue; } plane->liftoff = liftoff_plane_create(drm->liftoff, plane->id); if (plane->liftoff == NULL) { wlr_log(WLR_ERROR, "Failed to create liftoff plane"); return false; } } return true; } static void finish(struct wlr_drm_backend *drm) { for (size_t i = 0; i < drm->num_crtcs; i++) { struct wlr_drm_crtc *crtc = &drm->crtcs[i]; if (crtc->primary) { liftoff_layer_destroy(crtc->primary->liftoff_layer); } if (crtc->cursor) { liftoff_layer_destroy(crtc->cursor->liftoff_layer); } liftoff_layer_destroy(crtc->liftoff_composition_layer); liftoff_output_destroy(crtc->liftoff); } for (size_t i = 0; i < drm->num_planes; i++) { struct wlr_drm_plane *plane = &drm->planes[i]; liftoff_plane_destroy(plane->liftoff); } liftoff_device_destroy(drm->liftoff); } static bool add_prop(drmModeAtomicReq *req, uint32_t obj, uint32_t prop, uint64_t val) { if (drmModeAtomicAddProperty(req, obj, prop, val) < 0) { wlr_log_errno(WLR_ERROR, "drmModeAtomicAddProperty failed"); return false; } return true; } static void commit_blob(struct wlr_drm_backend *drm, uint32_t *current, uint32_t next) { if (*current == next) { return; } if (*current != 0) { drmModeDestroyPropertyBlob(drm->fd, *current); } *current = next; } static void rollback_blob(struct wlr_drm_backend *drm, uint32_t *current, uint32_t next) { if (*current == next) { return; } if (next != 0) { drmModeDestroyPropertyBlob(drm->fd, next); } } static bool set_plane_props(struct wlr_drm_plane *plane, struct liftoff_layer *layer, struct wlr_drm_fb *fb, int32_t x, int32_t y, uint64_t zpos) { if (fb == NULL) { wlr_log(WLR_ERROR, "Failed to acquire FB for plane %"PRIu32, plane->id); return false; } uint32_t width = fb->wlr_buf->width; uint32_t height = fb->wlr_buf->height; // The SRC_* properties are in 16.16 fixed point return liftoff_layer_set_property(layer, "zpos", zpos) == 0 && liftoff_layer_set_property(layer, "SRC_X", 0) == 0 && liftoff_layer_set_property(layer, "SRC_Y", 0) == 0 && liftoff_layer_set_property(layer, "SRC_W", (uint64_t)width << 16) == 0 && liftoff_layer_set_property(layer, "SRC_H", (uint64_t)height << 16) == 0 && liftoff_layer_set_property(layer, "CRTC_X", (uint64_t)x) == 0 && liftoff_layer_set_property(layer, "CRTC_Y", (uint64_t)y) == 0 && liftoff_layer_set_property(layer, "CRTC_W", width) == 0 && liftoff_layer_set_property(layer, "CRTC_H", height) == 0 && liftoff_layer_set_property(layer, "FB_ID", fb->id) == 0; } static bool disable_plane(struct wlr_drm_plane *plane) { return liftoff_layer_set_property(plane->liftoff_layer, "FB_ID", 0) == 0; } static uint64_t to_fp16(double v) { return (uint64_t)round(v * (1 << 16)); } static bool set_layer_props(struct wlr_drm_backend *drm, const struct wlr_output_layer_state *state, uint64_t zpos, struct wl_array *fb_damage_clips_arr) { struct wlr_drm_layer *layer = get_drm_layer(drm, state->layer); uint32_t width = 0, height = 0; if (state->buffer != NULL) { width = state->buffer->width; height = state->buffer->height; } struct wlr_drm_fb *fb = layer->pending_fb; int ret = 0; if (state->buffer == NULL) { ret = liftoff_layer_set_property(layer->liftoff, "FB_ID", 0); } else if (fb == NULL) { liftoff_layer_set_fb_composited(layer->liftoff); } else { ret = liftoff_layer_set_property(layer->liftoff, "FB_ID", fb->id); } if (ret != 0) { return false; } uint64_t crtc_x = (uint64_t)state->dst_box.x; uint64_t crtc_y = (uint64_t)state->dst_box.y; uint64_t crtc_w = (uint64_t)state->dst_box.width; uint64_t crtc_h = (uint64_t)state->dst_box.height; struct wlr_fbox src_box = state->src_box; if (wlr_fbox_empty(&src_box)) { src_box = (struct wlr_fbox){ .width = width, .height = height, }; } uint64_t src_x = to_fp16(src_box.x); uint64_t src_y = to_fp16(src_box.y); uint64_t src_w = to_fp16(src_box.width); uint64_t src_h = to_fp16(src_box.height); uint32_t fb_damage_clips = 0; if (state->damage != NULL) { uint32_t *ptr = wl_array_add(fb_damage_clips_arr, sizeof(fb_damage_clips)); if (ptr == NULL) { return false; } create_fb_damage_clips_blob(drm, width, height, state->damage, &fb_damage_clips); *ptr = fb_damage_clips; } return liftoff_layer_set_property(layer->liftoff, "zpos", zpos) == 0 && liftoff_layer_set_property(layer->liftoff, "CRTC_X", crtc_x) == 0 && liftoff_layer_set_property(layer->liftoff, "CRTC_Y", crtc_y) == 0 && liftoff_layer_set_property(layer->liftoff, "CRTC_W", crtc_w) == 0 && liftoff_layer_set_property(layer->liftoff, "CRTC_H", crtc_h) == 0 && liftoff_layer_set_property(layer->liftoff, "SRC_X", src_x) == 0 && liftoff_layer_set_property(layer->liftoff, "SRC_Y", src_y) == 0 && liftoff_layer_set_property(layer->liftoff, "SRC_W", src_w) == 0 && liftoff_layer_set_property(layer->liftoff, "SRC_H", src_h) == 0 && liftoff_layer_set_property(layer->liftoff, "FB_DAMAGE_CLIPS", fb_damage_clips) == 0; } static bool devid_from_fd(int fd, dev_t *devid) { struct stat stat; if (fstat(fd, &stat) != 0) { wlr_log_errno(WLR_ERROR, "fstat failed"); return false; } *devid = stat.st_rdev; return true; } static void update_layer_feedback(struct wlr_drm_backend *drm, struct wlr_drm_layer *layer) { bool changed = false; for (size_t i = 0; i < drm->num_planes; i++) { struct wlr_drm_plane *plane = &drm->planes[i]; bool is_candidate = liftoff_layer_is_candidate_plane(layer->liftoff, plane->liftoff); if (layer->candidate_planes[i] != is_candidate) { layer->candidate_planes[i] = is_candidate; changed = true; } } if (!changed) { return; } dev_t target_device; if (!devid_from_fd(drm->fd, &target_device)) { return; } struct wlr_drm_format_set formats = {0}; for (size_t i = 0; i < drm->num_planes; i++) { struct wlr_drm_plane *plane = &drm->planes[i]; if (!layer->candidate_planes[i]) { continue; } for (size_t j = 0; j < plane->formats.len; j++) { const struct wlr_drm_format *format = &plane->formats.formats[j]; for (size_t k = 0; k < format->len; k++) { wlr_drm_format_set_add(&formats, format->format, format->modifiers[k]); } } } struct wlr_output_layer_feedback_event event = { .target_device = target_device, .formats = &formats, }; wl_signal_emit_mutable(&layer->wlr->events.feedback, &event); wlr_drm_format_set_finish(&formats); } static bool crtc_commit(struct wlr_drm_connector *conn, const struct wlr_drm_connector_state *state, struct wlr_drm_page_flip *page_flip, uint32_t flags, bool test_only) { struct wlr_drm_backend *drm = conn->backend; struct wlr_output *output = &conn->output; struct wlr_drm_crtc *crtc = conn->crtc; bool modeset = state->modeset; bool active = state->active; if (modeset && !register_planes_for_crtc(drm, crtc)) { return false; } uint32_t mode_id = crtc->mode_id; if (modeset) { if (!create_mode_blob(drm, conn, state, &mode_id)) { return false; } } uint32_t gamma_lut = crtc->gamma_lut; if (state->base->committed & WLR_OUTPUT_STATE_GAMMA_LUT) { // Fallback to legacy gamma interface when gamma properties are not // available (can happen on older Intel GPUs that support gamma but not // degamma). if (crtc->props.gamma_lut == 0) { if (!drm_legacy_crtc_set_gamma(drm, crtc, state->base->gamma_lut_size, state->base->gamma_lut)) { return false; } } else { if (!create_gamma_lut_blob(drm, state->base->gamma_lut_size, state->base->gamma_lut, &gamma_lut)) { return false; } } } struct wl_array fb_damage_clips_arr = {0}; uint32_t primary_fb_damage_clips = 0; if ((state->base->committed & WLR_OUTPUT_STATE_DAMAGE) && crtc->primary->props.fb_damage_clips != 0) { uint32_t *ptr = wl_array_add(&fb_damage_clips_arr, sizeof(primary_fb_damage_clips)); if (ptr == NULL) { return false; } create_fb_damage_clips_blob(drm, state->primary_fb->wlr_buf->width, state->primary_fb->wlr_buf->height, &state->base->damage, &primary_fb_damage_clips); *ptr = primary_fb_damage_clips; } bool prev_vrr_enabled = output->adaptive_sync_status == WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED; bool vrr_enabled = prev_vrr_enabled; if ((state->base->committed & WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED) && drm_connector_supports_vrr(conn)) { vrr_enabled = state->base->adaptive_sync_enabled; } if (test_only) { flags |= DRM_MODE_ATOMIC_TEST_ONLY; } if (modeset) { flags |= DRM_MODE_ATOMIC_ALLOW_MODESET; } if (!test_only && state->nonblock) { flags |= DRM_MODE_ATOMIC_NONBLOCK; } drmModeAtomicReq *req = drmModeAtomicAlloc(); if (req == NULL) { wlr_log(WLR_ERROR, "drmModeAtomicAlloc failed"); return false; } bool ok = add_prop(req, conn->id, conn->props.crtc_id, active ? crtc->id : 0); if (modeset && active && conn->props.link_status != 0) { ok = ok && add_prop(req, conn->id, conn->props.link_status, DRM_MODE_LINK_STATUS_GOOD); } if (active && conn->props.content_type != 0) { ok = ok && add_prop(req, conn->id, conn->props.content_type, DRM_MODE_CONTENT_TYPE_GRAPHICS); } // TODO: set "max bpc" ok = ok && add_prop(req, crtc->id, crtc->props.mode_id, mode_id) && add_prop(req, crtc->id, crtc->props.active, active); if (active) { if (crtc->props.gamma_lut != 0) { ok = ok && add_prop(req, crtc->id, crtc->props.gamma_lut, gamma_lut); } if (crtc->props.vrr_enabled != 0) { ok = ok && add_prop(req, crtc->id, crtc->props.vrr_enabled, vrr_enabled); } ok = ok && set_plane_props(crtc->primary, crtc->primary->liftoff_layer, state->primary_fb, 0, 0, 0) && set_plane_props(crtc->primary, crtc->liftoff_composition_layer, state->primary_fb, 0, 0, 0); liftoff_layer_set_property(crtc->primary->liftoff_layer, "FB_DAMAGE_CLIPS", primary_fb_damage_clips); liftoff_layer_set_property(crtc->liftoff_composition_layer, "FB_DAMAGE_CLIPS", primary_fb_damage_clips); if (state->base->committed & WLR_OUTPUT_STATE_LAYERS) { for (size_t i = 0; i < state->base->layers_len; i++) { const struct wlr_output_layer_state *layer_state = &state->base->layers[i]; ok = ok && set_layer_props(drm, layer_state, i + 1, &fb_damage_clips_arr); } } if (crtc->cursor) { if (drm_connector_is_cursor_visible(conn)) { ok = ok && set_plane_props(crtc->cursor, crtc->cursor->liftoff_layer, get_next_cursor_fb(conn), conn->cursor_x, conn->cursor_y, wl_list_length(&crtc->layers) + 1); } else { ok = ok && disable_plane(crtc->cursor); } } } else { ok = ok && disable_plane(crtc->primary); if (crtc->cursor) { ok = ok && disable_plane(crtc->cursor); } } if (!ok) { goto out; } int ret = liftoff_output_apply(crtc->liftoff, req, flags); if (ret != 0) { wlr_drm_conn_log(conn, test_only ? WLR_DEBUG : WLR_ERROR, "liftoff_output_apply failed: %s", strerror(-ret)); ok = false; goto out; } if (crtc->cursor && liftoff_layer_needs_composition(crtc->cursor->liftoff_layer)) { wlr_drm_conn_log(conn, WLR_DEBUG, "Failed to scan-out cursor plane"); ok = false; goto out; } ret = drmModeAtomicCommit(drm->fd, req, flags, page_flip); if (ret != 0) { wlr_drm_conn_log_errno(conn, test_only ? WLR_DEBUG : WLR_ERROR, "Atomic commit failed"); ok = false; goto out; } if (state->base->committed & WLR_OUTPUT_STATE_LAYERS) { for (size_t i = 0; i < state->base->layers_len; i++) { struct wlr_output_layer_state *layer_state = &state->base->layers[i]; struct wlr_drm_layer *layer = get_drm_layer(drm, layer_state->layer); layer_state->accepted = !liftoff_layer_needs_composition(layer->liftoff); if (!test_only && !layer_state->accepted) { update_layer_feedback(drm, layer); } } } out: drmModeAtomicFree(req); if (ok && !test_only) { commit_blob(drm, &crtc->mode_id, mode_id); commit_blob(drm, &crtc->gamma_lut, gamma_lut); if (vrr_enabled != prev_vrr_enabled) { output->adaptive_sync_status = vrr_enabled ? WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED : WLR_OUTPUT_ADAPTIVE_SYNC_DISABLED; wlr_drm_conn_log(conn, WLR_DEBUG, "VRR %s", vrr_enabled ? "enabled" : "disabled"); } } else { rollback_blob(drm, &crtc->mode_id, mode_id); rollback_blob(drm, &crtc->gamma_lut, gamma_lut); } uint32_t *fb_damage_clips_ptr; wl_array_for_each(fb_damage_clips_ptr, &fb_damage_clips_arr) { if (drmModeDestroyPropertyBlob(drm->fd, *fb_damage_clips_ptr) != 0) { wlr_log_errno(WLR_ERROR, "Failed to destroy FB_DAMAGE_CLIPS property blob"); } } return ok; } const struct wlr_drm_interface liftoff_iface = { .init = init, .finish = finish, .crtc_commit = crtc_commit, }; wlroots-0.17.1/backend/drm/meson.build000066400000000000000000000021011454110342200176120ustar00rootroot00000000000000hwdata = dependency( 'hwdata', required: 'drm' in backends, native: true, not_found_message: 'Required for the DRM backend.', ) libdisplay_info = dependency( 'libdisplay-info', required: 'drm' in backends, fallback: 'libdisplay-info', not_found_message: 'Required for the DRM backend.', ) libliftoff = dependency( 'libliftoff', version: '>=0.4.0', fallback: 'libliftoff', required: false, ) if not (hwdata.found() and libdisplay_info.found() and features['session']) subdir_done() endif hwdata_dir = hwdata.get_variable(pkgconfig: 'pkgdatadir') pnpids_c = custom_target( 'pnpids.c', output: 'pnpids.c', input: files(hwdata_dir / 'pnp.ids'), feed: true, capture: true, command: files('gen_pnpids.sh'), ) wlr_files += pnpids_c wlr_files += files( 'atomic.c', 'backend.c', 'drm.c', 'legacy.c', 'monitor.c', 'properties.c', 'renderer.c', 'util.c', ) if libliftoff.found() wlr_files += files('libliftoff.c') endif features += { 'drm-backend': true } internal_features += { 'libliftoff': libliftoff.found() } wlr_deps += libdisplay_info wlr_deps += libliftoff wlroots-0.17.1/backend/drm/monitor.c000066400000000000000000000062421454110342200173150ustar00rootroot00000000000000#include #include #include "backend/drm/monitor.h" #include "backend/multi.h" #include "backend/session/session.h" static void drm_backend_monitor_destroy(struct wlr_drm_backend_monitor* monitor) { wl_list_remove(&monitor->session_add_drm_card.link); wl_list_remove(&monitor->session_destroy.link); wl_list_remove(&monitor->primary_drm_destroy.link); wl_list_remove(&monitor->multi_destroy.link); free(monitor); } static void handle_add_drm_card(struct wl_listener *listener, void *data) { struct wlr_session_add_event *event = data; struct wlr_drm_backend_monitor *backend_monitor = wl_container_of(listener, backend_monitor, session_add_drm_card); struct wlr_device *dev = session_open_if_kms(backend_monitor->session, event->path); if (!dev) { wlr_log(WLR_ERROR, "Unable to open %s as DRM device", event->path); return; } wlr_log(WLR_DEBUG, "Creating DRM backend for %s after hotplug", event->path); struct wlr_backend *child_drm = wlr_drm_backend_create( backend_monitor->session->display, backend_monitor->session, dev, backend_monitor->primary_drm); if (!child_drm) { wlr_log(WLR_ERROR, "Failed to create DRM backend after hotplug"); return; } if (!wlr_multi_backend_add(backend_monitor->multi, child_drm)) { wlr_log(WLR_ERROR, "Failed to add new drm backend to multi backend"); wlr_backend_destroy(child_drm); return; } if (!wlr_backend_start(child_drm)) { wlr_log(WLR_ERROR, "Failed to start new child DRM backend"); wlr_backend_destroy(child_drm); } } static void handle_session_destroy(struct wl_listener *listener, void *data) { struct wlr_drm_backend_monitor *backend_monitor = wl_container_of(listener, backend_monitor, session_destroy); drm_backend_monitor_destroy(backend_monitor); } static void handle_primary_drm_destroy(struct wl_listener *listener, void *data) { struct wlr_drm_backend_monitor *backend_monitor = wl_container_of(listener, backend_monitor, primary_drm_destroy); drm_backend_monitor_destroy(backend_monitor); } static void handle_multi_destroy(struct wl_listener *listener, void *data) { struct wlr_drm_backend_monitor *backend_monitor = wl_container_of(listener, backend_monitor, multi_destroy); drm_backend_monitor_destroy(backend_monitor); } struct wlr_drm_backend_monitor *drm_backend_monitor_create( struct wlr_backend *multi, struct wlr_backend *primary_drm, struct wlr_session *session) { struct wlr_drm_backend_monitor *monitor = calloc(1, sizeof(*monitor)); if (!monitor) { wlr_log_errno(WLR_ERROR, "Allocation failed"); return NULL; } monitor->multi = multi; monitor->primary_drm = primary_drm; monitor->session = session; monitor->session_add_drm_card.notify = handle_add_drm_card; wl_signal_add(&session->events.add_drm_card, &monitor->session_add_drm_card); monitor->session_destroy.notify = handle_session_destroy; wl_signal_add(&session->events.destroy, &monitor->session_destroy); monitor->primary_drm_destroy.notify = handle_primary_drm_destroy; wl_signal_add(&primary_drm->events.destroy, &monitor->primary_drm_destroy); monitor->multi_destroy.notify = handle_multi_destroy; wl_signal_add(&multi->events.destroy, &monitor->multi_destroy); return monitor; } wlroots-0.17.1/backend/drm/properties.c000066400000000000000000000123711454110342200200220ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include #include "backend/drm/properties.h" /* * Creates a mapping between property names and an array index where to store * the ids. The prop_info arrays must be sorted by name, as bsearch is used to * search them. */ struct prop_info { const char *name; size_t index; }; static const struct prop_info connector_info[] = { #define INDEX(name) (offsetof(union wlr_drm_connector_props, name) / sizeof(uint32_t)) { "CRTC_ID", INDEX(crtc_id) }, { "DPMS", INDEX(dpms) }, { "EDID", INDEX(edid) }, { "PATH", INDEX(path) }, { "content type", INDEX(content_type) }, { "link-status", INDEX(link_status) }, { "max bpc", INDEX(max_bpc) }, { "non-desktop", INDEX(non_desktop) }, { "panel orientation", INDEX(panel_orientation) }, { "subconnector", INDEX(subconnector) }, { "vrr_capable", INDEX(vrr_capable) }, #undef INDEX }; static const struct prop_info crtc_info[] = { #define INDEX(name) (offsetof(union wlr_drm_crtc_props, name) / sizeof(uint32_t)) { "ACTIVE", INDEX(active) }, { "GAMMA_LUT", INDEX(gamma_lut) }, { "GAMMA_LUT_SIZE", INDEX(gamma_lut_size) }, { "MODE_ID", INDEX(mode_id) }, { "VRR_ENABLED", INDEX(vrr_enabled) }, #undef INDEX }; static const struct prop_info plane_info[] = { #define INDEX(name) (offsetof(union wlr_drm_plane_props, name) / sizeof(uint32_t)) { "CRTC_H", INDEX(crtc_h) }, { "CRTC_ID", INDEX(crtc_id) }, { "CRTC_W", INDEX(crtc_w) }, { "CRTC_X", INDEX(crtc_x) }, { "CRTC_Y", INDEX(crtc_y) }, { "FB_DAMAGE_CLIPS", INDEX(fb_damage_clips) }, { "FB_ID", INDEX(fb_id) }, { "IN_FORMATS", INDEX(in_formats) }, { "SRC_H", INDEX(src_h) }, { "SRC_W", INDEX(src_w) }, { "SRC_X", INDEX(src_x) }, { "SRC_Y", INDEX(src_y) }, { "rotation", INDEX(rotation) }, { "type", INDEX(type) }, #undef INDEX }; static int cmp_prop_info(const void *arg1, const void *arg2) { const char *key = arg1; const struct prop_info *elem = arg2; return strcmp(key, elem->name); } static bool scan_properties(int fd, uint32_t id, uint32_t type, uint32_t *result, const struct prop_info *info, size_t info_len) { drmModeObjectProperties *props = drmModeObjectGetProperties(fd, id, type); if (!props) { wlr_log_errno(WLR_ERROR, "Failed to get DRM object properties"); return false; } for (uint32_t i = 0; i < props->count_props; ++i) { drmModePropertyRes *prop = drmModeGetProperty(fd, props->props[i]); if (!prop) { wlr_log_errno(WLR_ERROR, "Failed to get DRM object property"); continue; } const struct prop_info *p = bsearch(prop->name, info, info_len, sizeof(info[0]), cmp_prop_info); if (p) { result[p->index] = prop->prop_id; } drmModeFreeProperty(prop); } drmModeFreeObjectProperties(props); return true; } bool get_drm_connector_props(int fd, uint32_t id, union wlr_drm_connector_props *out) { return scan_properties(fd, id, DRM_MODE_OBJECT_CONNECTOR, out->props, connector_info, sizeof(connector_info) / sizeof(connector_info[0])); } bool get_drm_crtc_props(int fd, uint32_t id, union wlr_drm_crtc_props *out) { return scan_properties(fd, id, DRM_MODE_OBJECT_CRTC, out->props, crtc_info, sizeof(crtc_info) / sizeof(crtc_info[0])); } bool get_drm_plane_props(int fd, uint32_t id, union wlr_drm_plane_props *out) { return scan_properties(fd, id, DRM_MODE_OBJECT_PLANE, out->props, plane_info, sizeof(plane_info) / sizeof(plane_info[0])); } bool get_drm_prop(int fd, uint32_t obj, uint32_t prop, uint64_t *ret) { drmModeObjectProperties *props = drmModeObjectGetProperties(fd, obj, DRM_MODE_OBJECT_ANY); if (!props) { return false; } bool found = false; for (uint32_t i = 0; i < props->count_props; ++i) { if (props->props[i] == prop) { *ret = props->prop_values[i]; found = true; break; } } drmModeFreeObjectProperties(props); return found; } void *get_drm_prop_blob(int fd, uint32_t obj, uint32_t prop, size_t *ret_len) { uint64_t blob_id; if (!get_drm_prop(fd, obj, prop, &blob_id)) { return NULL; } drmModePropertyBlobRes *blob = drmModeGetPropertyBlob(fd, blob_id); if (!blob) { return NULL; } void *ptr = malloc(blob->length); if (!ptr) { drmModeFreePropertyBlob(blob); return NULL; } memcpy(ptr, blob->data, blob->length); *ret_len = blob->length; drmModeFreePropertyBlob(blob); return ptr; } char *get_drm_prop_enum(int fd, uint32_t obj, uint32_t prop_id) { uint64_t value; if (!get_drm_prop(fd, obj, prop_id, &value)) { return NULL; } drmModePropertyRes *prop = drmModeGetProperty(fd, prop_id); if (!prop) { return NULL; } char *str = NULL; for (int i = 0; i < prop->count_enums; i++) { if (prop->enums[i].value == value) { str = strdup(prop->enums[i].name); break; } } drmModeFreeProperty(prop); return str; } bool introspect_drm_prop_range(int fd, uint32_t prop_id, uint64_t *min, uint64_t *max) { drmModePropertyRes *prop = drmModeGetProperty(fd, prop_id); if (!prop) { return false; } if (drmModeGetPropertyType(prop) != DRM_MODE_PROP_RANGE) { drmModeFreeProperty(prop); return false; } assert(prop->count_values == 2); if (min != NULL) { *min = prop->values[0]; } if (max != NULL) { *max = prop->values[1]; } drmModeFreeProperty(prop); return true; } wlroots-0.17.1/backend/drm/renderer.c000066400000000000000000000265601454110342200174410ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include #include #include #include #include #include "backend/drm/drm.h" #include "backend/drm/util.h" #include "render/drm_format_set.h" #include "render/allocator/allocator.h" #include "render/pixel_format.h" #include "render/wlr_renderer.h" bool init_drm_renderer(struct wlr_drm_backend *drm, struct wlr_drm_renderer *renderer) { renderer->backend = drm; renderer->wlr_rend = renderer_autocreate_with_drm_fd(drm->fd); if (!renderer->wlr_rend) { wlr_log(WLR_ERROR, "Failed to create renderer"); return false; } renderer->allocator = allocator_autocreate_with_drm_fd(&drm->backend, renderer->wlr_rend, drm->fd); if (renderer->allocator == NULL) { wlr_log(WLR_ERROR, "Failed to create allocator"); wlr_renderer_destroy(renderer->wlr_rend); return false; } return true; } void finish_drm_renderer(struct wlr_drm_renderer *renderer) { if (!renderer) { return; } wlr_allocator_destroy(renderer->allocator); wlr_renderer_destroy(renderer->wlr_rend); } static void finish_drm_surface(struct wlr_drm_surface *surf) { if (!surf || !surf->renderer) { return; } wlr_swapchain_destroy(surf->swapchain); *surf = (struct wlr_drm_surface){0}; } bool init_drm_surface(struct wlr_drm_surface *surf, struct wlr_drm_renderer *renderer, int width, int height, const struct wlr_drm_format *drm_format) { if (surf->swapchain != NULL && surf->swapchain->width == width && surf->swapchain->height == height) { return true; } finish_drm_surface(surf); surf->swapchain = wlr_swapchain_create(renderer->allocator, width, height, drm_format); if (surf->swapchain == NULL) { wlr_log(WLR_ERROR, "Failed to create swapchain"); return false; } surf->renderer = renderer; return true; } struct wlr_buffer *drm_surface_blit(struct wlr_drm_surface *surf, struct wlr_buffer *buffer) { struct wlr_renderer *renderer = surf->renderer->wlr_rend; if (surf->swapchain->width != buffer->width || surf->swapchain->height != buffer->height) { wlr_log(WLR_ERROR, "Surface size doesn't match buffer size"); return NULL; } struct wlr_texture *tex = wlr_texture_from_buffer(renderer, buffer); if (tex == NULL) { wlr_log(WLR_ERROR, "Failed to import source buffer into multi-GPU renderer"); return NULL; } struct wlr_buffer *dst = wlr_swapchain_acquire(surf->swapchain, NULL); if (!dst) { wlr_log(WLR_ERROR, "Failed to acquire multi-GPU swapchain buffer"); goto error_tex; } struct wlr_render_pass *pass = wlr_renderer_begin_buffer_pass(renderer, dst, NULL); if (pass == NULL) { wlr_log(WLR_ERROR, "Failed to begin render pass with multi-GPU destination buffer"); goto error_dst; } wlr_render_pass_add_texture(pass, &(struct wlr_render_texture_options){ .texture = tex, .blend_mode = WLR_RENDER_BLEND_MODE_NONE, }); if (!wlr_render_pass_submit(pass)) { wlr_log(WLR_ERROR, "Failed to submit multi-GPU render pass"); goto error_dst; } wlr_texture_destroy(tex); return dst; error_dst: wlr_buffer_unlock(dst); error_tex: wlr_texture_destroy(tex); return NULL; } void drm_plane_finish_surface(struct wlr_drm_plane *plane) { if (!plane) { return; } drm_fb_clear(&plane->queued_fb); drm_fb_clear(&plane->current_fb); finish_drm_surface(&plane->mgpu_surf); } bool drm_plane_pick_render_format(struct wlr_drm_plane *plane, struct wlr_drm_format *fmt, struct wlr_drm_renderer *renderer) { const struct wlr_drm_format_set *render_formats = wlr_renderer_get_render_formats(renderer->wlr_rend); if (render_formats == NULL) { wlr_log(WLR_ERROR, "Failed to get render formats"); return false; } const struct wlr_drm_format_set *plane_formats = &plane->formats; uint32_t format = DRM_FORMAT_ARGB8888; if (!wlr_drm_format_set_get(&plane->formats, format)) { const struct wlr_pixel_format_info *format_info = drm_get_pixel_format_info(format); assert(format_info != NULL && format_info->opaque_substitute != DRM_FORMAT_INVALID); format = format_info->opaque_substitute; } const struct wlr_drm_format *render_format = wlr_drm_format_set_get(render_formats, format); if (render_format == NULL) { wlr_log(WLR_DEBUG, "Renderer doesn't support format 0x%"PRIX32, format); return false; } const struct wlr_drm_format *plane_format = wlr_drm_format_set_get(plane_formats, format); if (plane_format == NULL) { wlr_log(WLR_DEBUG, "Plane %"PRIu32" doesn't support format 0x%"PRIX32, plane->id, format); return false; } if (!wlr_drm_format_intersect(fmt, plane_format, render_format)) { wlr_log(WLR_DEBUG, "Failed to intersect plane and render " "modifiers for format 0x%"PRIX32, format); return false; } if (fmt->len == 0) { wlr_drm_format_finish(fmt); wlr_log(WLR_DEBUG, "Failed to find matching plane and renderer modifiers"); return false; } return true; } void drm_fb_clear(struct wlr_drm_fb **fb_ptr) { if (*fb_ptr == NULL) { return; } struct wlr_drm_fb *fb = *fb_ptr; wlr_buffer_unlock(fb->wlr_buf); // may destroy the buffer *fb_ptr = NULL; } struct wlr_drm_fb *drm_fb_lock(struct wlr_drm_fb *fb) { wlr_buffer_lock(fb->wlr_buf); return fb; } static void drm_fb_handle_destroy(struct wlr_addon *addon) { struct wlr_drm_fb *fb = wl_container_of(addon, fb, addon); drm_fb_destroy(fb); } static const struct wlr_addon_interface fb_addon_impl = { .name = "wlr_drm_fb", .destroy = drm_fb_handle_destroy, }; static uint32_t get_fb_for_bo(struct wlr_drm_backend *drm, struct wlr_dmabuf_attributes *dmabuf, uint32_t handles[static 4]) { uint64_t modifiers[4] = {0}; for (int i = 0; i < dmabuf->n_planes; i++) { // KMS requires all BO planes to have the same modifier modifiers[i] = dmabuf->modifier; } uint32_t id = 0; if (drm->addfb2_modifiers && dmabuf->modifier != DRM_FORMAT_MOD_INVALID) { if (drmModeAddFB2WithModifiers(drm->fd, dmabuf->width, dmabuf->height, dmabuf->format, handles, dmabuf->stride, dmabuf->offset, modifiers, &id, DRM_MODE_FB_MODIFIERS) != 0) { wlr_log_errno(WLR_DEBUG, "drmModeAddFB2WithModifiers failed"); } } else { if (dmabuf->modifier != DRM_FORMAT_MOD_INVALID && dmabuf->modifier != DRM_FORMAT_MOD_LINEAR) { wlr_log(WLR_ERROR, "Cannot import DRM framebuffer with explicit " "modifier 0x%"PRIX64, dmabuf->modifier); return 0; } int ret = drmModeAddFB2(drm->fd, dmabuf->width, dmabuf->height, dmabuf->format, handles, dmabuf->stride, dmabuf->offset, &id, 0); if (ret != 0 && dmabuf->format == DRM_FORMAT_ARGB8888 && dmabuf->n_planes == 1 && dmabuf->offset[0] == 0) { // Some big-endian machines don't support drmModeAddFB2. Try a // last-resort fallback for ARGB8888 buffers, like Xorg's // modesetting driver does. wlr_log(WLR_DEBUG, "drmModeAddFB2 failed (%s), falling back to " "legacy drmModeAddFB", strerror(-ret)); uint32_t depth = 32; uint32_t bpp = 32; ret = drmModeAddFB(drm->fd, dmabuf->width, dmabuf->height, depth, bpp, dmabuf->stride[0], handles[0], &id); if (ret != 0) { wlr_log_errno(WLR_DEBUG, "drmModeAddFB failed"); } } else if (ret != 0) { wlr_log_errno(WLR_DEBUG, "drmModeAddFB2 failed"); } } return id; } static void close_all_bo_handles(struct wlr_drm_backend *drm, uint32_t handles[static 4]) { for (int i = 0; i < 4; ++i) { if (handles[i] == 0) { continue; } // If multiple planes share the same BO handle, avoid double-closing it bool already_closed = false; for (int j = 0; j < i; ++j) { if (handles[i] == handles[j]) { already_closed = true; break; } } if (already_closed) { continue; } if (drmCloseBufferHandle(drm->fd, handles[i]) != 0) { wlr_log_errno(WLR_ERROR, "drmCloseBufferHandle failed"); } } } static void drm_poisoned_fb_handle_destroy(struct wlr_addon *addon) { wlr_addon_finish(addon); free(addon); } static const struct wlr_addon_interface poisoned_fb_addon_impl = { .name = "wlr_drm_poisoned_fb", .destroy = drm_poisoned_fb_handle_destroy, }; static bool is_buffer_poisoned(struct wlr_drm_backend *drm, struct wlr_buffer *buf) { return wlr_addon_find(&buf->addons, drm, &poisoned_fb_addon_impl) != NULL; } /** * Mark the buffer as "poisoned", ie. it cannot be imported into KMS. This * allows us to avoid repeatedly trying to import it when it's not * scanout-capable. */ static void poison_buffer(struct wlr_drm_backend *drm, struct wlr_buffer *buf) { struct wlr_addon *addon = calloc(1, sizeof(*addon)); if (addon == NULL) { wlr_log_errno(WLR_ERROR, "Allocation failed"); return; } wlr_addon_init(addon, &buf->addons, drm, &poisoned_fb_addon_impl); wlr_log(WLR_DEBUG, "Poisoning buffer"); } static struct wlr_drm_fb *drm_fb_create(struct wlr_drm_backend *drm, struct wlr_buffer *buf, const struct wlr_drm_format_set *formats) { struct wlr_dmabuf_attributes attribs; if (!wlr_buffer_get_dmabuf(buf, &attribs)) { wlr_log(WLR_DEBUG, "Failed to get DMA-BUF from buffer"); return NULL; } if (is_buffer_poisoned(drm, buf)) { wlr_log(WLR_DEBUG, "Buffer is poisoned"); return NULL; } struct wlr_drm_fb *fb = calloc(1, sizeof(*fb)); if (!fb) { wlr_log_errno(WLR_ERROR, "Allocation failed"); return NULL; } if (formats && !wlr_drm_format_set_has(formats, attribs.format, attribs.modifier)) { // The format isn't supported by the plane. Try stripping the alpha // channel, if any. const struct wlr_pixel_format_info *info = drm_get_pixel_format_info(attribs.format); if (info != NULL && info->opaque_substitute != DRM_FORMAT_INVALID && wlr_drm_format_set_has(formats, info->opaque_substitute, attribs.modifier)) { attribs.format = info->opaque_substitute; } else { wlr_log(WLR_DEBUG, "Buffer format 0x%"PRIX32" with modifier " "0x%"PRIX64" cannot be scanned out", attribs.format, attribs.modifier); goto error_fb; } } uint32_t handles[4] = {0}; for (int i = 0; i < attribs.n_planes; ++i) { int ret = drmPrimeFDToHandle(drm->fd, attribs.fd[i], &handles[i]); if (ret != 0) { wlr_log_errno(WLR_DEBUG, "drmPrimeFDToHandle failed"); goto error_bo_handle; } } fb->id = get_fb_for_bo(drm, &attribs, handles); if (!fb->id) { wlr_log(WLR_DEBUG, "Failed to import BO in KMS"); poison_buffer(drm, buf); goto error_bo_handle; } close_all_bo_handles(drm, handles); fb->backend = drm; fb->wlr_buf = buf; wlr_addon_init(&fb->addon, &buf->addons, drm, &fb_addon_impl); wl_list_insert(&drm->fbs, &fb->link); return fb; error_bo_handle: close_all_bo_handles(drm, handles); error_fb: free(fb); return NULL; } void drm_fb_destroy(struct wlr_drm_fb *fb) { struct wlr_drm_backend *drm = fb->backend; wl_list_remove(&fb->link); wlr_addon_finish(&fb->addon); if (drmModeRmFB(drm->fd, fb->id) != 0) { wlr_log(WLR_ERROR, "drmModeRmFB failed"); } free(fb); } bool drm_fb_import(struct wlr_drm_fb **fb_ptr, struct wlr_drm_backend *drm, struct wlr_buffer *buf, const struct wlr_drm_format_set *formats) { struct wlr_drm_fb *fb; struct wlr_addon *addon = wlr_addon_find(&buf->addons, drm, &fb_addon_impl); if (addon != NULL) { fb = wl_container_of(addon, fb, addon); } else { fb = drm_fb_create(drm, buf, formats); if (!fb) { return false; } } wlr_buffer_lock(buf); drm_fb_move(fb_ptr, &fb); return true; } void drm_fb_move(struct wlr_drm_fb **new, struct wlr_drm_fb **old) { drm_fb_clear(new); *new = *old; *old = NULL; } wlroots-0.17.1/backend/drm/util.c000066400000000000000000000160311454110342200166000ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include #include #include #include #include "backend/drm/drm.h" #include "backend/drm/util.h" int32_t calculate_refresh_rate(const drmModeModeInfo *mode) { int32_t refresh = (mode->clock * 1000000LL / mode->htotal + mode->vtotal / 2) / mode->vtotal; if (mode->flags & DRM_MODE_FLAG_INTERLACE) { refresh *= 2; } if (mode->flags & DRM_MODE_FLAG_DBLSCAN) { refresh /= 2; } if (mode->vscan > 1) { refresh /= mode->vscan; } return refresh; } enum wlr_output_mode_aspect_ratio get_picture_aspect_ratio(const drmModeModeInfo *mode) { switch (mode->flags & DRM_MODE_FLAG_PIC_AR_MASK) { case DRM_MODE_FLAG_PIC_AR_NONE: return WLR_OUTPUT_MODE_ASPECT_RATIO_NONE; case DRM_MODE_FLAG_PIC_AR_4_3: return WLR_OUTPUT_MODE_ASPECT_RATIO_4_3; case DRM_MODE_FLAG_PIC_AR_16_9: return WLR_OUTPUT_MODE_ASPECT_RATIO_16_9; case DRM_MODE_FLAG_PIC_AR_64_27: return WLR_OUTPUT_MODE_ASPECT_RATIO_64_27; case DRM_MODE_FLAG_PIC_AR_256_135: return WLR_OUTPUT_MODE_ASPECT_RATIO_256_135; default: wlr_log(WLR_ERROR, "Unknown mode picture aspect ratio: %u", mode->flags & DRM_MODE_FLAG_PIC_AR_MASK); return WLR_OUTPUT_MODE_ASPECT_RATIO_NONE; } } void parse_edid(struct wlr_drm_connector *conn, size_t len, const uint8_t *data) { struct wlr_output *output = &conn->output; free(output->make); free(output->model); free(output->serial); output->make = NULL; output->model = NULL; output->serial = NULL; struct di_info *info = di_info_parse_edid(data, len); if (info == NULL) { wlr_log(WLR_ERROR, "Failed to parse EDID"); return; } const struct di_edid *edid = di_info_get_edid(info); const struct di_edid_vendor_product *vendor_product = di_edid_get_vendor_product(edid); char pnp_id[] = { vendor_product->manufacturer[0], vendor_product->manufacturer[1], vendor_product->manufacturer[2], '\0', }; const char *manu = get_pnp_manufacturer(vendor_product->manufacturer); if (!manu) { manu = pnp_id; } output->make = strdup(manu); output->model = di_info_get_model(info); output->serial = di_info_get_serial(info); di_info_destroy(info); } const char *drm_connector_status_str(drmModeConnection status) { switch (status) { case DRM_MODE_CONNECTED: return "connected"; case DRM_MODE_DISCONNECTED: return "disconnected"; case DRM_MODE_UNKNOWNCONNECTION: return "unknown"; } return ""; } static bool is_taken(size_t n, const uint32_t arr[static n], uint32_t key) { for (size_t i = 0; i < n; ++i) { if (arr[i] == key) { return true; } } return false; } /* * Store all of the non-recursive state in a struct, so we aren't literally * passing 12 arguments to a function. */ struct match_state { const size_t num_objs; const uint32_t *restrict objs; const size_t num_res; size_t score; size_t replaced; uint32_t *restrict res; uint32_t *restrict best; const uint32_t *restrict orig; bool exit_early; }; /* * skips: The number of SKIP elements encountered so far. * score: The number of resources we've matched so far. * replaced: The number of changes from the original solution. * i: The index of the current element. * * This tries to match a solution as close to st->orig as it can. * * Returns whether we've set a new best element with this solution. */ static bool match_obj_(struct match_state *st, size_t skips, size_t score, size_t replaced, size_t i) { // Finished if (i >= st->num_res) { if (score > st->score || (score == st->score && replaced < st->replaced)) { st->score = score; st->replaced = replaced; memcpy(st->best, st->res, sizeof(st->best[0]) * st->num_res); st->exit_early = (st->score == st->num_res - skips || st->score == st->num_objs) && st->replaced == 0; return true; } else { return false; } } if (st->orig[i] == SKIP) { st->res[i] = SKIP; return match_obj_(st, skips + 1, score, replaced, i + 1); } bool has_best = false; /* * Attempt to use the current solution first, to try and avoid * recalculating everything */ if (st->orig[i] != UNMATCHED && !is_taken(i, st->res, st->orig[i])) { st->res[i] = st->orig[i]; size_t obj_score = st->objs[st->res[i]] != 0 ? 1 : 0; if (match_obj_(st, skips, score + obj_score, replaced, i + 1)) { has_best = true; } } if (st->orig[i] == UNMATCHED) { st->res[i] = UNMATCHED; if (match_obj_(st, skips, score, replaced, i + 1)) { has_best = true; } } if (st->exit_early) { return true; } if (st->orig[i] != UNMATCHED) { ++replaced; } for (size_t candidate = 0; candidate < st->num_objs; ++candidate) { // We tried this earlier if (candidate == st->orig[i]) { continue; } // Not compatible if (!(st->objs[candidate] & (1 << i))) { continue; } // Already taken if (is_taken(i, st->res, candidate)) { continue; } st->res[i] = candidate; size_t obj_score = st->objs[candidate] != 0 ? 1 : 0; if (match_obj_(st, skips, score + obj_score, replaced, i + 1)) { has_best = true; } if (st->exit_early) { return true; } } if (has_best) { return true; } // Maybe this resource can't be matched st->res[i] = UNMATCHED; return match_obj_(st, skips, score, replaced, i + 1); } size_t match_obj(size_t num_objs, const uint32_t objs[static restrict num_objs], size_t num_res, const uint32_t res[static restrict num_res], uint32_t out[static restrict num_res]) { uint32_t solution[num_res]; for (size_t i = 0; i < num_res; ++i) { solution[i] = UNMATCHED; } struct match_state st = { .num_objs = num_objs, .num_res = num_res, .score = 0, .replaced = SIZE_MAX, .objs = objs, .res = solution, .best = out, .orig = res, .exit_early = false, }; match_obj_(&st, 0, 0, 0, 0); return st.score; } void generate_cvt_mode(drmModeModeInfo *mode, int hdisplay, int vdisplay, float vrefresh) { // TODO: depending on capabilities advertised in the EDID, use reduced // blanking if possible (and update sync polarity) struct di_cvt_options options = { .red_blank_ver = DI_CVT_REDUCED_BLANKING_NONE, .h_pixels = hdisplay, .v_lines = vdisplay, .ip_freq_rqd = vrefresh ? vrefresh : 60, }; struct di_cvt_timing timing; di_cvt_compute(&timing, &options); uint16_t hsync_start = hdisplay + timing.h_front_porch; uint16_t vsync_start = timing.v_lines_rnd + timing.v_front_porch; uint16_t hsync_end = hsync_start + timing.h_sync; uint16_t vsync_end = vsync_start + timing.v_sync; *mode = (drmModeModeInfo){ .clock = roundf(timing.act_pixel_freq * 1000), .hdisplay = hdisplay, .vdisplay = timing.v_lines_rnd, .hsync_start = hsync_start, .vsync_start = vsync_start, .hsync_end = hsync_end, .vsync_end = vsync_end, .htotal = hsync_end + timing.h_back_porch, .vtotal = vsync_end + timing.v_back_porch, .vrefresh = roundf(timing.act_frame_rate), .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC, }; snprintf(mode->name, sizeof(mode->name), "%dx%d", hdisplay, vdisplay); } wlroots-0.17.1/backend/headless/000077500000000000000000000000001454110342200164645ustar00rootroot00000000000000wlroots-0.17.1/backend/headless/backend.c000066400000000000000000000046311454110342200202230ustar00rootroot00000000000000#include #include #include #include #include "backend/headless.h" struct wlr_headless_backend *headless_backend_from_backend( struct wlr_backend *wlr_backend) { assert(wlr_backend_is_headless(wlr_backend)); struct wlr_headless_backend *backend = wl_container_of(wlr_backend, backend, backend); return backend; } static bool backend_start(struct wlr_backend *wlr_backend) { struct wlr_headless_backend *backend = headless_backend_from_backend(wlr_backend); wlr_log(WLR_INFO, "Starting headless backend"); struct wlr_headless_output *output; wl_list_for_each(output, &backend->outputs, link) { wl_signal_emit_mutable(&backend->backend.events.new_output, &output->wlr_output); } backend->started = true; return true; } static void backend_destroy(struct wlr_backend *wlr_backend) { struct wlr_headless_backend *backend = headless_backend_from_backend(wlr_backend); if (!wlr_backend) { return; } wlr_backend_finish(wlr_backend); struct wlr_headless_output *output, *output_tmp; wl_list_for_each_safe(output, output_tmp, &backend->outputs, link) { wlr_output_destroy(&output->wlr_output); } wl_list_remove(&backend->display_destroy.link); free(backend); } static uint32_t get_buffer_caps(struct wlr_backend *wlr_backend) { return WLR_BUFFER_CAP_DATA_PTR | WLR_BUFFER_CAP_DMABUF | WLR_BUFFER_CAP_SHM; } static const struct wlr_backend_impl backend_impl = { .start = backend_start, .destroy = backend_destroy, .get_buffer_caps = get_buffer_caps, }; static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_headless_backend *backend = wl_container_of(listener, backend, display_destroy); backend_destroy(&backend->backend); } struct wlr_backend *wlr_headless_backend_create(struct wl_display *display) { wlr_log(WLR_INFO, "Creating headless backend"); struct wlr_headless_backend *backend = calloc(1, sizeof(*backend)); if (!backend) { wlr_log(WLR_ERROR, "Failed to allocate wlr_headless_backend"); return NULL; } wlr_backend_init(&backend->backend, &backend_impl); backend->display = display; wl_list_init(&backend->outputs); backend->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &backend->display_destroy); return &backend->backend; } bool wlr_backend_is_headless(struct wlr_backend *backend) { return backend->impl == &backend_impl; } wlroots-0.17.1/backend/headless/meson.build000066400000000000000000000000611454110342200206230ustar00rootroot00000000000000wlr_files += files( 'backend.c', 'output.c', ) wlroots-0.17.1/backend/headless/output.c000066400000000000000000000100111454110342200201610ustar00rootroot00000000000000#include #include #include #include #include #include #include "backend/headless.h" #include "types/wlr_output.h" static const uint32_t SUPPORTED_OUTPUT_STATE = WLR_OUTPUT_STATE_BACKEND_OPTIONAL | WLR_OUTPUT_STATE_BUFFER | WLR_OUTPUT_STATE_ENABLED | WLR_OUTPUT_STATE_MODE; static size_t last_output_num = 0; static struct wlr_headless_output *headless_output_from_output( struct wlr_output *wlr_output) { assert(wlr_output_is_headless(wlr_output)); struct wlr_headless_output *output = wl_container_of(wlr_output, output, wlr_output); return output; } static void output_update_refresh(struct wlr_headless_output *output, int32_t refresh) { if (refresh <= 0) { refresh = HEADLESS_DEFAULT_REFRESH; } output->frame_delay = 1000000 / refresh; } static bool output_test(struct wlr_output *wlr_output, const struct wlr_output_state *state) { uint32_t unsupported = state->committed & ~SUPPORTED_OUTPUT_STATE; if (unsupported != 0) { wlr_log(WLR_DEBUG, "Unsupported output state fields: 0x%"PRIx32, unsupported); return false; } if (state->committed & WLR_OUTPUT_STATE_MODE) { assert(state->mode_type == WLR_OUTPUT_STATE_MODE_CUSTOM); } if (state->committed & WLR_OUTPUT_STATE_LAYERS) { for (size_t i = 0; i < state->layers_len; i++) { state->layers[i].accepted = true; } } return true; } static bool output_commit(struct wlr_output *wlr_output, const struct wlr_output_state *state) { struct wlr_headless_output *output = headless_output_from_output(wlr_output); if (!output_test(wlr_output, state)) { return false; } if (state->committed & WLR_OUTPUT_STATE_MODE) { output_update_refresh(output, state->custom_mode.refresh); } if (output_pending_enabled(wlr_output, state)) { struct wlr_output_event_present present_event = { .commit_seq = wlr_output->commit_seq + 1, .presented = true, }; output_defer_present(wlr_output, present_event); wl_event_source_timer_update(output->frame_timer, output->frame_delay); } return true; } static void output_destroy(struct wlr_output *wlr_output) { struct wlr_headless_output *output = headless_output_from_output(wlr_output); wl_list_remove(&output->link); wl_event_source_remove(output->frame_timer); free(output); } static const struct wlr_output_impl output_impl = { .destroy = output_destroy, .commit = output_commit, }; bool wlr_output_is_headless(struct wlr_output *wlr_output) { return wlr_output->impl == &output_impl; } static int signal_frame(void *data) { struct wlr_headless_output *output = data; wlr_output_send_frame(&output->wlr_output); return 0; } struct wlr_output *wlr_headless_add_output(struct wlr_backend *wlr_backend, unsigned int width, unsigned int height) { struct wlr_headless_backend *backend = headless_backend_from_backend(wlr_backend); struct wlr_headless_output *output = calloc(1, sizeof(*output)); if (output == NULL) { wlr_log(WLR_ERROR, "Failed to allocate wlr_headless_output"); return NULL; } output->backend = backend; struct wlr_output *wlr_output = &output->wlr_output; struct wlr_output_state state; wlr_output_state_init(&state); wlr_output_state_set_custom_mode(&state, width, height, 0); wlr_output_init(wlr_output, &backend->backend, &output_impl, backend->display, &state); wlr_output_state_finish(&state); output_update_refresh(output, 0); size_t output_num = ++last_output_num; char name[64]; snprintf(name, sizeof(name), "HEADLESS-%zu", output_num); wlr_output_set_name(wlr_output, name); char description[128]; snprintf(description, sizeof(description), "Headless output %zu", output_num); wlr_output_set_description(wlr_output, description); struct wl_event_loop *ev = wl_display_get_event_loop(backend->display); output->frame_timer = wl_event_loop_add_timer(ev, signal_frame, output); wl_list_insert(&backend->outputs, &output->link); if (backend->started) { wl_signal_emit_mutable(&backend->backend.events.new_output, wlr_output); } return wlr_output; } wlroots-0.17.1/backend/libinput/000077500000000000000000000000001454110342200165225ustar00rootroot00000000000000wlroots-0.17.1/backend/libinput/backend.c000066400000000000000000000165701454110342200202660ustar00rootroot00000000000000#include #include #include #include #include #include #include #include "backend/libinput.h" #include "util/env.h" static struct wlr_libinput_backend *get_libinput_backend_from_backend( struct wlr_backend *wlr_backend) { assert(wlr_backend_is_libinput(wlr_backend)); struct wlr_libinput_backend *backend = wl_container_of(wlr_backend, backend, backend); return backend; } static int libinput_open_restricted(const char *path, int flags, void *_backend) { struct wlr_libinput_backend *backend = _backend; struct wlr_device *dev = wlr_session_open_file(backend->session, path); if (dev == NULL) { return -1; } return dev->fd; } static void libinput_close_restricted(int fd, void *_backend) { struct wlr_libinput_backend *backend = _backend; struct wlr_device *dev; bool found = false; wl_list_for_each(dev, &backend->session->devices, link) { if (dev->fd == fd) { found = true; break; } } if (found) { wlr_session_close_file(backend->session, dev); } } static const struct libinput_interface libinput_impl = { .open_restricted = libinput_open_restricted, .close_restricted = libinput_close_restricted }; static int handle_libinput_readable(int fd, uint32_t mask, void *_backend) { struct wlr_libinput_backend *backend = _backend; int ret = libinput_dispatch(backend->libinput_context); if (ret != 0) { wlr_log(WLR_ERROR, "Failed to dispatch libinput: %s", strerror(-ret)); wl_display_terminate(backend->display); return 0; } struct libinput_event *event; while ((event = libinput_get_event(backend->libinput_context))) { handle_libinput_event(backend, event); libinput_event_destroy(event); } return 0; } static enum wlr_log_importance libinput_log_priority_to_wlr( enum libinput_log_priority priority) { switch (priority) { case LIBINPUT_LOG_PRIORITY_ERROR: return WLR_ERROR; case LIBINPUT_LOG_PRIORITY_INFO: return WLR_INFO; default: return WLR_DEBUG; } } static void log_libinput(struct libinput *libinput_context, enum libinput_log_priority priority, const char *fmt, va_list args) { enum wlr_log_importance importance = libinput_log_priority_to_wlr(priority); static char wlr_fmt[1024]; snprintf(wlr_fmt, sizeof(wlr_fmt), "[libinput] %s", fmt); _wlr_vlog(importance, wlr_fmt, args); } static bool backend_start(struct wlr_backend *wlr_backend) { struct wlr_libinput_backend *backend = get_libinput_backend_from_backend(wlr_backend); wlr_log(WLR_DEBUG, "Starting libinput backend"); backend->libinput_context = libinput_udev_create_context(&libinput_impl, backend, backend->session->udev); if (!backend->libinput_context) { wlr_log(WLR_ERROR, "Failed to create libinput context"); return false; } if (libinput_udev_assign_seat(backend->libinput_context, backend->session->seat) != 0) { wlr_log(WLR_ERROR, "Failed to assign libinput seat"); return false; } // TODO: More sophisticated logging libinput_log_set_handler(backend->libinput_context, log_libinput); libinput_log_set_priority(backend->libinput_context, LIBINPUT_LOG_PRIORITY_ERROR); int libinput_fd = libinput_get_fd(backend->libinput_context); if (!env_parse_bool("WLR_LIBINPUT_NO_DEVICES") && wl_list_empty(&backend->devices)) { handle_libinput_readable(libinput_fd, WL_EVENT_READABLE, backend); if (wl_list_empty(&backend->devices)) { wlr_log(WLR_ERROR, "libinput initialization failed, no input devices"); wlr_log(WLR_ERROR, "Set WLR_LIBINPUT_NO_DEVICES=1 to suppress this check"); return false; } } struct wl_event_loop *event_loop = wl_display_get_event_loop(backend->display); if (backend->input_event) { wl_event_source_remove(backend->input_event); } backend->input_event = wl_event_loop_add_fd(event_loop, libinput_fd, WL_EVENT_READABLE, handle_libinput_readable, backend); if (!backend->input_event) { wlr_log(WLR_ERROR, "Failed to create input event on event loop"); return false; } wlr_log(WLR_DEBUG, "libinput successfully initialized"); return true; } static void backend_destroy(struct wlr_backend *wlr_backend) { if (!wlr_backend) { return; } struct wlr_libinput_backend *backend = get_libinput_backend_from_backend(wlr_backend); struct wlr_libinput_input_device *dev, *tmp; wl_list_for_each_safe(dev, tmp, &backend->devices, link) { destroy_libinput_input_device(dev); } wlr_backend_finish(wlr_backend); wl_list_remove(&backend->display_destroy.link); wl_list_remove(&backend->session_destroy.link); wl_list_remove(&backend->session_signal.link); if (backend->input_event) { wl_event_source_remove(backend->input_event); } libinput_unref(backend->libinput_context); free(backend); } static const struct wlr_backend_impl backend_impl = { .start = backend_start, .destroy = backend_destroy, }; bool wlr_backend_is_libinput(struct wlr_backend *b) { return b->impl == &backend_impl; } static void session_signal(struct wl_listener *listener, void *data) { struct wlr_libinput_backend *backend = wl_container_of(listener, backend, session_signal); struct wlr_session *session = backend->session; if (!backend->libinput_context) { return; } if (session->active) { libinput_resume(backend->libinput_context); } else { libinput_suspend(backend->libinput_context); } } static void handle_session_destroy(struct wl_listener *listener, void *data) { struct wlr_libinput_backend *backend = wl_container_of(listener, backend, session_destroy); backend_destroy(&backend->backend); } static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_libinput_backend *backend = wl_container_of(listener, backend, display_destroy); backend_destroy(&backend->backend); } struct wlr_backend *wlr_libinput_backend_create(struct wl_display *display, struct wlr_session *session) { struct wlr_libinput_backend *backend = calloc(1, sizeof(*backend)); if (!backend) { wlr_log(WLR_ERROR, "Allocation failed: %s", strerror(errno)); return NULL; } wlr_backend_init(&backend->backend, &backend_impl); wl_list_init(&backend->devices); backend->session = session; backend->display = display; backend->session_signal.notify = session_signal; wl_signal_add(&session->events.active, &backend->session_signal); backend->session_destroy.notify = handle_session_destroy; wl_signal_add(&session->events.destroy, &backend->session_destroy); backend->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &backend->display_destroy); return &backend->backend; } struct libinput_device *wlr_libinput_get_device_handle( struct wlr_input_device *wlr_dev) { struct wlr_libinput_input_device *dev = NULL; switch (wlr_dev->type) { case WLR_INPUT_DEVICE_KEYBOARD: dev = device_from_keyboard(wlr_keyboard_from_input_device(wlr_dev)); break; case WLR_INPUT_DEVICE_POINTER: dev = device_from_pointer(wlr_pointer_from_input_device(wlr_dev)); break; case WLR_INPUT_DEVICE_SWITCH: dev = device_from_switch(wlr_switch_from_input_device(wlr_dev)); break; case WLR_INPUT_DEVICE_TOUCH: dev = device_from_touch(wlr_touch_from_input_device(wlr_dev)); break; case WLR_INPUT_DEVICE_TABLET_TOOL: dev = device_from_tablet(wlr_tablet_from_input_device(wlr_dev)); break; case WLR_INPUT_DEVICE_TABLET_PAD: dev = device_from_tablet_pad(wlr_tablet_pad_from_input_device(wlr_dev)); break; } return dev->handle; } uint32_t usec_to_msec(uint64_t usec) { return (uint32_t)(usec / 1000); } wlroots-0.17.1/backend/libinput/events.c000066400000000000000000000177071454110342200202060ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include #include #include #include "backend/libinput.h" void destroy_libinput_input_device(struct wlr_libinput_input_device *dev) { if (dev->keyboard.impl) { wlr_keyboard_finish(&dev->keyboard); } if (dev->pointer.impl) { wlr_pointer_finish(&dev->pointer); } if (dev->switch_device.impl) { wlr_switch_finish(&dev->switch_device); } if (dev->touch.impl) { wlr_touch_finish(&dev->touch); } if (dev->tablet.impl) { finish_device_tablet(dev); } if (dev->tablet_pad.impl) { finish_device_tablet_pad(dev); } libinput_device_unref(dev->handle); wl_list_remove(&dev->link); free(dev); } bool wlr_input_device_is_libinput(struct wlr_input_device *wlr_dev) { switch (wlr_dev->type) { case WLR_INPUT_DEVICE_KEYBOARD: return wlr_keyboard_from_input_device(wlr_dev)->impl == &libinput_keyboard_impl; case WLR_INPUT_DEVICE_POINTER: return wlr_pointer_from_input_device(wlr_dev)->impl == &libinput_pointer_impl; case WLR_INPUT_DEVICE_TOUCH: return wlr_touch_from_input_device(wlr_dev)->impl == &libinput_touch_impl; case WLR_INPUT_DEVICE_TABLET_TOOL: return wlr_tablet_from_input_device(wlr_dev)-> impl == &libinput_tablet_impl; case WLR_INPUT_DEVICE_TABLET_PAD: return wlr_tablet_pad_from_input_device(wlr_dev)->impl == &libinput_tablet_pad_impl; case WLR_INPUT_DEVICE_SWITCH: return wlr_switch_from_input_device(wlr_dev)->impl == &libinput_switch_impl; default: return false; } } static void handle_device_added(struct wlr_libinput_backend *backend, struct libinput_device *libinput_dev) { int vendor = libinput_device_get_id_vendor(libinput_dev); int product = libinput_device_get_id_product(libinput_dev); const char *name = libinput_device_get_name(libinput_dev); wlr_log(WLR_DEBUG, "Adding %s [%d:%d]", name, vendor, product); struct wlr_libinput_input_device *dev = calloc(1, sizeof(*dev)); if (dev == NULL) { wlr_log_errno(WLR_ERROR, "failed to allocate wlr_libinput_input_device"); return; } dev->handle = libinput_dev; libinput_device_ref(libinput_dev); libinput_device_set_user_data(libinput_dev, dev); wl_list_insert(&backend->devices, &dev->link); if (libinput_device_has_capability( libinput_dev, LIBINPUT_DEVICE_CAP_KEYBOARD)) { init_device_keyboard(dev); wl_signal_emit_mutable(&backend->backend.events.new_input, &dev->keyboard.base); } if (libinput_device_has_capability( libinput_dev, LIBINPUT_DEVICE_CAP_POINTER)) { init_device_pointer(dev); wl_signal_emit_mutable(&backend->backend.events.new_input, &dev->pointer.base); } if (libinput_device_has_capability( libinput_dev, LIBINPUT_DEVICE_CAP_SWITCH)) { init_device_switch(dev); wl_signal_emit_mutable(&backend->backend.events.new_input, &dev->switch_device.base); } if (libinput_device_has_capability( libinput_dev, LIBINPUT_DEVICE_CAP_TOUCH)) { init_device_touch(dev); wl_signal_emit_mutable(&backend->backend.events.new_input, &dev->touch.base); } if (libinput_device_has_capability(libinput_dev, LIBINPUT_DEVICE_CAP_TABLET_TOOL)) { init_device_tablet(dev); wl_signal_emit_mutable(&backend->backend.events.new_input, &dev->tablet.base); } if (libinput_device_has_capability( libinput_dev, LIBINPUT_DEVICE_CAP_TABLET_PAD)) { init_device_tablet_pad(dev); wl_signal_emit_mutable(&backend->backend.events.new_input, &dev->tablet_pad.base); } if (libinput_device_has_capability( libinput_dev, LIBINPUT_DEVICE_CAP_GESTURE)) { wlr_log(WLR_DEBUG, "libinput gesture not handled"); } } static void handle_device_removed(struct wlr_libinput_backend *backend, struct wlr_libinput_input_device *dev) { int vendor = libinput_device_get_id_vendor(dev->handle); int product = libinput_device_get_id_product(dev->handle); const char *name = libinput_device_get_name(dev->handle); wlr_log(WLR_DEBUG, "Removing %s [%d:%d]", name, vendor, product); destroy_libinput_input_device(dev); } void handle_libinput_event(struct wlr_libinput_backend *backend, struct libinput_event *event) { struct libinput_device *libinput_dev = libinput_event_get_device(event); struct wlr_libinput_input_device *dev = libinput_device_get_user_data(libinput_dev); enum libinput_event_type event_type = libinput_event_get_type(event); if (dev == NULL && event_type != LIBINPUT_EVENT_DEVICE_ADDED) { wlr_log(WLR_ERROR, "libinput_device has no wlr_libinput_input_device"); return; } switch (event_type) { case LIBINPUT_EVENT_DEVICE_ADDED: handle_device_added(backend, libinput_dev); break; case LIBINPUT_EVENT_DEVICE_REMOVED: handle_device_removed(backend, dev); break; case LIBINPUT_EVENT_KEYBOARD_KEY: handle_keyboard_key(event, &dev->keyboard); break; case LIBINPUT_EVENT_POINTER_MOTION: handle_pointer_motion(event, &dev->pointer); break; case LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE: handle_pointer_motion_abs(event, &dev->pointer); break; case LIBINPUT_EVENT_POINTER_BUTTON: handle_pointer_button(event, &dev->pointer); break; case LIBINPUT_EVENT_POINTER_AXIS: #if !HAVE_LIBINPUT_SCROLL_VALUE120 /* This event must be ignored in favour of the SCROLL_* events */ handle_pointer_axis(event, &dev->pointer); #endif break; #if HAVE_LIBINPUT_SCROLL_VALUE120 case LIBINPUT_EVENT_POINTER_SCROLL_WHEEL: handle_pointer_axis_value120(event, &dev->pointer, WLR_AXIS_SOURCE_WHEEL); break; case LIBINPUT_EVENT_POINTER_SCROLL_FINGER: handle_pointer_axis_value120(event, &dev->pointer, WLR_AXIS_SOURCE_FINGER); break; case LIBINPUT_EVENT_POINTER_SCROLL_CONTINUOUS: handle_pointer_axis_value120(event, &dev->pointer, WLR_AXIS_SOURCE_CONTINUOUS); break; #endif case LIBINPUT_EVENT_TOUCH_DOWN: handle_touch_down(event, &dev->touch); break; case LIBINPUT_EVENT_TOUCH_UP: handle_touch_up(event, &dev->touch); break; case LIBINPUT_EVENT_TOUCH_MOTION: handle_touch_motion(event, &dev->touch); break; case LIBINPUT_EVENT_TOUCH_CANCEL: handle_touch_cancel(event, &dev->touch); break; case LIBINPUT_EVENT_TOUCH_FRAME: handle_touch_frame(event, &dev->touch); break; case LIBINPUT_EVENT_TABLET_TOOL_AXIS: handle_tablet_tool_axis(event, &dev->tablet); break; case LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY: handle_tablet_tool_proximity(event, &dev->tablet); break; case LIBINPUT_EVENT_TABLET_TOOL_TIP: handle_tablet_tool_tip(event, &dev->tablet); break; case LIBINPUT_EVENT_TABLET_TOOL_BUTTON: handle_tablet_tool_button(event, &dev->tablet); break; case LIBINPUT_EVENT_TABLET_PAD_BUTTON: handle_tablet_pad_button(event, &dev->tablet_pad); break; case LIBINPUT_EVENT_TABLET_PAD_RING: handle_tablet_pad_ring(event, &dev->tablet_pad); break; case LIBINPUT_EVENT_TABLET_PAD_STRIP: handle_tablet_pad_strip(event, &dev->tablet_pad); break; case LIBINPUT_EVENT_SWITCH_TOGGLE: handle_switch_toggle(event, &dev->switch_device); break; case LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN: handle_pointer_swipe_begin(event, &dev->pointer); break; case LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE: handle_pointer_swipe_update(event, &dev->pointer); break; case LIBINPUT_EVENT_GESTURE_SWIPE_END: handle_pointer_swipe_end(event, &dev->pointer); break; case LIBINPUT_EVENT_GESTURE_PINCH_BEGIN: handle_pointer_pinch_begin(event, &dev->pointer); break; case LIBINPUT_EVENT_GESTURE_PINCH_UPDATE: handle_pointer_pinch_update(event, &dev->pointer); break; case LIBINPUT_EVENT_GESTURE_PINCH_END: handle_pointer_pinch_end(event, &dev->pointer); break; #if HAVE_LIBINPUT_HOLD_GESTURES case LIBINPUT_EVENT_GESTURE_HOLD_BEGIN: handle_pointer_hold_begin(event, &dev->pointer); break; case LIBINPUT_EVENT_GESTURE_HOLD_END: handle_pointer_hold_end(event, &dev->pointer); break; #endif default: wlr_log(WLR_DEBUG, "Unknown libinput event %d", event_type); break; } } wlroots-0.17.1/backend/libinput/keyboard.c000066400000000000000000000033531454110342200204720ustar00rootroot00000000000000#include #include #include #include #include "backend/libinput.h" struct wlr_libinput_input_device *device_from_keyboard( struct wlr_keyboard *kb) { assert(kb->impl == &libinput_keyboard_impl); struct wlr_libinput_input_device *dev = wl_container_of(kb, dev, keyboard); return dev; } static void keyboard_set_leds(struct wlr_keyboard *wlr_kb, uint32_t leds) { struct wlr_libinput_input_device *dev = device_from_keyboard(wlr_kb); libinput_device_led_update(dev->handle, leds); } const struct wlr_keyboard_impl libinput_keyboard_impl = { .name = "libinput-keyboard", .led_update = keyboard_set_leds }; void init_device_keyboard(struct wlr_libinput_input_device *dev) { const char *name = libinput_device_get_name(dev->handle); struct wlr_keyboard *wlr_kb = &dev->keyboard; wlr_keyboard_init(wlr_kb, &libinput_keyboard_impl, name); wlr_kb->base.vendor = libinput_device_get_id_vendor(dev->handle); wlr_kb->base.product = libinput_device_get_id_product(dev->handle); libinput_device_led_update(dev->handle, 0); } void handle_keyboard_key(struct libinput_event *event, struct wlr_keyboard *kb) { struct libinput_event_keyboard *kbevent = libinput_event_get_keyboard_event(event); struct wlr_keyboard_key_event wlr_event = { .time_msec = usec_to_msec(libinput_event_keyboard_get_time_usec(kbevent)), .keycode = libinput_event_keyboard_get_key(kbevent), .update_state = true, }; switch (libinput_event_keyboard_get_key_state(kbevent)) { case LIBINPUT_KEY_STATE_RELEASED: wlr_event.state = WL_KEYBOARD_KEY_STATE_RELEASED; break; case LIBINPUT_KEY_STATE_PRESSED: wlr_event.state = WL_KEYBOARD_KEY_STATE_PRESSED; break; } wlr_keyboard_notify_key(kb, &wlr_event); } wlroots-0.17.1/backend/libinput/meson.build000066400000000000000000000015011454110342200206610ustar00rootroot00000000000000msg = ['Required for libinput backend support.'] if 'libinput' in backends msg += 'Install "libinput" or disable the libinput backend.' endif libinput = dependency( 'libinput', version: '>=1.14.0', required: 'libinput' in backends, not_found_message: '\n'.join(msg), ) if not (libinput.found() and features['session']) subdir_done() endif wlr_files += files( 'backend.c', 'events.c', 'keyboard.c', 'pointer.c', 'switch.c', 'tablet_pad.c', 'tablet_tool.c', 'touch.c', ) features += { 'libinput-backend': true } wlr_deps += libinput # libinput hold gestures and high resolution scroll are available since 1.19.0 internal_config.set10('HAVE_LIBINPUT_HOLD_GESTURES', libinput.version().version_compare('>=1.19.0')) internal_config.set10('HAVE_LIBINPUT_SCROLL_VALUE120', libinput.version().version_compare('>=1.19.0')) wlroots-0.17.1/backend/libinput/pointer.c000066400000000000000000000236711454110342200203570ustar00rootroot00000000000000#include #include #include #include "backend/libinput.h" const struct wlr_pointer_impl libinput_pointer_impl = { .name = "libinput-pointer", }; void init_device_pointer(struct wlr_libinput_input_device *dev) { const char *name = libinput_device_get_name(dev->handle); struct wlr_pointer *wlr_pointer = &dev->pointer; wlr_pointer_init(wlr_pointer, &libinput_pointer_impl, name); wlr_pointer->base.vendor = libinput_device_get_id_vendor(dev->handle); wlr_pointer->base.product = libinput_device_get_id_product(dev->handle); } struct wlr_libinput_input_device *device_from_pointer( struct wlr_pointer *wlr_pointer) { assert(wlr_pointer->impl == &libinput_pointer_impl); struct wlr_libinput_input_device *dev = wl_container_of(wlr_pointer, dev, pointer); return dev; } void handle_pointer_motion(struct libinput_event *event, struct wlr_pointer *pointer) { struct libinput_event_pointer *pevent = libinput_event_get_pointer_event(event); struct wlr_pointer_motion_event wlr_event = { .pointer = pointer, .time_msec = usec_to_msec(libinput_event_pointer_get_time_usec(pevent)), .delta_x = libinput_event_pointer_get_dx(pevent), .delta_y = libinput_event_pointer_get_dy(pevent), .unaccel_dx = libinput_event_pointer_get_dx_unaccelerated(pevent), .unaccel_dy = libinput_event_pointer_get_dy_unaccelerated(pevent), }; wl_signal_emit_mutable(&pointer->events.motion, &wlr_event); wl_signal_emit_mutable(&pointer->events.frame, pointer); } void handle_pointer_motion_abs(struct libinput_event *event, struct wlr_pointer *pointer) { struct libinput_event_pointer *pevent = libinput_event_get_pointer_event(event); struct wlr_pointer_motion_absolute_event wlr_event = { .pointer = pointer, .time_msec = usec_to_msec(libinput_event_pointer_get_time_usec(pevent)), .x = libinput_event_pointer_get_absolute_x_transformed(pevent, 1), .y = libinput_event_pointer_get_absolute_y_transformed(pevent, 1), }; wl_signal_emit_mutable(&pointer->events.motion_absolute, &wlr_event); wl_signal_emit_mutable(&pointer->events.frame, pointer); } void handle_pointer_button(struct libinput_event *event, struct wlr_pointer *pointer) { struct libinput_event_pointer *pevent = libinput_event_get_pointer_event(event); struct wlr_pointer_button_event wlr_event = { .pointer = pointer, .time_msec = usec_to_msec(libinput_event_pointer_get_time_usec(pevent)), .button = libinput_event_pointer_get_button(pevent), }; // Ignore events which aren't a seat-wide state change. For instance, if // the same button is pressed twice on the same seat, ignore the second // press. uint32_t seat_count = libinput_event_pointer_get_seat_button_count(pevent); switch (libinput_event_pointer_get_button_state(pevent)) { case LIBINPUT_BUTTON_STATE_PRESSED: wlr_event.state = WLR_BUTTON_PRESSED; if (seat_count != 1) { return; } break; case LIBINPUT_BUTTON_STATE_RELEASED: wlr_event.state = WLR_BUTTON_RELEASED; if (seat_count != 0) { return; } break; } wl_signal_emit_mutable(&pointer->events.button, &wlr_event); wl_signal_emit_mutable(&pointer->events.frame, pointer); } void handle_pointer_axis(struct libinput_event *event, struct wlr_pointer *pointer) { struct libinput_event_pointer *pevent = libinput_event_get_pointer_event(event); struct wlr_pointer_axis_event wlr_event = { .pointer = pointer, .time_msec = usec_to_msec(libinput_event_pointer_get_time_usec(pevent)), }; switch (libinput_event_pointer_get_axis_source(pevent)) { case LIBINPUT_POINTER_AXIS_SOURCE_WHEEL: wlr_event.source = WLR_AXIS_SOURCE_WHEEL; break; case LIBINPUT_POINTER_AXIS_SOURCE_FINGER: wlr_event.source = WLR_AXIS_SOURCE_FINGER; break; case LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS: wlr_event.source = WLR_AXIS_SOURCE_CONTINUOUS; break; case LIBINPUT_POINTER_AXIS_SOURCE_WHEEL_TILT: wlr_event.source = WLR_AXIS_SOURCE_WHEEL_TILT; break; } const enum libinput_pointer_axis axes[] = { LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL, LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL, }; for (size_t i = 0; i < sizeof(axes) / sizeof(axes[0]); ++i) { if (!libinput_event_pointer_has_axis(pevent, axes[i])) { continue; } switch (axes[i]) { case LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL: wlr_event.orientation = WLR_AXIS_ORIENTATION_VERTICAL; break; case LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL: wlr_event.orientation = WLR_AXIS_ORIENTATION_HORIZONTAL; break; } wlr_event.delta = libinput_event_pointer_get_axis_value(pevent, axes[i]); wlr_event.delta_discrete = libinput_event_pointer_get_axis_value_discrete(pevent, axes[i]); wlr_event.delta_discrete *= WLR_POINTER_AXIS_DISCRETE_STEP; wl_signal_emit_mutable(&pointer->events.axis, &wlr_event); } wl_signal_emit_mutable(&pointer->events.frame, pointer); } #if HAVE_LIBINPUT_SCROLL_VALUE120 void handle_pointer_axis_value120(struct libinput_event *event, struct wlr_pointer *pointer, enum wlr_axis_source source) { struct libinput_event_pointer *pevent = libinput_event_get_pointer_event(event); struct wlr_pointer_axis_event wlr_event = { .pointer = pointer, .time_msec = usec_to_msec(libinput_event_pointer_get_time_usec(pevent)), .source = source, }; const enum libinput_pointer_axis axes[] = { LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL, LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL, }; for (size_t i = 0; i < sizeof(axes) / sizeof(axes[0]); ++i) { if (!libinput_event_pointer_has_axis(pevent, axes[i])) { continue; } switch (axes[i]) { case LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL: wlr_event.orientation = WLR_AXIS_ORIENTATION_VERTICAL; break; case LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL: wlr_event.orientation = WLR_AXIS_ORIENTATION_HORIZONTAL; break; } wlr_event.delta = libinput_event_pointer_get_scroll_value(pevent, axes[i]); if (source == WLR_AXIS_SOURCE_WHEEL) { wlr_event.delta_discrete = libinput_event_pointer_get_scroll_value_v120(pevent, axes[i]); } wl_signal_emit_mutable(&pointer->events.axis, &wlr_event); } wl_signal_emit_mutable(&pointer->events.frame, pointer); } #endif void handle_pointer_swipe_begin(struct libinput_event *event, struct wlr_pointer *pointer) { struct libinput_event_gesture *gevent = libinput_event_get_gesture_event(event); struct wlr_pointer_swipe_begin_event wlr_event = { .pointer = pointer, .time_msec = usec_to_msec(libinput_event_gesture_get_time_usec(gevent)), .fingers = libinput_event_gesture_get_finger_count(gevent), }; wl_signal_emit_mutable(&pointer->events.swipe_begin, &wlr_event); } void handle_pointer_swipe_update(struct libinput_event *event, struct wlr_pointer *pointer) { struct libinput_event_gesture *gevent = libinput_event_get_gesture_event(event); struct wlr_pointer_swipe_update_event wlr_event = { .pointer = pointer, .time_msec = usec_to_msec(libinput_event_gesture_get_time_usec(gevent)), .fingers = libinput_event_gesture_get_finger_count(gevent), .dx = libinput_event_gesture_get_dx(gevent), .dy = libinput_event_gesture_get_dy(gevent), }; wl_signal_emit_mutable(&pointer->events.swipe_update, &wlr_event); } void handle_pointer_swipe_end(struct libinput_event *event, struct wlr_pointer *pointer) { struct libinput_event_gesture *gevent = libinput_event_get_gesture_event(event); struct wlr_pointer_swipe_end_event wlr_event = { .pointer = pointer, .time_msec = usec_to_msec(libinput_event_gesture_get_time_usec(gevent)), .cancelled = libinput_event_gesture_get_cancelled(gevent), }; wl_signal_emit_mutable(&pointer->events.swipe_end, &wlr_event); } void handle_pointer_pinch_begin(struct libinput_event *event, struct wlr_pointer *pointer) { struct libinput_event_gesture *gevent = libinput_event_get_gesture_event(event); struct wlr_pointer_pinch_begin_event wlr_event = { .pointer = pointer, .time_msec = usec_to_msec(libinput_event_gesture_get_time_usec(gevent)), .fingers = libinput_event_gesture_get_finger_count(gevent), }; wl_signal_emit_mutable(&pointer->events.pinch_begin, &wlr_event); } void handle_pointer_pinch_update(struct libinput_event *event, struct wlr_pointer *pointer) { struct libinput_event_gesture *gevent = libinput_event_get_gesture_event(event); struct wlr_pointer_pinch_update_event wlr_event = { .pointer = pointer, .time_msec = usec_to_msec(libinput_event_gesture_get_time_usec(gevent)), .fingers = libinput_event_gesture_get_finger_count(gevent), .dx = libinput_event_gesture_get_dx(gevent), .dy = libinput_event_gesture_get_dy(gevent), .scale = libinput_event_gesture_get_scale(gevent), .rotation = libinput_event_gesture_get_angle_delta(gevent), }; wl_signal_emit_mutable(&pointer->events.pinch_update, &wlr_event); } void handle_pointer_pinch_end(struct libinput_event *event, struct wlr_pointer *pointer) { struct libinput_event_gesture *gevent = libinput_event_get_gesture_event(event); struct wlr_pointer_pinch_end_event wlr_event = { .pointer = pointer, .time_msec = usec_to_msec(libinput_event_gesture_get_time_usec(gevent)), .cancelled = libinput_event_gesture_get_cancelled(gevent), }; wl_signal_emit_mutable(&pointer->events.pinch_end, &wlr_event); } void handle_pointer_hold_begin(struct libinput_event *event, struct wlr_pointer *pointer) { struct libinput_event_gesture *gevent = libinput_event_get_gesture_event(event); struct wlr_pointer_hold_begin_event wlr_event = { .pointer = pointer, .time_msec = usec_to_msec(libinput_event_gesture_get_time_usec(gevent)), .fingers = libinput_event_gesture_get_finger_count(gevent), }; wl_signal_emit_mutable(&pointer->events.hold_begin, &wlr_event); } void handle_pointer_hold_end(struct libinput_event *event, struct wlr_pointer *pointer) { struct libinput_event_gesture *gevent = libinput_event_get_gesture_event(event); struct wlr_pointer_hold_end_event wlr_event = { .pointer = pointer, .time_msec = usec_to_msec(libinput_event_gesture_get_time_usec(gevent)), .cancelled = libinput_event_gesture_get_cancelled(gevent), }; wl_signal_emit_mutable(&pointer->events.hold_end, &wlr_event); } wlroots-0.17.1/backend/libinput/switch.c000066400000000000000000000032151454110342200201700ustar00rootroot00000000000000#include #include #include #include "backend/libinput.h" const struct wlr_switch_impl libinput_switch_impl = { .name = "libinput-switch", }; void init_device_switch(struct wlr_libinput_input_device *dev) { const char *name = libinput_device_get_name(dev->handle); struct wlr_switch *wlr_switch = &dev->switch_device; wlr_switch_init(wlr_switch, &libinput_switch_impl, name); wlr_switch->base.vendor = libinput_device_get_id_vendor(dev->handle); wlr_switch->base.product = libinput_device_get_id_product(dev->handle); } struct wlr_libinput_input_device *device_from_switch( struct wlr_switch *wlr_switch) { assert(wlr_switch->impl == &libinput_switch_impl); struct wlr_libinput_input_device *dev = wl_container_of(wlr_switch, dev, switch_device); return dev; } void handle_switch_toggle(struct libinput_event *event, struct wlr_switch *wlr_switch) { struct libinput_event_switch *sevent = libinput_event_get_switch_event (event); struct wlr_switch_toggle_event wlr_event = { .time_msec = usec_to_msec(libinput_event_switch_get_time_usec(sevent)), }; switch (libinput_event_switch_get_switch(sevent)) { case LIBINPUT_SWITCH_LID: wlr_event.switch_type = WLR_SWITCH_TYPE_LID; break; case LIBINPUT_SWITCH_TABLET_MODE: wlr_event.switch_type = WLR_SWITCH_TYPE_TABLET_MODE; break; } switch (libinput_event_switch_get_switch_state(sevent)) { case LIBINPUT_SWITCH_STATE_OFF: wlr_event.switch_state = WLR_SWITCH_STATE_OFF; break; case LIBINPUT_SWITCH_STATE_ON: wlr_event.switch_state = WLR_SWITCH_STATE_ON; break; } wl_signal_emit_mutable(&wlr_switch->events.toggle, &wlr_event); } wlroots-0.17.1/backend/libinput/tablet_pad.c000066400000000000000000000153301454110342200207670ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include "backend/libinput.h" const struct wlr_tablet_pad_impl libinput_tablet_pad_impl = { .name = "libinput-tablet-pad", }; static void group_destroy(struct wlr_tablet_pad_group *group) { free(group->buttons); free(group->strips); free(group->rings); free(group); } static void add_pad_group_from_libinput(struct wlr_tablet_pad *pad, struct libinput_device *device, unsigned int index) { struct libinput_tablet_pad_mode_group *li_group = libinput_device_tablet_pad_get_mode_group(device, index); struct wlr_tablet_pad_group *group = calloc(1, sizeof(*group)); if (!group) { wlr_log_errno(WLR_ERROR, "failed to allocate wlr_tablet_pad_group"); return; } for (size_t i = 0; i < pad->ring_count; ++i) { if (libinput_tablet_pad_mode_group_has_ring(li_group, i)) { ++group->ring_count; } } group->rings = calloc(sizeof(unsigned int), group->ring_count); if (group->rings == NULL) { goto group_fail; } size_t ring = 0; for (size_t i = 0; i < pad->ring_count; ++i) { if (libinput_tablet_pad_mode_group_has_ring(li_group, i)) { group->rings[ring++] = i; } } for (size_t i = 0; i < pad->strip_count; ++i) { if (libinput_tablet_pad_mode_group_has_strip(li_group, i)) { ++group->strip_count; } } group->strips = calloc(sizeof(unsigned int), group->strip_count); if (group->strips == NULL) { goto group_fail; } size_t strip = 0; for (size_t i = 0; i < pad->strip_count; ++i) { if (libinput_tablet_pad_mode_group_has_strip(li_group, i)) { group->strips[strip++] = i; } } for (size_t i = 0; i < pad->button_count; ++i) { if (libinput_tablet_pad_mode_group_has_button(li_group, i)) { ++group->button_count; } } group->buttons = calloc(sizeof(unsigned int), group->button_count); if (group->buttons == NULL) { goto group_fail; } size_t button = 0; for (size_t i = 0; i < pad->button_count; ++i) { if (libinput_tablet_pad_mode_group_has_button(li_group, i)) { group->buttons[button++] = i; } } group->mode_count = libinput_tablet_pad_mode_group_get_num_modes(li_group); libinput_tablet_pad_mode_group_ref(li_group); wl_list_insert(&pad->groups, &group->link); return; group_fail: wlr_log(WLR_ERROR, "failed to configure wlr_tablet_pad_group"); group_destroy(group); } void init_device_tablet_pad(struct wlr_libinput_input_device *dev) { struct libinput_device *handle = dev->handle; const char *name = libinput_device_get_name(handle); struct wlr_tablet_pad *wlr_tablet_pad = &dev->tablet_pad; wlr_tablet_pad_init(wlr_tablet_pad, &libinput_tablet_pad_impl, name); wlr_tablet_pad->base.vendor = libinput_device_get_id_vendor(handle); wlr_tablet_pad->base.product = libinput_device_get_id_product(handle); wlr_tablet_pad->button_count = libinput_device_tablet_pad_get_num_buttons(handle); wlr_tablet_pad->ring_count = libinput_device_tablet_pad_get_num_rings(handle); wlr_tablet_pad->strip_count = libinput_device_tablet_pad_get_num_strips(handle); struct udev_device *udev = libinput_device_get_udev_device(handle); char **dst = wl_array_add(&wlr_tablet_pad->paths, sizeof(char *)); *dst = strdup(udev_device_get_syspath(udev)); int groups = libinput_device_tablet_pad_get_num_mode_groups(handle); for (int i = 0; i < groups; ++i) { add_pad_group_from_libinput(wlr_tablet_pad, handle, i); } } void finish_device_tablet_pad(struct wlr_libinput_input_device *dev) { struct wlr_tablet_pad_group *group, *tmp; wl_list_for_each_safe(group, tmp, &dev->tablet_pad.groups, link) { group_destroy(group); } wlr_tablet_pad_finish(&dev->tablet_pad); int groups = libinput_device_tablet_pad_get_num_mode_groups(dev->handle); for (int i = 0; i < groups; ++i) { struct libinput_tablet_pad_mode_group *li_group = libinput_device_tablet_pad_get_mode_group(dev->handle, i); libinput_tablet_pad_mode_group_unref(li_group); } } struct wlr_libinput_input_device *device_from_tablet_pad( struct wlr_tablet_pad *wlr_tablet_pad) { assert(wlr_tablet_pad->impl == &libinput_tablet_pad_impl); struct wlr_libinput_input_device *dev = wl_container_of(wlr_tablet_pad, dev, tablet_pad); return dev; } void handle_tablet_pad_button(struct libinput_event *event, struct wlr_tablet_pad *tablet_pad) { struct libinput_event_tablet_pad *pevent = libinput_event_get_tablet_pad_event(event); struct wlr_tablet_pad_button_event wlr_event = { .time_msec = usec_to_msec(libinput_event_tablet_pad_get_time_usec(pevent)), .button = libinput_event_tablet_pad_get_button_number(pevent), .mode = libinput_event_tablet_pad_get_mode(pevent), .group = libinput_tablet_pad_mode_group_get_index( libinput_event_tablet_pad_get_mode_group(pevent)), }; switch (libinput_event_tablet_pad_get_button_state(pevent)) { case LIBINPUT_BUTTON_STATE_PRESSED: wlr_event.state = WLR_BUTTON_PRESSED; break; case LIBINPUT_BUTTON_STATE_RELEASED: wlr_event.state = WLR_BUTTON_RELEASED; break; } wl_signal_emit_mutable(&tablet_pad->events.button, &wlr_event); } void handle_tablet_pad_ring(struct libinput_event *event, struct wlr_tablet_pad *tablet_pad) { struct libinput_event_tablet_pad *pevent = libinput_event_get_tablet_pad_event(event); struct wlr_tablet_pad_ring_event wlr_event = { .time_msec = usec_to_msec(libinput_event_tablet_pad_get_time_usec(pevent)), .ring = libinput_event_tablet_pad_get_ring_number(pevent), .position = libinput_event_tablet_pad_get_ring_position(pevent), .mode = libinput_event_tablet_pad_get_mode(pevent), }; switch (libinput_event_tablet_pad_get_ring_source(pevent)) { case LIBINPUT_TABLET_PAD_RING_SOURCE_UNKNOWN: wlr_event.source = WLR_TABLET_PAD_RING_SOURCE_UNKNOWN; break; case LIBINPUT_TABLET_PAD_RING_SOURCE_FINGER: wlr_event.source = WLR_TABLET_PAD_RING_SOURCE_FINGER; break; } wl_signal_emit_mutable(&tablet_pad->events.ring, &wlr_event); } void handle_tablet_pad_strip(struct libinput_event *event, struct wlr_tablet_pad *tablet_pad) { struct libinput_event_tablet_pad *pevent = libinput_event_get_tablet_pad_event(event); struct wlr_tablet_pad_strip_event wlr_event = { .time_msec = usec_to_msec(libinput_event_tablet_pad_get_time_usec(pevent)), .strip = libinput_event_tablet_pad_get_strip_number(pevent), .position = libinput_event_tablet_pad_get_strip_position(pevent), .mode = libinput_event_tablet_pad_get_mode(pevent), }; switch (libinput_event_tablet_pad_get_strip_source(pevent)) { case LIBINPUT_TABLET_PAD_STRIP_SOURCE_UNKNOWN: wlr_event.source = WLR_TABLET_PAD_STRIP_SOURCE_UNKNOWN; break; case LIBINPUT_TABLET_PAD_STRIP_SOURCE_FINGER: wlr_event.source = WLR_TABLET_PAD_STRIP_SOURCE_FINGER; break; } wl_signal_emit_mutable(&tablet_pad->events.strip, &wlr_event); } wlroots-0.17.1/backend/libinput/tablet_tool.c000066400000000000000000000234261454110342200212050ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include "backend/libinput.h" struct tablet_tool { struct wlr_tablet_tool wlr_tool; struct libinput_tablet_tool *handle; struct wl_list link; // wlr_libinput_input_device.tablet_tools }; const struct wlr_tablet_impl libinput_tablet_impl = { .name = "libinput-tablet-tool", }; void init_device_tablet(struct wlr_libinput_input_device *dev) { const char *name = libinput_device_get_name(dev->handle); struct wlr_tablet *wlr_tablet = &dev->tablet; wlr_tablet_init(wlr_tablet, &libinput_tablet_impl, name); wlr_tablet->base.vendor = libinput_device_get_id_vendor(dev->handle); wlr_tablet->base.product = libinput_device_get_id_product(dev->handle); libinput_device_get_size(dev->handle, &wlr_tablet->width_mm, &wlr_tablet->height_mm); struct udev_device *udev = libinput_device_get_udev_device(dev->handle); char **dst = wl_array_add(&wlr_tablet->paths, sizeof(char *)); *dst = strdup(udev_device_get_syspath(udev)); wl_list_init(&dev->tablet_tools); } static void tool_destroy(struct tablet_tool *tool) { wl_signal_emit_mutable(&tool->wlr_tool.events.destroy, &tool->wlr_tool); libinput_tablet_tool_unref(tool->handle); libinput_tablet_tool_set_user_data(tool->handle, NULL); wl_list_remove(&tool->link); free(tool); } void finish_device_tablet(struct wlr_libinput_input_device *dev) { struct tablet_tool *tool, *tmp; wl_list_for_each_safe(tool, tmp, &dev->tablet_tools, link) { tool_destroy(tool); } wlr_tablet_finish(&dev->tablet); } struct wlr_libinput_input_device *device_from_tablet( struct wlr_tablet *wlr_tablet) { assert(wlr_tablet->impl == &libinput_tablet_impl); struct wlr_libinput_input_device *dev = wl_container_of(wlr_tablet, dev, tablet); return dev; } static enum wlr_tablet_tool_type wlr_type_from_libinput_type( enum libinput_tablet_tool_type value) { switch (value) { case LIBINPUT_TABLET_TOOL_TYPE_PEN: return WLR_TABLET_TOOL_TYPE_PEN; case LIBINPUT_TABLET_TOOL_TYPE_ERASER: return WLR_TABLET_TOOL_TYPE_ERASER; case LIBINPUT_TABLET_TOOL_TYPE_BRUSH: return WLR_TABLET_TOOL_TYPE_BRUSH; case LIBINPUT_TABLET_TOOL_TYPE_PENCIL: return WLR_TABLET_TOOL_TYPE_PENCIL; case LIBINPUT_TABLET_TOOL_TYPE_AIRBRUSH: return WLR_TABLET_TOOL_TYPE_AIRBRUSH; case LIBINPUT_TABLET_TOOL_TYPE_MOUSE: return WLR_TABLET_TOOL_TYPE_MOUSE; case LIBINPUT_TABLET_TOOL_TYPE_LENS: return WLR_TABLET_TOOL_TYPE_LENS; case LIBINPUT_TABLET_TOOL_TYPE_TOTEM: return WLR_TABLET_TOOL_TYPE_TOTEM; } abort(); // unreachable } static struct tablet_tool *get_tablet_tool( struct wlr_libinput_input_device *dev, struct libinput_tablet_tool *libinput_tool) { struct tablet_tool *tool = libinput_tablet_tool_get_user_data(libinput_tool); if (tool) { return tool; } tool = calloc(1, sizeof(*tool)); if (tool == NULL) { wlr_log_errno(WLR_ERROR, "failed to allocate wlr_libinput_tablet_tool"); return NULL; } tool->wlr_tool.type = wlr_type_from_libinput_type( libinput_tablet_tool_get_type(libinput_tool)); tool->wlr_tool.hardware_serial = libinput_tablet_tool_get_serial(libinput_tool); tool->wlr_tool.hardware_wacom = libinput_tablet_tool_get_tool_id(libinput_tool); tool->wlr_tool.pressure = libinput_tablet_tool_has_pressure(libinput_tool); tool->wlr_tool.distance = libinput_tablet_tool_has_distance(libinput_tool); tool->wlr_tool.tilt = libinput_tablet_tool_has_tilt(libinput_tool); tool->wlr_tool.rotation = libinput_tablet_tool_has_rotation(libinput_tool); tool->wlr_tool.slider = libinput_tablet_tool_has_slider(libinput_tool); tool->wlr_tool.wheel = libinput_tablet_tool_has_wheel(libinput_tool); wl_signal_init(&tool->wlr_tool.events.destroy); tool->handle = libinput_tablet_tool_ref(libinput_tool); libinput_tablet_tool_set_user_data(libinput_tool, tool); wl_list_insert(&dev->tablet_tools, &tool->link); return tool; } void handle_tablet_tool_axis(struct libinput_event *event, struct wlr_tablet *wlr_tablet) { struct libinput_event_tablet_tool *tevent = libinput_event_get_tablet_tool_event(event); struct wlr_libinput_input_device *dev = device_from_tablet(wlr_tablet); struct tablet_tool *tool = get_tablet_tool(dev, libinput_event_tablet_tool_get_tool(tevent)); struct wlr_tablet_tool_axis_event wlr_event = { .tablet = wlr_tablet, .tool = &tool->wlr_tool, .time_msec = usec_to_msec(libinput_event_tablet_tool_get_time_usec(tevent)), }; if (libinput_event_tablet_tool_x_has_changed(tevent)) { wlr_event.updated_axes |= WLR_TABLET_TOOL_AXIS_X; wlr_event.x = libinput_event_tablet_tool_get_x_transformed(tevent, 1); wlr_event.dx = libinput_event_tablet_tool_get_dx(tevent); } if (libinput_event_tablet_tool_y_has_changed(tevent)) { wlr_event.updated_axes |= WLR_TABLET_TOOL_AXIS_Y; wlr_event.y = libinput_event_tablet_tool_get_y_transformed(tevent, 1); wlr_event.dy = libinput_event_tablet_tool_get_dy(tevent); } if (libinput_event_tablet_tool_pressure_has_changed(tevent)) { wlr_event.updated_axes |= WLR_TABLET_TOOL_AXIS_PRESSURE; wlr_event.pressure = libinput_event_tablet_tool_get_pressure(tevent); } if (libinput_event_tablet_tool_distance_has_changed(tevent)) { wlr_event.updated_axes |= WLR_TABLET_TOOL_AXIS_DISTANCE; wlr_event.distance = libinput_event_tablet_tool_get_distance(tevent); } if (libinput_event_tablet_tool_tilt_x_has_changed(tevent)) { wlr_event.updated_axes |= WLR_TABLET_TOOL_AXIS_TILT_X; wlr_event.tilt_x = libinput_event_tablet_tool_get_tilt_x(tevent); } if (libinput_event_tablet_tool_tilt_y_has_changed(tevent)) { wlr_event.updated_axes |= WLR_TABLET_TOOL_AXIS_TILT_Y; wlr_event.tilt_y = libinput_event_tablet_tool_get_tilt_y(tevent); } if (libinput_event_tablet_tool_rotation_has_changed(tevent)) { wlr_event.updated_axes |= WLR_TABLET_TOOL_AXIS_ROTATION; wlr_event.rotation = libinput_event_tablet_tool_get_rotation(tevent); } if (libinput_event_tablet_tool_slider_has_changed(tevent)) { wlr_event.updated_axes |= WLR_TABLET_TOOL_AXIS_SLIDER; wlr_event.slider = libinput_event_tablet_tool_get_slider_position(tevent); } if (libinput_event_tablet_tool_wheel_has_changed(tevent)) { wlr_event.updated_axes |= WLR_TABLET_TOOL_AXIS_WHEEL; wlr_event.wheel_delta = libinput_event_tablet_tool_get_wheel_delta(tevent); } wl_signal_emit_mutable(&wlr_tablet->events.axis, &wlr_event); } void handle_tablet_tool_proximity(struct libinput_event *event, struct wlr_tablet *wlr_tablet) { struct libinput_event_tablet_tool *tevent = libinput_event_get_tablet_tool_event(event); struct wlr_libinput_input_device *dev = device_from_tablet(wlr_tablet); struct tablet_tool *tool = get_tablet_tool(dev, libinput_event_tablet_tool_get_tool(tevent)); struct wlr_tablet_tool_proximity_event wlr_event = { .tablet = wlr_tablet, .tool = &tool->wlr_tool, .time_msec = usec_to_msec(libinput_event_tablet_tool_get_time_usec(tevent)), .x = libinput_event_tablet_tool_get_x_transformed(tevent, 1), .y = libinput_event_tablet_tool_get_y_transformed(tevent, 1), }; switch (libinput_event_tablet_tool_get_proximity_state(tevent)) { case LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT: wlr_event.state = WLR_TABLET_TOOL_PROXIMITY_OUT; break; case LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN: wlr_event.state = WLR_TABLET_TOOL_PROXIMITY_IN; break; } wl_signal_emit_mutable(&wlr_tablet->events.proximity, &wlr_event); if (libinput_event_tablet_tool_get_proximity_state(tevent) == LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN) { handle_tablet_tool_axis(event, wlr_tablet); } // If the tool is not unique, libinput will not find it again after the // proximity out, so we should destroy it if (!libinput_tablet_tool_is_unique(tool->handle) && libinput_event_tablet_tool_get_proximity_state(tevent) == LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT) { // The tool isn't unique, it can't be on multiple tablets tool_destroy(tool); } } void handle_tablet_tool_tip(struct libinput_event *event, struct wlr_tablet *wlr_tablet) { handle_tablet_tool_axis(event, wlr_tablet); struct libinput_event_tablet_tool *tevent = libinput_event_get_tablet_tool_event(event); struct wlr_libinput_input_device *dev = device_from_tablet(wlr_tablet); struct tablet_tool *tool = get_tablet_tool(dev, libinput_event_tablet_tool_get_tool(tevent)); struct wlr_tablet_tool_tip_event wlr_event = { .tablet = wlr_tablet, .tool = &tool->wlr_tool, .time_msec = usec_to_msec(libinput_event_tablet_tool_get_time_usec(tevent)), .x = libinput_event_tablet_tool_get_x_transformed(tevent, 1), .y = libinput_event_tablet_tool_get_y_transformed(tevent, 1), }; switch (libinput_event_tablet_tool_get_tip_state(tevent)) { case LIBINPUT_TABLET_TOOL_TIP_UP: wlr_event.state = WLR_TABLET_TOOL_TIP_UP; break; case LIBINPUT_TABLET_TOOL_TIP_DOWN: wlr_event.state = WLR_TABLET_TOOL_TIP_DOWN; break; } wl_signal_emit_mutable(&wlr_tablet->events.tip, &wlr_event); } void handle_tablet_tool_button(struct libinput_event *event, struct wlr_tablet *wlr_tablet) { handle_tablet_tool_axis(event, wlr_tablet); struct libinput_event_tablet_tool *tevent = libinput_event_get_tablet_tool_event(event); struct wlr_libinput_input_device *dev = device_from_tablet(wlr_tablet); struct tablet_tool *tool = get_tablet_tool(dev, libinput_event_tablet_tool_get_tool(tevent)); struct wlr_tablet_tool_button_event wlr_event = { .tablet = wlr_tablet, .tool = &tool->wlr_tool, .time_msec = usec_to_msec(libinput_event_tablet_tool_get_time_usec(tevent)), .button = libinput_event_tablet_tool_get_button(tevent), }; switch (libinput_event_tablet_tool_get_button_state(tevent)) { case LIBINPUT_BUTTON_STATE_RELEASED: wlr_event.state = WLR_BUTTON_RELEASED; break; case LIBINPUT_BUTTON_STATE_PRESSED: wlr_event.state = WLR_BUTTON_PRESSED; break; } wl_signal_emit_mutable(&wlr_tablet->events.button, &wlr_event); } wlroots-0.17.1/backend/libinput/touch.c000066400000000000000000000056451454110342200200220ustar00rootroot00000000000000#include #include #include #include "backend/libinput.h" const struct wlr_touch_impl libinput_touch_impl = { .name = "libinput-touch", }; void init_device_touch(struct wlr_libinput_input_device *dev) { const char *name = libinput_device_get_name(dev->handle); struct wlr_touch *wlr_touch = &dev->touch; wlr_touch_init(wlr_touch, &libinput_touch_impl, name); wlr_touch->base.vendor = libinput_device_get_id_vendor(dev->handle); wlr_touch->base.product = libinput_device_get_id_product(dev->handle); libinput_device_get_size(dev->handle, &wlr_touch->width_mm, &wlr_touch->height_mm); } struct wlr_libinput_input_device *device_from_touch( struct wlr_touch *wlr_touch) { assert(wlr_touch->impl == &libinput_touch_impl); struct wlr_libinput_input_device *dev = wl_container_of(wlr_touch, dev, touch); return dev; } void handle_touch_down(struct libinput_event *event, struct wlr_touch *touch) { struct libinput_event_touch *tevent = libinput_event_get_touch_event(event); struct wlr_touch_down_event wlr_event = { 0 }; wlr_event.touch = touch; wlr_event.time_msec = usec_to_msec(libinput_event_touch_get_time_usec(tevent)); wlr_event.touch_id = libinput_event_touch_get_seat_slot(tevent); wlr_event.x = libinput_event_touch_get_x_transformed(tevent, 1); wlr_event.y = libinput_event_touch_get_y_transformed(tevent, 1); wl_signal_emit_mutable(&touch->events.down, &wlr_event); } void handle_touch_up(struct libinput_event *event, struct wlr_touch *touch) { struct libinput_event_touch *tevent = libinput_event_get_touch_event(event); struct wlr_touch_up_event wlr_event = { .touch = touch, .time_msec = usec_to_msec(libinput_event_touch_get_time_usec(tevent)), .touch_id = libinput_event_touch_get_seat_slot(tevent), }; wl_signal_emit_mutable(&touch->events.up, &wlr_event); } void handle_touch_motion(struct libinput_event *event, struct wlr_touch *touch) { struct libinput_event_touch *tevent = libinput_event_get_touch_event(event); struct wlr_touch_motion_event wlr_event = { .touch = touch, .time_msec = usec_to_msec(libinput_event_touch_get_time_usec(tevent)), .touch_id = libinput_event_touch_get_seat_slot(tevent), .x = libinput_event_touch_get_x_transformed(tevent, 1), .y = libinput_event_touch_get_y_transformed(tevent, 1), }; wl_signal_emit_mutable(&touch->events.motion, &wlr_event); } void handle_touch_cancel(struct libinput_event *event, struct wlr_touch *touch) { struct libinput_event_touch *tevent = libinput_event_get_touch_event(event); struct wlr_touch_cancel_event wlr_event = { .touch = touch, .time_msec = usec_to_msec(libinput_event_touch_get_time_usec(tevent)), .touch_id = libinput_event_touch_get_seat_slot(tevent), }; wl_signal_emit_mutable(&touch->events.cancel, &wlr_event); } void handle_touch_frame(struct libinput_event *event, struct wlr_touch *touch) { wl_signal_emit_mutable(&touch->events.frame, NULL); } wlroots-0.17.1/backend/meson.build000066400000000000000000000013511454110342200170360ustar00rootroot00000000000000wlr_files += files('backend.c') all_backends = ['drm', 'libinput', 'x11'] backends = get_option('backends') if 'auto' in backends and get_option('auto_features').enabled() backends = all_backends elif 'auto' in backends and get_option('auto_features').disabled() backends = [] endif session_required = 'drm' in backends or 'libinput' in backends or get_option('session').enabled() if get_option('session').disabled() if session_required error('Session support is required for the DRM or libinput backends') endif session_required = disabler() endif subdir('session') foreach backend : all_backends if backend in backends or 'auto' in backends subdir(backend) endif endforeach subdir('multi') subdir('wayland') subdir('headless') wlroots-0.17.1/backend/multi/000077500000000000000000000000001454110342200160265ustar00rootroot00000000000000wlroots-0.17.1/backend/multi/backend.c000066400000000000000000000152261454110342200175670ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200112L #include #include #include #include #include #include #include #include "backend/backend.h" #include "backend/multi.h" struct subbackend_state { struct wlr_backend *backend; struct wlr_backend *container; struct wl_listener new_input; struct wl_listener new_output; struct wl_listener destroy; struct wl_list link; }; static struct wlr_multi_backend *multi_backend_from_backend( struct wlr_backend *wlr_backend) { assert(wlr_backend_is_multi(wlr_backend)); struct wlr_multi_backend *backend = wl_container_of(wlr_backend, backend, backend); return backend; } static bool multi_backend_start(struct wlr_backend *wlr_backend) { struct wlr_multi_backend *backend = multi_backend_from_backend(wlr_backend); struct subbackend_state *sub; wl_list_for_each(sub, &backend->backends, link) { if (!wlr_backend_start(sub->backend)) { wlr_log(WLR_ERROR, "Failed to initialize backend."); return false; } } return true; } static void subbackend_state_destroy(struct subbackend_state *sub) { wl_list_remove(&sub->new_input.link); wl_list_remove(&sub->new_output.link); wl_list_remove(&sub->destroy.link); wl_list_remove(&sub->link); free(sub); } static void multi_backend_destroy(struct wlr_backend *wlr_backend) { struct wlr_multi_backend *backend = multi_backend_from_backend(wlr_backend); wl_list_remove(&backend->display_destroy.link); // Some backends may depend on other backends, ie. destroying a backend may // also destroy other backends while (!wl_list_empty(&backend->backends)) { struct subbackend_state *sub = wl_container_of(backend->backends.next, sub, link); wlr_backend_destroy(sub->backend); } // Destroy this backend only after removing all sub-backends wlr_backend_finish(wlr_backend); free(backend); } static int multi_backend_get_drm_fd(struct wlr_backend *backend) { struct wlr_multi_backend *multi = multi_backend_from_backend(backend); struct subbackend_state *sub; wl_list_for_each(sub, &multi->backends, link) { if (sub->backend->impl->get_drm_fd) { return wlr_backend_get_drm_fd(sub->backend); } } return -1; } static uint32_t multi_backend_get_buffer_caps(struct wlr_backend *backend) { struct wlr_multi_backend *multi = multi_backend_from_backend(backend); if (wl_list_empty(&multi->backends)) { return 0; } uint32_t caps = WLR_BUFFER_CAP_DATA_PTR | WLR_BUFFER_CAP_DMABUF | WLR_BUFFER_CAP_SHM; struct subbackend_state *sub; wl_list_for_each(sub, &multi->backends, link) { uint32_t backend_caps = backend_get_buffer_caps(sub->backend); if (backend_caps != 0) { // only count backend capable of presenting a buffer caps = caps & backend_caps; } } return caps; } static const struct wlr_backend_impl backend_impl = { .start = multi_backend_start, .destroy = multi_backend_destroy, .get_drm_fd = multi_backend_get_drm_fd, .get_buffer_caps = multi_backend_get_buffer_caps, }; static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_multi_backend *backend = wl_container_of(listener, backend, display_destroy); multi_backend_destroy((struct wlr_backend*)backend); } struct wlr_backend *wlr_multi_backend_create(struct wl_display *display) { struct wlr_multi_backend *backend = calloc(1, sizeof(*backend)); if (!backend) { wlr_log(WLR_ERROR, "Backend allocation failed"); return NULL; } wl_list_init(&backend->backends); wlr_backend_init(&backend->backend, &backend_impl); wl_signal_init(&backend->events.backend_add); wl_signal_init(&backend->events.backend_remove); backend->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &backend->display_destroy); return &backend->backend; } bool wlr_backend_is_multi(struct wlr_backend *b) { return b->impl == &backend_impl; } static void new_input_reemit(struct wl_listener *listener, void *data) { struct subbackend_state *state = wl_container_of(listener, state, new_input); wl_signal_emit_mutable(&state->container->events.new_input, data); } static void new_output_reemit(struct wl_listener *listener, void *data) { struct subbackend_state *state = wl_container_of(listener, state, new_output); wl_signal_emit_mutable(&state->container->events.new_output, data); } static void handle_subbackend_destroy(struct wl_listener *listener, void *data) { struct subbackend_state *state = wl_container_of(listener, state, destroy); subbackend_state_destroy(state); } static struct subbackend_state *multi_backend_get_subbackend(struct wlr_multi_backend *multi, struct wlr_backend *backend) { struct subbackend_state *sub = NULL; wl_list_for_each(sub, &multi->backends, link) { if (sub->backend == backend) { return sub; } } return NULL; } bool wlr_multi_backend_add(struct wlr_backend *_multi, struct wlr_backend *backend) { assert(_multi && backend); assert(_multi != backend); struct wlr_multi_backend *multi = multi_backend_from_backend(_multi); if (multi_backend_get_subbackend(multi, backend)) { // already added return true; } struct subbackend_state *sub = calloc(1, sizeof(*sub)); if (sub == NULL) { wlr_log(WLR_ERROR, "Could not add backend: allocation failed"); return false; } wl_list_insert(multi->backends.prev, &sub->link); sub->backend = backend; sub->container = &multi->backend; wl_signal_add(&backend->events.destroy, &sub->destroy); sub->destroy.notify = handle_subbackend_destroy; wl_signal_add(&backend->events.new_input, &sub->new_input); sub->new_input.notify = new_input_reemit; wl_signal_add(&backend->events.new_output, &sub->new_output); sub->new_output.notify = new_output_reemit; wl_signal_emit_mutable(&multi->events.backend_add, backend); return true; } void wlr_multi_backend_remove(struct wlr_backend *_multi, struct wlr_backend *backend) { struct wlr_multi_backend *multi = multi_backend_from_backend(_multi); struct subbackend_state *sub = multi_backend_get_subbackend(multi, backend); if (sub) { wl_signal_emit_mutable(&multi->events.backend_remove, backend); subbackend_state_destroy(sub); } } bool wlr_multi_is_empty(struct wlr_backend *_backend) { assert(wlr_backend_is_multi(_backend)); struct wlr_multi_backend *backend = (struct wlr_multi_backend *)_backend; return wl_list_length(&backend->backends) < 1; } void wlr_multi_for_each_backend(struct wlr_backend *_backend, void (*callback)(struct wlr_backend *backend, void *data), void *data) { assert(wlr_backend_is_multi(_backend)); struct wlr_multi_backend *backend = (struct wlr_multi_backend *)_backend; struct subbackend_state *sub; wl_list_for_each(sub, &backend->backends, link) { callback(sub->backend, data); } } wlroots-0.17.1/backend/multi/meson.build000066400000000000000000000000401454110342200201620ustar00rootroot00000000000000wlr_files += files('backend.c') wlroots-0.17.1/backend/session/000077500000000000000000000000001454110342200163575ustar00rootroot00000000000000wlroots-0.17.1/backend/session/meson.build000066400000000000000000000007501454110342200205230ustar00rootroot00000000000000msg = 'Required for session support.' udev = dependency('libudev', required: session_required, not_found_message: msg) libseat = dependency( 'libseat', version: '>=0.2.0', fallback: 'seatd', default_options: ['server=disabled', 'man-pages=disabled', 'examples=disabled'], required: session_required, not_found_message: msg, ) if not (udev.found() and libseat.found()) subdir_done() endif wlr_files += files('session.c') wlr_deps += [udev, libseat] features += { 'session': true } wlroots-0.17.1/backend/session/session.c000066400000000000000000000346111454110342200202130ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "backend/session/session.h" #include "util/time.h" #include #define WAIT_GPU_TIMEOUT 10000 // ms static void handle_enable_seat(struct libseat *seat, void *data) { struct wlr_session *session = data; session->active = true; wl_signal_emit_mutable(&session->events.active, NULL); } static void handle_disable_seat(struct libseat *seat, void *data) { struct wlr_session *session = data; session->active = false; wl_signal_emit_mutable(&session->events.active, NULL); libseat_disable_seat(session->seat_handle); } static int libseat_event(int fd, uint32_t mask, void *data) { struct wlr_session *session = data; if (libseat_dispatch(session->seat_handle, 0) == -1) { wlr_log_errno(WLR_ERROR, "Failed to dispatch libseat"); wl_display_terminate(session->display); } return 1; } static struct libseat_seat_listener seat_listener = { .enable_seat = handle_enable_seat, .disable_seat = handle_disable_seat, }; static enum wlr_log_importance libseat_log_level_to_wlr( enum libseat_log_level level) { switch (level) { case LIBSEAT_LOG_LEVEL_ERROR: return WLR_ERROR; case LIBSEAT_LOG_LEVEL_INFO: return WLR_INFO; default: return WLR_DEBUG; } } static void log_libseat(enum libseat_log_level level, const char *fmt, va_list args) { enum wlr_log_importance importance = libseat_log_level_to_wlr(level); static char wlr_fmt[1024]; snprintf(wlr_fmt, sizeof(wlr_fmt), "[libseat] %s", fmt); _wlr_vlog(importance, wlr_fmt, args); } static int libseat_session_init(struct wlr_session *session, struct wl_display *disp) { libseat_set_log_handler(log_libseat); libseat_set_log_level(LIBSEAT_LOG_LEVEL_INFO); // libseat will take care of updating the logind state if necessary setenv("XDG_SESSION_TYPE", "wayland", 1); session->seat_handle = libseat_open_seat(&seat_listener, session); if (session->seat_handle == NULL) { wlr_log_errno(WLR_ERROR, "Unable to create seat"); return -1; } const char *seat_name = libseat_seat_name(session->seat_handle); if (seat_name == NULL) { wlr_log_errno(WLR_ERROR, "Unable to get seat info"); goto error; } snprintf(session->seat, sizeof(session->seat), "%s", seat_name); struct wl_event_loop *event_loop = wl_display_get_event_loop(disp); session->libseat_event = wl_event_loop_add_fd(event_loop, libseat_get_fd(session->seat_handle), WL_EVENT_READABLE, libseat_event, session); if (session->libseat_event == NULL) { wlr_log(WLR_ERROR, "Failed to create libseat event source"); goto error; } // We may have received enable_seat immediately after the open_seat result, // so, dispatch once without timeout to speed up activation. if (libseat_dispatch(session->seat_handle, 0) == -1) { wlr_log_errno(WLR_ERROR, "libseat dispatch failed"); goto error_dispatch; } wlr_log(WLR_INFO, "Successfully loaded libseat session"); return 0; error_dispatch: wl_event_source_remove(session->libseat_event); session->libseat_event = NULL; error: libseat_close_seat(session->seat_handle); session->seat_handle = NULL; return -1; } static void libseat_session_finish(struct wlr_session *session) { libseat_close_seat(session->seat_handle); wl_event_source_remove(session->libseat_event); session->seat_handle = NULL; session->libseat_event = NULL; } static bool is_drm_card(const char *sysname) { const char prefix[] = DRM_PRIMARY_MINOR_NAME; if (strncmp(sysname, prefix, strlen(prefix)) != 0) { return false; } for (size_t i = strlen(prefix); sysname[i] != '\0'; i++) { if (sysname[i] < '0' || sysname[i] > '9') { return false; } } return true; } static void read_udev_change_event(struct wlr_device_change_event *event, struct udev_device *udev_dev) { const char *hotplug = udev_device_get_property_value(udev_dev, "HOTPLUG"); if (hotplug != NULL && strcmp(hotplug, "1") == 0) { event->type = WLR_DEVICE_HOTPLUG; struct wlr_device_hotplug_event *hotplug = &event->hotplug; const char *connector = udev_device_get_property_value(udev_dev, "CONNECTOR"); if (connector != NULL) { hotplug->connector_id = strtoul(connector, NULL, 10); } const char *prop = udev_device_get_property_value(udev_dev, "PROPERTY"); if (prop != NULL) { hotplug->prop_id = strtoul(prop, NULL, 10); } return; } const char *lease = udev_device_get_property_value(udev_dev, "LEASE"); if (lease != NULL && strcmp(lease, "1") == 0) { event->type = WLR_DEVICE_LEASE; return; } } static int handle_udev_event(int fd, uint32_t mask, void *data) { struct wlr_session *session = data; struct udev_device *udev_dev = udev_monitor_receive_device(session->mon); if (!udev_dev) { return 1; } const char *sysname = udev_device_get_sysname(udev_dev); const char *devnode = udev_device_get_devnode(udev_dev); const char *action = udev_device_get_action(udev_dev); wlr_log(WLR_DEBUG, "udev event for %s (%s)", sysname, action); if (!is_drm_card(sysname) || !action || !devnode) { goto out; } const char *seat = udev_device_get_property_value(udev_dev, "ID_SEAT"); if (!seat) { seat = "seat0"; } if (session->seat[0] != '\0' && strcmp(session->seat, seat) != 0) { goto out; } if (strcmp(action, "add") == 0) { wlr_log(WLR_DEBUG, "DRM device %s added", sysname); struct wlr_session_add_event event = { .path = devnode, }; wl_signal_emit_mutable(&session->events.add_drm_card, &event); } else if (strcmp(action, "change") == 0 || strcmp(action, "remove") == 0) { dev_t devnum = udev_device_get_devnum(udev_dev); struct wlr_device *dev; wl_list_for_each(dev, &session->devices, link) { if (dev->dev != devnum) { continue; } if (strcmp(action, "change") == 0) { wlr_log(WLR_DEBUG, "DRM device %s changed", sysname); struct wlr_device_change_event event = {0}; read_udev_change_event(&event, udev_dev); wl_signal_emit_mutable(&dev->events.change, &event); } else if (strcmp(action, "remove") == 0) { wlr_log(WLR_DEBUG, "DRM device %s removed", sysname); wl_signal_emit_mutable(&dev->events.remove, NULL); } else { assert(0); } break; } } out: udev_device_unref(udev_dev); return 1; } static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_session *session = wl_container_of(listener, session, display_destroy); wlr_session_destroy(session); } struct wlr_session *wlr_session_create(struct wl_display *disp) { struct wlr_session *session = calloc(1, sizeof(*session)); if (!session) { wlr_log_errno(WLR_ERROR, "Allocation failed"); return NULL; } wl_signal_init(&session->events.active); wl_signal_init(&session->events.add_drm_card); wl_signal_init(&session->events.destroy); wl_list_init(&session->devices); if (libseat_session_init(session, disp) == -1) { wlr_log(WLR_ERROR, "Failed to load session backend"); goto error_open; } session->udev = udev_new(); if (!session->udev) { wlr_log_errno(WLR_ERROR, "Failed to create udev context"); goto error_session; } session->mon = udev_monitor_new_from_netlink(session->udev, "udev"); if (!session->mon) { wlr_log_errno(WLR_ERROR, "Failed to create udev monitor"); goto error_udev; } udev_monitor_filter_add_match_subsystem_devtype(session->mon, "drm", NULL); udev_monitor_enable_receiving(session->mon); struct wl_event_loop *event_loop = wl_display_get_event_loop(disp); int fd = udev_monitor_get_fd(session->mon); session->udev_event = wl_event_loop_add_fd(event_loop, fd, WL_EVENT_READABLE, handle_udev_event, session); if (!session->udev_event) { wlr_log_errno(WLR_ERROR, "Failed to create udev event source"); goto error_mon; } session->display = disp; session->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(disp, &session->display_destroy); return session; error_mon: udev_monitor_unref(session->mon); error_udev: udev_unref(session->udev); error_session: libseat_session_finish(session); error_open: free(session); return NULL; } void wlr_session_destroy(struct wlr_session *session) { if (!session) { return; } wl_signal_emit_mutable(&session->events.destroy, session); wl_list_remove(&session->display_destroy.link); wl_event_source_remove(session->udev_event); udev_monitor_unref(session->mon); udev_unref(session->udev); struct wlr_device *dev, *tmp_dev; wl_list_for_each_safe(dev, tmp_dev, &session->devices, link) { wlr_session_close_file(session, dev); } libseat_session_finish(session); free(session); } struct wlr_device *wlr_session_open_file(struct wlr_session *session, const char *path) { int fd; int device_id = libseat_open_device(session->seat_handle, path, &fd); if (device_id == -1) { wlr_log_errno(WLR_ERROR, "Failed to open device: '%s'", path); return NULL; } struct wlr_device *dev = malloc(sizeof(*dev)); if (!dev) { wlr_log_errno(WLR_ERROR, "Allocation failed"); goto error; } struct stat st; if (fstat(fd, &st) < 0) { wlr_log_errno(WLR_ERROR, "Stat failed"); goto error; } dev->fd = fd; dev->dev = st.st_rdev; dev->device_id = device_id; wl_signal_init(&dev->events.change); wl_signal_init(&dev->events.remove); wl_list_insert(&session->devices, &dev->link); return dev; error: libseat_close_device(session->seat_handle, device_id); free(dev); close(fd); return NULL; } void wlr_session_close_file(struct wlr_session *session, struct wlr_device *dev) { if (libseat_close_device(session->seat_handle, dev->device_id) == -1) { wlr_log_errno(WLR_ERROR, "Failed to close device %d", dev->device_id); } close(dev->fd); wl_list_remove(&dev->link); free(dev); } bool wlr_session_change_vt(struct wlr_session *session, unsigned vt) { if (!session) { return false; } return libseat_switch_session(session->seat_handle, vt) == 0; } /* Tests if 'path' is KMS compatible by trying to open it. Returns the opened * device on success. */ struct wlr_device *session_open_if_kms(struct wlr_session *restrict session, const char *restrict path) { if (!path) { return NULL; } struct wlr_device *dev = wlr_session_open_file(session, path); if (!dev) { return NULL; } if (!drmIsKMS(dev->fd)) { wlr_log(WLR_DEBUG, "Ignoring '%s': not a KMS device", path); wlr_session_close_file(session, dev); return NULL; } return dev; } static ssize_t explicit_find_gpus(struct wlr_session *session, size_t ret_len, struct wlr_device *ret[static ret_len], const char *str) { char *gpus = strdup(str); if (!gpus) { wlr_log_errno(WLR_ERROR, "Allocation failed"); return -1; } size_t i = 0; char *save; char *ptr = strtok_r(gpus, ":", &save); do { if (i >= ret_len) { break; } ret[i] = session_open_if_kms(session, ptr); if (!ret[i]) { wlr_log(WLR_ERROR, "Unable to open %s as DRM device", ptr); } else { ++i; } } while ((ptr = strtok_r(NULL, ":", &save))); free(gpus); return i; } static struct udev_enumerate *enumerate_drm_cards(struct udev *udev) { struct udev_enumerate *en = udev_enumerate_new(udev); if (!en) { wlr_log(WLR_ERROR, "udev_enumerate_new failed"); return NULL; } udev_enumerate_add_match_subsystem(en, "drm"); udev_enumerate_add_match_sysname(en, DRM_PRIMARY_MINOR_NAME "[0-9]*"); if (udev_enumerate_scan_devices(en) != 0) { wlr_log(WLR_ERROR, "udev_enumerate_scan_devices failed"); udev_enumerate_unref(en); return NULL; } return en; } struct find_gpus_add_handler { bool added; struct wl_listener listener; }; static void find_gpus_handle_add(struct wl_listener *listener, void *data) { struct find_gpus_add_handler *handler = wl_container_of(listener, handler, listener); handler->added = true; } /* Tries to find the primary GPU by checking for the "boot_vga" attribute. * If it's not found, it returns the first valid GPU it finds. */ ssize_t wlr_session_find_gpus(struct wlr_session *session, size_t ret_len, struct wlr_device **ret) { const char *explicit = getenv("WLR_DRM_DEVICES"); if (explicit) { return explicit_find_gpus(session, ret_len, ret, explicit); } struct udev_enumerate *en = enumerate_drm_cards(session->udev); if (!en) { return -1; } if (udev_enumerate_get_list_entry(en) == NULL) { udev_enumerate_unref(en); wlr_log(WLR_INFO, "Waiting for a DRM card device"); struct find_gpus_add_handler handler = {0}; handler.listener.notify = find_gpus_handle_add; wl_signal_add(&session->events.add_drm_card, &handler.listener); int64_t started_at = get_current_time_msec(); int64_t timeout = WAIT_GPU_TIMEOUT; struct wl_event_loop *event_loop = wl_display_get_event_loop(session->display); while (!handler.added) { int ret = wl_event_loop_dispatch(event_loop, (int)timeout); if (ret < 0) { wlr_log_errno(WLR_ERROR, "Failed to wait for DRM card device: " "wl_event_loop_dispatch failed"); udev_enumerate_unref(en); return -1; } int64_t now = get_current_time_msec(); if (now >= started_at + WAIT_GPU_TIMEOUT) { break; } timeout = started_at + WAIT_GPU_TIMEOUT - now; } wl_list_remove(&handler.listener.link); en = enumerate_drm_cards(session->udev); if (!en) { return -1; } } struct udev_list_entry *entry; size_t i = 0; udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(en)) { if (i == ret_len) { break; } bool is_boot_vga = false; const char *path = udev_list_entry_get_name(entry); struct udev_device *dev = udev_device_new_from_syspath(session->udev, path); if (!dev) { continue; } const char *seat = udev_device_get_property_value(dev, "ID_SEAT"); if (!seat) { seat = "seat0"; } if (session->seat[0] && strcmp(session->seat, seat) != 0) { udev_device_unref(dev); continue; } // This is owned by 'dev', so we don't need to free it struct udev_device *pci = udev_device_get_parent_with_subsystem_devtype(dev, "pci", NULL); if (pci) { const char *id = udev_device_get_sysattr_value(pci, "boot_vga"); if (id && strcmp(id, "1") == 0) { is_boot_vga = true; } } struct wlr_device *wlr_dev = session_open_if_kms(session, udev_device_get_devnode(dev)); if (!wlr_dev) { udev_device_unref(dev); continue; } udev_device_unref(dev); ret[i] = wlr_dev; if (is_boot_vga) { struct wlr_device *tmp = ret[0]; ret[0] = ret[i]; ret[i] = tmp; } ++i; } udev_enumerate_unref(en); return i; } wlroots-0.17.1/backend/wayland/000077500000000000000000000000001454110342200163335ustar00rootroot00000000000000wlroots-0.17.1/backend/wayland/backend.c000066400000000000000000000526661454110342200201050ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "backend/wayland.h" #include "render/drm_format_set.h" #include "render/pixel_format.h" #include "drm-client-protocol.h" #include "linux-dmabuf-unstable-v1-client-protocol.h" #include "pointer-gestures-unstable-v1-client-protocol.h" #include "presentation-time-client-protocol.h" #include "xdg-activation-v1-client-protocol.h" #include "xdg-decoration-unstable-v1-client-protocol.h" #include "xdg-shell-client-protocol.h" #include "tablet-unstable-v2-client-protocol.h" #include "relative-pointer-unstable-v1-client-protocol.h" #include "viewporter-client-protocol.h" struct wlr_wl_linux_dmabuf_feedback_v1 { struct wlr_wl_backend *backend; dev_t main_device_id; struct wlr_wl_linux_dmabuf_v1_table_entry *format_table; size_t format_table_size; dev_t tranche_target_device_id; }; struct wlr_wl_linux_dmabuf_v1_table_entry { uint32_t format; uint32_t pad; /* unused */ uint64_t modifier; }; struct wlr_wl_backend *get_wl_backend_from_backend(struct wlr_backend *wlr_backend) { assert(wlr_backend_is_wl(wlr_backend)); struct wlr_wl_backend *backend = wl_container_of(wlr_backend, backend, backend); return backend; } static int dispatch_events(int fd, uint32_t mask, void *data) { struct wlr_wl_backend *wl = data; if ((mask & WL_EVENT_HANGUP) || (mask & WL_EVENT_ERROR)) { if (mask & WL_EVENT_ERROR) { wlr_log(WLR_ERROR, "Failed to read from remote Wayland display"); } wl_display_terminate(wl->local_display); return 0; } int count = 0; if (mask & WL_EVENT_READABLE) { count = wl_display_dispatch(wl->remote_display); } if (mask & WL_EVENT_WRITABLE) { wl_display_flush(wl->remote_display); } if (mask == 0) { count = wl_display_dispatch_pending(wl->remote_display); wl_display_flush(wl->remote_display); } if (count < 0) { wlr_log(WLR_ERROR, "Failed to dispatch remote Wayland display"); wl_display_terminate(wl->local_display); return 0; } return count; } static void xdg_wm_base_handle_ping(void *data, struct xdg_wm_base *base, uint32_t serial) { xdg_wm_base_pong(base, serial); } static const struct xdg_wm_base_listener xdg_wm_base_listener = { xdg_wm_base_handle_ping, }; static void presentation_handle_clock_id(void *data, struct wp_presentation *presentation, uint32_t clock) { struct wlr_wl_backend *wl = data; if (clock != CLOCK_MONOTONIC) { wp_presentation_destroy(wl->presentation); wl->presentation = NULL; } } static const struct wp_presentation_listener presentation_listener = { .clock_id = presentation_handle_clock_id, }; static void linux_dmabuf_v1_handle_format(void *data, struct zwp_linux_dmabuf_v1 *linux_dmabuf_v1, uint32_t format) { // Note, this event is deprecated struct wlr_wl_backend *wl = data; wlr_drm_format_set_add(&wl->linux_dmabuf_v1_formats, format, DRM_FORMAT_MOD_INVALID); } static void linux_dmabuf_v1_handle_modifier(void *data, struct zwp_linux_dmabuf_v1 *linux_dmabuf_v1, uint32_t format, uint32_t modifier_hi, uint32_t modifier_lo) { struct wlr_wl_backend *wl = data; uint64_t modifier = ((uint64_t)modifier_hi << 32) | modifier_lo; wlr_drm_format_set_add(&wl->linux_dmabuf_v1_formats, format, modifier); } static const struct zwp_linux_dmabuf_v1_listener linux_dmabuf_v1_listener = { .format = linux_dmabuf_v1_handle_format, .modifier = linux_dmabuf_v1_handle_modifier, }; static void linux_dmabuf_feedback_v1_handle_done(void *data, struct zwp_linux_dmabuf_feedback_v1 *feedback) { // This space is intentionally left blank } static void linux_dmabuf_feedback_v1_handle_format_table(void *data, struct zwp_linux_dmabuf_feedback_v1 *feedback, int fd, uint32_t size) { struct wlr_wl_linux_dmabuf_feedback_v1 *feedback_data = data; feedback_data->format_table = NULL; void *table_data = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); if (table_data == MAP_FAILED) { wlr_log_errno(WLR_ERROR, "failed to mmap DMA-BUF format table"); } else { feedback_data->format_table = table_data; feedback_data->format_table_size = size; } close(fd); } static void linux_dmabuf_feedback_v1_handle_main_device(void *data, struct zwp_linux_dmabuf_feedback_v1 *feedback, struct wl_array *dev_id_arr) { struct wlr_wl_linux_dmabuf_feedback_v1 *feedback_data = data; dev_t dev_id; assert(dev_id_arr->size == sizeof(dev_id)); memcpy(&dev_id, dev_id_arr->data, sizeof(dev_id)); feedback_data->main_device_id = dev_id; drmDevice *device = NULL; if (drmGetDeviceFromDevId(dev_id, 0, &device) != 0) { wlr_log_errno(WLR_ERROR, "drmGetDeviceFromDevId failed"); return; } const char *name = NULL; if (device->available_nodes & (1 << DRM_NODE_RENDER)) { name = device->nodes[DRM_NODE_RENDER]; } else { // Likely a split display/render setup. Pick the primary node and hope // Mesa will open the right render node under-the-hood. assert(device->available_nodes & (1 << DRM_NODE_PRIMARY)); name = device->nodes[DRM_NODE_PRIMARY]; wlr_log(WLR_DEBUG, "DRM device %s has no render node, " "falling back to primary node", name); } feedback_data->backend->drm_render_name = strdup(name); drmFreeDevice(&device); } static void linux_dmabuf_feedback_v1_handle_tranche_done(void *data, struct zwp_linux_dmabuf_feedback_v1 *feedback) { struct wlr_wl_linux_dmabuf_feedback_v1 *feedback_data = data; feedback_data->tranche_target_device_id = 0; } static void linux_dmabuf_feedback_v1_handle_tranche_target_device(void *data, struct zwp_linux_dmabuf_feedback_v1 *feedback, struct wl_array *dev_id_arr) { struct wlr_wl_linux_dmabuf_feedback_v1 *feedback_data = data; dev_t dev_id; assert(dev_id_arr->size == sizeof(dev_id)); memcpy(&dev_id, dev_id_arr->data, sizeof(dev_id)); feedback_data->tranche_target_device_id = dev_id; } static void linux_dmabuf_feedback_v1_handle_tranche_formats(void *data, struct zwp_linux_dmabuf_feedback_v1 *feedback, struct wl_array *indices_arr) { struct wlr_wl_linux_dmabuf_feedback_v1 *feedback_data = data; if (feedback_data->format_table == NULL) { return; } if (feedback_data->tranche_target_device_id != feedback_data->main_device_id) { return; } size_t table_cap = feedback_data->format_table_size / sizeof(struct wlr_wl_linux_dmabuf_v1_table_entry); uint16_t *index_ptr; wl_array_for_each(index_ptr, indices_arr) { assert(*index_ptr < table_cap); const struct wlr_wl_linux_dmabuf_v1_table_entry *entry = &feedback_data->format_table[*index_ptr]; wlr_drm_format_set_add(&feedback_data->backend->linux_dmabuf_v1_formats, entry->format, entry->modifier); } } static void linux_dmabuf_feedback_v1_handle_tranche_flags(void *data, struct zwp_linux_dmabuf_feedback_v1 *feedback, uint32_t flags) { // TODO: handle SCANOUT flag } static const struct zwp_linux_dmabuf_feedback_v1_listener linux_dmabuf_feedback_v1_listener = { .done = linux_dmabuf_feedback_v1_handle_done, .format_table = linux_dmabuf_feedback_v1_handle_format_table, .main_device = linux_dmabuf_feedback_v1_handle_main_device, .tranche_done = linux_dmabuf_feedback_v1_handle_tranche_done, .tranche_target_device = linux_dmabuf_feedback_v1_handle_tranche_target_device, .tranche_formats = linux_dmabuf_feedback_v1_handle_tranche_formats, .tranche_flags = linux_dmabuf_feedback_v1_handle_tranche_flags, }; static bool device_has_name(const drmDevice *device, const char *name) { for (size_t i = 0; i < DRM_NODE_MAX; i++) { if (!(device->available_nodes & (1 << i))) { continue; } if (strcmp(device->nodes[i], name) == 0) { return true; } } return false; } static char *get_render_name(const char *name) { uint32_t flags = 0; int devices_len = drmGetDevices2(flags, NULL, 0); if (devices_len < 0) { wlr_log(WLR_ERROR, "drmGetDevices2 failed: %s", strerror(-devices_len)); return NULL; } drmDevice **devices = calloc(devices_len, sizeof(*devices)); if (devices == NULL) { wlr_log_errno(WLR_ERROR, "Allocation failed"); return NULL; } devices_len = drmGetDevices2(flags, devices, devices_len); if (devices_len < 0) { free(devices); wlr_log(WLR_ERROR, "drmGetDevices2 failed: %s", strerror(-devices_len)); return NULL; } const drmDevice *match = NULL; for (int i = 0; i < devices_len; i++) { if (device_has_name(devices[i], name)) { match = devices[i]; break; } } char *render_name = NULL; if (match == NULL) { wlr_log(WLR_ERROR, "Cannot find DRM device %s", name); } else if (!(match->available_nodes & (1 << DRM_NODE_RENDER))) { // Likely a split display/render setup. Pick the primary node and hope // Mesa will open the right render node under-the-hood. wlr_log(WLR_DEBUG, "DRM device %s has no render node, " "falling back to primary node", name); assert(match->available_nodes & (1 << DRM_NODE_PRIMARY)); render_name = strdup(match->nodes[DRM_NODE_PRIMARY]); } else { render_name = strdup(match->nodes[DRM_NODE_RENDER]); } for (int i = 0; i < devices_len; i++) { drmFreeDevice(&devices[i]); } free(devices); return render_name; } static void legacy_drm_handle_device(void *data, struct wl_drm *drm, const char *name) { struct wlr_wl_backend *wl = data; wl->drm_render_name = get_render_name(name); } static void legacy_drm_handle_format(void *data, struct wl_drm *drm, uint32_t format) { // This space is intentionally left blank } static void legacy_drm_handle_authenticated(void *data, struct wl_drm *drm) { // This space is intentionally left blank } static void legacy_drm_handle_capabilities(void *data, struct wl_drm *drm, uint32_t caps) { // This space is intentionally left blank } static const struct wl_drm_listener legacy_drm_listener = { .device = legacy_drm_handle_device, .format = legacy_drm_handle_format, .authenticated = legacy_drm_handle_authenticated, .capabilities = legacy_drm_handle_capabilities, }; static void shm_handle_format(void *data, struct wl_shm *shm, uint32_t shm_format) { struct wlr_wl_backend *wl = data; uint32_t drm_format = convert_wl_shm_format_to_drm(shm_format); wlr_drm_format_set_add(&wl->shm_formats, drm_format, DRM_FORMAT_MOD_INVALID); } static const struct wl_shm_listener shm_listener = { .format = shm_handle_format, }; static void registry_global(void *data, struct wl_registry *registry, uint32_t name, const char *iface, uint32_t version) { struct wlr_wl_backend *wl = data; wlr_log(WLR_DEBUG, "Remote wayland global: %s v%" PRIu32, iface, version); if (strcmp(iface, wl_compositor_interface.name) == 0) { wl->compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 4); } else if (strcmp(iface, wl_seat_interface.name) == 0) { uint32_t target_version = version; if (version < 5) { target_version = 5; } if (version > 8) { target_version = 8; } struct wl_seat *wl_seat = wl_registry_bind(registry, name, &wl_seat_interface, target_version); if (!create_wl_seat(wl_seat, wl, name)) { wl_seat_destroy(wl_seat); } } else if (strcmp(iface, xdg_wm_base_interface.name) == 0) { wl->xdg_wm_base = wl_registry_bind(registry, name, &xdg_wm_base_interface, 1); xdg_wm_base_add_listener(wl->xdg_wm_base, &xdg_wm_base_listener, NULL); } else if (strcmp(iface, zxdg_decoration_manager_v1_interface.name) == 0) { wl->zxdg_decoration_manager_v1 = wl_registry_bind(registry, name, &zxdg_decoration_manager_v1_interface, 1); } else if (strcmp(iface, zwp_pointer_gestures_v1_interface.name) == 0) { wl->zwp_pointer_gestures_v1 = wl_registry_bind(registry, name, &zwp_pointer_gestures_v1_interface, version < 3 ? version : 3); } else if (strcmp(iface, wp_presentation_interface.name) == 0) { wl->presentation = wl_registry_bind(registry, name, &wp_presentation_interface, 1); wp_presentation_add_listener(wl->presentation, &presentation_listener, wl); } else if (strcmp(iface, zwp_tablet_manager_v2_interface.name) == 0) { wl->tablet_manager = wl_registry_bind(registry, name, &zwp_tablet_manager_v2_interface, 1); } else if (strcmp(iface, zwp_linux_dmabuf_v1_interface.name) == 0 && version >= 3) { wl->zwp_linux_dmabuf_v1 = wl_registry_bind(registry, name, &zwp_linux_dmabuf_v1_interface, version >= 4 ? 4 : version); zwp_linux_dmabuf_v1_add_listener(wl->zwp_linux_dmabuf_v1, &linux_dmabuf_v1_listener, wl); } else if (strcmp(iface, zwp_relative_pointer_manager_v1_interface.name) == 0) { wl->zwp_relative_pointer_manager_v1 = wl_registry_bind(registry, name, &zwp_relative_pointer_manager_v1_interface, 1); } else if (strcmp(iface, wl_drm_interface.name) == 0) { wl->legacy_drm = wl_registry_bind(registry, name, &wl_drm_interface, 1); wl_drm_add_listener(wl->legacy_drm, &legacy_drm_listener, wl); } else if (strcmp(iface, wl_shm_interface.name) == 0) { wl->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1); wl_shm_add_listener(wl->shm, &shm_listener, wl); } else if (strcmp(iface, xdg_activation_v1_interface.name) == 0) { wl->activation_v1 = wl_registry_bind(registry, name, &xdg_activation_v1_interface, 1); } else if (strcmp(iface, wl_subcompositor_interface.name) == 0) { wl->subcompositor = wl_registry_bind(registry, name, &wl_subcompositor_interface, 1); } else if (strcmp(iface, wp_viewporter_interface.name) == 0) { wl->viewporter = wl_registry_bind(registry, name, &wp_viewporter_interface, 1); } } static void registry_global_remove(void *data, struct wl_registry *registry, uint32_t name) { struct wlr_wl_backend *wl = data; struct wlr_wl_seat *seat; wl_list_for_each(seat, &wl->seats, link) { if (seat->global_name == name) { destroy_wl_seat(seat); break; } } } static const struct wl_registry_listener registry_listener = { .global = registry_global, .global_remove = registry_global_remove }; /* * Initializes the wayland backend. Opens a connection to a remote wayland * compositor and creates surfaces for each output, then registers globals on * the specified display. */ static bool backend_start(struct wlr_backend *backend) { struct wlr_wl_backend *wl = get_wl_backend_from_backend(backend); wlr_log(WLR_INFO, "Starting Wayland backend"); wl->started = true; struct wlr_wl_seat *seat; wl_list_for_each(seat, &wl->seats, link) { if (seat->wl_keyboard) { init_seat_keyboard(seat); } if (seat->wl_touch) { init_seat_touch(seat); } if (wl->tablet_manager) { init_seat_tablet(seat); } } for (size_t i = 0; i < wl->requested_outputs; ++i) { wlr_wl_output_create(&wl->backend); } return true; } static void backend_destroy(struct wlr_backend *backend) { if (!backend) { return; } struct wlr_wl_backend *wl = get_wl_backend_from_backend(backend); struct wlr_wl_output *output, *tmp_output; wl_list_for_each_safe(output, tmp_output, &wl->outputs, link) { wlr_output_destroy(&output->wlr_output); } // Avoid using wl_list_for_each_safe() here: destroying a buffer may // have the side-effect of destroying the next one in the list while (!wl_list_empty(&wl->buffers)) { struct wlr_wl_buffer *buffer = wl_container_of(wl->buffers.next, buffer, link); destroy_wl_buffer(buffer); } wlr_backend_finish(backend); wl_list_remove(&wl->local_display_destroy.link); wl_event_source_remove(wl->remote_display_src); close(wl->drm_fd); wlr_drm_format_set_finish(&wl->shm_formats); wlr_drm_format_set_finish(&wl->linux_dmabuf_v1_formats); struct wlr_wl_seat *seat, *tmp_seat; wl_list_for_each_safe(seat, tmp_seat, &wl->seats, link) { destroy_wl_seat(seat); } if (wl->activation_v1) { xdg_activation_v1_destroy(wl->activation_v1); } if (wl->zxdg_decoration_manager_v1) { zxdg_decoration_manager_v1_destroy(wl->zxdg_decoration_manager_v1); } if (wl->zwp_pointer_gestures_v1) { zwp_pointer_gestures_v1_destroy(wl->zwp_pointer_gestures_v1); } if (wl->tablet_manager) { zwp_tablet_manager_v2_destroy(wl->tablet_manager); } if (wl->presentation) { wp_presentation_destroy(wl->presentation); } if (wl->zwp_linux_dmabuf_v1) { zwp_linux_dmabuf_v1_destroy(wl->zwp_linux_dmabuf_v1); } if (wl->legacy_drm != NULL) { wl_drm_destroy(wl->legacy_drm); } if (wl->shm) { wl_shm_destroy(wl->shm); } if (wl->zwp_relative_pointer_manager_v1) { zwp_relative_pointer_manager_v1_destroy(wl->zwp_relative_pointer_manager_v1); } if (wl->subcompositor) { wl_subcompositor_destroy(wl->subcompositor); } if (wl->viewporter) { wp_viewporter_destroy(wl->viewporter); } free(wl->drm_render_name); free(wl->activation_token); xdg_wm_base_destroy(wl->xdg_wm_base); wl_compositor_destroy(wl->compositor); wl_registry_destroy(wl->registry); wl_display_flush(wl->remote_display); if (wl->own_remote_display) { wl_display_disconnect(wl->remote_display); } free(wl); } static int backend_get_drm_fd(struct wlr_backend *backend) { struct wlr_wl_backend *wl = get_wl_backend_from_backend(backend); return wl->drm_fd; } static uint32_t get_buffer_caps(struct wlr_backend *backend) { struct wlr_wl_backend *wl = get_wl_backend_from_backend(backend); return (wl->zwp_linux_dmabuf_v1 ? WLR_BUFFER_CAP_DMABUF : 0) | (wl->shm ? WLR_BUFFER_CAP_SHM : 0); } static const struct wlr_backend_impl backend_impl = { .start = backend_start, .destroy = backend_destroy, .get_drm_fd = backend_get_drm_fd, .get_buffer_caps = get_buffer_caps, }; bool wlr_backend_is_wl(struct wlr_backend *b) { return b->impl == &backend_impl; } static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_wl_backend *wl = wl_container_of(listener, wl, local_display_destroy); backend_destroy(&wl->backend); } struct wlr_backend *wlr_wl_backend_create(struct wl_display *display, struct wl_display *remote_display) { wlr_log(WLR_INFO, "Creating wayland backend"); struct wlr_wl_backend *wl = calloc(1, sizeof(*wl)); if (!wl) { wlr_log_errno(WLR_ERROR, "Allocation failed"); return NULL; } wlr_backend_init(&wl->backend, &backend_impl); wl->local_display = display; wl_list_init(&wl->outputs); wl_list_init(&wl->seats); wl_list_init(&wl->buffers); if (remote_display != NULL) { wl->remote_display = remote_display; } else { wl->remote_display = wl_display_connect(NULL); if (!wl->remote_display) { wlr_log_errno(WLR_ERROR, "Could not connect to remote display"); goto error_wl; } wl->own_remote_display = true; } wl->registry = wl_display_get_registry(wl->remote_display); if (!wl->registry) { wlr_log_errno(WLR_ERROR, "Could not obtain reference to remote registry"); goto error_display; } wl_registry_add_listener(wl->registry, ®istry_listener, wl); wl_display_roundtrip(wl->remote_display); // get globals if (!wl->compositor) { wlr_log(WLR_ERROR, "Remote Wayland compositor does not support wl_compositor"); goto error_registry; } if (!wl->xdg_wm_base) { wlr_log(WLR_ERROR, "Remote Wayland compositor does not support xdg-shell"); goto error_registry; } struct zwp_linux_dmabuf_feedback_v1 *linux_dmabuf_feedback_v1 = NULL; struct wlr_wl_linux_dmabuf_feedback_v1 feedback_data = { .backend = wl }; if (wl->zwp_linux_dmabuf_v1 != NULL && zwp_linux_dmabuf_v1_get_version(wl->zwp_linux_dmabuf_v1) >= ZWP_LINUX_DMABUF_V1_GET_DEFAULT_FEEDBACK_SINCE_VERSION) { linux_dmabuf_feedback_v1 = zwp_linux_dmabuf_v1_get_default_feedback(wl->zwp_linux_dmabuf_v1); if (linux_dmabuf_feedback_v1 == NULL) { wlr_log(WLR_ERROR, "Allocation failed"); goto error_registry; } zwp_linux_dmabuf_feedback_v1_add_listener(linux_dmabuf_feedback_v1, &linux_dmabuf_feedback_v1_listener, &feedback_data); if (wl->legacy_drm != NULL) { wl_drm_destroy(wl->legacy_drm); wl->legacy_drm = NULL; } } wl_display_roundtrip(wl->remote_display); // get linux-dmabuf formats if (feedback_data.format_table != NULL) { munmap(feedback_data.format_table, feedback_data.format_table_size); } if (linux_dmabuf_feedback_v1 != NULL) { zwp_linux_dmabuf_feedback_v1_destroy(linux_dmabuf_feedback_v1); } struct wl_event_loop *loop = wl_display_get_event_loop(wl->local_display); int fd = wl_display_get_fd(wl->remote_display); wl->remote_display_src = wl_event_loop_add_fd(loop, fd, WL_EVENT_READABLE, dispatch_events, wl); if (!wl->remote_display_src) { wlr_log(WLR_ERROR, "Failed to create event source"); goto error_registry; } wl_event_source_check(wl->remote_display_src); if (wl->drm_render_name != NULL) { wlr_log(WLR_DEBUG, "Opening DRM render node %s", wl->drm_render_name); wl->drm_fd = open(wl->drm_render_name, O_RDWR | O_NONBLOCK | O_CLOEXEC); if (wl->drm_fd < 0) { wlr_log_errno(WLR_ERROR, "Failed to open DRM render node %s", wl->drm_render_name); goto error_remote_display_src; } } else { wl->drm_fd = -1; } wl->local_display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &wl->local_display_destroy); const char *token = getenv("XDG_ACTIVATION_TOKEN"); if (token != NULL) { wl->activation_token = strdup(token); unsetenv("XDG_ACTIVATION_TOKEN"); } return &wl->backend; error_remote_display_src: wl_event_source_remove(wl->remote_display_src); error_registry: free(wl->drm_render_name); if (wl->compositor) { wl_compositor_destroy(wl->compositor); } if (wl->xdg_wm_base) { xdg_wm_base_destroy(wl->xdg_wm_base); } wl_registry_destroy(wl->registry); error_display: if (wl->own_remote_display) { wl_display_disconnect(wl->remote_display); } error_wl: wlr_backend_finish(&wl->backend); free(wl); return NULL; } struct wl_display *wlr_wl_backend_get_remote_display(struct wlr_backend *backend) { struct wlr_wl_backend *wl = get_wl_backend_from_backend(backend); return wl->remote_display; } wlroots-0.17.1/backend/wayland/meson.build000066400000000000000000000010711454110342200204740ustar00rootroot00000000000000wayland_client = dependency('wayland-client', fallback: 'wayland', default_options: wayland_project_options, ) wlr_deps += wayland_client wlr_files += files( 'backend.c', 'output.c', 'seat.c', 'pointer.c', 'tablet_v2.c', ) client_protos = [ 'drm', 'linux-dmabuf-unstable-v1', 'pointer-gestures-unstable-v1', 'presentation-time', 'relative-pointer-unstable-v1', 'tablet-unstable-v2', 'viewporter', 'xdg-activation-v1', 'xdg-decoration-unstable-v1', 'xdg-shell', ] foreach proto : client_protos wlr_files += protocols_client_header[proto] endforeach wlroots-0.17.1/backend/wayland/output.c000066400000000000000000000657301454110342200200520ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "backend/wayland.h" #include "render/pixel_format.h" #include "render/wlr_renderer.h" #include "types/wlr_output.h" #include "linux-dmabuf-unstable-v1-client-protocol.h" #include "presentation-time-client-protocol.h" #include "viewporter-client-protocol.h" #include "xdg-activation-v1-client-protocol.h" #include "xdg-decoration-unstable-v1-client-protocol.h" #include "xdg-shell-client-protocol.h" static const uint32_t SUPPORTED_OUTPUT_STATE = WLR_OUTPUT_STATE_BACKEND_OPTIONAL | WLR_OUTPUT_STATE_BUFFER | WLR_OUTPUT_STATE_ENABLED | WLR_OUTPUT_STATE_MODE | WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED; static size_t last_output_num = 0; static const char *surface_tag = "wlr_wl_output"; static struct wlr_wl_output *get_wl_output_from_output( struct wlr_output *wlr_output) { assert(wlr_output_is_wl(wlr_output)); struct wlr_wl_output *output = wl_container_of(wlr_output, output, wlr_output); return output; } struct wlr_wl_output *get_wl_output_from_surface(struct wlr_wl_backend *wl, struct wl_surface *surface) { if (wl_proxy_get_tag((struct wl_proxy *)surface) != &surface_tag) { return NULL; } struct wlr_wl_output *output = wl_surface_get_user_data(surface); assert(output != NULL); if (output->backend != wl) { return NULL; } return output; } static void surface_frame_callback(void *data, struct wl_callback *cb, uint32_t time) { struct wlr_wl_output *output = data; if (cb == NULL) { return; } assert(output->frame_callback == cb); wl_callback_destroy(cb); output->frame_callback = NULL; wlr_output_send_frame(&output->wlr_output); } static const struct wl_callback_listener frame_listener = { .done = surface_frame_callback }; static void presentation_feedback_destroy( struct wlr_wl_presentation_feedback *feedback) { wl_list_remove(&feedback->link); wp_presentation_feedback_destroy(feedback->feedback); free(feedback); } static void presentation_feedback_handle_sync_output(void *data, struct wp_presentation_feedback *feedback, struct wl_output *output) { // This space is intentionally left blank } static void presentation_feedback_handle_presented(void *data, struct wp_presentation_feedback *wp_feedback, uint32_t tv_sec_hi, uint32_t tv_sec_lo, uint32_t tv_nsec, uint32_t refresh_ns, uint32_t seq_hi, uint32_t seq_lo, uint32_t flags) { struct wlr_wl_presentation_feedback *feedback = data; struct timespec t = { .tv_sec = ((uint64_t)tv_sec_hi << 32) | tv_sec_lo, .tv_nsec = tv_nsec, }; struct wlr_output_event_present event = { .commit_seq = feedback->commit_seq, .presented = true, .when = &t, .seq = ((uint64_t)seq_hi << 32) | seq_lo, .refresh = refresh_ns, .flags = flags, }; wlr_output_send_present(&feedback->output->wlr_output, &event); presentation_feedback_destroy(feedback); } static void presentation_feedback_handle_discarded(void *data, struct wp_presentation_feedback *wp_feedback) { struct wlr_wl_presentation_feedback *feedback = data; struct wlr_output_event_present event = { .commit_seq = feedback->commit_seq, .presented = false, }; wlr_output_send_present(&feedback->output->wlr_output, &event); presentation_feedback_destroy(feedback); } static const struct wp_presentation_feedback_listener presentation_feedback_listener = { .sync_output = presentation_feedback_handle_sync_output, .presented = presentation_feedback_handle_presented, .discarded = presentation_feedback_handle_discarded, }; void destroy_wl_buffer(struct wlr_wl_buffer *buffer) { if (buffer == NULL) { return; } wl_list_remove(&buffer->buffer_destroy.link); wl_list_remove(&buffer->link); wl_buffer_destroy(buffer->wl_buffer); if (!buffer->released) { wlr_buffer_unlock(buffer->buffer); } free(buffer); } static void buffer_handle_release(void *data, struct wl_buffer *wl_buffer) { struct wlr_wl_buffer *buffer = data; buffer->released = true; wlr_buffer_unlock(buffer->buffer); // might free buffer } static const struct wl_buffer_listener buffer_listener = { .release = buffer_handle_release, }; static void buffer_handle_buffer_destroy(struct wl_listener *listener, void *data) { struct wlr_wl_buffer *buffer = wl_container_of(listener, buffer, buffer_destroy); destroy_wl_buffer(buffer); } static bool test_buffer(struct wlr_wl_backend *wl, struct wlr_buffer *wlr_buffer) { struct wlr_dmabuf_attributes dmabuf; struct wlr_shm_attributes shm; if (wlr_buffer_get_dmabuf(wlr_buffer, &dmabuf)) { return wlr_drm_format_set_has(&wl->linux_dmabuf_v1_formats, dmabuf.format, dmabuf.modifier); } else if (wlr_buffer_get_shm(wlr_buffer, &shm)) { return wlr_drm_format_set_has(&wl->shm_formats, shm.format, DRM_FORMAT_MOD_INVALID); } else { return false; } } static struct wl_buffer *import_dmabuf(struct wlr_wl_backend *wl, struct wlr_dmabuf_attributes *dmabuf) { uint32_t modifier_hi = dmabuf->modifier >> 32; uint32_t modifier_lo = (uint32_t)dmabuf->modifier; struct zwp_linux_buffer_params_v1 *params = zwp_linux_dmabuf_v1_create_params(wl->zwp_linux_dmabuf_v1); for (int i = 0; i < dmabuf->n_planes; i++) { zwp_linux_buffer_params_v1_add(params, dmabuf->fd[i], i, dmabuf->offset[i], dmabuf->stride[i], modifier_hi, modifier_lo); } struct wl_buffer *wl_buffer = zwp_linux_buffer_params_v1_create_immed( params, dmabuf->width, dmabuf->height, dmabuf->format, 0); zwp_linux_buffer_params_v1_destroy(params); // TODO: handle create() errors return wl_buffer; } static struct wl_buffer *import_shm(struct wlr_wl_backend *wl, struct wlr_shm_attributes *shm) { enum wl_shm_format wl_shm_format = convert_drm_format_to_wl_shm(shm->format); uint32_t size = shm->stride * shm->height; struct wl_shm_pool *pool = wl_shm_create_pool(wl->shm, shm->fd, size); if (pool == NULL) { return NULL; } struct wl_buffer *wl_buffer = wl_shm_pool_create_buffer(pool, shm->offset, shm->width, shm->height, shm->stride, wl_shm_format); wl_shm_pool_destroy(pool); return wl_buffer; } static struct wlr_wl_buffer *create_wl_buffer(struct wlr_wl_backend *wl, struct wlr_buffer *wlr_buffer) { if (!test_buffer(wl, wlr_buffer)) { return NULL; } struct wlr_dmabuf_attributes dmabuf; struct wlr_shm_attributes shm; struct wl_buffer *wl_buffer; if (wlr_buffer_get_dmabuf(wlr_buffer, &dmabuf)) { wl_buffer = import_dmabuf(wl, &dmabuf); } else if (wlr_buffer_get_shm(wlr_buffer, &shm)) { wl_buffer = import_shm(wl, &shm); } else { return NULL; } if (wl_buffer == NULL) { return NULL; } struct wlr_wl_buffer *buffer = calloc(1, sizeof(*buffer)); if (buffer == NULL) { wl_buffer_destroy(wl_buffer); return NULL; } buffer->wl_buffer = wl_buffer; buffer->buffer = wlr_buffer_lock(wlr_buffer); wl_list_insert(&wl->buffers, &buffer->link); wl_buffer_add_listener(wl_buffer, &buffer_listener, buffer); buffer->buffer_destroy.notify = buffer_handle_buffer_destroy; wl_signal_add(&wlr_buffer->events.destroy, &buffer->buffer_destroy); return buffer; } static struct wlr_wl_buffer *get_or_create_wl_buffer(struct wlr_wl_backend *wl, struct wlr_buffer *wlr_buffer) { struct wlr_wl_buffer *buffer; wl_list_for_each(buffer, &wl->buffers, link) { // We can only re-use a wlr_wl_buffer if the parent compositor has // released it, because wl_buffer.release is per-wl_buffer, not per // wl_surface.commit. if (buffer->buffer == wlr_buffer && buffer->released) { buffer->released = false; wlr_buffer_lock(buffer->buffer); return buffer; } } return create_wl_buffer(wl, wlr_buffer); } static bool output_test(struct wlr_output *wlr_output, const struct wlr_output_state *state) { struct wlr_wl_output *output = get_wl_output_from_output(wlr_output); uint32_t unsupported = state->committed & ~SUPPORTED_OUTPUT_STATE; if (unsupported != 0) { wlr_log(WLR_DEBUG, "Unsupported output state fields: 0x%"PRIx32, unsupported); return false; } // Adaptive sync is effectively always enabled when using the Wayland // backend. This is not something we have control over, so we set the state // to enabled on creating the output and never allow changing it. assert(wlr_output->adaptive_sync_status == WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED); if (state->committed & WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED) { if (!state->adaptive_sync_enabled) { wlr_log(WLR_DEBUG, "Disabling adaptive sync is not supported"); return false; } } if (state->committed & WLR_OUTPUT_STATE_MODE) { assert(state->mode_type == WLR_OUTPUT_STATE_MODE_CUSTOM); if (state->custom_mode.refresh != 0) { wlr_log(WLR_DEBUG, "Refresh rates are not supported"); return false; } } if ((state->committed & WLR_OUTPUT_STATE_BUFFER) && !test_buffer(output->backend, state->buffer)) { wlr_log(WLR_DEBUG, "Unsupported buffer format"); return false; } if (state->committed & WLR_OUTPUT_STATE_LAYERS) { // If we can't use a sub-surface for a layer, then we can't use a // sub-surface for any layer underneath bool supported = output->backend->subcompositor != NULL; for (ssize_t i = state->layers_len - 1; i >= 0; i--) { struct wlr_output_layer_state *layer_state = &state->layers[i]; if (layer_state->buffer != NULL) { int x = layer_state->dst_box.x; int y = layer_state->dst_box.y; int width = layer_state->dst_box.width; int height = layer_state->dst_box.height; bool needs_viewport = width != layer_state->buffer->width || height != layer_state->buffer->height; if (!wlr_fbox_empty(&layer_state->src_box)) { needs_viewport = needs_viewport || layer_state->src_box.x != 0 || layer_state->src_box.y != 0 || layer_state->src_box.width != width || layer_state->src_box.height != height; } if (x < 0 || y < 0 || x + width > wlr_output->width || y + height > wlr_output->height || (output->backend->viewporter == NULL && needs_viewport)) { supported = false; } supported = supported && test_buffer(output->backend, layer_state->buffer); } layer_state->accepted = supported; } } return true; } static void output_layer_handle_addon_destroy(struct wlr_addon *addon) { struct wlr_wl_output_layer *layer = wl_container_of(addon, layer, addon); wlr_addon_finish(&layer->addon); if (layer->viewport != NULL) { wp_viewport_destroy(layer->viewport); } wl_subsurface_destroy(layer->subsurface); wl_surface_destroy(layer->surface); free(layer); } static const struct wlr_addon_interface output_layer_addon_impl = { .name = "wlr_wl_output_layer", .destroy = output_layer_handle_addon_destroy, }; static struct wlr_wl_output_layer *get_or_create_output_layer( struct wlr_wl_output *output, struct wlr_output_layer *wlr_layer) { assert(output->backend->subcompositor != NULL); struct wlr_wl_output_layer *layer; struct wlr_addon *addon = wlr_addon_find(&wlr_layer->addons, output, &output_layer_addon_impl); if (addon != NULL) { layer = wl_container_of(addon, layer, addon); return layer; } layer = calloc(1, sizeof(*layer)); if (layer == NULL) { return NULL; } wlr_addon_init(&layer->addon, &wlr_layer->addons, output, &output_layer_addon_impl); layer->surface = wl_compositor_create_surface(output->backend->compositor); layer->subsurface = wl_subcompositor_get_subsurface( output->backend->subcompositor, layer->surface, output->surface); // Set an empty input region so that input events are handled by the main // surface struct wl_region *region = wl_compositor_create_region(output->backend->compositor); wl_surface_set_input_region(layer->surface, region); wl_region_destroy(region); if (output->backend->viewporter != NULL) { layer->viewport = wp_viewporter_get_viewport(output->backend->viewporter, layer->surface); } return layer; } static bool has_layers_order_changed(struct wlr_wl_output *output, struct wlr_output_layer_state *layers, size_t layers_len) { // output_basic_check() ensures that layers_len equals the number of // registered output layers size_t i = 0; struct wlr_output_layer *layer; wl_list_for_each(layer, &output->wlr_output.layers, link) { assert(i < layers_len); const struct wlr_output_layer_state *layer_state = &layers[i]; if (layer_state->layer != layer) { return true; } i++; } assert(i == layers_len); return false; } static void output_layer_unmap(struct wlr_wl_output_layer *layer) { if (!layer->mapped) { return; } wl_surface_attach(layer->surface, NULL, 0, 0); wl_surface_commit(layer->surface); layer->mapped = false; } static void damage_surface(struct wl_surface *surface, const pixman_region32_t *damage) { if (damage == NULL) { wl_surface_damage_buffer(surface, 0, 0, INT32_MAX, INT32_MAX); return; } int rects_len; const pixman_box32_t *rects = pixman_region32_rectangles(damage, &rects_len); for (int i = 0; i < rects_len; i++) { const pixman_box32_t *r = &rects[i]; wl_surface_damage_buffer(surface, r->x1, r->y1, r->x2 - r->x1, r->y2 - r->y1); } } static bool output_layer_commit(struct wlr_wl_output *output, struct wlr_wl_output_layer *layer, const struct wlr_output_layer_state *state) { if (state->layer->dst_box.x != state->dst_box.x || state->layer->dst_box.y != state->dst_box.y) { wl_subsurface_set_position(layer->subsurface, state->dst_box.x, state->dst_box.y); } if (state->buffer == NULL) { output_layer_unmap(layer); return true; } struct wlr_wl_buffer *buffer = get_or_create_wl_buffer(output->backend, state->buffer); if (buffer == NULL) { return false; } if (layer->viewport != NULL && (state->layer->dst_box.width != state->dst_box.width || state->layer->dst_box.height != state->dst_box.height)) { wp_viewport_set_destination(layer->viewport, state->dst_box.width, state->dst_box.height); } if (layer->viewport != NULL && !wlr_fbox_equal(&state->layer->src_box, &state->src_box)) { struct wlr_fbox src_box = state->src_box; if (wlr_fbox_empty(&src_box)) { // -1 resets the box src_box = (struct wlr_fbox){ .x = -1, .y = -1, .width = -1, .height = -1, }; } wp_viewport_set_source(layer->viewport, wl_fixed_from_double(src_box.x), wl_fixed_from_double(src_box.y), wl_fixed_from_double(src_box.width), wl_fixed_from_double(src_box.height)); } wl_surface_attach(layer->surface, buffer->wl_buffer, 0, 0); damage_surface(layer->surface, state->damage); wl_surface_commit(layer->surface); layer->mapped = true; return true; } static bool commit_layers(struct wlr_wl_output *output, struct wlr_output_layer_state *layers, size_t layers_len) { if (output->backend->subcompositor == NULL) { return true; } bool reordered = has_layers_order_changed(output, layers, layers_len); struct wlr_wl_output_layer *prev_layer = NULL; for (size_t i = 0; i < layers_len; i++) { struct wlr_wl_output_layer *layer = get_or_create_output_layer(output, layers[i].layer); if (layer == NULL) { return false; } if (!layers[i].accepted) { output_layer_unmap(layer); continue; } if (prev_layer != NULL && reordered) { wl_subsurface_place_above(layer->subsurface, prev_layer->surface); } if (!output_layer_commit(output, layer, &layers[i])) { return false; } prev_layer = layer; } return true; } static bool output_commit(struct wlr_output *wlr_output, const struct wlr_output_state *state) { struct wlr_wl_output *output = get_wl_output_from_output(wlr_output); if (!output_test(wlr_output, state)) { return false; } if ((state->committed & WLR_OUTPUT_STATE_ENABLED) && !state->enabled) { wl_surface_attach(output->surface, NULL, 0, 0); wl_surface_commit(output->surface); } if (state->committed & WLR_OUTPUT_STATE_BUFFER) { const pixman_region32_t *damage = NULL; if (state->committed & WLR_OUTPUT_STATE_DAMAGE) { damage = &state->damage; } struct wlr_buffer *wlr_buffer = state->buffer; struct wlr_wl_buffer *buffer = get_or_create_wl_buffer(output->backend, wlr_buffer); if (buffer == NULL) { return false; } wl_surface_attach(output->surface, buffer->wl_buffer, 0, 0); damage_surface(output->surface, damage); } if ((state->committed & WLR_OUTPUT_STATE_LAYERS) && !commit_layers(output, state->layers, state->layers_len)) { return false; } if (output_pending_enabled(wlr_output, state)) { if (output->frame_callback != NULL) { wl_callback_destroy(output->frame_callback); } output->frame_callback = wl_surface_frame(output->surface); wl_callback_add_listener(output->frame_callback, &frame_listener, output); struct wp_presentation_feedback *wp_feedback = NULL; if (output->backend->presentation != NULL) { wp_feedback = wp_presentation_feedback(output->backend->presentation, output->surface); } wl_surface_commit(output->surface); if (wp_feedback != NULL) { struct wlr_wl_presentation_feedback *feedback = calloc(1, sizeof(*feedback)); if (feedback == NULL) { wp_presentation_feedback_destroy(wp_feedback); return false; } feedback->output = output; feedback->feedback = wp_feedback; feedback->commit_seq = output->wlr_output.commit_seq + 1; wl_list_insert(&output->presentation_feedbacks, &feedback->link); wp_presentation_feedback_add_listener(wp_feedback, &presentation_feedback_listener, feedback); } else { struct wlr_output_event_present present_event = { .commit_seq = wlr_output->commit_seq + 1, .presented = true, }; output_defer_present(wlr_output, present_event); } } wl_display_flush(output->backend->remote_display); return true; } static bool output_set_cursor(struct wlr_output *wlr_output, struct wlr_buffer *wlr_buffer, int hotspot_x, int hotspot_y) { struct wlr_wl_output *output = get_wl_output_from_output(wlr_output); struct wlr_wl_backend *backend = output->backend; output->cursor.hotspot_x = hotspot_x; output->cursor.hotspot_y = hotspot_y; if (output->cursor.surface == NULL) { output->cursor.surface = wl_compositor_create_surface(backend->compositor); } struct wl_surface *surface = output->cursor.surface; if (wlr_buffer != NULL) { struct wlr_wl_buffer *buffer = get_or_create_wl_buffer(output->backend, wlr_buffer); if (buffer == NULL) { return false; } wl_surface_attach(surface, buffer->wl_buffer, 0, 0); wl_surface_damage_buffer(surface, 0, 0, INT32_MAX, INT32_MAX); wl_surface_commit(surface); } else { wl_surface_attach(surface, NULL, 0, 0); wl_surface_commit(surface); } update_wl_output_cursor(output); wl_display_flush(backend->remote_display); return true; } static const struct wlr_drm_format_set *output_get_formats( struct wlr_output *wlr_output, uint32_t buffer_caps) { struct wlr_wl_output *output = get_wl_output_from_output(wlr_output); if (buffer_caps & WLR_BUFFER_CAP_DMABUF) { return &output->backend->linux_dmabuf_v1_formats; } else if (buffer_caps & WLR_BUFFER_CAP_SHM) { return &output->backend->shm_formats; } return NULL; } static void output_destroy(struct wlr_output *wlr_output) { struct wlr_wl_output *output = get_wl_output_from_output(wlr_output); if (output == NULL) { return; } wl_list_remove(&output->link); if (output->cursor.surface) { wl_surface_destroy(output->cursor.surface); } if (output->frame_callback) { wl_callback_destroy(output->frame_callback); } struct wlr_wl_presentation_feedback *feedback, *feedback_tmp; wl_list_for_each_safe(feedback, feedback_tmp, &output->presentation_feedbacks, link) { presentation_feedback_destroy(feedback); } if (output->zxdg_toplevel_decoration_v1) { zxdg_toplevel_decoration_v1_destroy(output->zxdg_toplevel_decoration_v1); } if (output->xdg_toplevel) { xdg_toplevel_destroy(output->xdg_toplevel); } if (output->xdg_surface) { xdg_surface_destroy(output->xdg_surface); } if (output->own_surface) { wl_surface_destroy(output->surface); } wl_display_flush(output->backend->remote_display); free(output); } void update_wl_output_cursor(struct wlr_wl_output *output) { struct wlr_wl_pointer *pointer = output->cursor.pointer; if (pointer) { assert(pointer->output == output); assert(output->enter_serial); struct wlr_wl_seat *seat = pointer->seat; wl_pointer_set_cursor(seat->wl_pointer, output->enter_serial, output->cursor.surface, output->cursor.hotspot_x, output->cursor.hotspot_y); } } static bool output_move_cursor(struct wlr_output *_output, int x, int y) { // TODO: only return true if x == current x and y == current y return true; } static const struct wlr_output_impl output_impl = { .destroy = output_destroy, .test = output_test, .commit = output_commit, .set_cursor = output_set_cursor, .move_cursor = output_move_cursor, .get_cursor_formats = output_get_formats, .get_primary_formats = output_get_formats, }; bool wlr_output_is_wl(struct wlr_output *wlr_output) { return wlr_output->impl == &output_impl; } static void xdg_surface_handle_configure(void *data, struct xdg_surface *xdg_surface, uint32_t serial) { struct wlr_wl_output *output = data; assert(output && output->xdg_surface == xdg_surface); output->configured = true; xdg_surface_ack_configure(xdg_surface, serial); // nothing else? } static const struct xdg_surface_listener xdg_surface_listener = { .configure = xdg_surface_handle_configure, }; static void xdg_toplevel_handle_configure(void *data, struct xdg_toplevel *xdg_toplevel, int32_t width, int32_t height, struct wl_array *states) { struct wlr_wl_output *output = data; assert(output && output->xdg_toplevel == xdg_toplevel); if (width == 0 || height == 0) { return; } struct wlr_output_state state; wlr_output_state_init(&state); wlr_output_state_set_custom_mode(&state, width, height, 0); wlr_output_send_request_state(&output->wlr_output, &state); wlr_output_state_finish(&state); } static void xdg_toplevel_handle_close(void *data, struct xdg_toplevel *xdg_toplevel) { struct wlr_wl_output *output = data; assert(output && output->xdg_toplevel == xdg_toplevel); wlr_output_destroy(&output->wlr_output); } static const struct xdg_toplevel_listener xdg_toplevel_listener = { .configure = xdg_toplevel_handle_configure, .close = xdg_toplevel_handle_close, }; static struct wlr_wl_output *output_create(struct wlr_wl_backend *backend, struct wl_surface *surface) { struct wlr_wl_output *output = calloc(1, sizeof(*output)); if (output == NULL) { wlr_log(WLR_ERROR, "Failed to allocate wlr_wl_output"); return NULL; } struct wlr_output *wlr_output = &output->wlr_output; struct wlr_output_state state; wlr_output_state_init(&state); wlr_output_state_set_custom_mode(&state, 1280, 720, 0); wlr_output_init(wlr_output, &backend->backend, &output_impl, backend->local_display, &state); wlr_output_state_finish(&state); wlr_output->adaptive_sync_status = WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED; size_t output_num = ++last_output_num; char name[64]; snprintf(name, sizeof(name), "WL-%zu", output_num); wlr_output_set_name(wlr_output, name); char description[128]; snprintf(description, sizeof(description), "Wayland output %zu", output_num); wlr_output_set_description(wlr_output, description); output->surface = surface; output->backend = backend; wl_list_init(&output->presentation_feedbacks); wl_proxy_set_tag((struct wl_proxy *)output->surface, &surface_tag); wl_surface_set_user_data(output->surface, output); wl_list_insert(&backend->outputs, &output->link); return output; } static void output_start(struct wlr_wl_output *output) { struct wlr_output *wlr_output = &output->wlr_output; struct wlr_wl_backend *backend = output->backend; wl_signal_emit_mutable(&backend->backend.events.new_output, wlr_output); struct wlr_wl_seat *seat; wl_list_for_each(seat, &backend->seats, link) { if (seat->wl_pointer) { create_pointer(seat, output); } } } struct wlr_output *wlr_wl_output_create(struct wlr_backend *wlr_backend) { struct wlr_wl_backend *backend = get_wl_backend_from_backend(wlr_backend); if (!backend->started) { ++backend->requested_outputs; return NULL; } struct wl_surface *surface = wl_compositor_create_surface(backend->compositor); if (surface == NULL) { wlr_log(WLR_ERROR, "Could not create output surface"); return NULL; } struct wlr_wl_output *output = output_create(backend, surface); if (output == NULL) { wl_surface_destroy(surface); return NULL; } output->own_surface = true; output->xdg_surface = xdg_wm_base_get_xdg_surface(backend->xdg_wm_base, output->surface); if (!output->xdg_surface) { wlr_log_errno(WLR_ERROR, "Could not get xdg surface"); goto error; } output->xdg_toplevel = xdg_surface_get_toplevel(output->xdg_surface); if (!output->xdg_toplevel) { wlr_log_errno(WLR_ERROR, "Could not get xdg toplevel"); goto error; } if (backend->zxdg_decoration_manager_v1) { output->zxdg_toplevel_decoration_v1 = zxdg_decoration_manager_v1_get_toplevel_decoration( backend->zxdg_decoration_manager_v1, output->xdg_toplevel); if (!output->zxdg_toplevel_decoration_v1) { wlr_log_errno(WLR_ERROR, "Could not get xdg toplevel decoration"); goto error; } zxdg_toplevel_decoration_v1_set_mode(output->zxdg_toplevel_decoration_v1, ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); } wlr_wl_output_set_title(&output->wlr_output, NULL); xdg_toplevel_set_app_id(output->xdg_toplevel, "wlroots"); xdg_surface_add_listener(output->xdg_surface, &xdg_surface_listener, output); xdg_toplevel_add_listener(output->xdg_toplevel, &xdg_toplevel_listener, output); wl_surface_commit(output->surface); struct wl_event_loop *event_loop = wl_display_get_event_loop(backend->local_display); while (!output->configured) { int ret = wl_event_loop_dispatch(event_loop, -1); if (ret < 0) { wlr_log(WLR_ERROR, "wl_event_loop_dispatch() failed"); goto error; } } output_start(output); // TODO: let the compositor do this bit if (backend->activation_v1 && backend->activation_token) { xdg_activation_v1_activate(backend->activation_v1, backend->activation_token, output->surface); } return &output->wlr_output; error: wlr_output_destroy(&output->wlr_output); return NULL; } struct wlr_output *wlr_wl_output_create_from_surface(struct wlr_backend *wlr_backend, struct wl_surface *surface) { struct wlr_wl_backend *backend = get_wl_backend_from_backend(wlr_backend); assert(backend->started); struct wlr_wl_output *output = output_create(backend, surface); if (output == NULL) { wl_surface_destroy(surface); return NULL; } output_start(output); return &output->wlr_output; } void wlr_wl_output_set_title(struct wlr_output *output, const char *title) { struct wlr_wl_output *wl_output = get_wl_output_from_output(output); assert(wl_output->xdg_toplevel != NULL); char wl_title[32]; if (title == NULL) { if (snprintf(wl_title, sizeof(wl_title), "wlroots - %s", output->name) <= 0) { return; } title = wl_title; } xdg_toplevel_set_title(wl_output->xdg_toplevel, title); wl_display_flush(wl_output->backend->remote_display); } struct wl_surface *wlr_wl_output_get_surface(struct wlr_output *output) { struct wlr_wl_output *wl_output = get_wl_output_from_output(output); return wl_output->surface; } wlroots-0.17.1/backend/wayland/pointer.c000066400000000000000000000371441454110342200201700ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include "backend/wayland.h" #include "pointer-gestures-unstable-v1-client-protocol.h" #include "relative-pointer-unstable-v1-client-protocol.h" static struct wlr_wl_pointer *output_get_pointer(struct wlr_wl_output *output, const struct wl_pointer *wl_pointer) { struct wlr_wl_seat *seat; wl_list_for_each(seat, &output->backend->seats, link) { if (seat->wl_pointer != wl_pointer) { continue; } struct wlr_wl_pointer *pointer; wl_list_for_each(pointer, &seat->pointers, link) { if (pointer->output == output) { return pointer; } } } return NULL; } static void pointer_handle_enter(void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface, wl_fixed_t sx, wl_fixed_t sy) { struct wlr_wl_seat *seat = data; if (surface == NULL) { return; } struct wlr_wl_output *output = get_wl_output_from_surface(seat->backend, surface); if (output == NULL) { return; } struct wlr_wl_pointer *pointer = output_get_pointer(output, wl_pointer); seat->active_pointer = pointer; // Manage cursor icon/rendering on output struct wlr_wl_pointer *current = output->cursor.pointer; if (current && current != pointer) { wlr_log(WLR_INFO, "Ignoring seat '%s' pointer in favor of seat '%s'", seat->name, current->seat->name); return; } output->enter_serial = serial; output->cursor.pointer = pointer; update_wl_output_cursor(output); } static void pointer_handle_leave(void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface) { struct wlr_wl_seat *seat = data; if (surface == NULL) { return; } struct wlr_wl_output *output = get_wl_output_from_surface(seat->backend, surface); if (output == NULL) { return; } if (seat->active_pointer != NULL && seat->active_pointer->output == output) { seat->active_pointer = NULL; } if (output->cursor.pointer == seat->active_pointer) { output->enter_serial = 0; output->cursor.pointer = NULL; } } static void pointer_handle_motion(void *data, struct wl_pointer *wl_pointer, uint32_t time, wl_fixed_t sx, wl_fixed_t sy) { struct wlr_wl_seat *seat = data; struct wlr_wl_pointer *pointer = seat->active_pointer; if (pointer == NULL) { return; } struct wlr_output *wlr_output = &pointer->output->wlr_output; struct wlr_pointer_motion_absolute_event event = { .pointer = &pointer->wlr_pointer, .time_msec = time, .x = wl_fixed_to_double(sx) / wlr_output->width, .y = wl_fixed_to_double(sy) / wlr_output->height, }; wl_signal_emit_mutable(&pointer->wlr_pointer.events.motion_absolute, &event); } static void pointer_handle_button(void *data, struct wl_pointer *wl_pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t state) { struct wlr_wl_seat *seat = data; struct wlr_wl_pointer *pointer = seat->active_pointer; if (pointer == NULL) { return; } struct wlr_pointer_button_event event = { .pointer = &pointer->wlr_pointer, .button = button, .state = state, .time_msec = time, }; wl_signal_emit_mutable(&pointer->wlr_pointer.events.button, &event); } static void pointer_handle_axis(void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis, wl_fixed_t value) { struct wlr_wl_seat *seat = data; struct wlr_wl_pointer *pointer = seat->active_pointer; if (pointer == NULL) { return; } struct wlr_pointer_axis_event event = { .pointer = &pointer->wlr_pointer, .delta = wl_fixed_to_double(value), .delta_discrete = pointer->axis_discrete, .orientation = axis, .time_msec = time, .source = pointer->axis_source, }; wl_signal_emit_mutable(&pointer->wlr_pointer.events.axis, &event); pointer->axis_discrete = 0; } static void pointer_handle_frame(void *data, struct wl_pointer *wl_pointer) { struct wlr_wl_seat *seat = data; struct wlr_wl_pointer *pointer = seat->active_pointer; if (pointer == NULL) { return; } wl_signal_emit_mutable(&pointer->wlr_pointer.events.frame, &pointer->wlr_pointer); } static void pointer_handle_axis_source(void *data, struct wl_pointer *wl_pointer, uint32_t axis_source) { struct wlr_wl_seat *seat = data; struct wlr_wl_pointer *pointer = seat->active_pointer; if (pointer == NULL) { return; } pointer->axis_source = axis_source; } static void pointer_handle_axis_stop(void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis) { struct wlr_wl_seat *seat = data; struct wlr_wl_pointer *pointer = seat->active_pointer; if (pointer == NULL) { return; } struct wlr_pointer_axis_event event = { .pointer = &pointer->wlr_pointer, .delta = 0, .delta_discrete = 0, .orientation = axis, .time_msec = time, .source = pointer->axis_source, }; wl_signal_emit_mutable(&pointer->wlr_pointer.events.axis, &event); } static void pointer_handle_axis_discrete(void *data, struct wl_pointer *wl_pointer, uint32_t axis, int32_t discrete) { struct wlr_wl_seat *seat = data; struct wlr_wl_pointer *pointer = seat->active_pointer; if (pointer == NULL) { return; } pointer->axis_discrete = discrete * WLR_POINTER_AXIS_DISCRETE_STEP; } static void pointer_handle_axis_value120(void *data, struct wl_pointer *wl_pointer, uint32_t axis, int32_t value120) { struct wlr_wl_seat *seat = data; struct wlr_wl_pointer *pointer = seat->active_pointer; if (pointer == NULL) { return; } pointer->axis_discrete = value120; } static const struct wl_pointer_listener pointer_listener = { .enter = pointer_handle_enter, .leave = pointer_handle_leave, .motion = pointer_handle_motion, .button = pointer_handle_button, .axis = pointer_handle_axis, .frame = pointer_handle_frame, .axis_source = pointer_handle_axis_source, .axis_stop = pointer_handle_axis_stop, .axis_discrete = pointer_handle_axis_discrete, .axis_value120 = pointer_handle_axis_value120, }; static void gesture_swipe_begin(void *data, struct zwp_pointer_gesture_swipe_v1 *zwp_pointer_gesture_swipe_v1, uint32_t serial, uint32_t time, struct wl_surface *surface, uint32_t fingers) { struct wlr_wl_seat *seat = data; struct wlr_wl_pointer *pointer = seat->active_pointer; if (pointer == NULL) { return; } pointer->fingers = fingers; struct wlr_pointer_swipe_begin_event wlr_event = { .pointer = &pointer->wlr_pointer, .time_msec = time, .fingers = fingers, }; wl_signal_emit_mutable(&pointer->wlr_pointer.events.swipe_begin, &wlr_event); } static void gesture_swipe_update(void *data, struct zwp_pointer_gesture_swipe_v1 *zwp_pointer_gesture_swipe_v1, uint32_t time, wl_fixed_t dx, wl_fixed_t dy) { struct wlr_wl_seat *seat = data; struct wlr_wl_pointer *pointer = seat->active_pointer; if (pointer == NULL) { return; } struct wlr_pointer_swipe_update_event wlr_event = { .pointer = &pointer->wlr_pointer, .time_msec = time, .fingers = pointer->fingers, .dx = wl_fixed_to_double(dx), .dy = wl_fixed_to_double(dy), }; wl_signal_emit_mutable(&pointer->wlr_pointer.events.swipe_update, &wlr_event); } static void gesture_swipe_end(void *data, struct zwp_pointer_gesture_swipe_v1 *zwp_pointer_gesture_swipe_v1, uint32_t serial, uint32_t time, int32_t cancelled) { struct wlr_wl_seat *seat = data; struct wlr_wl_pointer *pointer = seat->active_pointer; if (pointer == NULL) { return; } struct wlr_pointer_swipe_end_event wlr_event = { .pointer = &pointer->wlr_pointer, .time_msec = time, .cancelled = cancelled, }; wl_signal_emit_mutable(&pointer->wlr_pointer.events.swipe_end, &wlr_event); } static const struct zwp_pointer_gesture_swipe_v1_listener gesture_swipe_impl = { .begin = gesture_swipe_begin, .update = gesture_swipe_update, .end = gesture_swipe_end, }; static void gesture_pinch_begin(void *data, struct zwp_pointer_gesture_pinch_v1 *zwp_pointer_gesture_pinch_v1, uint32_t serial, uint32_t time, struct wl_surface *surface, uint32_t fingers) { struct wlr_wl_seat *seat = data; struct wlr_wl_pointer *pointer = seat->active_pointer; if (pointer == NULL) { return; } pointer->fingers = fingers; struct wlr_pointer_pinch_begin_event wlr_event = { .pointer = &pointer->wlr_pointer, .time_msec = time, .fingers = pointer->fingers, }; wl_signal_emit_mutable(&pointer->wlr_pointer.events.pinch_begin, &wlr_event); } static void gesture_pinch_update(void *data, struct zwp_pointer_gesture_pinch_v1 *zwp_pointer_gesture_pinch_v1, uint32_t time, wl_fixed_t dx, wl_fixed_t dy, wl_fixed_t scale, wl_fixed_t rotation) { struct wlr_wl_seat *seat = data; struct wlr_wl_pointer *pointer = seat->active_pointer; if (pointer == NULL) { return; } struct wlr_pointer_pinch_update_event wlr_event = { .pointer = &pointer->wlr_pointer, .time_msec = time, .fingers = pointer->fingers, .dx = wl_fixed_to_double(dx), .dy = wl_fixed_to_double(dy), .scale = wl_fixed_to_double(scale), .rotation = wl_fixed_to_double(rotation), }; wl_signal_emit_mutable(&pointer->wlr_pointer.events.pinch_update, &wlr_event); } static void gesture_pinch_end(void *data, struct zwp_pointer_gesture_pinch_v1 *zwp_pointer_gesture_pinch_v1, uint32_t serial, uint32_t time, int32_t cancelled) { struct wlr_wl_seat *seat = data; struct wlr_wl_pointer *pointer = seat->active_pointer; if (pointer == NULL) { return; } struct wlr_pointer_pinch_end_event wlr_event = { .pointer = &pointer->wlr_pointer, .time_msec = time, .cancelled = cancelled, }; wl_signal_emit_mutable(&pointer->wlr_pointer.events.pinch_end, &wlr_event); } static const struct zwp_pointer_gesture_pinch_v1_listener gesture_pinch_impl = { .begin = gesture_pinch_begin, .update = gesture_pinch_update, .end = gesture_pinch_end, }; static void gesture_hold_begin(void *data, struct zwp_pointer_gesture_hold_v1 *zwp_pointer_gesture_hold_v1, uint32_t serial, uint32_t time, struct wl_surface *surface, uint32_t fingers) { struct wlr_wl_seat *seat = data; struct wlr_wl_pointer *pointer = seat->active_pointer; if (pointer == NULL) { return; } pointer->fingers = fingers; struct wlr_pointer_hold_begin_event wlr_event = { .pointer = &pointer->wlr_pointer, .time_msec = time, .fingers = fingers, }; wl_signal_emit_mutable(&pointer->wlr_pointer.events.hold_begin, &wlr_event); } static void gesture_hold_end(void *data, struct zwp_pointer_gesture_hold_v1 *zwp_pointer_gesture_hold_v1, uint32_t serial, uint32_t time, int32_t cancelled) { struct wlr_wl_seat *seat = data; struct wlr_wl_pointer *pointer = seat->active_pointer; if (pointer == NULL) { return; } struct wlr_pointer_hold_end_event wlr_event = { .pointer = &pointer->wlr_pointer, .time_msec = time, .cancelled = cancelled, }; wl_signal_emit_mutable(&pointer->wlr_pointer.events.hold_end, &wlr_event); } static const struct zwp_pointer_gesture_hold_v1_listener gesture_hold_impl = { .begin = gesture_hold_begin, .end = gesture_hold_end, }; static void relative_pointer_handle_relative_motion(void *data, struct zwp_relative_pointer_v1 *relative_pointer, uint32_t utime_hi, uint32_t utime_lo, wl_fixed_t dx, wl_fixed_t dy, wl_fixed_t dx_unaccel, wl_fixed_t dy_unaccel) { struct wlr_wl_seat *seat = data; struct wlr_wl_pointer *pointer = seat->active_pointer; if (pointer == NULL) { return; } uint64_t time_usec = (uint64_t)utime_hi << 32 | utime_lo; struct wlr_pointer_motion_event wlr_event = { .pointer = &pointer->wlr_pointer, .time_msec = (uint32_t)(time_usec / 1000), .delta_x = wl_fixed_to_double(dx), .delta_y = wl_fixed_to_double(dy), .unaccel_dx = wl_fixed_to_double(dx_unaccel), .unaccel_dy = wl_fixed_to_double(dy_unaccel), }; wl_signal_emit_mutable(&pointer->wlr_pointer.events.motion, &wlr_event); } static const struct zwp_relative_pointer_v1_listener relative_pointer_listener = { .relative_motion = relative_pointer_handle_relative_motion, }; const struct wlr_pointer_impl wl_pointer_impl = { .name = "wl-pointer", }; static void destroy_pointer(struct wlr_wl_pointer *pointer) { if (pointer->output->cursor.pointer == pointer) { pointer->output->cursor.pointer = NULL; } if (pointer->seat->active_pointer == pointer) { pointer->seat->active_pointer = NULL; } wlr_pointer_finish(&pointer->wlr_pointer); wl_list_remove(&pointer->output_destroy.link); wl_list_remove(&pointer->link); free(pointer); } static void pointer_output_destroy(struct wl_listener *listener, void *data) { struct wlr_wl_pointer *pointer = wl_container_of(listener, pointer, output_destroy); destroy_pointer(pointer); } void create_pointer(struct wlr_wl_seat *seat, struct wlr_wl_output *output) { assert(seat->wl_pointer); if (output_get_pointer(output, seat->wl_pointer)) { wlr_log(WLR_DEBUG, "pointer for output '%s' from seat '%s' already exists", output->wlr_output.name, seat->name); return; } wlr_log(WLR_DEBUG, "creating pointer for output '%s' from seat '%s'", output->wlr_output.name, seat->name); struct wlr_wl_pointer *pointer = calloc(1, sizeof(*pointer)); if (pointer == NULL) { wlr_log(WLR_ERROR, "failed to allocate wlr_wl_pointer"); return; } char name[64] = {0}; snprintf(name, sizeof(name), "wayland-pointer-%s", seat->name); wlr_pointer_init(&pointer->wlr_pointer, &wl_pointer_impl, name); pointer->wlr_pointer.output_name = strdup(output->wlr_output.name); pointer->seat = seat; pointer->output = output; wl_signal_add(&output->wlr_output.events.destroy, &pointer->output_destroy); pointer->output_destroy.notify = pointer_output_destroy; wl_signal_emit_mutable(&seat->backend->backend.events.new_input, &pointer->wlr_pointer.base); wl_list_insert(&seat->pointers, &pointer->link); } void init_seat_pointer(struct wlr_wl_seat *seat) { assert(seat->wl_pointer); struct wlr_wl_backend *backend = seat->backend; wl_list_init(&seat->pointers); struct wlr_wl_output *output; wl_list_for_each(output, &backend->outputs, link) { create_pointer(seat, output); } if (backend->zwp_pointer_gestures_v1) { uint32_t version = zwp_pointer_gestures_v1_get_version( backend->zwp_pointer_gestures_v1); seat->gesture_swipe = zwp_pointer_gestures_v1_get_swipe_gesture( backend->zwp_pointer_gestures_v1, seat->wl_pointer); zwp_pointer_gesture_swipe_v1_add_listener(seat->gesture_swipe, &gesture_swipe_impl, seat); seat->gesture_pinch = zwp_pointer_gestures_v1_get_pinch_gesture( backend->zwp_pointer_gestures_v1, seat->wl_pointer); zwp_pointer_gesture_pinch_v1_add_listener(seat->gesture_pinch, &gesture_pinch_impl, seat); if (version >= ZWP_POINTER_GESTURES_V1_GET_HOLD_GESTURE) { seat->gesture_hold = zwp_pointer_gestures_v1_get_hold_gesture( backend->zwp_pointer_gestures_v1, seat->wl_pointer); zwp_pointer_gesture_hold_v1_add_listener(seat->gesture_hold, &gesture_hold_impl, seat); } } if (backend->zwp_relative_pointer_manager_v1) { seat->relative_pointer = zwp_relative_pointer_manager_v1_get_relative_pointer( backend->zwp_relative_pointer_manager_v1, seat->wl_pointer); zwp_relative_pointer_v1_add_listener(seat->relative_pointer, &relative_pointer_listener, seat); } wl_pointer_add_listener(seat->wl_pointer, &pointer_listener, seat); } void finish_seat_pointer(struct wlr_wl_seat *seat) { assert(seat->wl_pointer); wl_pointer_release(seat->wl_pointer); struct wlr_wl_pointer *pointer, *tmp; wl_list_for_each_safe(pointer, tmp, &seat->pointers, link) { destroy_pointer(pointer); } if (seat->gesture_swipe != NULL) { zwp_pointer_gesture_swipe_v1_destroy(seat->gesture_swipe); } if (seat->gesture_pinch != NULL) { zwp_pointer_gesture_pinch_v1_destroy(seat->gesture_pinch); } if (seat->gesture_hold != NULL) { zwp_pointer_gesture_hold_v1_destroy(seat->gesture_hold); } if (seat->relative_pointer != NULL) { zwp_relative_pointer_v1_destroy(seat->relative_pointer); } seat->wl_pointer = NULL; seat->active_pointer = NULL; } wlroots-0.17.1/backend/wayland/seat.c000066400000000000000000000262551454110342200174450ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "backend/wayland.h" #include "util/time.h" static void keyboard_handle_keymap(void *data, struct wl_keyboard *wl_keyboard, uint32_t format, int32_t fd, uint32_t size) { close(fd); } static void keyboard_handle_enter(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface, struct wl_array *keys) { struct wlr_keyboard *keyboard = data; uint32_t *keycode_ptr; wl_array_for_each(keycode_ptr, keys) { struct wlr_keyboard_key_event event = { .keycode = *keycode_ptr, .state = WL_KEYBOARD_KEY_STATE_PRESSED, .time_msec = get_current_time_msec(), .update_state = false, }; wlr_keyboard_notify_key(keyboard, &event); } } static void keyboard_handle_leave(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface) { struct wlr_keyboard *keyboard = data; size_t num_keycodes = keyboard->num_keycodes; uint32_t pressed[num_keycodes + 1]; memcpy(pressed, keyboard->keycodes, num_keycodes * sizeof(uint32_t)); for (size_t i = 0; i < num_keycodes; ++i) { uint32_t keycode = pressed[i]; struct wlr_keyboard_key_event event = { .keycode = keycode, .state = WL_KEYBOARD_KEY_STATE_RELEASED, .time_msec = get_current_time_msec(), .update_state = false, }; wlr_keyboard_notify_key(keyboard, &event); } } static void keyboard_handle_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state) { struct wlr_keyboard *keyboard = data; struct wlr_keyboard_key_event wlr_event = { .keycode = key, .state = state, .time_msec = time, .update_state = false, }; wlr_keyboard_notify_key(keyboard, &wlr_event); } static void keyboard_handle_modifiers(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group) { struct wlr_keyboard *keyboard = data; wlr_keyboard_notify_modifiers(keyboard, mods_depressed, mods_latched, mods_locked, group); } static void keyboard_handle_repeat_info(void *data, struct wl_keyboard *wl_keyboard, int32_t rate, int32_t delay) { // This space is intentionally left blank } static const struct wl_keyboard_listener keyboard_listener = { .keymap = keyboard_handle_keymap, .enter = keyboard_handle_enter, .leave = keyboard_handle_leave, .key = keyboard_handle_key, .modifiers = keyboard_handle_modifiers, .repeat_info = keyboard_handle_repeat_info }; static const struct wlr_keyboard_impl keyboard_impl = { .name = "wl-keyboard", }; void init_seat_keyboard(struct wlr_wl_seat *seat) { assert(seat->wl_keyboard); char name[128] = {0}; snprintf(name, sizeof(name), "wayland-keyboard-%s", seat->name); wlr_keyboard_init(&seat->wlr_keyboard, &keyboard_impl, name); wl_keyboard_add_listener(seat->wl_keyboard, &keyboard_listener, &seat->wlr_keyboard); wl_signal_emit_mutable(&seat->backend->backend.events.new_input, &seat->wlr_keyboard.base); } static void touch_coordinates_to_absolute(struct wlr_wl_seat *seat, wl_fixed_t x, wl_fixed_t y, double *sx, double *sy) { /** * TODO: multi-output touch support * Although the wayland backend supports multi-output pointers, the support * for multi-output touch has been left on the side for simplicity reasons. * If this is a feature you want/need, please open an issue on the wlroots * tracker here https://gitlab.freedesktop.org/wlroots/wlroots/-/issues */ struct wlr_wl_output *output, *tmp; wl_list_for_each_safe(output, tmp, &seat->backend->outputs, link) { *sx = wl_fixed_to_double(x) / output->wlr_output.width; *sy = wl_fixed_to_double(y) / output->wlr_output.height; return; // Choose the first output in the list } *sx = *sy = 0; } static void touch_handle_down(void *data, struct wl_touch *wl_touch, uint32_t serial, uint32_t time, struct wl_surface *surface, int32_t id, wl_fixed_t x, wl_fixed_t y) { struct wlr_wl_seat *seat = data; struct wlr_touch *touch = &seat->wlr_touch; struct wlr_wl_touch_points *points = &seat->touch_points; assert(points->len != sizeof(points->ids) / sizeof(points->ids[0])); points->ids[points->len++] = id; struct wlr_touch_down_event event = { .touch = touch, .time_msec = time, .touch_id = id, }; touch_coordinates_to_absolute(seat, x, y, &event.x, &event.y); wl_signal_emit_mutable(&touch->events.down, &event); } static bool remove_touch_point(struct wlr_wl_touch_points *points, int32_t id) { size_t i = 0; for (; i < points->len; i++) { if (points->ids[i] == id) { size_t remaining = points->len - i - 1; memmove(&points->ids[i], &points->ids[i + 1], remaining * sizeof(id)); points->len--; return true; } } return false; } static void touch_handle_up(void *data, struct wl_touch *wl_touch, uint32_t serial, uint32_t time, int32_t id) { struct wlr_wl_seat *seat = data; struct wlr_touch *touch = &seat->wlr_touch; remove_touch_point(&seat->touch_points, id); struct wlr_touch_up_event event = { .touch = touch, .time_msec = time, .touch_id = id, }; wl_signal_emit_mutable(&touch->events.up, &event); } static void touch_handle_motion(void *data, struct wl_touch *wl_touch, uint32_t time, int32_t id, wl_fixed_t x, wl_fixed_t y) { struct wlr_wl_seat *seat = data; struct wlr_touch *touch = &seat->wlr_touch; struct wlr_touch_motion_event event = { .touch = touch, .time_msec = time, .touch_id = id, }; touch_coordinates_to_absolute(seat, x, y, &event.x, &event.y); wl_signal_emit_mutable(&touch->events.motion, &event); } static void touch_handle_frame(void *data, struct wl_touch *wl_touch) { struct wlr_wl_seat *seat = data; wl_signal_emit_mutable(&seat->wlr_touch.events.frame, NULL); } static void touch_handle_cancel(void *data, struct wl_touch *wl_touch) { struct wlr_wl_seat *seat = data; struct wlr_touch *touch = &seat->wlr_touch; // wayland's cancel event applies to all active touch points for (size_t i = 0; i < seat->touch_points.len; i++) { struct wlr_touch_cancel_event event = { .touch = touch, .time_msec = 0, .touch_id = seat->touch_points.ids[i], }; wl_signal_emit_mutable(&touch->events.cancel, &event); } seat->touch_points.len = 0; } static void touch_handle_shape(void *data, struct wl_touch *wl_touch, int32_t id, wl_fixed_t major, wl_fixed_t minor) { // no-op } static void touch_handle_orientation(void *data, struct wl_touch *wl_touch, int32_t id, wl_fixed_t orientation) { // no-op } static const struct wl_touch_listener touch_listener = { .down = touch_handle_down, .up = touch_handle_up, .motion = touch_handle_motion, .frame = touch_handle_frame, .cancel = touch_handle_cancel, .shape = touch_handle_shape, .orientation = touch_handle_orientation, }; static const struct wlr_touch_impl touch_impl = { .name = "wl-touch", }; void init_seat_touch(struct wlr_wl_seat *seat) { assert(seat->wl_touch); char name[128] = {0}; snprintf(name, sizeof(name), "wayland-touch-%s", seat->name); wlr_touch_init(&seat->wlr_touch, &touch_impl, name); struct wlr_wl_output *output; wl_list_for_each(output, &seat->backend->outputs, link) { /* Multi-output touch not supproted */ seat->wlr_touch.output_name = strdup(output->wlr_output.name); break; } wl_touch_add_listener(seat->wl_touch, &touch_listener, seat); wl_signal_emit_mutable(&seat->backend->backend.events.new_input, &seat->wlr_touch.base); } static const struct wl_seat_listener seat_listener; bool create_wl_seat(struct wl_seat *wl_seat, struct wlr_wl_backend *wl, uint32_t global_name) { struct wlr_wl_seat *seat = calloc(1, sizeof(*seat)); if (!seat) { wlr_log_errno(WLR_ERROR, "Allocation failed"); return false; } seat->wl_seat = wl_seat; seat->backend = wl; seat->global_name = global_name; wl_list_insert(&wl->seats, &seat->link); wl_seat_add_listener(wl_seat, &seat_listener, seat); return true; } void destroy_wl_seat(struct wlr_wl_seat *seat) { if (seat->wl_touch) { wl_touch_release(seat->wl_touch); wlr_touch_finish(&seat->wlr_touch); } if (seat->wl_pointer) { finish_seat_pointer(seat); } if (seat->wl_keyboard) { wl_keyboard_release(seat->wl_keyboard); if (seat->backend->started) { wlr_keyboard_finish(&seat->wlr_keyboard); } } if (seat->zwp_tablet_seat_v2) { finish_seat_tablet(seat); } free(seat->name); assert(seat->wl_seat); wl_seat_destroy(seat->wl_seat); wl_list_remove(&seat->link); free(seat); } bool wlr_input_device_is_wl(struct wlr_input_device *dev) { switch (dev->type) { case WLR_INPUT_DEVICE_KEYBOARD: return wlr_keyboard_from_input_device(dev)->impl == &keyboard_impl; case WLR_INPUT_DEVICE_POINTER: return wlr_pointer_from_input_device(dev)->impl == &wl_pointer_impl; case WLR_INPUT_DEVICE_TOUCH: return wlr_touch_from_input_device(dev)->impl == &touch_impl; case WLR_INPUT_DEVICE_TABLET_TOOL: return wlr_tablet_from_input_device(dev)-> impl == &wl_tablet_impl; case WLR_INPUT_DEVICE_TABLET_PAD: return wlr_tablet_pad_from_input_device(dev)->impl == &wl_tablet_pad_impl; default: return false; } } static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat, enum wl_seat_capability caps) { struct wlr_wl_seat *seat = data; struct wlr_wl_backend *backend = seat->backend; if ((caps & WL_SEAT_CAPABILITY_POINTER) && seat->wl_pointer == NULL) { wlr_log(WLR_DEBUG, "seat '%s' offering pointer", seat->name); seat->wl_pointer = wl_seat_get_pointer(wl_seat); init_seat_pointer(seat); } if (!(caps & WL_SEAT_CAPABILITY_POINTER) && seat->wl_pointer != NULL) { wlr_log(WLR_DEBUG, "seat '%s' dropping pointer", seat->name); finish_seat_pointer(seat); } if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && seat->wl_keyboard == NULL) { wlr_log(WLR_DEBUG, "seat '%s' offering keyboard", seat->name); struct wl_keyboard *wl_keyboard = wl_seat_get_keyboard(wl_seat); seat->wl_keyboard = wl_keyboard; if (backend->started) { init_seat_keyboard(seat); } } if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && seat->wl_keyboard != NULL) { wlr_log(WLR_DEBUG, "seat '%s' dropping keyboard", seat->name); wl_keyboard_release(seat->wl_keyboard); wlr_keyboard_finish(&seat->wlr_keyboard); seat->wl_keyboard = NULL; } if ((caps & WL_SEAT_CAPABILITY_TOUCH) && seat->wl_touch == NULL) { wlr_log(WLR_DEBUG, "seat '%s' offering touch", seat->name); seat->wl_touch = wl_seat_get_touch(wl_seat); if (backend->started) { init_seat_touch(seat); } } if (!(caps & WL_SEAT_CAPABILITY_TOUCH) && seat->wl_touch != NULL) { wlr_log(WLR_DEBUG, "seat '%s' dropping touch", seat->name); wl_touch_release(seat->wl_touch); wlr_touch_finish(&seat->wlr_touch); seat->wl_touch = NULL; } } static void seat_handle_name(void *data, struct wl_seat *wl_seat, const char *name) { struct wlr_wl_seat *seat = data; free(seat->name); seat->name = strdup(name); } static const struct wl_seat_listener seat_listener = { .capabilities = seat_handle_capabilities, .name = seat_handle_name, }; wlroots-0.17.1/backend/wayland/tablet_v2.c000066400000000000000000000634101454110342200203650ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include "backend/wayland.h" #include "util/time.h" #include "tablet-unstable-v2-client-protocol.h" struct tablet_tool { /* static */ struct wlr_wl_seat *seat; /* semi-static */ struct wlr_wl_output *output; double pre_x, pre_y; /* per frame */ double x, y; double pressure; double distance; double tilt_x, tilt_y; double rotation; double slider; double wheel_delta; bool is_in; bool is_out; bool is_up; bool is_down; }; struct tablet_pad_ring { struct wl_list link; // tablet_pad_group.rings /* static */ struct zwp_tablet_pad_ring_v2 *ring; struct tablet_pad_group *group; size_t index; /* per frame */ enum wlr_tablet_pad_ring_source source; double angle; bool stopped; }; struct tablet_pad_strip { struct wl_list link; // tablet_pad_group.strips struct zwp_tablet_pad_strip_v2 *strip; struct tablet_pad_group *group; size_t index; enum wlr_tablet_pad_strip_source source; double position; bool stopped; }; struct tablet_pad_group { struct zwp_tablet_pad_group_v2 *pad_group; struct wlr_tablet_pad *pad; unsigned int mode; struct wlr_tablet_pad_group group; struct wl_list rings; // tablet_pad_ring.link struct wl_list strips; // tablet_pad_strips.link }; static void handle_tablet_pad_ring_source(void *data, struct zwp_tablet_pad_ring_v2 *zwp_tablet_pad_ring_v2, uint32_t source) { struct tablet_pad_ring *ring = data; ring->source = source; } static void handle_tablet_pad_ring_angle(void *data, struct zwp_tablet_pad_ring_v2 *zwp_tablet_pad_ring_v2, wl_fixed_t degrees) { struct tablet_pad_ring *ring = data; ring->angle = wl_fixed_to_double(degrees); } static void handle_tablet_pad_ring_stop(void *data, struct zwp_tablet_pad_ring_v2 *zwp_tablet_pad_ring_v2) { struct tablet_pad_ring *ring = data; ring->stopped = true; } static void handle_tablet_pad_ring_frame(void *data, struct zwp_tablet_pad_ring_v2 *zwp_tablet_pad_ring_v2, uint32_t time) { struct tablet_pad_ring *ring = data; struct wlr_tablet_pad_ring_event evt = { .time_msec = time, .source = ring->source, .ring = ring->index, .position = ring->angle, .mode = ring->group->mode, }; if (ring->angle >= 0) { wl_signal_emit_mutable(&ring->group->pad->events.ring, &evt); } if (ring->stopped) { evt.position = -1; wl_signal_emit_mutable(&ring->group->pad->events.ring, &evt); } ring->angle = -1; ring->stopped = false; ring->source = 0; } static const struct zwp_tablet_pad_ring_v2_listener tablet_pad_ring_listener = { .source = handle_tablet_pad_ring_source, .angle = handle_tablet_pad_ring_angle, .stop = handle_tablet_pad_ring_stop, .frame = handle_tablet_pad_ring_frame, }; static void handle_tablet_pad_strip_source(void *data, struct zwp_tablet_pad_strip_v2 *zwp_tablet_pad_strip_v2, uint32_t source) { struct tablet_pad_strip *strip = data; strip->source = source; } static void handle_tablet_pad_strip_position(void *data, struct zwp_tablet_pad_strip_v2 *zwp_tablet_pad_strip_v2, uint32_t position) { struct tablet_pad_strip *strip = data; strip->position = (double) position / 65536.0; } static void handle_tablet_pad_strip_stop(void *data, struct zwp_tablet_pad_strip_v2 *zwp_tablet_pad_strip_v2) { struct tablet_pad_strip *strip = data; strip->stopped = true; } static void handle_tablet_pad_strip_frame(void *data, struct zwp_tablet_pad_strip_v2 *zwp_tablet_pad_strip_v2, uint32_t time) { struct tablet_pad_strip *strip = data; struct wlr_tablet_pad_strip_event evt = { .time_msec = time, .source = strip->source, .strip = strip->index, .position = strip->position, .mode = strip->group->mode, }; if (strip->position >= 0) { wl_signal_emit_mutable(&strip->group->pad->events.strip, &evt); } if (strip->stopped) { evt.position = -1; wl_signal_emit_mutable(&strip->group->pad->events.strip, &evt); } strip->position = -1; strip->stopped = false; strip->source = 0; } static const struct zwp_tablet_pad_strip_v2_listener tablet_pad_strip_listener = { .source = handle_tablet_pad_strip_source, .position = handle_tablet_pad_strip_position, .stop = handle_tablet_pad_strip_stop, .frame = handle_tablet_pad_strip_frame, }; static void handle_tablet_pad_group_buttons(void *data, struct zwp_tablet_pad_group_v2 *pad_group, struct wl_array *buttons) { struct tablet_pad_group *group = data; free(group->group.buttons); group->group.buttons = calloc(1, buttons->size); if (!group->group.buttons) { // FIXME: Add actual error handling return; } group->group.button_count = buttons->size / sizeof(int); memcpy(group->group.buttons, buttons->data, buttons->size); } static void handle_tablet_pad_group_modes(void *data, struct zwp_tablet_pad_group_v2 *pad_group, uint32_t modes) { struct tablet_pad_group *group = data; group->group.mode_count = modes; } static void handle_tablet_pad_group_ring(void *data, struct zwp_tablet_pad_group_v2 *pad_group, struct zwp_tablet_pad_ring_v2 *ring) { struct tablet_pad_group *group = data; struct tablet_pad_ring *tablet_ring = calloc(1, sizeof(*tablet_ring)); if (!tablet_ring) { zwp_tablet_pad_ring_v2_destroy(ring); return; } tablet_ring->index = group->pad->ring_count++; tablet_ring->group = group; zwp_tablet_pad_ring_v2_add_listener(ring, &tablet_pad_ring_listener, tablet_ring); group->group.rings = realloc(group->group.rings, ++group->group.ring_count * sizeof(unsigned int)); group->group.rings[group->group.ring_count - 1] = tablet_ring->index; } static void handle_tablet_pad_group_strip(void *data, struct zwp_tablet_pad_group_v2 *pad_group, struct zwp_tablet_pad_strip_v2 *strip) { struct tablet_pad_group *group = data; struct tablet_pad_strip *tablet_strip = calloc(1, sizeof(*tablet_strip)); if (!tablet_strip) { zwp_tablet_pad_strip_v2_destroy(strip); return; } tablet_strip->index = group->pad->strip_count++; tablet_strip->group = group; zwp_tablet_pad_strip_v2_add_listener(strip, &tablet_pad_strip_listener, tablet_strip); group->group.strips = realloc(group->group.strips, ++group->group.strip_count * sizeof(unsigned int)); group->group.strips[group->group.strip_count - 1] = tablet_strip->index; } static void handle_tablet_pad_group_done(void *data, struct zwp_tablet_pad_group_v2 *pad_group) { /* Empty for now */ } static void handle_tablet_pad_group_mode_switch(void *data, struct zwp_tablet_pad_group_v2 *pad_group, uint32_t time, uint32_t serial, uint32_t mode) { struct tablet_pad_group *group = data; group->mode = mode; } static void destroy_tablet_pad_group(struct tablet_pad_group *group) { /* No need to remove the link on strips rings as long as we do *not* * wl_list_remove on the wl_groups ring/strip attributes here */ struct tablet_pad_ring *ring, *tmp_ring; wl_list_for_each_safe(ring, tmp_ring, &group->rings, link) { zwp_tablet_pad_ring_v2_destroy(ring->ring); free(ring); } struct tablet_pad_strip *strip, *tmp_strip; wl_list_for_each_safe(strip, tmp_strip, &group->strips, link) { zwp_tablet_pad_strip_v2_destroy(strip->strip); free(strip); } zwp_tablet_pad_group_v2_destroy(group->pad_group); free(group->group.buttons); free(group->group.strips); free(group->group.rings); wl_list_remove(&group->group.link); free(group); } static const struct zwp_tablet_pad_group_v2_listener tablet_pad_group_listener = { .buttons = handle_tablet_pad_group_buttons, .modes = handle_tablet_pad_group_modes, .ring = handle_tablet_pad_group_ring, .strip = handle_tablet_pad_group_strip, .done = handle_tablet_pad_group_done, .mode_switch = handle_tablet_pad_group_mode_switch, }; static void handle_tablet_pad_group(void *data, struct zwp_tablet_pad_v2 *zwp_tablet_pad, struct zwp_tablet_pad_group_v2 *pad_group) { struct wlr_wl_seat *seat = data; struct wlr_tablet_pad *pad = &seat->wlr_tablet_pad; struct tablet_pad_group *group = calloc(1, sizeof(*group)); if (!group) { wlr_log_errno(WLR_ERROR, "failed to allocate tablet_pad_group"); zwp_tablet_pad_group_v2_destroy(pad_group); return; } group->pad_group = pad_group; group->pad = pad; wl_list_init(&group->rings); wl_list_init(&group->strips); zwp_tablet_pad_group_v2_add_listener(pad_group, &tablet_pad_group_listener, group); wl_list_insert(&pad->groups, &group->group.link); } static void handle_tablet_pad_path(void *data, struct zwp_tablet_pad_v2 *zwp_tablet_pad_v2, const char *path) { struct wlr_wl_seat *seat = data; struct wlr_tablet_pad *tablet_pad = &seat->wlr_tablet_pad; char **dst = wl_array_add(&tablet_pad->paths, sizeof(char *)); *dst = strdup(path); } static void handle_tablet_pad_buttons(void *data, struct zwp_tablet_pad_v2 *zwp_tablet_pad_v2, uint32_t buttons) { struct wlr_wl_seat *seat = data; struct wlr_tablet_pad *tablet_pad = &seat->wlr_tablet_pad; tablet_pad->button_count = buttons; } static void handle_tablet_pad_button(void *data, struct zwp_tablet_pad_v2 *zwp_tablet_pad_v2, uint32_t time, uint32_t button, uint32_t state) { struct wlr_wl_seat *seat = data; struct wlr_tablet_pad_button_event evt = { .time_msec = time, .button = button, .state = state, .mode = 0, .group = 0, }; wl_signal_emit_mutable(&seat->wlr_tablet_pad.events.button, &evt); } static void handle_tablet_pad_done(void *data, struct zwp_tablet_pad_v2 *zwp_tablet_pad_v2) { struct wlr_wl_seat *seat = data; wl_signal_emit_mutable(&seat->backend->backend.events.new_input, &seat->wlr_tablet_pad.base); } static void handle_tablet_pad_enter(void *data, struct zwp_tablet_pad_v2 *zwp_tablet_pad_v2, uint32_t serial, struct zwp_tablet_v2 *tablet_p, struct wl_surface *surface) { struct wlr_wl_seat *seat = data; assert(seat->zwp_tablet_v2 == tablet_p); wl_signal_emit_mutable(&seat->wlr_tablet_pad.events.attach_tablet, &seat->wlr_tablet_tool); } static void handle_tablet_pad_leave(void *data, struct zwp_tablet_pad_v2 *zwp_tablet_pad_v2, uint32_t serial, struct wl_surface *surface) { /* Empty. Probably staying that way, unless we want to create/destroy * tablet on enter/leave events (ehh) */ } static void handle_tablet_pad_removed(void *data, struct zwp_tablet_pad_v2 *zwp_tablet_pad_v2) { struct wlr_wl_seat *seat = data; struct wlr_tablet_pad *tablet_pad = &seat->wlr_tablet_pad; struct tablet_pad_group *group, *it; wl_list_for_each_safe(group, it, &tablet_pad->groups, group.link) { destroy_tablet_pad_group(group); } wlr_tablet_pad_finish(tablet_pad); zwp_tablet_pad_v2_destroy(seat->zwp_tablet_pad_v2); seat->zwp_tablet_pad_v2 = NULL; } static const struct zwp_tablet_pad_v2_listener tablet_pad_listener = { .group = handle_tablet_pad_group, .path = handle_tablet_pad_path, .buttons = handle_tablet_pad_buttons, .button = handle_tablet_pad_button, .done = handle_tablet_pad_done, .enter = handle_tablet_pad_enter, .leave = handle_tablet_pad_leave, .removed = handle_tablet_pad_removed, }; const struct wlr_tablet_pad_impl wl_tablet_pad_impl = { .name = "wl-tablet-pad", }; static void handle_pad_added(void *data, struct zwp_tablet_seat_v2 *zwp_tablet_seat_v2, struct zwp_tablet_pad_v2 *zwp_tablet_pad_v2) { struct wlr_wl_seat *seat = data; if (seat->zwp_tablet_pad_v2 != NULL) { wlr_log(WLR_ERROR, "zwp_tablet_pad_v2 is already present"); return; } seat->zwp_tablet_pad_v2 = zwp_tablet_pad_v2; zwp_tablet_pad_v2_add_listener(zwp_tablet_pad_v2, &tablet_pad_listener, seat); wlr_tablet_pad_init(&seat->wlr_tablet_pad, &wl_tablet_pad_impl, "wlr_tablet_v2"); } static void handle_tablet_tool_done(void *data, struct zwp_tablet_tool_v2 *id) { /* empty */ } static enum wlr_tablet_tool_type tablet_type_to_wlr_type( enum zwp_tablet_tool_v2_type type) { switch (type) { case ZWP_TABLET_TOOL_V2_TYPE_PEN: return WLR_TABLET_TOOL_TYPE_PEN; case ZWP_TABLET_TOOL_V2_TYPE_ERASER: return WLR_TABLET_TOOL_TYPE_ERASER; case ZWP_TABLET_TOOL_V2_TYPE_BRUSH: return WLR_TABLET_TOOL_TYPE_BRUSH; case ZWP_TABLET_TOOL_V2_TYPE_PENCIL: return WLR_TABLET_TOOL_TYPE_PENCIL; case ZWP_TABLET_TOOL_V2_TYPE_AIRBRUSH: return WLR_TABLET_TOOL_TYPE_AIRBRUSH; case ZWP_TABLET_TOOL_V2_TYPE_MOUSE: return WLR_TABLET_TOOL_TYPE_MOUSE; case ZWP_TABLET_TOOL_V2_TYPE_LENS: return WLR_TABLET_TOOL_TYPE_LENS; case ZWP_TABLET_TOOL_V2_TYPE_FINGER: // unused, see: // https://gitlab.freedesktop.org/wayland/wayland-protocols/-/issues/18 abort(); } abort(); // unreachable } static void handle_tablet_tool_type(void *data, struct zwp_tablet_tool_v2 *id, uint32_t tool_type) { struct tablet_tool *tool = data; struct wlr_tablet_tool *wlr_tool = &tool->seat->wlr_tablet_tool; wlr_tool->type = tablet_type_to_wlr_type(tool_type); } static void handle_tablet_tool_serial(void *data, struct zwp_tablet_tool_v2 *id, uint32_t high, uint32_t low) { struct tablet_tool *tool = data; struct wlr_tablet_tool *wlr_tool = &tool->seat->wlr_tablet_tool; wlr_tool->hardware_serial = ((uint64_t) high) << 32 | (uint64_t) low; } static void handle_tablet_tool_id_wacom(void *data, struct zwp_tablet_tool_v2 *id, uint32_t high, uint32_t low) { struct tablet_tool *tool = data; struct wlr_tablet_tool *wlr_tool = &tool->seat->wlr_tablet_tool; wlr_tool->hardware_wacom = ((uint64_t) high) << 32 | (uint64_t) low; } static void handle_tablet_tool_capability(void *data, struct zwp_tablet_tool_v2 *id, uint32_t capability) { struct tablet_tool *tool = data; struct wlr_tablet_tool *wlr_tool = &tool->seat->wlr_tablet_tool; /* One event is sent for each capability */ switch (capability) { case ZWP_TABLET_TOOL_V2_CAPABILITY_TILT: wlr_tool->tilt = true; break; case ZWP_TABLET_TOOL_V2_CAPABILITY_PRESSURE: wlr_tool->pressure = true; break; case ZWP_TABLET_TOOL_V2_CAPABILITY_DISTANCE: wlr_tool->distance = true; break; case ZWP_TABLET_TOOL_V2_CAPABILITY_ROTATION: wlr_tool->rotation = true; break; case ZWP_TABLET_TOOL_V2_CAPABILITY_SLIDER: wlr_tool->slider = true; break; case ZWP_TABLET_TOOL_V2_CAPABILITY_WHEEL: wlr_tool->wheel = true; break; } } static void handle_tablet_tool_proximity_in(void *data, struct zwp_tablet_tool_v2 *id, uint32_t serial, struct zwp_tablet_v2 *tablet_id, struct wl_surface *surface) { struct tablet_tool *tool = data; assert(tablet_id == tool->seat->zwp_tablet_v2); struct wlr_wl_output *output = get_wl_output_from_surface(tool->seat->backend, surface); if (output == NULL) { return; } tool->is_in = true; tool->output = output; } static void handle_tablet_tool_proximity_out(void *data, struct zwp_tablet_tool_v2 *id) { struct tablet_tool *tool = data; tool->is_out = true; tool->output = NULL; } static void handle_tablet_tool_down(void *data, struct zwp_tablet_tool_v2 *id, unsigned int serial) { struct tablet_tool *tool = data; tool->is_down = true; } static void handle_tablet_tool_up(void *data, struct zwp_tablet_tool_v2 *id) { struct tablet_tool *tool = data; tool->is_up = true; } static void handle_tablet_tool_motion(void *data, struct zwp_tablet_tool_v2 *id, wl_fixed_t x, wl_fixed_t y) { struct tablet_tool *tool = data; struct wlr_wl_output *output = tool->output; assert(output); tool->x = wl_fixed_to_double(x) / output->wlr_output.width; tool->y = wl_fixed_to_double(y) / output->wlr_output.height; } static void handle_tablet_tool_pressure(void *data, struct zwp_tablet_tool_v2 *id, uint32_t pressure) { struct tablet_tool *tool = data; tool->pressure = (double) pressure / 65535.0; } static void handle_tablet_tool_distance(void *data, struct zwp_tablet_tool_v2 *id, uint32_t distance) { struct tablet_tool *tool = data; tool->distance = (double) distance / 65535.0; } static void handle_tablet_tool_tilt(void *data, struct zwp_tablet_tool_v2 *id, wl_fixed_t x, wl_fixed_t y) { struct tablet_tool *tool = data; tool->tilt_x = wl_fixed_to_double(x); tool->tilt_y = wl_fixed_to_double(y); } static void handle_tablet_tool_rotation(void *data, struct zwp_tablet_tool_v2 *id, wl_fixed_t rotation) { struct tablet_tool *tool = data; tool->rotation = wl_fixed_to_double(rotation); } static void handle_tablet_tool_slider(void *data, struct zwp_tablet_tool_v2 *id, int slider) { struct tablet_tool *tool = data; tool->slider = (double) slider / 65535.0;; } // TODO: This looks wrong :/ static void handle_tablet_tool_wheel(void *data, struct zwp_tablet_tool_v2 *id, wl_fixed_t degree, int clicks) { struct tablet_tool *tool = data; tool->wheel_delta = wl_fixed_to_double(degree); } static void handle_tablet_tool_button(void *data, struct zwp_tablet_tool_v2 *id, uint32_t serial, uint32_t button, uint32_t state) { struct tablet_tool *tool = data; struct wlr_wl_seat *seat = tool->seat; struct wlr_tablet *tablet = &seat->wlr_tablet; struct wlr_tablet_tool_button_event evt = { .tablet = tablet, .tool = &seat->wlr_tablet_tool, .time_msec = get_current_time_msec(), .button = button, .state = state == ZWP_TABLET_TOOL_V2_BUTTON_STATE_RELEASED ? WLR_BUTTON_RELEASED : WLR_BUTTON_PRESSED, }; wl_signal_emit_mutable(&tablet->events.button, &evt); } static void clear_tablet_tool_values(struct tablet_tool *tool) { tool->is_out = tool->is_in = false; tool->is_up = tool->is_down = false; tool->x = tool->y = NAN; tool->pressure = NAN; tool->distance = NAN; tool->tilt_x = tool->tilt_y = NAN; tool->rotation = NAN; tool->slider = NAN; tool->wheel_delta = NAN; } static void handle_tablet_tool_frame(void *data, struct zwp_tablet_tool_v2 *id, uint32_t time) { struct tablet_tool *tool = data; struct wlr_wl_seat *seat = tool->seat; if (tool->is_out && tool->is_in) { /* we got a tablet tool coming in and out of proximity before * we could process it. Just ignore anything it did */ goto clear_values; } struct wlr_tablet *tablet = &seat->wlr_tablet; if (tool->is_in) { struct wlr_tablet_tool_proximity_event evt = { .tablet = tablet, .tool = &seat->wlr_tablet_tool, .time_msec = time, .x = tool->x, .y = tool->y, .state = WLR_TABLET_TOOL_PROXIMITY_IN, }; wl_signal_emit_mutable(&tablet->events.proximity, &evt); } { struct wlr_tablet_tool_axis_event evt = { .tablet = tablet, .tool = &seat->wlr_tablet_tool, .time_msec = time, .updated_axes = 0, }; if (!isnan(tool->x) && !tool->is_in) { evt.updated_axes |= WLR_TABLET_TOOL_AXIS_X; evt.x = tool->x; } if (!isnan(tool->y) && !tool->is_in) { evt.updated_axes |= WLR_TABLET_TOOL_AXIS_Y; evt.y = tool->y; } if (!isnan(tool->pressure)) { evt.updated_axes |= WLR_TABLET_TOOL_AXIS_PRESSURE; evt.pressure = tool->pressure; } if (!isnan(tool->distance)) { evt.updated_axes |= WLR_TABLET_TOOL_AXIS_DISTANCE; evt.distance = tool->distance; } if (!isnan(tool->tilt_x)) { evt.updated_axes |= WLR_TABLET_TOOL_AXIS_TILT_X; evt.tilt_x = tool->tilt_x; } if (!isnan(tool->tilt_y)) { evt.updated_axes |= WLR_TABLET_TOOL_AXIS_TILT_Y; evt.tilt_y = tool->tilt_y; } if (!isnan(tool->rotation)) { evt.updated_axes |= WLR_TABLET_TOOL_AXIS_ROTATION; evt.rotation = tool->rotation; } if (!isnan(tool->slider)) { evt.updated_axes |= WLR_TABLET_TOOL_AXIS_SLIDER; evt.slider = tool->slider; } if (!isnan(tool->wheel_delta)) { evt.updated_axes |= WLR_TABLET_TOOL_AXIS_WHEEL; evt.wheel_delta = tool->wheel_delta; } if (evt.updated_axes) { wl_signal_emit_mutable(&tablet->events.axis, &evt); } } /* This will always send down then up if we got both. * Maybe we should send them right away, in case we get up then both in * series? * Downside: Here we have the frame time, if we sent right away, we * need to generate the time */ if (tool->is_down) { struct wlr_tablet_tool_tip_event evt = { .tablet = tablet, .tool = &seat->wlr_tablet_tool, .time_msec = time, .x = tool->x, .y = tool->y, .state = WLR_TABLET_TOOL_TIP_DOWN, }; wl_signal_emit_mutable(&tablet->events.tip, &evt); } if (tool->is_up) { struct wlr_tablet_tool_tip_event evt = { .tablet = tablet, .tool = &seat->wlr_tablet_tool, .time_msec = time, .x = tool->x, .y = tool->y, .state = WLR_TABLET_TOOL_TIP_UP, }; wl_signal_emit_mutable(&tablet->events.tip, &evt); } if (tool->is_out) { struct wlr_tablet_tool_proximity_event evt = { .tablet = tablet, .tool = &seat->wlr_tablet_tool, .time_msec = time, .x = tool->x, .y = tool->y, .state = WLR_TABLET_TOOL_PROXIMITY_OUT, }; wl_signal_emit_mutable(&tablet->events.proximity, &evt); } clear_values: clear_tablet_tool_values(tool); } static void handle_tablet_tool_removed(void *data, struct zwp_tablet_tool_v2 *id) { struct tablet_tool *tool = data; struct wlr_wl_seat *seat = tool->seat; zwp_tablet_tool_v2_destroy(seat->zwp_tablet_tool_v2); seat->zwp_tablet_tool_v2 = NULL; free(tool); } static const struct zwp_tablet_tool_v2_listener tablet_tool_listener = { .removed = handle_tablet_tool_removed, .done = handle_tablet_tool_done, .type = handle_tablet_tool_type, .hardware_serial = handle_tablet_tool_serial, .hardware_id_wacom = handle_tablet_tool_id_wacom, .capability = handle_tablet_tool_capability, .proximity_in = handle_tablet_tool_proximity_in, .proximity_out = handle_tablet_tool_proximity_out, .down = handle_tablet_tool_down, .up = handle_tablet_tool_up, .motion = handle_tablet_tool_motion, .pressure = handle_tablet_tool_pressure, .distance = handle_tablet_tool_distance, .tilt = handle_tablet_tool_tilt, .rotation = handle_tablet_tool_rotation, .slider = handle_tablet_tool_slider, .wheel = handle_tablet_tool_wheel, .button = handle_tablet_tool_button, .frame = handle_tablet_tool_frame, }; static void handle_tool_added(void *data, struct zwp_tablet_seat_v2 *zwp_tablet_seat_v2, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2) { struct wlr_wl_seat *seat = data; if (seat->zwp_tablet_tool_v2 != NULL) { wlr_log(WLR_ERROR, "zwp_tablet_tool_v2 already present"); return; } wl_signal_init(&seat->wlr_tablet_tool.events.destroy); struct tablet_tool *tool = calloc(1, sizeof(*tool)); if (tool == NULL) { wlr_log_errno(WLR_ERROR, "failed to allocate tablet_tool"); zwp_tablet_tool_v2_destroy(zwp_tablet_tool_v2); return; } tool->seat = seat; clear_tablet_tool_values(tool); seat->zwp_tablet_tool_v2 = zwp_tablet_tool_v2; zwp_tablet_tool_v2_add_listener(seat->zwp_tablet_tool_v2, &tablet_tool_listener, tool); } static void handle_tablet_name(void *data, struct zwp_tablet_v2 *zwp_tablet_v2, const char *name) { struct wlr_wl_seat *seat = data; struct wlr_tablet *tablet = &seat->wlr_tablet; free(tablet->base.name); tablet->base.name = strdup(name); } static void handle_tablet_id(void *data, struct zwp_tablet_v2 *zwp_tablet_v2, uint32_t vid, uint32_t pid) { struct wlr_wl_seat *seat = data; struct wlr_tablet *tablet = &seat->wlr_tablet; tablet->base.vendor = vid; tablet->base.product = pid; } static void handle_tablet_path(void *data, struct zwp_tablet_v2 *zwp_tablet_v2, const char *path) { struct wlr_wl_seat *seat = data; struct wlr_tablet *tablet = &seat->wlr_tablet; char **dst = wl_array_add(&tablet->paths, sizeof(char *)); *dst = strdup(path); } static void handle_tablet_done(void *data, struct zwp_tablet_v2 *zwp_tablet_v2) { struct wlr_wl_seat *seat = data; wl_signal_emit_mutable(&seat->backend->backend.events.new_input, &seat->wlr_tablet.base); } static void handle_tablet_removed(void *data, struct zwp_tablet_v2 *zwp_tablet_v2) { struct wlr_wl_seat *seat = data; wlr_tablet_finish(&seat->wlr_tablet); zwp_tablet_v2_destroy(seat->zwp_tablet_v2); seat->zwp_tablet_v2 = NULL; } static const struct zwp_tablet_v2_listener tablet_listener = { .name = handle_tablet_name, .id = handle_tablet_id, .path = handle_tablet_path, .done = handle_tablet_done, .removed = handle_tablet_removed, }; const struct wlr_tablet_impl wl_tablet_impl = { .name = "wl-tablet-tool", }; static void handle_tab_added(void *data, struct zwp_tablet_seat_v2 *zwp_tablet_seat_v2, struct zwp_tablet_v2 *zwp_tablet_v2) { struct wlr_wl_seat *seat = data; if (seat->zwp_tablet_v2 != NULL) { wlr_log(WLR_ERROR, "zwp_tablet_v2 already present"); return; } seat->zwp_tablet_v2 = zwp_tablet_v2; zwp_tablet_v2_add_listener(zwp_tablet_v2, &tablet_listener, seat); wlr_tablet_init(&seat->wlr_tablet, &wl_tablet_impl, "wlr_tablet_v2"); } static const struct zwp_tablet_seat_v2_listener tablet_seat_listener = { .tablet_added = handle_tab_added, .tool_added = handle_tool_added, .pad_added = handle_pad_added, }; void init_seat_tablet(struct wlr_wl_seat *seat) { struct zwp_tablet_manager_v2 *manager = seat->backend->tablet_manager; assert(manager); /** * TODO: multi tablet support * The wlr_wl_seat should support multiple tablet_v2 devices, but for * the sake of simplicity, it supports only one device of each. * If this is a feature you want/need, please open an issue on the wlroots * tracker here https://gitlab.freedesktop.org/wlroots/wlroots/-/issues */ seat->zwp_tablet_seat_v2 = zwp_tablet_manager_v2_get_tablet_seat(manager, seat->wl_seat); if (seat->zwp_tablet_seat_v2 == NULL) { wlr_log(WLR_ERROR, "failed to get zwp_tablet_manager_v2 from seat '%s'", seat->name); return; } zwp_tablet_seat_v2_add_listener(seat->zwp_tablet_seat_v2, &tablet_seat_listener, seat); } void finish_seat_tablet(struct wlr_wl_seat *seat) { if (seat->zwp_tablet_v2 != NULL) { wlr_tablet_finish(&seat->wlr_tablet); zwp_tablet_v2_destroy(seat->zwp_tablet_v2); } if (seat->zwp_tablet_tool_v2 != NULL) { struct tablet_tool *tool = zwp_tablet_tool_v2_get_user_data(seat->zwp_tablet_tool_v2); free(tool); zwp_tablet_tool_v2_destroy(seat->zwp_tablet_tool_v2); } if (seat->zwp_tablet_pad_v2 != NULL) { struct wlr_tablet_pad *tablet_pad = &seat->wlr_tablet_pad; struct tablet_pad_group *group, *it; wl_list_for_each_safe(group, it, &tablet_pad->groups, group.link) { destroy_tablet_pad_group(group); } wlr_tablet_pad_finish(tablet_pad); zwp_tablet_pad_v2_destroy(seat->zwp_tablet_pad_v2); } zwp_tablet_seat_v2_destroy(seat->zwp_tablet_seat_v2); seat->zwp_tablet_seat_v2 = NULL; } wlroots-0.17.1/backend/x11/000077500000000000000000000000001454110342200153055ustar00rootroot00000000000000wlroots-0.17.1/backend/x11/backend.c000066400000000000000000000512251454110342200170450ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "backend/x11.h" #include "render/drm_format_set.h" // See dri2_format_for_depth in mesa const struct wlr_x11_format formats[] = { { .drm = DRM_FORMAT_XRGB8888, .depth = 24, .bpp = 32 }, { .drm = DRM_FORMAT_ARGB8888, .depth = 32, .bpp = 32 }, }; static const struct wlr_x11_format *x11_format_from_depth(uint8_t depth) { for (size_t i = 0; i < sizeof(formats) / sizeof(formats[0]); i++) { if (formats[i].depth == depth) { return &formats[i]; } } return NULL; } struct wlr_x11_output *get_x11_output_from_window_id( struct wlr_x11_backend *x11, xcb_window_t window) { struct wlr_x11_output *output; wl_list_for_each(output, &x11->outputs, link) { if (output->win == window) { return output; } } return NULL; } static void handle_x11_error(struct wlr_x11_backend *x11, xcb_value_error_t *ev); static void handle_x11_unknown_event(struct wlr_x11_backend *x11, xcb_generic_event_t *ev); static void handle_x11_event(struct wlr_x11_backend *x11, xcb_generic_event_t *event) { switch (event->response_type & XCB_EVENT_RESPONSE_TYPE_MASK) { case XCB_EXPOSE: { xcb_expose_event_t *ev = (xcb_expose_event_t *)event; struct wlr_x11_output *output = get_x11_output_from_window_id(x11, ev->window); if (output != NULL) { pixman_region32_union_rect( &output->exposed, &output->exposed, ev->x, ev->y, ev->width, ev->height); wlr_output_update_needs_frame(&output->wlr_output); } break; } case XCB_CONFIGURE_NOTIFY: { xcb_configure_notify_event_t *ev = (xcb_configure_notify_event_t *)event; struct wlr_x11_output *output = get_x11_output_from_window_id(x11, ev->window); if (output != NULL) { handle_x11_configure_notify(output, ev); } break; } case XCB_CLIENT_MESSAGE: { xcb_client_message_event_t *ev = (xcb_client_message_event_t *)event; if (ev->data.data32[0] == x11->atoms.wm_delete_window) { struct wlr_x11_output *output = get_x11_output_from_window_id(x11, ev->window); if (output != NULL) { wlr_output_destroy(&output->wlr_output); } } else { wlr_log(WLR_DEBUG, "Unhandled client message %"PRIu32, ev->data.data32[0]); } break; } case XCB_GE_GENERIC: { xcb_ge_generic_event_t *ev = (xcb_ge_generic_event_t *)event; if (ev->extension == x11->xinput_opcode) { handle_x11_xinput_event(x11, ev); } else if (ev->extension == x11->present_opcode) { handle_x11_present_event(x11, ev); } else { handle_x11_unknown_event(x11, event); } break; } case 0: { xcb_value_error_t *ev = (xcb_value_error_t *)event; handle_x11_error(x11, ev); break; } case XCB_UNMAP_NOTIFY: case XCB_MAP_NOTIFY: break; default: handle_x11_unknown_event(x11, event); break; } } static int x11_event(int fd, uint32_t mask, void *data) { struct wlr_x11_backend *x11 = data; if ((mask & WL_EVENT_HANGUP) || (mask & WL_EVENT_ERROR)) { if (mask & WL_EVENT_ERROR) { wlr_log(WLR_ERROR, "Failed to read from X11 server"); } wl_display_terminate(x11->wl_display); return 0; } xcb_generic_event_t *e; while ((e = xcb_poll_for_event(x11->xcb))) { handle_x11_event(x11, e); free(e); } int ret = xcb_connection_has_error(x11->xcb); if (ret != 0) { wlr_log(WLR_ERROR, "X11 connection error (%d)", ret); wl_display_terminate(x11->wl_display); } return 0; } struct wlr_x11_backend *get_x11_backend_from_backend( struct wlr_backend *wlr_backend) { assert(wlr_backend_is_x11(wlr_backend)); struct wlr_x11_backend *backend = wl_container_of(wlr_backend, backend, backend); return backend; } static bool backend_start(struct wlr_backend *backend) { struct wlr_x11_backend *x11 = get_x11_backend_from_backend(backend); x11->started = true; wlr_log(WLR_INFO, "Starting X11 backend"); wl_signal_emit_mutable(&x11->backend.events.new_input, &x11->keyboard.base); for (size_t i = 0; i < x11->requested_outputs; ++i) { wlr_x11_output_create(&x11->backend); } return true; } static void backend_destroy(struct wlr_backend *backend) { if (!backend) { return; } struct wlr_x11_backend *x11 = get_x11_backend_from_backend(backend); struct wlr_x11_output *output, *tmp; wl_list_for_each_safe(output, tmp, &x11->outputs, link) { wlr_output_destroy(&output->wlr_output); } wlr_keyboard_finish(&x11->keyboard); wlr_backend_finish(backend); if (x11->event_source) { wl_event_source_remove(x11->event_source); } wl_list_remove(&x11->display_destroy.link); wlr_drm_format_set_finish(&x11->primary_dri3_formats); wlr_drm_format_set_finish(&x11->primary_shm_formats); wlr_drm_format_set_finish(&x11->dri3_formats); wlr_drm_format_set_finish(&x11->shm_formats); #if HAVE_XCB_ERRORS xcb_errors_context_free(x11->errors_context); #endif close(x11->drm_fd); xcb_disconnect(x11->xcb); free(x11); } static int backend_get_drm_fd(struct wlr_backend *backend) { struct wlr_x11_backend *x11 = get_x11_backend_from_backend(backend); return x11->drm_fd; } static uint32_t get_buffer_caps(struct wlr_backend *backend) { struct wlr_x11_backend *x11 = get_x11_backend_from_backend(backend); return (x11->have_dri3 ? WLR_BUFFER_CAP_DMABUF : 0) | (x11->have_shm ? WLR_BUFFER_CAP_SHM : 0); } static const struct wlr_backend_impl backend_impl = { .start = backend_start, .destroy = backend_destroy, .get_drm_fd = backend_get_drm_fd, .get_buffer_caps = get_buffer_caps, }; bool wlr_backend_is_x11(struct wlr_backend *backend) { return backend->impl == &backend_impl; } static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_x11_backend *x11 = wl_container_of(listener, x11, display_destroy); backend_destroy(&x11->backend); } static xcb_depth_t *get_depth(xcb_screen_t *screen, uint8_t depth) { xcb_depth_iterator_t iter = xcb_screen_allowed_depths_iterator(screen); while (iter.rem > 0) { if (iter.data->depth == depth) { return iter.data; } xcb_depth_next(&iter); } return NULL; } static xcb_visualid_t pick_visualid(xcb_depth_t *depth) { xcb_visualtype_t *visuals = xcb_depth_visuals(depth); for (int i = 0; i < xcb_depth_visuals_length(depth); i++) { if (visuals[i]._class == XCB_VISUAL_CLASS_TRUE_COLOR) { return visuals[i].visual_id; } } return 0; } static int query_dri3_drm_fd(struct wlr_x11_backend *x11) { xcb_dri3_open_cookie_t open_cookie = xcb_dri3_open(x11->xcb, x11->screen->root, 0); xcb_dri3_open_reply_t *open_reply = xcb_dri3_open_reply(x11->xcb, open_cookie, NULL); if (open_reply == NULL) { return -1; } int *open_fds = xcb_dri3_open_reply_fds(x11->xcb, open_reply); if (open_fds == NULL) { free(open_reply); return -1; } assert(open_reply->nfd == 1); int drm_fd = open_fds[0]; free(open_reply); int flags = fcntl(drm_fd, F_GETFD); if (flags < 0) { close(drm_fd); return -1; } if (fcntl(drm_fd, F_SETFD, flags | FD_CLOEXEC) < 0) { close(drm_fd); return -1; } if (drmGetNodeTypeFromFd(drm_fd) != DRM_NODE_RENDER) { char *render_name = drmGetRenderDeviceNameFromFd(drm_fd); if (render_name == NULL) { close(drm_fd); return -1; } close(drm_fd); drm_fd = open(render_name, O_RDWR | O_CLOEXEC); if (drm_fd < 0) { free(render_name); return -1; } free(render_name); } return drm_fd; } static bool query_dri3_modifiers(struct wlr_x11_backend *x11, const struct wlr_x11_format *format) { if (x11->dri3_major_version == 1 && x11->dri3_minor_version < 2) { return true; // GetSupportedModifiers requires DRI3 1.2 } // Query the root window's supported modifiers, because we only care about // screen_modifiers for now xcb_dri3_get_supported_modifiers_cookie_t modifiers_cookie = xcb_dri3_get_supported_modifiers(x11->xcb, x11->screen->root, format->depth, format->bpp); xcb_dri3_get_supported_modifiers_reply_t *modifiers_reply = xcb_dri3_get_supported_modifiers_reply(x11->xcb, modifiers_cookie, NULL); if (!modifiers_reply) { wlr_log(WLR_ERROR, "Failed to get DMA-BUF modifiers supported by " "the X11 server for the format 0x%"PRIX32, format->drm); return false; } // If modifiers aren't supported, DRI3 will return an empty list const uint64_t *modifiers = xcb_dri3_get_supported_modifiers_screen_modifiers(modifiers_reply); int modifiers_len = xcb_dri3_get_supported_modifiers_screen_modifiers_length(modifiers_reply); for (int i = 0; i < modifiers_len; i++) { wlr_drm_format_set_add(&x11->dri3_formats, format->drm, modifiers[i]); } free(modifiers_reply); return true; } static bool query_formats(struct wlr_x11_backend *x11) { xcb_depth_iterator_t iter = xcb_screen_allowed_depths_iterator(x11->screen); while (iter.rem > 0) { uint8_t depth = iter.data->depth; const struct wlr_x11_format *format = x11_format_from_depth(depth); if (format != NULL) { if (x11->have_shm) { wlr_drm_format_set_add(&x11->shm_formats, format->drm, DRM_FORMAT_MOD_INVALID); } if (x11->have_dri3) { // X11 always supports implicit modifiers wlr_drm_format_set_add(&x11->dri3_formats, format->drm, DRM_FORMAT_MOD_INVALID); if (!query_dri3_modifiers(x11, format)) { return false; } } } xcb_depth_next(&iter); } return true; } static void x11_get_argb32(struct wlr_x11_backend *x11) { xcb_render_query_pict_formats_cookie_t cookie = xcb_render_query_pict_formats(x11->xcb); xcb_render_query_pict_formats_reply_t *reply = xcb_render_query_pict_formats_reply(x11->xcb, cookie, NULL); if (!reply) { wlr_log(WLR_ERROR, "Did not get any reply from xcb_render_query_pict_formats"); return; } xcb_render_pictforminfo_t *format = xcb_render_util_find_standard_format(reply, XCB_PICT_STANDARD_ARGB_32); if (format == NULL) { wlr_log(WLR_DEBUG, "No ARGB_32 render format"); free(reply); return; } x11->argb32 = format->id; free(reply); } struct wlr_backend *wlr_x11_backend_create(struct wl_display *display, const char *x11_display) { wlr_log(WLR_INFO, "Creating X11 backend"); struct wlr_x11_backend *x11 = calloc(1, sizeof(*x11)); if (!x11) { return NULL; } wlr_backend_init(&x11->backend, &backend_impl); x11->wl_display = display; wl_list_init(&x11->outputs); x11->xcb = xcb_connect(x11_display, NULL); if (!x11->xcb || xcb_connection_has_error(x11->xcb)) { wlr_log(WLR_ERROR, "Failed to open xcb connection"); goto error_x11; } struct { const char *name; xcb_intern_atom_cookie_t cookie; xcb_atom_t *atom; } atom[] = { { .name = "WM_PROTOCOLS", .atom = &x11->atoms.wm_protocols }, { .name = "WM_DELETE_WINDOW", .atom = &x11->atoms.wm_delete_window }, { .name = "_NET_WM_NAME", .atom = &x11->atoms.net_wm_name }, { .name = "UTF8_STRING", .atom = &x11->atoms.utf8_string }, { .name = "_VARIABLE_REFRESH", .atom = &x11->atoms.variable_refresh }, }; for (size_t i = 0; i < sizeof(atom) / sizeof(atom[0]); ++i) { atom[i].cookie = xcb_intern_atom(x11->xcb, true, strlen(atom[i].name), atom[i].name); } for (size_t i = 0; i < sizeof(atom) / sizeof(atom[0]); ++i) { xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply( x11->xcb, atom[i].cookie, NULL); if (reply) { *atom[i].atom = reply->atom; free(reply); } else { *atom[i].atom = XCB_ATOM_NONE; } } const xcb_query_extension_reply_t *ext; // DRI3 extension ext = xcb_get_extension_data(x11->xcb, &xcb_dri3_id); if (ext && ext->present) { xcb_dri3_query_version_cookie_t dri3_cookie = xcb_dri3_query_version(x11->xcb, 1, 2); xcb_dri3_query_version_reply_t *dri3_reply = xcb_dri3_query_version_reply(x11->xcb, dri3_cookie, NULL); if (dri3_reply) { if (dri3_reply->major_version >= 1) { x11->have_dri3 = true; x11->dri3_major_version = dri3_reply->major_version; x11->dri3_minor_version = dri3_reply->minor_version; } else { wlr_log(WLR_INFO, "X11 does not support required DRI3 version " "(has %"PRIu32".%"PRIu32", want 1.0)", dri3_reply->major_version, dri3_reply->minor_version); } free(dri3_reply); } else { wlr_log(WLR_INFO, "X11 does not support required DRi3 version"); } } else { wlr_log(WLR_INFO, "X11 does not support DRI3 extension"); } // SHM extension ext = xcb_get_extension_data(x11->xcb, &xcb_shm_id); if (ext && ext->present) { xcb_shm_query_version_cookie_t shm_cookie = xcb_shm_query_version(x11->xcb); xcb_shm_query_version_reply_t *shm_reply = xcb_shm_query_version_reply(x11->xcb, shm_cookie, NULL); if (shm_reply) { if (shm_reply->major_version >= 1 || shm_reply->minor_version >= 2) { if (shm_reply->shared_pixmaps) { x11->have_shm = true; } else { wlr_log(WLR_INFO, "X11 does not support shared pixmaps"); } } else { wlr_log(WLR_INFO, "X11 does not support required SHM version " "(has %"PRIu32".%"PRIu32", want 1.2)", shm_reply->major_version, shm_reply->minor_version); } } else { wlr_log(WLR_INFO, "X11 does not support required SHM version"); } free(shm_reply); } else { wlr_log(WLR_INFO, "X11 does not support SHM extension"); } // Present extension ext = xcb_get_extension_data(x11->xcb, &xcb_present_id); if (!ext || !ext->present) { wlr_log(WLR_ERROR, "X11 does not support Present extension"); goto error_display; } x11->present_opcode = ext->major_opcode; xcb_present_query_version_cookie_t present_cookie = xcb_present_query_version(x11->xcb, 1, 2); xcb_present_query_version_reply_t *present_reply = xcb_present_query_version_reply(x11->xcb, present_cookie, NULL); if (!present_reply || present_reply->major_version < 1) { wlr_log(WLR_ERROR, "X11 does not support required Present version " "(has %"PRIu32".%"PRIu32", want 1.0)", present_reply->major_version, present_reply->minor_version); free(present_reply); goto error_display; } free(present_reply); // Xfixes extension ext = xcb_get_extension_data(x11->xcb, &xcb_xfixes_id); if (!ext || !ext->present) { wlr_log(WLR_ERROR, "X11 does not support Xfixes extension"); goto error_display; } xcb_xfixes_query_version_cookie_t fixes_cookie = xcb_xfixes_query_version(x11->xcb, 4, 0); xcb_xfixes_query_version_reply_t *fixes_reply = xcb_xfixes_query_version_reply(x11->xcb, fixes_cookie, NULL); if (!fixes_reply || fixes_reply->major_version < 4) { wlr_log(WLR_ERROR, "X11 does not support required Xfixes version " "(has %"PRIu32".%"PRIu32", want 4.0)", fixes_reply->major_version, fixes_reply->minor_version); free(fixes_reply); goto error_display; } free(fixes_reply); // Xinput extension ext = xcb_get_extension_data(x11->xcb, &xcb_input_id); if (!ext || !ext->present) { wlr_log(WLR_ERROR, "X11 does not support Xinput extension"); goto error_display; } x11->xinput_opcode = ext->major_opcode; xcb_input_xi_query_version_cookie_t xi_cookie = xcb_input_xi_query_version(x11->xcb, 2, 0); xcb_input_xi_query_version_reply_t *xi_reply = xcb_input_xi_query_version_reply(x11->xcb, xi_cookie, NULL); if (!xi_reply || xi_reply->major_version < 2) { wlr_log(WLR_ERROR, "X11 does not support required Xinput version " "(has %"PRIu32".%"PRIu32", want 2.0)", xi_reply->major_version, xi_reply->minor_version); free(xi_reply); goto error_display; } free(xi_reply); int fd = xcb_get_file_descriptor(x11->xcb); struct wl_event_loop *ev = wl_display_get_event_loop(display); uint32_t events = WL_EVENT_READABLE | WL_EVENT_ERROR | WL_EVENT_HANGUP; x11->event_source = wl_event_loop_add_fd(ev, fd, events, x11_event, x11); if (!x11->event_source) { wlr_log(WLR_ERROR, "Could not create event source"); goto error_display; } wl_event_source_check(x11->event_source); x11->screen = xcb_setup_roots_iterator(xcb_get_setup(x11->xcb)).data; if (!x11->screen) { wlr_log(WLR_ERROR, "Failed to get X11 screen"); goto error_event; } x11->depth = get_depth(x11->screen, 24); if (!x11->depth) { wlr_log(WLR_ERROR, "Failed to get 24-bit depth for X11 screen"); goto error_event; } x11->visualid = pick_visualid(x11->depth); if (!x11->visualid) { wlr_log(WLR_ERROR, "Failed to pick X11 visual"); goto error_event; } x11->x11_format = x11_format_from_depth(x11->depth->depth); if (!x11->x11_format) { wlr_log(WLR_ERROR, "Unsupported depth %"PRIu8, x11->depth->depth); goto error_event; } x11->colormap = xcb_generate_id(x11->xcb); xcb_create_colormap(x11->xcb, XCB_COLORMAP_ALLOC_NONE, x11->colormap, x11->screen->root, x11->visualid); if (!query_formats(x11)) { wlr_log(WLR_ERROR, "Failed to query supported DRM formats"); return false; } x11->drm_fd = -1; if (x11->have_dri3) { // DRI3 may return a render node (Xwayland) or an authenticated primary // node (plain Glamor). x11->drm_fd = query_dri3_drm_fd(x11); if (x11->drm_fd < 0) { wlr_log(WLR_ERROR, "Failed to query DRI3 DRM FD"); goto error_event; } } // Windows can only display buffers with the depth they were created with // TODO: look into changing the window's depth at runtime const struct wlr_drm_format *dri3_format = wlr_drm_format_set_get(&x11->dri3_formats, x11->x11_format->drm); if (x11->have_dri3 && dri3_format != NULL) { wlr_drm_format_set_add(&x11->primary_dri3_formats, dri3_format->format, DRM_FORMAT_MOD_INVALID); for (size_t i = 0; i < dri3_format->len; i++) { wlr_drm_format_set_add(&x11->primary_dri3_formats, dri3_format->format, dri3_format->modifiers[i]); } } const struct wlr_drm_format *shm_format = wlr_drm_format_set_get(&x11->shm_formats, x11->x11_format->drm); if (x11->have_shm && shm_format != NULL) { wlr_drm_format_set_add(&x11->primary_shm_formats, shm_format->format, DRM_FORMAT_MOD_INVALID); } #if HAVE_XCB_ERRORS if (xcb_errors_context_new(x11->xcb, &x11->errors_context) != 0) { wlr_log(WLR_ERROR, "Failed to create error context"); return false; } #endif wlr_keyboard_init(&x11->keyboard, &x11_keyboard_impl, x11_keyboard_impl.name); x11->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &x11->display_destroy); // Create an empty pixmap to be used as the cursor. The // default GC foreground is 0, and that is what it will be // filled with. xcb_pixmap_t blank = xcb_generate_id(x11->xcb); xcb_create_pixmap(x11->xcb, 1, blank, x11->screen->root, 1, 1); xcb_gcontext_t gc = xcb_generate_id(x11->xcb); xcb_create_gc(x11->xcb, gc, blank, 0, NULL); xcb_rectangle_t rect = { .x = 0, .y = 0, .width = 1, .height = 1 }; xcb_poly_fill_rectangle(x11->xcb, blank, gc, 1, &rect); x11->transparent_cursor = xcb_generate_id(x11->xcb); xcb_create_cursor(x11->xcb, x11->transparent_cursor, blank, blank, 0, 0, 0, 0, 0, 0, 0, 0); xcb_free_gc(x11->xcb, gc); xcb_free_pixmap(x11->xcb, blank); x11_get_argb32(x11); return &x11->backend; error_event: wl_event_source_remove(x11->event_source); error_display: xcb_disconnect(x11->xcb); error_x11: free(x11); return NULL; } static void handle_x11_error(struct wlr_x11_backend *x11, xcb_value_error_t *ev) { #if HAVE_XCB_ERRORS const char *major_name = xcb_errors_get_name_for_major_code( x11->errors_context, ev->major_opcode); if (!major_name) { wlr_log(WLR_DEBUG, "X11 error happened, but could not get major name"); goto log_raw; } const char *minor_name = xcb_errors_get_name_for_minor_code( x11->errors_context, ev->major_opcode, ev->minor_opcode); const char *extension; const char *error_name = xcb_errors_get_name_for_error(x11->errors_context, ev->error_code, &extension); if (!error_name) { wlr_log(WLR_DEBUG, "X11 error happened, but could not get error name"); goto log_raw; } wlr_log(WLR_ERROR, "X11 error: op %s (%s), code %s (%s), " "sequence %"PRIu16", value %"PRIu32, major_name, minor_name ? minor_name : "no minor", error_name, extension ? extension : "no extension", ev->sequence, ev->bad_value); return; log_raw: #endif wlr_log(WLR_ERROR, "X11 error: op %"PRIu8":%"PRIu16", code %"PRIu8", " "sequence %"PRIu16", value %"PRIu32, ev->major_opcode, ev->minor_opcode, ev->error_code, ev->sequence, ev->bad_value); } static void handle_x11_unknown_event(struct wlr_x11_backend *x11, xcb_generic_event_t *ev) { #if HAVE_XCB_ERRORS const char *extension; const char *event_name = xcb_errors_get_name_for_xcb_event( x11->errors_context, ev, &extension); if (!event_name) { wlr_log(WLR_DEBUG, "No name for unhandled event: %u", ev->response_type); return; } wlr_log(WLR_DEBUG, "Unhandled X11 event: %s (%u)", event_name, ev->response_type); #else wlr_log(WLR_DEBUG, "Unhandled X11 event: %u", ev->response_type); #endif } wlroots-0.17.1/backend/x11/input_device.c000066400000000000000000000216421454110342200201340ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include "backend/x11.h" static void send_key_event(struct wlr_x11_backend *x11, uint32_t key, enum wl_keyboard_key_state st, xcb_timestamp_t time) { struct wlr_keyboard_key_event ev = { .time_msec = time, .keycode = key, .state = st, .update_state = true, }; wlr_keyboard_notify_key(&x11->keyboard, &ev); } static void send_button_event(struct wlr_x11_output *output, uint32_t key, enum wlr_button_state st, xcb_timestamp_t time) { struct wlr_pointer_button_event ev = { .pointer = &output->pointer, .time_msec = time, .button = key, .state = st, }; wl_signal_emit_mutable(&output->pointer.events.button, &ev); wl_signal_emit_mutable(&output->pointer.events.frame, &output->pointer); } static void send_axis_event(struct wlr_x11_output *output, int32_t delta, xcb_timestamp_t time) { struct wlr_pointer_axis_event ev = { .pointer = &output->pointer, .time_msec = time, .source = WLR_AXIS_SOURCE_WHEEL, .orientation = WLR_AXIS_ORIENTATION_VERTICAL, // Most mice use a 15 degree angle per scroll click .delta = delta * 15, .delta_discrete = delta * WLR_POINTER_AXIS_DISCRETE_STEP, }; wl_signal_emit_mutable(&output->pointer.events.axis, &ev); wl_signal_emit_mutable(&output->pointer.events.frame, &output->pointer); } static void send_pointer_position_event(struct wlr_x11_output *output, int16_t x, int16_t y, xcb_timestamp_t time) { struct wlr_pointer_motion_absolute_event ev = { .pointer = &output->pointer, .time_msec = time, .x = (double)x / output->wlr_output.width, .y = (double)y / output->wlr_output.height, }; wl_signal_emit_mutable(&output->pointer.events.motion_absolute, &ev); wl_signal_emit_mutable(&output->pointer.events.frame, &output->pointer); } static void send_touch_down_event(struct wlr_x11_output *output, int16_t x, int16_t y, int32_t touch_id, xcb_timestamp_t time) { struct wlr_touch_down_event ev = { .touch = &output->touch, .time_msec = time, .x = (double)x / output->wlr_output.width, .y = (double)y / output->wlr_output.height, .touch_id = touch_id, }; wl_signal_emit_mutable(&output->touch.events.down, &ev); wl_signal_emit_mutable(&output->touch.events.frame, NULL); } static void send_touch_motion_event(struct wlr_x11_output *output, int16_t x, int16_t y, int32_t touch_id, xcb_timestamp_t time) { struct wlr_touch_motion_event ev = { .touch = &output->touch, .time_msec = time, .x = (double)x / output->wlr_output.width, .y = (double)y / output->wlr_output.height, .touch_id = touch_id, }; wl_signal_emit_mutable(&output->touch.events.motion, &ev); wl_signal_emit_mutable(&output->touch.events.frame, NULL); } static void send_touch_up_event(struct wlr_x11_output *output, int32_t touch_id, xcb_timestamp_t time) { struct wlr_touch_up_event ev = { .touch = &output->touch, .time_msec = time, .touch_id = touch_id, }; wl_signal_emit_mutable(&output->touch.events.up, &ev); wl_signal_emit_mutable(&output->touch.events.frame, NULL); } static struct wlr_x11_touchpoint *get_touchpoint_from_x11_touch_id( struct wlr_x11_output *output, uint32_t id) { struct wlr_x11_touchpoint *touchpoint; wl_list_for_each(touchpoint, &output->touchpoints, link) { if (touchpoint->x11_id == id) { return touchpoint; } } return NULL; } void handle_x11_xinput_event(struct wlr_x11_backend *x11, xcb_ge_generic_event_t *event) { struct wlr_x11_output *output; switch (event->event_type) { case XCB_INPUT_KEY_PRESS: { xcb_input_key_press_event_t *ev = (xcb_input_key_press_event_t *)event; if (ev->flags & XCB_INPUT_KEY_EVENT_FLAGS_KEY_REPEAT) { return; } wlr_keyboard_notify_modifiers(&x11->keyboard, ev->mods.base, ev->mods.latched, ev->mods.locked, ev->mods.effective); send_key_event(x11, ev->detail - 8, WL_KEYBOARD_KEY_STATE_PRESSED, ev->time); x11->time = ev->time; break; } case XCB_INPUT_KEY_RELEASE: { xcb_input_key_release_event_t *ev = (xcb_input_key_release_event_t *)event; wlr_keyboard_notify_modifiers(&x11->keyboard, ev->mods.base, ev->mods.latched, ev->mods.locked, ev->mods.effective); send_key_event(x11, ev->detail - 8, WL_KEYBOARD_KEY_STATE_RELEASED, ev->time); x11->time = ev->time; break; } case XCB_INPUT_BUTTON_PRESS: { xcb_input_button_press_event_t *ev = (xcb_input_button_press_event_t *)event; output = get_x11_output_from_window_id(x11, ev->event); if (!output) { return; } switch (ev->detail) { case XCB_BUTTON_INDEX_1: send_button_event(output, BTN_LEFT, WLR_BUTTON_PRESSED, ev->time); break; case XCB_BUTTON_INDEX_2: send_button_event(output, BTN_MIDDLE, WLR_BUTTON_PRESSED, ev->time); break; case XCB_BUTTON_INDEX_3: send_button_event(output, BTN_RIGHT, WLR_BUTTON_PRESSED, ev->time); break; case XCB_BUTTON_INDEX_4: send_axis_event(output, -1, ev->time); break; case XCB_BUTTON_INDEX_5: send_axis_event(output, 1, ev->time); break; } x11->time = ev->time; break; } case XCB_INPUT_BUTTON_RELEASE: { xcb_input_button_release_event_t *ev = (xcb_input_button_release_event_t *)event; output = get_x11_output_from_window_id(x11, ev->event); if (!output) { return; } switch (ev->detail) { case XCB_BUTTON_INDEX_1: send_button_event(output, BTN_LEFT, WLR_BUTTON_RELEASED, ev->time); break; case XCB_BUTTON_INDEX_2: send_button_event(output, BTN_MIDDLE, WLR_BUTTON_RELEASED, ev->time); break; case XCB_BUTTON_INDEX_3: send_button_event(output, BTN_RIGHT, WLR_BUTTON_RELEASED, ev->time); break; } x11->time = ev->time; break; } case XCB_INPUT_MOTION: { xcb_input_motion_event_t *ev = (xcb_input_motion_event_t *)event; output = get_x11_output_from_window_id(x11, ev->event); if (!output) { return; } send_pointer_position_event(output, ev->event_x >> 16, ev->event_y >> 16, ev->time); x11->time = ev->time; break; } case XCB_INPUT_TOUCH_BEGIN: { xcb_input_touch_begin_event_t *ev = (xcb_input_touch_begin_event_t *)event; output = get_x11_output_from_window_id(x11, ev->event); if (!output) { return; } int32_t id = 0; if (!wl_list_empty(&output->touchpoints)) { struct wlr_x11_touchpoint *last_touchpoint = wl_container_of( output->touchpoints.next, last_touchpoint, link); id = last_touchpoint->wayland_id + 1; } struct wlr_x11_touchpoint *touchpoint = calloc(1, sizeof(*touchpoint)); touchpoint->x11_id = ev->detail; touchpoint->wayland_id = id; wl_list_init(&touchpoint->link); wl_list_insert(&output->touchpoints, &touchpoint->link); send_touch_down_event(output, ev->event_x >> 16, ev->event_y >> 16, touchpoint->wayland_id, ev->time); x11->time = ev->time; break; } case XCB_INPUT_TOUCH_END: { xcb_input_touch_end_event_t *ev = (xcb_input_touch_end_event_t *)event; output = get_x11_output_from_window_id(x11, ev->event); if (!output) { return; } struct wlr_x11_touchpoint *touchpoint = get_touchpoint_from_x11_touch_id(output, ev->detail); if (!touchpoint) { return; } send_touch_up_event(output, touchpoint->wayland_id, ev->time); x11->time = ev->time; wl_list_remove(&touchpoint->link); free(touchpoint); break; } case XCB_INPUT_TOUCH_UPDATE: { xcb_input_touch_update_event_t *ev = (xcb_input_touch_update_event_t *)event; output = get_x11_output_from_window_id(x11, ev->event); if (!output) { return; } struct wlr_x11_touchpoint *touchpoint = get_touchpoint_from_x11_touch_id(output, ev->detail); if (!touchpoint) { return; } send_touch_motion_event(output, ev->event_x >> 16, ev->event_y >> 16, touchpoint->wayland_id, ev->time); x11->time = ev->time; break; } } } const struct wlr_keyboard_impl x11_keyboard_impl = { .name = "x11-keyboard", }; const struct wlr_pointer_impl x11_pointer_impl = { .name = "x11-pointer", }; const struct wlr_touch_impl x11_touch_impl = { .name = "x11-touch", }; void update_x11_pointer_position(struct wlr_x11_output *output, xcb_timestamp_t time) { struct wlr_x11_backend *x11 = output->x11; xcb_query_pointer_cookie_t cookie = xcb_query_pointer(x11->xcb, output->win); xcb_query_pointer_reply_t *reply = xcb_query_pointer_reply(x11->xcb, cookie, NULL); if (!reply) { return; } send_pointer_position_event(output, reply->win_x, reply->win_y, time); free(reply); } bool wlr_input_device_is_x11(struct wlr_input_device *wlr_dev) { switch (wlr_dev->type) { case WLR_INPUT_DEVICE_KEYBOARD: return wlr_keyboard_from_input_device(wlr_dev)->impl == &x11_keyboard_impl; case WLR_INPUT_DEVICE_POINTER: return wlr_pointer_from_input_device(wlr_dev)->impl == &x11_pointer_impl; case WLR_INPUT_DEVICE_TOUCH: return wlr_touch_from_input_device(wlr_dev)->impl == &x11_touch_impl; default: return false; } } wlroots-0.17.1/backend/x11/meson.build000066400000000000000000000011321454110342200174440ustar00rootroot00000000000000x11_libs = [] x11_required = [ 'xcb', 'xcb-dri3', 'xcb-present', 'xcb-render', 'xcb-renderutil', 'xcb-shm', 'xcb-xfixes', 'xcb-xinput', ] msg = ['Required for X11 backend support.'] if 'x11' in backends msg += 'Install "@0@" or disable the X11 backend.' endif foreach lib : x11_required dep = dependency(lib, required: 'x11' in backends, not_found_message: '\n'.join(msg).format(lib), ) if not dep.found() subdir_done() endif x11_libs += dep endforeach wlr_files += files( 'backend.c', 'input_device.c', 'output.c', ) wlr_deps += x11_libs features += { 'x11-backend': true } wlroots-0.17.1/backend/x11/output.c000066400000000000000000000540761454110342200170250ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "backend/x11.h" #include "util/time.h" #include "types/wlr_output.h" static const uint32_t SUPPORTED_OUTPUT_STATE = WLR_OUTPUT_STATE_BACKEND_OPTIONAL | WLR_OUTPUT_STATE_BUFFER | WLR_OUTPUT_STATE_ENABLED | WLR_OUTPUT_STATE_MODE | WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED; static size_t last_output_num = 0; static void parse_xcb_setup(struct wlr_output *output, xcb_connection_t *xcb) { const xcb_setup_t *xcb_setup = xcb_get_setup(xcb); output->make = calloc(1, xcb_setup_vendor_length(xcb_setup) + 1); if (output->make == NULL) { wlr_log_errno(WLR_ERROR, "Allocation failed"); return; } memcpy(output->make, xcb_setup_vendor(xcb_setup), xcb_setup_vendor_length(xcb_setup)); char model[64]; snprintf(model, sizeof(model), "%"PRIu16".%"PRIu16, xcb_setup->protocol_major_version, xcb_setup->protocol_minor_version); output->model = strdup(model); } static struct wlr_x11_output *get_x11_output_from_output( struct wlr_output *wlr_output) { assert(wlr_output_is_x11(wlr_output)); struct wlr_x11_output *output = wl_container_of(wlr_output, output, wlr_output); return output; } static bool output_set_custom_mode(struct wlr_output *wlr_output, int32_t width, int32_t height, int32_t refresh) { struct wlr_x11_output *output = get_x11_output_from_output(wlr_output); struct wlr_x11_backend *x11 = output->x11; if (width == output->win_width && height == output->win_height) { return true; } const uint32_t values[] = { width, height }; xcb_void_cookie_t cookie = xcb_configure_window_checked( x11->xcb, output->win, XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT, values); xcb_generic_error_t *error; if ((error = xcb_request_check(x11->xcb, cookie))) { wlr_log(WLR_ERROR, "Could not set window size to %dx%d\n", width, height); free(error); return false; } output->win_width = width; output->win_height = height; // Move the pointer to its new location update_x11_pointer_position(output, output->x11->time); return true; } static void destroy_x11_buffer(struct wlr_x11_buffer *buffer); static void output_destroy(struct wlr_output *wlr_output) { struct wlr_x11_output *output = get_x11_output_from_output(wlr_output); struct wlr_x11_backend *x11 = output->x11; pixman_region32_fini(&output->exposed); wlr_pointer_finish(&output->pointer); wlr_touch_finish(&output->touch); struct wlr_x11_buffer *buffer, *buffer_tmp; wl_list_for_each_safe(buffer, buffer_tmp, &output->buffers, link) { destroy_x11_buffer(buffer); } wl_list_remove(&output->link); if (output->cursor.pic != XCB_NONE) { xcb_render_free_picture(x11->xcb, output->cursor.pic); } // A zero event mask deletes the event context xcb_present_select_input(x11->xcb, output->present_event_id, output->win, 0); xcb_destroy_window(x11->xcb, output->win); xcb_flush(x11->xcb); free(output); } static bool output_test(struct wlr_output *wlr_output, const struct wlr_output_state *state) { struct wlr_x11_output *output = get_x11_output_from_output(wlr_output); struct wlr_x11_backend *x11 = output->x11; uint32_t unsupported = state->committed & ~SUPPORTED_OUTPUT_STATE; if (unsupported != 0) { wlr_log(WLR_DEBUG, "Unsupported output state fields: 0x%"PRIx32, unsupported); return false; } // All we can do to influence adaptive sync on the X11 backend is set the // _VARIABLE_REFRESH window property like mesa automatically does. We don't // have any control beyond that, so we set the state to enabled on creating // the output and never allow changing it (just like the Wayland backend). assert(wlr_output->adaptive_sync_status == WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED); if (state->committed & WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED) { if (!state->adaptive_sync_enabled) { wlr_log(WLR_DEBUG, "Disabling adaptive sync is not supported"); return false; } } if (state->committed & WLR_OUTPUT_STATE_BUFFER) { struct wlr_buffer *buffer = state->buffer; struct wlr_dmabuf_attributes dmabuf_attrs; struct wlr_shm_attributes shm_attrs; uint32_t format = DRM_FORMAT_INVALID; if (wlr_buffer_get_dmabuf(buffer, &dmabuf_attrs)) { format = dmabuf_attrs.format; } else if (wlr_buffer_get_shm(buffer, &shm_attrs)) { format = shm_attrs.format; } if (format != x11->x11_format->drm) { wlr_log(WLR_DEBUG, "Unsupported buffer format"); return false; } } if (state->committed & WLR_OUTPUT_STATE_MODE) { assert(state->mode_type == WLR_OUTPUT_STATE_MODE_CUSTOM); if (state->custom_mode.refresh != 0) { wlr_log(WLR_DEBUG, "Refresh rates are not supported"); return false; } } return true; } static void destroy_x11_buffer(struct wlr_x11_buffer *buffer) { if (!buffer) { return; } wl_list_remove(&buffer->buffer_destroy.link); wl_list_remove(&buffer->link); xcb_free_pixmap(buffer->x11->xcb, buffer->pixmap); for (size_t i = 0; i < buffer->n_busy; i++) { wlr_buffer_unlock(buffer->buffer); } free(buffer); } static void buffer_handle_buffer_destroy(struct wl_listener *listener, void *data) { struct wlr_x11_buffer *buffer = wl_container_of(listener, buffer, buffer_destroy); destroy_x11_buffer(buffer); } static xcb_pixmap_t import_dmabuf(struct wlr_x11_output *output, struct wlr_dmabuf_attributes *dmabuf) { struct wlr_x11_backend *x11 = output->x11; if (dmabuf->format != x11->x11_format->drm) { // The pixmap's depth must match the window's depth, otherwise Present // will throw a Match error return XCB_PIXMAP_NONE; } // xcb closes the FDs after sending them, so we need to dup them here struct wlr_dmabuf_attributes dup_attrs = {0}; if (!wlr_dmabuf_attributes_copy(&dup_attrs, dmabuf)) { return XCB_PIXMAP_NONE; } const struct wlr_x11_format *x11_fmt = x11->x11_format; xcb_pixmap_t pixmap = xcb_generate_id(x11->xcb); if (x11->dri3_major_version > 1 || x11->dri3_minor_version >= 2) { if (dmabuf->n_planes > 4) { wlr_dmabuf_attributes_finish(&dup_attrs); return XCB_PIXMAP_NONE; } xcb_dri3_pixmap_from_buffers(x11->xcb, pixmap, output->win, dmabuf->n_planes, dmabuf->width, dmabuf->height, dmabuf->stride[0], dmabuf->offset[0], dmabuf->stride[1], dmabuf->offset[1], dmabuf->stride[2], dmabuf->offset[2], dmabuf->stride[3], dmabuf->offset[3], x11_fmt->depth, x11_fmt->bpp, dmabuf->modifier, dup_attrs.fd); } else { // PixmapFromBuffers requires DRI3 1.2 if (dmabuf->n_planes != 1 || dmabuf->modifier != DRM_FORMAT_MOD_INVALID) { wlr_dmabuf_attributes_finish(&dup_attrs); return XCB_PIXMAP_NONE; } xcb_dri3_pixmap_from_buffer(x11->xcb, pixmap, output->win, dmabuf->height * dmabuf->stride[0], dmabuf->width, dmabuf->height, dmabuf->stride[0], x11_fmt->depth, x11_fmt->bpp, dup_attrs.fd[0]); } return pixmap; } static xcb_pixmap_t import_shm(struct wlr_x11_output *output, struct wlr_shm_attributes *shm) { struct wlr_x11_backend *x11 = output->x11; if (shm->format != x11->x11_format->drm) { // The pixmap's depth must match the window's depth, otherwise Present // will throw a Match error return XCB_PIXMAP_NONE; } // xcb closes the FD after sending it int fd = fcntl(shm->fd, F_DUPFD_CLOEXEC, 0); if (fd < 0) { wlr_log_errno(WLR_ERROR, "fcntl(F_DUPFD_CLOEXEC) failed"); return XCB_PIXMAP_NONE; } xcb_shm_seg_t seg = xcb_generate_id(x11->xcb); xcb_shm_attach_fd(x11->xcb, seg, fd, false); xcb_pixmap_t pixmap = xcb_generate_id(x11->xcb); xcb_shm_create_pixmap(x11->xcb, pixmap, output->win, shm->width, shm->height, x11->x11_format->depth, seg, shm->offset); xcb_shm_detach(x11->xcb, seg); return pixmap; } static struct wlr_x11_buffer *create_x11_buffer(struct wlr_x11_output *output, struct wlr_buffer *wlr_buffer) { struct wlr_x11_backend *x11 = output->x11; xcb_pixmap_t pixmap = XCB_PIXMAP_NONE; struct wlr_dmabuf_attributes dmabuf_attrs; struct wlr_shm_attributes shm_attrs; if (wlr_buffer_get_dmabuf(wlr_buffer, &dmabuf_attrs)) { pixmap = import_dmabuf(output, &dmabuf_attrs); } else if (wlr_buffer_get_shm(wlr_buffer, &shm_attrs)) { pixmap = import_shm(output, &shm_attrs); } if (pixmap == XCB_PIXMAP_NONE) { return NULL; } struct wlr_x11_buffer *buffer = calloc(1, sizeof(*buffer)); if (!buffer) { xcb_free_pixmap(x11->xcb, pixmap); return NULL; } buffer->buffer = wlr_buffer_lock(wlr_buffer); buffer->n_busy = 1; buffer->pixmap = pixmap; buffer->x11 = x11; wl_list_insert(&output->buffers, &buffer->link); buffer->buffer_destroy.notify = buffer_handle_buffer_destroy; wl_signal_add(&wlr_buffer->events.destroy, &buffer->buffer_destroy); return buffer; } static struct wlr_x11_buffer *get_or_create_x11_buffer( struct wlr_x11_output *output, struct wlr_buffer *wlr_buffer) { struct wlr_x11_buffer *buffer; wl_list_for_each(buffer, &output->buffers, link) { if (buffer->buffer == wlr_buffer) { wlr_buffer_lock(buffer->buffer); buffer->n_busy++; return buffer; } } return create_x11_buffer(output, wlr_buffer); } static bool output_commit_buffer(struct wlr_x11_output *output, const struct wlr_output_state *state) { struct wlr_x11_backend *x11 = output->x11; struct wlr_buffer *buffer = state->buffer; struct wlr_x11_buffer *x11_buffer = get_or_create_x11_buffer(output, buffer); if (!x11_buffer) { goto error; } xcb_xfixes_region_t region = XCB_NONE; if (state->committed & WLR_OUTPUT_STATE_DAMAGE) { pixman_region32_union(&output->exposed, &output->exposed, &state->damage); int rects_len = 0; const pixman_box32_t *rects = pixman_region32_rectangles(&output->exposed, &rects_len); xcb_rectangle_t *xcb_rects = calloc(rects_len, sizeof(xcb_rectangle_t)); if (!xcb_rects) { goto error; } for (int i = 0; i < rects_len; i++) { const pixman_box32_t *box = &rects[i]; xcb_rects[i] = (struct xcb_rectangle_t){ .x = box->x1, .y = box->y1, .width = box->x2 - box->x1, .height = box->y2 - box->y1, }; } region = xcb_generate_id(x11->xcb); xcb_xfixes_create_region(x11->xcb, region, rects_len, xcb_rects); free(xcb_rects); } pixman_region32_clear(&output->exposed); uint32_t serial = output->wlr_output.commit_seq; uint32_t options = 0; uint64_t target_msc = output->last_msc ? output->last_msc + 1 : 0; xcb_present_pixmap(x11->xcb, output->win, x11_buffer->pixmap, serial, 0, region, 0, 0, XCB_NONE, XCB_NONE, XCB_NONE, options, target_msc, 0, 0, 0, NULL); if (region != XCB_NONE) { xcb_xfixes_destroy_region(x11->xcb, region); } return true; error: destroy_x11_buffer(x11_buffer); return false; } static bool output_commit(struct wlr_output *wlr_output, const struct wlr_output_state *state) { struct wlr_x11_output *output = get_x11_output_from_output(wlr_output); struct wlr_x11_backend *x11 = output->x11; if (!output_test(wlr_output, state)) { return false; } if (state->committed & WLR_OUTPUT_STATE_ENABLED) { if (state->enabled) { xcb_map_window(x11->xcb, output->win); } else { xcb_unmap_window(x11->xcb, output->win); } } if (state->committed & WLR_OUTPUT_STATE_MODE) { if (!output_set_custom_mode(wlr_output, state->custom_mode.width, state->custom_mode.height, state->custom_mode.refresh)) { return false; } } if (state->committed & WLR_OUTPUT_STATE_BUFFER) { if (!output_commit_buffer(output, state)) { return false; } } else if (output_pending_enabled(wlr_output, state)) { uint32_t serial = output->wlr_output.commit_seq; uint64_t target_msc = output->last_msc ? output->last_msc + 1 : 0; xcb_present_notify_msc(x11->xcb, output->win, serial, target_msc, 0, 0); } xcb_flush(x11->xcb); return true; } static void update_x11_output_cursor(struct wlr_x11_output *output, int32_t hotspot_x, int32_t hotspot_y) { struct wlr_x11_backend *x11 = output->x11; xcb_cursor_t cursor = x11->transparent_cursor; if (output->cursor.pic != XCB_NONE) { cursor = xcb_generate_id(x11->xcb); xcb_render_create_cursor(x11->xcb, cursor, output->cursor.pic, hotspot_x, hotspot_y); } uint32_t values[] = {cursor}; xcb_change_window_attributes(x11->xcb, output->win, XCB_CW_CURSOR, values); xcb_flush(x11->xcb); if (cursor != x11->transparent_cursor) { xcb_free_cursor(x11->xcb, cursor); } } static bool output_cursor_to_picture(struct wlr_x11_output *output, struct wlr_buffer *buffer) { struct wlr_x11_backend *x11 = output->x11; struct wlr_renderer *renderer = output->wlr_output.renderer; if (output->cursor.pic != XCB_NONE) { xcb_render_free_picture(x11->xcb, output->cursor.pic); } output->cursor.pic = XCB_NONE; if (buffer == NULL) { return true; } int depth = 32; int stride = buffer->width * 4; uint8_t *data = malloc(buffer->height * stride); if (data == NULL) { return false; } if (!wlr_renderer_begin_with_buffer(renderer, buffer)) { free(data); return false; } bool result = wlr_renderer_read_pixels( renderer, DRM_FORMAT_ARGB8888, stride, buffer->width, buffer->height, 0, 0, 0, 0, data); wlr_renderer_end(renderer); if (!result) { free(data); return false; } xcb_pixmap_t pix = xcb_generate_id(x11->xcb); xcb_create_pixmap(x11->xcb, depth, pix, output->win, buffer->width, buffer->height); output->cursor.pic = xcb_generate_id(x11->xcb); xcb_render_create_picture(x11->xcb, output->cursor.pic, pix, x11->argb32, 0, 0); xcb_gcontext_t gc = xcb_generate_id(x11->xcb); xcb_create_gc(x11->xcb, gc, pix, 0, NULL); xcb_put_image(x11->xcb, XCB_IMAGE_FORMAT_Z_PIXMAP, pix, gc, buffer->width, buffer->height, 0, 0, 0, depth, stride * buffer->height * sizeof(uint8_t), data); free(data); xcb_free_gc(x11->xcb, gc); xcb_free_pixmap(x11->xcb, pix); return true; } static bool output_set_cursor(struct wlr_output *wlr_output, struct wlr_buffer *buffer, int32_t hotspot_x, int32_t hotspot_y) { struct wlr_x11_output *output = get_x11_output_from_output(wlr_output); struct wlr_x11_backend *x11 = output->x11; if (x11->argb32 == XCB_NONE) { return false; } if (buffer != NULL) { if (hotspot_x < 0) { hotspot_x = 0; } if (hotspot_x > buffer->width) { hotspot_x = buffer->width; } if (hotspot_y < 0) { hotspot_y = 0; } if (hotspot_y > buffer->height) { hotspot_y = buffer->height; } } bool success = output_cursor_to_picture(output, buffer); update_x11_output_cursor(output, hotspot_x, hotspot_y); return success; } static bool output_move_cursor(struct wlr_output *_output, int x, int y) { // TODO: only return true if x == current x and y == current y return true; } static const struct wlr_drm_format_set *output_get_primary_formats( struct wlr_output *wlr_output, uint32_t buffer_caps) { struct wlr_x11_output *output = get_x11_output_from_output(wlr_output); struct wlr_x11_backend *x11 = output->x11; if (x11->have_dri3 && (buffer_caps & WLR_BUFFER_CAP_DMABUF)) { return &output->x11->primary_dri3_formats; } else if (x11->have_shm && (buffer_caps & WLR_BUFFER_CAP_SHM)) { return &output->x11->primary_shm_formats; } return NULL; } static const struct wlr_output_impl output_impl = { .destroy = output_destroy, .test = output_test, .commit = output_commit, .set_cursor = output_set_cursor, .move_cursor = output_move_cursor, .get_primary_formats = output_get_primary_formats, }; struct wlr_output *wlr_x11_output_create(struct wlr_backend *backend) { struct wlr_x11_backend *x11 = get_x11_backend_from_backend(backend); if (!x11->started) { ++x11->requested_outputs; return NULL; } struct wlr_x11_output *output = calloc(1, sizeof(*output)); if (output == NULL) { return NULL; } output->x11 = x11; wl_list_init(&output->buffers); pixman_region32_init(&output->exposed); struct wlr_output *wlr_output = &output->wlr_output; struct wlr_output_state state; wlr_output_state_init(&state); wlr_output_state_set_custom_mode(&state, 1024, 768, 0); wlr_output_init(wlr_output, &x11->backend, &output_impl, x11->wl_display, &state); wlr_output_state_finish(&state); size_t output_num = ++last_output_num; char name[64]; snprintf(name, sizeof(name), "X11-%zu", output_num); wlr_output_set_name(wlr_output, name); parse_xcb_setup(wlr_output, x11->xcb); char description[128]; snprintf(description, sizeof(description), "X11 output %zu", output_num); wlr_output_set_description(wlr_output, description); // The X11 protocol requires us to set a colormap and border pixel if the // depth doesn't match the root window's uint32_t mask = XCB_CW_BORDER_PIXEL | XCB_CW_EVENT_MASK | XCB_CW_COLORMAP | XCB_CW_CURSOR; uint32_t values[] = { 0, XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_STRUCTURE_NOTIFY, x11->colormap, x11->transparent_cursor, }; output->win = xcb_generate_id(x11->xcb); xcb_create_window(x11->xcb, x11->depth->depth, output->win, x11->screen->root, 0, 0, wlr_output->width, wlr_output->height, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, x11->visualid, mask, values); output->win_width = wlr_output->width; output->win_height = wlr_output->height; struct { xcb_input_event_mask_t head; xcb_input_xi_event_mask_t mask; } xinput_mask = { .head = { .deviceid = XCB_INPUT_DEVICE_ALL_MASTER, .mask_len = 1 }, .mask = XCB_INPUT_XI_EVENT_MASK_KEY_PRESS | XCB_INPUT_XI_EVENT_MASK_KEY_RELEASE | XCB_INPUT_XI_EVENT_MASK_BUTTON_PRESS | XCB_INPUT_XI_EVENT_MASK_BUTTON_RELEASE | XCB_INPUT_XI_EVENT_MASK_MOTION | XCB_INPUT_XI_EVENT_MASK_TOUCH_BEGIN | XCB_INPUT_XI_EVENT_MASK_TOUCH_END | XCB_INPUT_XI_EVENT_MASK_TOUCH_UPDATE, }; xcb_input_xi_select_events(x11->xcb, output->win, 1, &xinput_mask.head); uint32_t present_mask = XCB_PRESENT_EVENT_MASK_IDLE_NOTIFY | XCB_PRESENT_EVENT_MASK_COMPLETE_NOTIFY; output->present_event_id = xcb_generate_id(x11->xcb); xcb_present_select_input(x11->xcb, output->present_event_id, output->win, present_mask); xcb_change_property(x11->xcb, XCB_PROP_MODE_REPLACE, output->win, x11->atoms.wm_protocols, XCB_ATOM_ATOM, 32, 1, &x11->atoms.wm_delete_window); uint32_t enabled = 1; xcb_change_property(x11->xcb, XCB_PROP_MODE_REPLACE, output->win, x11->atoms.variable_refresh, XCB_ATOM_CARDINAL, 32, 1, &enabled); wlr_output->adaptive_sync_status = WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED; wlr_x11_output_set_title(wlr_output, NULL); xcb_flush(x11->xcb); wl_list_insert(&x11->outputs, &output->link); wlr_pointer_init(&output->pointer, &x11_pointer_impl, "x11-pointer"); output->pointer.output_name = strdup(wlr_output->name); wlr_touch_init(&output->touch, &x11_touch_impl, "x11-touch"); output->touch.output_name = strdup(wlr_output->name); wl_list_init(&output->touchpoints); wl_signal_emit_mutable(&x11->backend.events.new_output, wlr_output); wl_signal_emit_mutable(&x11->backend.events.new_input, &output->pointer.base); wl_signal_emit_mutable(&x11->backend.events.new_input, &output->touch.base); return wlr_output; } void handle_x11_configure_notify(struct wlr_x11_output *output, xcb_configure_notify_event_t *ev) { if (ev->width == 0 || ev->height == 0) { wlr_log(WLR_DEBUG, "Ignoring X11 configure event for height=%d, width=%d", ev->width, ev->height); return; } output->win_width = ev->width; output->win_height = ev->height; struct wlr_output_state state; wlr_output_state_init(&state); wlr_output_state_set_custom_mode(&state, ev->width, ev->height, 0); wlr_output_send_request_state(&output->wlr_output, &state); wlr_output_state_finish(&state); } bool wlr_output_is_x11(struct wlr_output *wlr_output) { return wlr_output->impl == &output_impl; } void wlr_x11_output_set_title(struct wlr_output *output, const char *title) { struct wlr_x11_output *x11_output = get_x11_output_from_output(output); char wl_title[32]; if (title == NULL) { if (snprintf(wl_title, sizeof(wl_title), "wlroots - %s", output->name) <= 0) { return; } title = wl_title; } xcb_change_property(x11_output->x11->xcb, XCB_PROP_MODE_REPLACE, x11_output->win, x11_output->x11->atoms.net_wm_name, x11_output->x11->atoms.utf8_string, 8, strlen(title), title); } static struct wlr_x11_buffer *get_x11_buffer(struct wlr_x11_output *output, xcb_pixmap_t pixmap) { struct wlr_x11_buffer *buffer; wl_list_for_each(buffer, &output->buffers, link) { if (buffer->pixmap == pixmap) { return buffer; } } return NULL; } void handle_x11_present_event(struct wlr_x11_backend *x11, xcb_ge_generic_event_t *event) { struct wlr_x11_output *output; switch (event->event_type) { case XCB_PRESENT_EVENT_IDLE_NOTIFY:; xcb_present_idle_notify_event_t *idle_notify = (xcb_present_idle_notify_event_t *)event; output = get_x11_output_from_window_id(x11, idle_notify->window); if (!output) { wlr_log(WLR_DEBUG, "Got PresentIdleNotify event for unknown window"); return; } struct wlr_x11_buffer *buffer = get_x11_buffer(output, idle_notify->pixmap); if (!buffer) { wlr_log(WLR_DEBUG, "Got PresentIdleNotify event for unknown buffer"); return; } assert(buffer->n_busy > 0); buffer->n_busy--; wlr_buffer_unlock(buffer->buffer); // may destroy buffer break; case XCB_PRESENT_COMPLETE_NOTIFY:; xcb_present_complete_notify_event_t *complete_notify = (xcb_present_complete_notify_event_t *)event; output = get_x11_output_from_window_id(x11, complete_notify->window); if (!output) { wlr_log(WLR_DEBUG, "Got PresentCompleteNotify event for unknown window"); return; } output->last_msc = complete_notify->msc; struct timespec t; timespec_from_nsec(&t, complete_notify->ust * 1000); uint32_t flags = 0; if (complete_notify->mode == XCB_PRESENT_COMPLETE_MODE_FLIP) { flags |= WLR_OUTPUT_PRESENT_ZERO_COPY; } bool presented = complete_notify->mode != XCB_PRESENT_COMPLETE_MODE_SKIP; struct wlr_output_event_present present_event = { .output = &output->wlr_output, .commit_seq = complete_notify->serial, .presented = presented, .when = &t, .seq = complete_notify->msc, .flags = flags, }; wlr_output_send_present(&output->wlr_output, &present_event); wlr_output_send_frame(&output->wlr_output); break; default: wlr_log(WLR_DEBUG, "Unhandled Present event %"PRIu16, event->event_type); } } wlroots-0.17.1/docs/000077500000000000000000000000001454110342200142355ustar00rootroot00000000000000wlroots-0.17.1/docs/architecture.md000066400000000000000000000070531454110342200172460ustar00rootroot00000000000000# Architecture This document describes the high-level design of wlroots. wlroots is modular: each module can be used mostly independently from the rest of wlroots. For instance, some wlroots-based compositors only use its backends, some only use its protocol implementations. ## Backends Backends are responsible for exposing input devices and output devices. wlroots provides DRM and libinput backends to directly drive physical devices, Wayland and X11 backends to run nested inside another compositor, and a headless backend. A special "multi" backend is used to combine together multiple backends, for instance DRM and libinput. Compositors can also implement their own custom backends if they have special needs. Input devices such as pointers, keyboards, touch screens, tablets, switches are supported. They emit input events (e.g. a keyboard key is pressed) which compositors can handle and forward to Wayland clients. Output devices are tasked with presenting buffers to the user. They also provide feedback, for instance presentation timestamps. Some backends support more advanced functionality, such as displaying multiple buffers (e.g. for the cursor image) or basic 2D transformations (e.g. rotation, clipping, scaling). ## Renderers Renderers provide utilities to import buffers submitted by Wayland clients, and a basic 2D drawing API suitable for simple compositors. wlroots provides renderer implementations based on OpenGL ES 2, Vulkan and Pixman. Just like backends, compositors can implement their own renderers, or use the graphics APIs directly. To draw an image onto a buffer, compositors will first need to create a texture, representing a source of pixels the renderer can sample from. This can be done either by uploading pixels from CPU memory, or by importing already existing GPU memory via DMA-BUFs. Compositors can then create a render pass and submit drawing operations. Once they are done drawing, compositors can submit the rendered buffer to an output. ## Protocol implementations A number of Wayland interface implementations are provided. ### Plumbing protocols wlroots ships unopinionated implementations of core plumbing interfaces, for instance: - `wl_compositor` and `wl_surface` - `wl_seat` and all input-related interfaces - Buffer factories such as `wl_shm` and linux-dmabuf - Additional protocols such as viewporter and presentation-time ### Shells Shells give a meaning to surfaces. There are many kinds of surfaces: application windows, tooltips, right-click menus, desktop panels, wallpapers, lock screens, on-screen keyboards, and so on. Each of these use-cases is fulfilled with a shell. wlroots supports xdg-shell for regular windows and popups, Xwayland for interoperability with X11 applications, layer-shell for desktop UI elements, and more. ### Other protocols Many other protocol implementations are included, for instance: - xdg-activation for raising application windows - idle-inhibit for preventing the screen from blanking when the user is watching a video - ext-idle-notify for notifying when the user is idle ## Helpers wlroots provides additional helpers which can make it easier for compositors to tie everything together: - `wlr_output_layout` organises output devices in the physical space - `wlr_cursor` stores the current position and image of the cursor - `wlr_scene` provides a declarative way to display surfaces ## tinywl tinywl is a minimal wlroots compositor. It implements basic stacking window management and only supports xdg-shell. It's extensively commented and is a good learning resource for developers new to wlroots. wlroots-0.17.1/docs/env_vars.md000066400000000000000000000047651454110342200164160ustar00rootroot00000000000000wlroots reads these environment variables # wlroots specific * *WLR_BACKENDS*: comma-separated list of backends to use (available backends: libinput, drm, wayland, x11, headless) * *WLR_NO_HARDWARE_CURSORS*: set to 1 to use software cursors instead of hardware cursors * *WLR_XWAYLAND*: specifies the path to an Xwayland binary to be used (instead of following shell search semantics for "Xwayland") * *WLR_RENDERER*: forces the creation of a specified renderer (available renderers: gles2, pixman, vulkan) * *WLR_RENDER_DRM_DEVICE*: specifies the DRM node to use for hardware-accelerated renderers. * *WLR_EGL_NO_MODIFIERS*: set to 1 to disable format modifiers in EGL, this can be used to understand and work around driver bugs. ## DRM backend * *WLR_DRM_DEVICES*: specifies the DRM devices (as a colon separated list) instead of auto probing them. The first existing device in this list is considered the primary DRM device. * *WLR_DRM_NO_ATOMIC*: set to 1 to use legacy DRM interface instead of atomic mode setting * *WLR_DRM_NO_MODIFIERS*: set to 1 to always allocate planes without modifiers, this can fix certain modeset failures because of bandwidth restrictions. * *WLR_DRM_FORCE_LIBLIFTOFF*: set to 1 to force libliftoff (by default, libliftoff is never used) ## Headless backend * *WLR_HEADLESS_OUTPUTS*: when using the headless backend specifies the number of outputs ## libinput backend * *WLR_LIBINPUT_NO_DEVICES*: set to 1 to not fail without any input devices ## Wayland backend * *WLR_WL_OUTPUTS*: when using the wayland backend specifies the number of outputs ## X11 backend * *WLR_X11_OUTPUTS*: when using the X11 backend specifies the number of outputs ## gles2 renderer * *WLR_RENDERER_ALLOW_SOFTWARE*: allows the gles2 renderer to use software rendering ## scenes * *WLR_SCENE_DEBUG_DAMAGE*: specifies debug options for screen damage related tasks for compositors that use scenes (available options: none, rerender, highlight) * *WLR_SCENE_DISABLE_DIRECT_SCANOUT*: disables direct scan-out for debugging. * *WLR_SCENE_DISABLE_VISIBILITY*: If set to 1, the visibility of all scene nodes will be considered to be the full node. Intelligent visibility canculations will be disabled. # Generic * *DISPLAY*: if set probe X11 backend in `wlr_backend_autocreate` * *WAYLAND_DISPLAY*, *WAYLAND_SOCKET*: if set probe Wayland backend in `wlr_backend_autocreate` * *XCURSOR_PATH*: directory where xcursors are located * *XDG_SESSION_ID*: if set, session ID used by the logind session wlroots-0.17.1/examples/000077500000000000000000000000001454110342200151235ustar00rootroot00000000000000wlroots-0.17.1/examples/.gitignore000066400000000000000000000000261454110342200171110ustar00rootroot00000000000000/compositor/protocols wlroots-0.17.1/examples/cairo-buffer.c000066400000000000000000000123421454110342200176350ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200112L #include #include #include #include #include #include #include #include #include #include #include #include #include /* Simple scene-graph example with a custom buffer drawn by Cairo. * * Input is unimplemented. Surfaces are unimplemented. */ struct cairo_buffer { struct wlr_buffer base; cairo_surface_t *surface; }; static void cairo_buffer_destroy(struct wlr_buffer *wlr_buffer) { struct cairo_buffer *buffer = wl_container_of(wlr_buffer, buffer, base); cairo_surface_destroy(buffer->surface); free(buffer); } static bool cairo_buffer_begin_data_ptr_access(struct wlr_buffer *wlr_buffer, uint32_t flags, void **data, uint32_t *format, size_t *stride) { struct cairo_buffer *buffer = wl_container_of(wlr_buffer, buffer, base); if (flags & WLR_BUFFER_DATA_PTR_ACCESS_WRITE) { return false; } *format = DRM_FORMAT_ARGB8888; *data = cairo_image_surface_get_data(buffer->surface); *stride = cairo_image_surface_get_stride(buffer->surface); return true; } static void cairo_buffer_end_data_ptr_access(struct wlr_buffer *wlr_buffer) { } static const struct wlr_buffer_impl cairo_buffer_impl = { .destroy = cairo_buffer_destroy, .begin_data_ptr_access = cairo_buffer_begin_data_ptr_access, .end_data_ptr_access = cairo_buffer_end_data_ptr_access }; static struct cairo_buffer *create_cairo_buffer(int width, int height) { struct cairo_buffer *buffer = calloc(1, sizeof(*buffer)); if (!buffer) { return NULL; } wlr_buffer_init(&buffer->base, &cairo_buffer_impl, width, height); buffer->surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); if (cairo_surface_status(buffer->surface) != CAIRO_STATUS_SUCCESS) { free(buffer); return NULL; } return buffer; } struct server { struct wl_display *display; struct wlr_backend *backend; struct wlr_renderer *renderer; struct wlr_allocator *allocator; struct wlr_scene *scene; struct wl_listener new_output; }; struct output { struct wl_list link; struct server *server; struct wlr_output *wlr; struct wlr_scene_output *scene_output; struct wl_listener frame; }; static void output_handle_frame(struct wl_listener *listener, void *data) { struct output *output = wl_container_of(listener, output, frame); wlr_scene_output_commit(output->scene_output, NULL); } static void server_handle_new_output(struct wl_listener *listener, void *data) { struct server *server = wl_container_of(listener, server, new_output); struct wlr_output *wlr_output = data; wlr_output_init_render(wlr_output, server->allocator, server->renderer); struct output *output = calloc(1, sizeof(*output)); output->wlr = wlr_output; output->server = server; output->frame.notify = output_handle_frame; wl_signal_add(&wlr_output->events.frame, &output->frame); output->scene_output = wlr_scene_output_create(server->scene, wlr_output); struct wlr_output_state state; wlr_output_state_init(&state); wlr_output_state_set_enabled(&state, true); struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output); if (mode != NULL) { wlr_output_state_set_mode(&state, mode); } wlr_output_commit_state(wlr_output, &state); wlr_output_state_finish(&state); } int main(void) { wlr_log_init(WLR_DEBUG, NULL); struct server server = {0}; server.display = wl_display_create(); server.backend = wlr_backend_autocreate(server.display, NULL); server.scene = wlr_scene_create(); server.renderer = wlr_renderer_autocreate(server.backend); wlr_renderer_init_wl_display(server.renderer, server.display); server.allocator = wlr_allocator_autocreate(server.backend, server.renderer); server.new_output.notify = server_handle_new_output; wl_signal_add(&server.backend->events.new_output, &server.new_output); if (!wlr_backend_start(server.backend)) { wl_display_destroy(server.display); return EXIT_FAILURE; } struct cairo_buffer *buffer = create_cairo_buffer(256, 256); if (!buffer) { wl_display_destroy(server.display); return EXIT_FAILURE; } /* Begin drawing * From cairo samples at https://www.cairographics.org/samples/ */ cairo_t *cr = cairo_create(buffer->surface); cairo_set_source_rgb(cr, 1, 1, 1); cairo_paint(cr); cairo_set_source_rgb(cr, 0, 0, 0); double x = 25.6, y = 128.0; double x1 = 102.4, y1 = 230.4, x2 = 153.6, y2 = 25.6, x3 = 230.4, y3 = 128.0; cairo_move_to(cr, x, y); cairo_curve_to(cr, x1, y1, x2, y2, x3, y3); cairo_set_line_width(cr, 10.0); cairo_stroke(cr); cairo_set_source_rgba(cr, 1, 0.2, 0.2, 0.6); cairo_set_line_width(cr, 6.0); cairo_move_to(cr, x, y); cairo_line_to(cr, x1, y1); cairo_move_to(cr, x2, y2); cairo_line_to(cr, x3, y3); cairo_stroke(cr); cairo_destroy(cr); /* End drawing */ struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_create( &server.scene->tree, &buffer->base); if (!scene_buffer) { wl_display_destroy(server.display); return EXIT_FAILURE; } wlr_scene_node_set_position(&scene_buffer->node, 50, 50); wlr_buffer_drop(&buffer->base); wl_display_run(server.display); wl_display_destroy(server.display); return EXIT_SUCCESS; } wlroots-0.17.1/examples/cat.c000066400000000000000000006677231454110342200160630ustar00rootroot00000000000000/* GIMP RGBA C-Source image dump (cat.c) */ #include "cat.h" const struct gimp_texture cat_tex = { 128, 128, 4, "[\227\017\377L\206\001\377M\212\002\377T\227\011\377V\231\010\377W\224\001\377[\222" "\001\377T\212\001\377P\211\001\377M\203\001\377P\212\001\377Q\217\001\377K\210\001\377" "K\210\001\377N\214\001\377Q\217\002\377W\226\001\377X\224\001\377U\214\001\377U\216\001" "\377[\225\003\377Y\221\002\377W\217\001\377^\230\005\377^\230\006\377`\231\011\377Z" "\225\003\377X\221\002\377Y\221\003\377X\220\003\377^\227\007\377Z\225\004\377Z\226\002" "\377W\217\001\377Y\224\002\377X\222\002\377W\220\001\377[\227\001\377c\241\004\377a\237" "\003\377Z\230\001\377Y\222\001\377Z\220\001\377X\216\001\377[\231\005\377j\246\025\377" "_\232\010\377U\214\000\377o\242\026\377k\234\013\377g\231\001\377c\222\001\377X\213" "\005\377_\221\023\377V\214\010\377T\216\002\377U\222\002\377Q\211\001\377J|\001\377W" "\214\003\377j\235\006\377o\243\004\377g\235\002\377X\217\002\377X\226\002\377U\221\001" "\377T\210\002\377V\212\001\377V\216\002\377Z\226\002\377Y\221\001\377[\221\002\377g\234" "\014\377d\232\016\377g\240\032\377[\226\022\377\\\224\020\377g\235\026\377\\\225" "\005\377V\221\002\377U\212\002\377Y\217\001\377Z\220\001\377Y\221\001\377Y\226\002\377" "W\227\001\377P\217\001\377O\217\002\377V\231\004\377X\230\003\377[\231\004\377X\217\001" "\377Z\215\002\377Y\212\002\377[\214\001\377\\\215\002\377]\220\001\377]\227\002\377]" "\233\001\377X\220\001\377X\217\001\377X\215\001\377^\234\001\377]\237\002\377U\221\001" "\377R\214\001\377S\217\001\377T\220\001\377P\217\001\377P\224\001\377O\216\001\377M\210" "\001\377V\225\001\377S\216\001\377S\216\001\377S\224\001\377N\220\001\377Q\226\004\377" "S\227\013\377Y\235\023\377a\241\025\377`\240\015\377]\234\005\377V\227\002\377Y\233" "\010\377W\231\011\377P\222\002\377R\217\001\377Y\237\021\377S\232\010\377K\224\002" "\377L\222\001\377P\214\002\377]\222\001\377]\224\002\377U\217\001\377L\212\001\377K\206" "\001\377Q\213\001\377T\222\001\377U\220\001\377V\224\001\377V\223\001\377Y\223\001\377" "[\223\001\377Z\221\001\377Y\222\001\377X\225\001\377V\222\002\377U\210\002\377^\225\001" "\377i\240\013\377g\236\020\377h\240\023\377T\223\002\377R\216\001\377V\215\001\377" "[\225\001\377Y\226\001\377[\227\001\377Z\227\001\377Z\226\002\377^\226\005\377e\232\014" "\377\\\225\004\377a\233\005\377_\231\002\377m\246\013\377a\232\003\377_\226\002\377" "\\\223\001\377X\224\001\377]\235\012\377c\241\016\377^\231\002\377_\222\002\377f\235" "\006\377s\252\012\377j\242\001\377b\233\002\377a\235\006\377_\232\011\377V\220\002\377" "T\210\002\377T\216\001\377S\216\001\377U\220\002\377e\234\005\377k\241\004\377n\244\004" "\377s\252\014\377a\234\003\377c\235\010\377W\217\001\377U\204\001\377X\212\001\377" "[\217\001\377]\225\001\377[\224\001\377W\216\002\377V\216\003\377T\214\002\377R\212\002" "\377T\214\002\377U\213\001\377\\\223\005\377e\235\014\377W\221\002\377W\220\001\377" "Y\220\001\377W\213\001\377V\214\001\377V\217\001\377W\227\002\377V\227\001\377U\227\001" "\377V\231\003\377N\211\001\377S\215\001\377W\216\001\377T\210\002\377Q\204\002\377W\213" "\001\377Z\217\001\377Z\216\000\377[\225\002\377b\243\013\377V\220\001\377T\207\001\377" "U\214\001\377[\231\002\377\\\236\002\377U\220\001\377M\207\002\377N\207\001\377Q\215" "\001\377S\216\001\377V\226\002\377U\216\000\377V\212\002\377c\233\001\377]\221\002\377" "V\215\001\377X\230\001\377S\226\001\377L\215\001\377I\211\001\377L\217\001\377T\225\004" "\377j\244\014\377l\246\013\377X\226\001\377T\225\004\377\\\235\016\377c\243\025\377" "^\236\012\377K\223\002\377M\230\007\377S\235\016\377N\227\007\377P\215\001\377^\224" "\001\377`\226\001\377Z\225\001\377Q\216\001\377M\210\001\377V\216\002\377]\227\001\377" "_\232\001\377]\226\001\377[\223\001\377Z\222\001\377Y\216\001\377Y\216\001\377X\225\001" "\377W\226\001\377V\216\001\377T\210\001\377]\225\001\377o\246\014\377e\235\012\377" "b\236\014\377S\222\001\377T\213\001\377W\220\001\377W\223\001\377W\216\002\377\\\231" "\002\377c\235\011\377l\242\027\377e\233\022\377d\234\020\377Y\223\003\377b\235\007" "\377b\233\003\377f\237\004\377b\233\001\377b\232\002\377`\232\001\377]\235\001\377^\237" "\005\377Y\227\002\377U\211\001\377]\220\002\377_\225\001\377c\240\001\377l\250\005\377" "b\237\002\377^\233\001\377W\223\002\377W\221\001\377T\214\001\377T\217\001\377Z\225\005" "\377]\230\002\377a\233\002\377b\234\001\377k\245\011\377l\247\014\377\\\227\003\377" "g\240\015\377V\213\001\377P\202\001\377T\212\001\377Z\224\001\377Z\226\003\377\\\231" "\010\377T\220\014\377L\207\004\377L\200\002\377Q\203\001\377^\223\003\377Y\216\001\377" "^\224\004\377p\247\026\377]\227\005\377Y\222\001\377X\222\001\377V\213\001\377W\213" "\001\377X\220\001\377[\233\001\377X\231\001\377O\212\002\377S\220\001\377S\211\001\377" "V\216\001\377W\214\002\377O\204\002\377U\213\001\377Z\217\002\377\\\225\001\377Z\223" "\001\377U\221\003\377g\247\027\377S\224\002\377O\213\001\377U\223\001\377Y\230\001\377" "\\\234\001\377Y\226\001\377Q\215\001\377O\207\001\377S\214\001\377V\217\001\377Y\225" "\001\377X\214\001\377_\223\001\377i\237\001\377_\223\001\377[\222\001\377Y\227\002\377" "V\226\001\377K\206\001\377H\203\002\377J\211\001\377T\224\003\377j\244\014\377i\244" "\010\377]\234\003\377T\222\001\377U\226\002\377\\\236\012\377a\242\012\377H\211\001" "\377E\214\001\377I\225\010\377V\240\021\377Q\232\002\377Y\224\001\377\\\222\002\377" "X\224\001\377Q\215\001\377P\212\001\377W\216\001\377_\226\001\377a\226\001\377^\224\001" "\377Z\221\001\377X\217\002\377S\210\001\377U\214\001\377[\227\003\377X\227\001\377U\221" "\001\377W\216\001\377^\227\001\377h\243\002\377`\231\001\377Z\226\002\377U\214\001\377" "W\217\001\377Y\222\001\377X\221\001\377X\226\002\377W\227\002\377X\227\004\377U\223\005" "\377U\220\002\377V\222\001\377V\222\001\377\\\235\004\377]\236\003\377X\226\002\377Z" "\225\002\377`\237\001\377`\241\002\377]\235\001\377Y\232\002\377X\224\001\377U\212\001" "\377W\214\002\377Z\221\002\377Y\216\001\377d\240\005\377`\233\003\377Z\226\001\377^\232" "\003\377Z\227\002\377X\222\001\377c\234\010\377i\242\014\377\\\227\003\377]\235\003\377" "[\234\002\377d\244\015\377e\244\017\377Z\230\004\377j\246\017\377W\215\001\377S\205" "\002\377X\223\003\377j\244\037\377l\246/\377~\261P\377\224\301r\377j\236\063\377" "M\201\001\377S\207\001\377[\222\002\377T\214\000\377`\233\013\377i\243\024\377`\233" "\014\377h\241\027\377e\240\023\377U\217\002\377V\213\001\377[\230\002\377Z\231\001\377" "N\210\002\377H\177\002\377W\225\001\377Y\220\001\377Z\217\001\377V\214\001\377R\210\001" "\377]\234\001\377_\231\002\377Z\222\001\377Y\223\001\377V\223\001\377c\244\020\377Y" "\233\007\377R\223\001\377U\224\001\377Z\232\001\377^\235\001\377]\234\002\377Y\232\002" "\377R\222\001\377T\223\001\377W\230\001\377W\230\002\377W\213\001\377h\240\001\377h\236" "\001\377_\222\002\377\\\221\002\377[\231\002\377X\231\001\377O\220\001\377K\213\001\377" "O\217\002\377b\236\015\377_\233\007\377Z\227\002\377_\240\004\377U\217\002\377T\221" "\001\377S\222\001\377U\221\001\377K\203\002\377F\202\001\377D\212\001\377K\225\004\377" "O\232\003\377R\230\002\377U\225\001\377T\223\001\377P\211\001\377K\204\001\377R\211\001" "\377X\223\001\377[\231\001\377[\233\001\377X\222\001\377S\212\001\377M\200\002\377U\214" "\003\377^\226\012\377X\225\005\377Y\232\006\377V\222\001\377\\\225\001\377d\240\001\377" "_\226\001\377Z\220\001\377X\221\001\377Y\222\001\377[\216\001\377\\\217\002\377^\233" "\002\377V\225\001\377R\221\001\377T\215\001\377V\220\001\377Z\224\001\377X\222\002\377" "W\225\001\377^\240\007\377V\224\001\377V\216\001\377^\236\002\377b\243\004\377[\233\003" "\377X\230\004\377\\\234\011\377V\222\003\377U\216\001\377X\215\001\377[\217\001\377" "h\240\005\377i\245\005\377]\227\001\377^\230\002\377Z\217\001\377[\221\001\377k\243\011" "\377e\236\007\377W\223\001\377c\243\014\377[\234\005\377f\244\017\377f\241\015\377" "b\236\012\377n\251\024\377W\217\001\377V\216\005\377e\237\040\377t\254B\377|\260" "T\377\230\305y\377\250\317\222\377o\242<\377P\210\002\377^\224\006\377\\\226" "\006\377Q\221\003\377b\241\024\377a\241\020\377S\224\005\377e\240\030\377l\245\033" "\377Z\222\001\377]\223\002\377`\232\001\377[\226\002\377P\204\001\377T\211\001\377_\231" "\001\377^\224\002\377^\224\001\377[\224\002\377Y\217\001\377Z\225\001\377Y\220\001\377" "Z\221\001\377X\224\001\377R\215\001\377X\231\005\377`\241\012\377S\221\001\377R\214" "\001\377W\226\001\377[\227\001\377_\237\002\377`\242\002\377W\226\001\377Y\232\002\377" "g\246\011\377a\237\003\377Z\220\002\377e\236\002\377_\226\001\377X\220\001\377X\223" "\001\377[\233\001\377Z\233\002\377V\230\002\377Q\220\001\377b\235\015\377m\247\025\377" "Z\232\004\377U\224\001\377[\234\004\377R\220\001\377R\220\001\377R\225\001\377V\221\001" "\377M\200\001\377M\205\002\377L\207\001\377N\217\002\377N\217\001\377J\217\002\377P\230" "\004\377O\227\003\377O\215\001\377E\201\001\377N\205\002\377Q\223\002\377`\242\021\377" "R\225\002\377T\223\001\377T\215\001\377T\205\001\377^\222\010\377b\226\015\377]\231" "\014\377]\235\011\377V\223\001\377Z\221\001\377c\233\001\377b\231\002\377`\226\001\377" "]\225\001\377Y\216\002\377[\213\002\377^\222\001\377b\235\003\377[\223\002\377Y\225\001" "\377Z\223\002\377]\223\002\377]\222\002\377]\227\002\377[\230\002\377a\243\010\377W" "\230\002\377R\217\001\377Z\234\003\377h\251\017\377e\245\022\377^\233\017\377`\234" "\025\377]\230\017\377i\243\032\377d\235\016\377]\224\001\377i\241\002\377i\243\002" "\377b\233\001\377_\233\002\377X\222\001\377W\223\001\377c\237\017\377h\243\026\377" "\\\234\014\377`\240\015\377Y\233\003\377^\235\004\377]\231\004\377c\235\007\377f\242" "\016\377]\232\016\377n\250/\377q\253<\377w\256H\377\177\263Q\377\234\307z\377" "\244\315\211\377g\241+\377O\216\002\377U\222\003\377S\222\003\377N\217\001\377_\241" "\020\377Z\235\011\377Q\220\002\377W\226\003\377\\\231\003\377d\236\003\377k\237\001\377" "f\233\001\377_\225\001\377X\213\002\377]\225\001\377f\236\002\377c\231\001\377`\234\002" "\377]\234\001\377W\217\001\377V\214\001\377V\223\001\377T\223\001\377T\224\002\377K\203" "\002\377N\212\001\377_\240\004\377X\222\001\377W\222\001\377Y\225\002\377W\220\001\377" "Z\227\001\377`\242\003\377^\241\003\377`\241\004\377h\247\007\377c\242\003\377]\231\001" "\377^\234\001\377S\220\001\377R\216\001\377U\225\001\377Z\231\002\377`\233\002\377[\225" "\003\377W\213\002\377h\235\016\377e\240\015\377[\233\003\377X\232\003\377\\\235\005\377" "V\225\002\377S\222\002\377Z\234\001\377b\235\001\377L~\002\377P\205\001\377O\206\002\377" "Z\223\001\377Y\226\001\377N\210\001\377I\212\001\377R\231\004\377J\213\001\377C}\001\377" "L\211\001\377V\232\015\377a\242\040\377L\212\001\377N\212\001\377T\217\001\377^\232" "\003\377a\233\003\377Y\223\001\377T\224\002\377W\232\003\377W\225\001\377\\\225\002\377" "b\230\001\377e\234\002\377a\226\001\377\\\217\001\377X\214\002\377\\\220\001\377a\232" "\003\377b\233\002\377[\222\001\377_\233\002\377_\225\001\377`\225\001\377a\225\001\377" "b\231\001\377\\\227\001\377a\243\005\377`\244\007\377U\224\002\377]\237\003\377b\242" "\007\377f\246\020\377Z\231\007\377X\225\006\377b\235\022\377w\253.\377\200\263:\377" "m\244\033\377j\245\002\377g\236\001\377b\232\002\377]\230\001\377X\224\001\377T\220" "\004\377f\241\034\377b\240\031\377i\250\035\377d\244\021\377Y\230\001\377^\231\001" "\377]\223\002\377b\231\003\377]\231\005\377^\233\024\377r\253\067\377x\262A\377{" "\261@\377\202\265I\377\230\305k\377\227\302t\377_\234\026\377P\213\002\377R" "\213\001\377V\220\002\377W\224\002\377a\241\010\377W\230\003\377N\213\001\377R\221\001" "\377[\231\001\377c\232\001\377i\240\002\377j\240\001\377[\221\001\377O\202\001\377]\225" "\001\377c\235\001\377a\230\001\377b\237\003\377_\240\003\377W\216\002\377\\\232\002\377" "Z\227\001\377U\222\001\377P\216\001\377M\210\002\377Q\207\002\377\\\232\001\377_\233" "\002\377^\236\001\377Z\224\001\377W\213\002\377[\231\001\377h\246\010\377f\247\014\377" "W\231\002\377]\237\003\377\\\234\001\377^\233\001\377Z\227\002\377V\225\001\377U\230" "\001\377Z\233\001\377\\\233\002\377c\240\002\377d\232\004\377^\223\005\377^\226\007\377" "[\231\004\377]\233\003\377`\237\005\377^\235\003\377^\236\002\377\\\235\002\377_\240" "\001\377`\230\002\377Ey\002\377H\201\001\377N\210\001\377R\216\001\377Z\231\001\377U\221" "\001\377N\211\001\377N\221\000\377J\212\001\377G\202\001\377G\212\001\377R\231\012\377" "X\233\021\377I\202\001\377M\212\001\377O\221\001\377W\227\001\377_\240\001\377]\223" "\001\377X\223\001\377W\227\001\377X\226\002\377^\231\001\377d\237\001\377i\247\002\377" "\\\231\001\377W\216\001\377X\220\001\377Z\227\002\377f\246\011\377\\\231\002\377Y\224" "\002\377]\227\001\377^\222\002\377c\225\001\377e\230\001\377b\227\001\377^\227\001\377" "b\243\003\377h\252\012\377]\236\001\377^\240\001\377Z\233\001\377]\235\002\377c\244" "\012\377g\244\020\377b\236\022\377i\243\037\377u\253\060\377}\262\060\377o\251" "\013\377f\234\002\377`\223\001\377e\235\006\377e\233\021\377q\247\063\377y\261F\377" "l\245\062\377o\253*\377f\246\024\377Z\231\002\377c\235\001\377a\221\003\377n\243" "\013\377s\247\034\377g\237\037\377s\253\064\377~\264?\377x\254\065\377\177\263" ";\377\204\265C\377q\246)\377V\224\000\377W\223\002\377T\216\001\377V\217\001\377" "\\\227\001\377e\241\005\377W\221\001\377M\200\002\377S\220\002\377]\233\001\377b\226" "\002\377j\234\002\377i\240\002\377\\\226\001\377R\207\001\377]\230\001\377b\233\002\377" "_\224\002\377f\240\002\377`\233\002\377[\222\001\377c\232\002\377e\240\001\377V\213\002" "\377R\212\001\377S\215\001\377[\226\001\377`\237\001\377^\235\001\377Y\233\001\377X\230" "\001\377U\212\001\377[\227\001\377j\250\007\377b\242\004\377S\215\002\377Y\226\001\377" "a\242\002\377a\240\001\377X\222\001\377Y\227\001\377^\242\001\377]\234\001\377_\234\002" "\377c\237\001\377f\237\004\377b\227\005\377a\230\005\377_\232\003\377f\240\004\377m\246" "\006\377g\242\001\377p\251\005\377l\246\003\377c\232\002\377a\232\001\377p\001\377M\207\001\377H\204\002\377D}\001\377I\210\001" "\377S\226\001\377N\224\002\377K\222\002\377\\\241\025\377H\217\003\377D\210\001\377" "U\233\004\377S\223\001\377X\223\001\377W\216\001\377S\216\001\377X\227\002\377\\\233" "\002\377Z\222\001\377Y\227\002\377V\227\002\377^\237\010\377]\234\002\377l\245\002\377" "u\256\003\377d\231\002\377\\\217\002\377Y\216\001\377c\237\004\377{\262\026\377j\240" "\003\377f\225\001\377e\222\002\377i\234\002\377h\240\001\377a\231\002\377]\226\001\377" "\\\221\001\377\\\223\001\377_\236\002\377c\237\001\377h\244\001\377d\235\002\377^\226" "\001\377_\234\001\377Z\230\002\377c\243\016\377a\235\017\377o\251\027\377g\241\005\377" "g\242\001\377l\246\002\377f\235\000\377j\242\013\377p\246'\377v\253A\377\217\300" "d\377w\261;\377Y\233\001\377e\250\005\377]\236\001\377_\237\003\377p\254\026\377x" "\263(\377p\255)\377b\236\036\377|\262<\377s\252-\377\\\233\010\377m\253\023" "\377d\241\005\377\\\231\002\377[\230\001\377Y\231\001\377\\\227\001\377c\231\001\377" "k\237\001\377p\247\002\377k\243\002\377e\241\002\377^\231\001\377i\243\001\377p\252\002" "\377o\246\005\377a\233\002\377_\232\001\377b\236\002\377d\236\002\377b\233\001\377s\255" "\013\377x\261\013\377j\241\002\377a\225\002\377Z\214\002\377_\225\002\377d\236\001\377" "b\232\001\377b\240\001\377]\232\001\377X\225\002\377c\245\016\377S\223\002\377[\233" "\002\377[\226\002\377c\237\004\377n\254\010\377a\242\001\377`\226\001\377h\241\001\377" "v\256\002\377r\247\001\377j\241\001\377e\236\001\377c\234\002\377f\242\001\377f\236\001" "\377f\234\002\377d\233\001\377a\225\002\377i\240\003\377k\242\003\377z\261\031\377~" "\263\037\377h\240\003\377j\244\002\377d\235\002\377`\220\001\377i\243\001\377Cu\002\377" "I\201\001\377F\201\001\377E{\002\377K\206\001\377U\225\001\377R\226\001\377J\221\001\377" "R\231\011\377H\220\004\377E\212\002\377R\230\002\377V\226\001\377Z\221\001\377O|\001\377" "P\203\001\377[\223\001\377`\232\001\377^\226\001\377Z\233\002\377R\226\001\377\\\235" "\014\377c\243\014\377c\240\001\377l\251\001\377c\235\001\377Z\223\001\377X\223\001\377" "i\246\010\377x\260\017\377j\235\001\377h\224\002\377h\230\001\377h\236\001\377c\231" "\001\377Z\223\002\377Z\231\001\377[\226\001\377^\230\001\377a\242\003\377]\237\001\377" "Y\230\001\377Z\225\001\377_\236\002\377c\240\001\377d\240\002\377d\242\007\377\\\231" "\006\377d\237\012\377q\255\015\377i\243\002\377k\250\002\377f\243\003\377a\236\010\377" "f\240\037\377s\254>\377\203\271Q\377s\256\062\377\\\235\003\377h\252\012\377a" "\242\005\377^\236\003\377n\253\027\377\200\273\060\377j\251\027\377\\\235\006\377" "]\235\010\377]\235\006\377_\231\002\377e\234\001\377i\240\002\377g\235\001\377a\230" "\002\377\\\221\001\377d\233\001\377n\246\002\377o\243\001\377p\246\002\377h\241\002\377" "a\232\002\377k\247\003\377r\255\002\377q\251\001\377j\235\002\377k\243\002\377k\242\001" "\377j\240\001\377h\234\001\377d\234\002\377x\261\015\377w\261\007\377j\240\002\377_" "\220\002\377Y\212\002\377V\210\002\377e\236\001\377m\250\002\377l\250\001\377f\242\002" "\377_\236\005\377b\242\011\377\\\235\003\377]\232\002\377Y\220\001\377n\247\013\377" "r\254\010\377i\245\001\377k\242\001\377l\241\001\377w\254\001\377x\252\001\377v\256" "\001\377m\243\002\377d\234\002\377e\236\002\377e\234\002\377c\233\002\377`\227\002\377" "g\236\002\377p\247\003\377h\234\001\377p\250\015\377z\261\032\377d\240\001\377d\236" "\001\377e\236\002\377a\224\002\377h\242\001\377G|\002\377K\206\004\377K\211\006\377\071" "m\001\377N\212\002\377]\237\001\377V\235\001\377M\222\001\377O\226\007\377N\226\007\377" "I\217\001\377V\233\003\377V\221\001\377N\177\001\377Hu\001\377M}\001\377Y\216\001\377h" "\241\001\377c\233\001\377[\231\002\377U\227\002\377M\215\002\377Y\230\003\377_\235\001" "\377g\243\001\377b\235\002\377\\\230\001\377X\224\001\377l\251\012\377n\247\005\377" "j\233\002\377i\227\002\377k\235\002\377g\236\001\377[\216\001\377U\211\001\377[\230\001" "\377_\233\001\377`\232\002\377a\243\002\377\\\236\002\377Z\237\001\377Z\232\001\377c" "\243\002\377o\255\004\377i\245\003\377_\235\001\377\\\233\002\377a\241\002\377i\247\004" "\377d\241\001\377l\250\010\377e\242\011\377a\237\015\377o\251%\377x\261\066\377" "v\260\066\377x\262.\377^\236\006\377\\\235\002\377c\245\006\377`\240\003\377q\256" "\030\377w\263\036\377e\244\007\377b\243\002\377Z\226\001\377]\231\002\377c\236\001\377" "j\242\002\377m\241\001\377k\241\002\377j\236\002\377f\231\001\377h\235\001\377i\240\001" "\377h\240\002\377g\244\002\377e\244\002\377j\246\005\377n\251\004\377q\251\001\377o\241" "\002\377p\241\001\377r\253\002\377p\244\001\377p\243\001\377j\235\002\377h\236\002\377" "t\257\002\377p\250\001\377j\235\001\377d\223\001\377h\227\003\377]\213\002\377Z\212\001" "\377i\241\002\377m\247\001\377n\246\003\377o\252\011\377`\241\004\377Z\231\001\377^" "\231\002\377^\231\001\377p\254\015\377g\245\004\377j\244\001\377p\252\001\377r\245\001" "\377w\254\001\377{\255\001\377y\262\001\377o\256\001\377a\235\002\377b\234\002\377b\236" "\001\377`\231\001\377`\227\001\377i\240\002\377m\244\002\377h\236\001\377p\254\016\377" "r\257\021\377`\237\001\377a\231\002\377c\232\002\377d\227\001\377g\240\001\377J\210" "\001\377]\233\026\377h\243)\377:j\000\377R\207\002\377b\243\002\377_\242\004\377M\222" "\001\377N\226\004\377Q\230\005\377Q\227\003\377U\232\003\377R\215\001\377K~\002\377M\200" "\002\377T\213\001\377a\231\002\377g\236\002\377`\224\002\377]\231\001\377Z\232\001\377" "Q\215\001\377Q\216\001\377\\\231\001\377f\242\001\377b\231\001\377X\217\002\377Y\225" "\000\377o\252\012\377f\235\003\377d\227\001\377h\233\002\377h\236\001\377d\240\002\377" "\\\227\001\377Y\225\001\377^\235\001\377a\234\001\377c\235\002\377c\240\002\377^\234" "\001\377Z\231\001\377\\\237\001\377a\242\001\377n\252\010\377i\245\010\377\\\235\002" "\377a\242\001\377d\242\001\377a\233\001\377]\232\001\377i\251\017\377a\241\014\377" "[\234\010\377l\250\030\377{\264+\377v\260&\377t\261\035\377^\236\003\377\\\230" "\002\377j\254\012\377c\245\006\377j\251\015\377k\253\014\377c\243\004\377g\251\003\377" "[\230\001\377Z\227\001\377d\245\001\377h\243\001\377j\235\001\377j\240\001\377m\244\002" "\377j\243\001\377f\237\002\377h\240\002\377k\245\002\377l\251\003\377f\247\001\377d\242" "\002\377i\242\001\377n\247\001\377i\234\002\377o\243\002\377x\252\002\377r\241\002\377" "t\247\001\377n\243\001\377l\240\001\377o\253\002\377j\243\001\377f\236\002\377b\231\002" "\377\177\255\033\377b\222\006\377U\204\001\377d\233\001\377p\252\002\377j\244\001\377" "l\253\002\377b\240\001\377a\234\002\377d\233\002\377g\241\003\377m\252\010\377_\237" "\001\377b\237\001\377h\240\002\377r\246\002\377x\247\002\377\206\267\002\377\200\265" "\001\377r\254\001\377k\251\002\377e\236\001\377a\232\001\377^\227\001\377a\231\001\377" "j\241\003\377w\255\020\377z\262\025\377r\256\017\377l\254\014\377\\\236\001\377\\" "\226\001\377b\233\001\377h\237\001\377l\245\001\377J\210\002\377S\225\016\377o\254-" "\377K\204\004\377S\217\002\377Z\232\001\377^\241\002\377Y\234\001\377X\236\003\377[\242" "\011\377\\\242\015\377P\230\003\377P\222\001\377P\212\001\377R\214\001\377[\223\001\377" "g\236\002\377i\235\001\377^\222\003\377]\231\001\377Y\230\001\377U\226\002\377Q\217\002" "\377U\224\001\377`\236\002\377c\233\001\377]\221\001\377b\241\003\377q\255\021\377_" "\233\002\377a\230\001\377e\233\002\377d\234\002\377b\240\002\377c\246\002\377d\247\004" "\377\\\234\001\377^\235\001\377`\235\002\377a\240\002\377^\233\002\377[\230\002\377a" "\242\001\377^\237\000\377d\243\012\377j\247\025\377j\250\017\377d\244\003\377e\247" "\001\377^\232\002\377W\223\001\377\\\240\006\377[\236\003\377Z\234\002\377a\241\006\377" "s\260\026\377y\266\035\377f\245\006\377a\234\001\377a\235\002\377n\255\007\377j\250" "\010\377j\247\011\377r\260\021\377f\246\005\377a\243\002\377Z\232\001\377f\246\012" "\377n\253\011\377h\233\002\377l\241\001\377e\235\001\377d\240\001\377c\241\001\377b" "\234\002\377j\245\001\377j\246\002\377g\243\002\377i\250\002\377k\245\002\377p\251\002" "\377o\247\001\377m\241\001\377j\235\002\377q\244\002\377r\242\002\377s\246\002\377n\243" "\001\377h\237\001\377h\243\001\377e\237\001\377`\233\002\377]\231\002\377j\246\016\377" "`\230\003\377a\227\001\377g\240\001\377m\252\002\377i\246\001\377i\244\001\377e\235\002" "\377a\227\002\377i\242\001\377q\253\003\377l\251\003\377b\237\001\377d\242\001\377g\240" "\001\377m\242\001\377r\244\001\377}\257\001\377\207\271\001\377~\256\001\377t\251\001\377" "k\242\001\377f\236\002\377b\232\001\377f\237\002\377m\245\003\377o\246\011\377z\263" "\026\377q\256\020\377q\260\022\377_\241\002\377^\234\002\377e\235\002\377m\245\001\377" "n\241\001\377V\230\010\377]\236\017\377w\261-\377f\244\026\377S\220\001\377V\222" "\001\377Z\227\001\377`\234\001\377_\235\002\377[\235\002\377`\245\014\377V\232\003\377" "W\233\002\377\\\237\001\377Y\233\001\377]\230\001\377d\232\002\377f\232\001\377b\230" "\001\377^\230\001\377[\231\001\377W\226\002\377X\227\001\377Y\221\001\377e\240\002\377" "g\236\002\377d\237\001\377m\253\014\377l\253\021\377[\233\001\377b\231\001\377l\237" "\001\377h\236\002\377c\237\001\377g\250\007\377c\245\005\377\\\236\002\377^\241\001\377" "\\\234\001\377[\232\001\377[\231\001\377^\232\001\377a\243\002\377Z\235\001\377\\\240" "\011\377U\231\006\377`\241\012\377f\250\007\377d\247\002\377`\242\002\377V\227\002\377" "U\232\003\377b\246\011\377\\\236\001\377^\236\001\377i\252\005\377i\251\003\377c\243" "\002\377b\243\001\377d\245\001\377g\250\002\377a\243\002\377`\241\002\377h\251\005\377" "e\247\002\377_\235\002\377_\237\002\377d\245\005\377e\236\003\377s\251\001\377w\254\002" "\377j\241\001\377h\247\002\377a\235\001\377c\234\001\377j\246\002\377n\252\004\377j\247" "\002\377k\245\001\377l\243\002\377s\255\001\377o\241\002\377k\237\002\377l\236\001\377" "p\245\002\377m\244\001\377l\244\001\377l\243\001\377i\241\001\377h\237\001\377a\225\002" "\377_\226\002\377]\224\001\377c\234\002\377j\237\001\377l\235\001\377l\240\001\377p\253" "\001\377k\242\001\377e\227\002\377f\234\001\377b\226\002\377n\247\002\377s\256\002\377" "m\241\001\377j\240\001\377j\243\002\377i\240\001\377l\242\001\377q\252\001\377t\253\001" "\377\210\267\001\377\216\274\001\377{\257\002\377l\245\002\377j\246\001\377j\243\002" "\377n\244\001\377t\251\005\377u\252\005\377l\245\002\377m\252\006\377r\257\021\377h" "\246\010\377d\241\002\377f\235\001\377o\245\002\377s\244\001\377O\212\003\377W\227\004" "\377f\243\022\377r\254\036\377V\223\001\377X\223\001\377Z\230\002\377^\223\001\377" "a\230\001\377\\\232\001\377Z\237\004\377M\221\001\377P\223\001\377^\243\003\377\\\237" "\001\377\\\233\001\377b\233\001\377d\241\001\377a\237\001\377\\\234\001\377\\\234\001\377" "[\224\002\377_\233\001\377_\230\001\377k\246\003\377d\242\004\377]\236\002\377g\250\015" "\377e\247\015\377[\233\002\377c\237\002\377f\233\002\377f\234\002\377d\242\001\377o" "\253\017\377f\246\012\377]\236\001\377_\240\002\377Y\225\001\377[\231\001\377`\234" "\001\377c\237\001\377c\240\001\377[\232\001\377X\232\002\377\\\237\003\377`\242\003\377" "b\243\004\377d\245\005\377f\250\012\377Z\237\003\377W\231\002\377Y\235\002\377[\235" "\001\377`\240\002\377b\243\001\377c\242\001\377d\244\001\377c\240\001\377g\246\002\377" "f\247\001\377`\240\001\377^\241\002\377c\245\001\377h\251\002\377n\253\012\377j\252" "\012\377b\241\002\377g\242\001\377t\261\002\377q\253\002\377l\247\002\377k\251\002\377" "b\236\002\377b\236\002\377l\253\012\377}\270\034\377k\250\004\377k\246\001\377m\247" "\002\377q\256\001\377m\241\001\377n\242\001\377o\242\001\377r\245\001\377q\247\002\377" "n\246\001\377p\252\001\377l\242\001\377g\235\002\377c\234\001\377`\226\001\377c\227\002" "\377l\234\001\377m\236\001\377i\231\001\377i\236\001\377i\242\001\377i\241\001\377d\230" "\001\377h\226\002\377l\233\002\377q\244\001\377t\253\001\377o\241\001\377k\241\002\377" "j\242\001\377j\243\001\377m\251\002\377m\247\002\377n\244\001\377{\256\001\377\203\266" "\001\377w\254\001\377m\246\002\377o\246\001\377t\247\002\377y\250\001\377\225\266\023" "\377\235\274\032\377u\252\003\377j\243\001\377w\264\017\377s\261\013\377g\237\002" "\377l\242\001\377s\251\001\377s\246\001\377R\212\001\377[\227\002\377c\241\010\377l" "\247\020\377`\235\002\377Z\225\002\377\\\234\001\377[\222\001\377\\\220\001\377`\235" "\002\377Z\240\002\377J\223\002\377R\227\003\377V\233\002\377X\226\001\377^\223\002\377" "a\232\001\377`\231\002\377X\222\002\377X\227\001\377[\233\001\377]\235\001\377\\\233" "\001\377`\235\001\377k\252\003\377b\234\003\377\\\231\001\377_\243\002\377[\234\001\377" "Y\224\002\377b\234\002\377b\232\001\377a\231\001\377a\236\002\377_\240\004\377d\247\010" "\377\\\236\001\377_\236\002\377a\237\001\377c\244\001\377c\240\001\377_\233\001\377\\" "\234\002\377[\236\001\377a\244\005\377b\244\004\377`\232\002\377d\237\000\377p\254\015" "\377r\255\031\377[\234\004\377[\234\001\377]\235\001\377`\236\001\377`\235\001\377c" "\240\001\377c\231\001\377f\246\001\377e\243\001\377g\243\001\377j\245\001\377e\240\002" "\377_\237\002\377d\245\006\377r\262\022\377w\267\032\377c\244\007\377`\240\002\377" "d\242\002\377p\257\001\377m\247\001\377h\246\001\377h\247\001\377d\242\002\377`\235\001" "\377m\255\021\377z\272#\377e\247\003\377i\247\001\377j\237\001\377n\250\002\377m\241" "\001\377l\237\002\377q\243\002\377s\246\001\377s\246\001\377p\242\001\377n\244\001\377" "g\236\001\377g\243\001\377e\237\001\377b\227\001\377h\234\001\377n\243\001\377m\244\002" "\377h\234\001\377g\240\002\377g\245\001\377n\251\002\377o\245\001\377o\236\002\377q\240" "\001\377r\243\002\377u\254\002\377n\241\002\377m\246\001\377r\255\002\377o\252\002\377" "j\237\001\377j\237\001\377m\242\001\377t\254\001\377z\257\001\377y\256\002\377t\251\001" "\377o\241\001\377q\241\001\377v\246\001\377~\255\005\377\206\264\014\377k\242\002\377" "i\243\001\377w\264\013\377p\256\006\377h\235\002\377n\244\002\377s\250\002\377r\244" "\002\377e\241\002\377\\\232\002\377\\\235\002\377_\240\003\377]\235\001\377]\226\001\377" "_\234\002\377\\\222\002\377X\215\001\377a\233\002\377^\240\002\377W\230\001\377Y\233" "\002\377Y\232\001\377\\\227\001\377^\222\002\377d\233\002\377^\225\001\377X\224\002\377" "X\224\001\377^\241\004\377i\252\014\377Z\233\002\377^\237\001\377k\254\006\377b\236" "\002\377^\227\001\377`\242\002\377Y\226\001\377\\\221\001\377d\237\001\377f\241\001\377" "b\231\002\377^\227\001\377Y\227\001\377Y\233\001\377X\225\001\377\\\233\001\377`\236" "\001\377c\242\001\377a\237\002\377`\235\001\377^\240\001\377Z\233\001\377\\\237\001\377" "[\231\001\377_\225\002\377a\234\001\377a\236\003\377a\237\005\377Y\232\001\377\\\234" "\001\377b\240\001\377a\237\001\377f\244\010\377|\265\034\377i\242\002\377g\242\001\377" "d\234\001\377i\244\001\377g\237\002\377b\240\001\377\\\236\001\377t\262\033\377\206" "\301*\377l\254\020\377Z\236\002\377Z\235\001\377a\233\002\377k\243\002\377j\241\001" "\377g\241\001\377c\241\001\377f\247\001\377a\241\002\377j\254\006\377f\251\010\377i" "\254\004\377k\247\002\377i\235\001\377l\243\002\377h\234\002\377f\232\002\377g\232\002" "\377g\232\002\377o\244\001\377n\242\001\377k\245\001\377d\237\001\377c\241\002\377b\231" "\001\377`\224\002\377b\227\001\377k\244\002\377p\253\002\377j\237\002\377j\243\002\377" "j\241\001\377p\250\001\377u\254\001\377s\246\002\377j\234\002\377h\233\001\377p\250\002" "\377o\251\001\377m\244\001\377z\265\004\377r\251\001\377n\242\001\377l\241\001\377i\240" "\001\377l\243\001\377l\237\001\377w\253\001\377w\253\001\377m\237\001\377o\241\001\377" "o\241\002\377o\246\001\377n\252\001\377j\240\001\377l\244\001\377y\264\005\377y\262\005" "\377l\242\001\377k\240\001\377n\245\002\377j\235\001\377a\240\001\377`\233\002\377_\237" "\001\377[\235\001\377X\227\001\377X\222\001\377[\231\001\377[\225\002\377W\213\001\377" "\\\223\001\377`\236\002\377\\\230\002\377\\\227\001\377Y\227\001\377\\\231\001\377[\226" "\001\377a\237\001\377a\237\002\377Z\231\001\377X\226\001\377_\236\002\377m\254\015\377" "^\240\002\377\\\235\001\377k\254\007\377c\240\003\377_\230\000\377b\243\001\377_\237" "\001\377Z\222\002\377`\227\002\377d\236\002\377e\241\001\377c\235\002\377a\237\001\377" "_\234\001\377\\\224\001\377_\231\001\377c\241\001\377b\236\002\377`\235\002\377_\237" "\003\377d\244\017\377_\236\016\377X\231\002\377X\223\001\377\\\231\002\377a\240\001\377" "[\230\001\377Y\230\001\377\\\234\001\377a\242\001\377b\242\001\377\\\233\002\377j\251" "\016\377y\261\035\377j\235\000\377o\243\002\377]\221\002\377f\236\002\377g\244\001\377" "_\237\001\377e\247\012\377\177\275)\377w\266\040\377b\244\011\377Z\236\004\377X" "\232\002\377^\231\001\377f\236\002\377f\234\001\377f\241\001\377c\242\001\377j\254\004" "\377h\250\003\377f\247\002\377c\241\001\377g\241\001\377n\250\001\377o\244\002\377r\246" "\002\377m\237\002\377c\226\001\377c\226\001\377g\236\001\377o\246\001\377o\243\001\377" "o\241\002\377m\237\002\377t\252\002\377f\230\001\377b\231\001\377a\225\001\377h\240\001" "\377j\244\002\377q\254\001\377k\244\001\377i\235\002\377q\250\001\377u\257\002\377o\246" "\001\377c\230\001\377e\233\002\377p\252\001\377n\247\002\377l\244\001\377t\261\002\377" "p\253\001\377n\251\002\377l\251\003\377e\236\002\377k\244\001\377j\240\001\377u\254\002" "\377z\256\001\377t\243\002\377m\235\001\377m\241\002\377s\256\001\377o\255\001\377h\235" "\002\377n\244\001\377u\252\001\377\177\265\003\377q\245\001\377j\235\002\377i\241\001\377" "f\236\002\377a\240\001\377`\231\001\377b\241\002\377`\240\001\377X\222\002\377W\220\001" "\377\\\232\001\377[\225\001\377Y\220\001\377]\226\001\377a\232\001\377c\230\001\377a" "\226\001\377^\222\002\377b\232\002\377`\234\004\377b\241\006\377e\245\007\377Z\226\001" "\377[\223\001\377_\237\002\377g\247\003\377f\245\001\377a\235\001\377e\246\002\377a\240" "\002\377a\231\001\377c\245\001\377a\243\002\377R\214\001\377Y\223\002\377`\234\002\377" "c\244\001\377b\243\003\377_\240\002\377Z\226\002\377`\230\001\377d\231\001\377n\253\003" "\377m\255\004\377a\243\004\377V\222\007\377Z\203)\377\213\256]\377g\235\"\377O" "\220\000\377X\224\001\377\\\233\001\377Z\231\001\377Y\226\001\377Z\226\001\377^\234\002" "\377a\243\001\377j\253\014\377h\247\007\377`\227\001\377m\243\001\377p\243\001\377Y" "\217\002\377h\236\001\377h\247\002\377]\235\002\377o\257\032\377x\266\040\377j\253" "\017\377Z\237\004\377X\236\004\377W\231\002\377\\\234\002\377j\250\007\377c\240\001\377" "g\246\002\377j\252\004\377n\256\007\377l\254\007\377`\237\002\377c\241\001\377i\240\001" "\377r\252\001\377w\253\001\377t\243\002\377l\234\001\377e\227\002\377a\225\001\377h\246" "\001\377k\250\001\377l\241\002\377q\243\002\377t\251\002\377o\242\002\377h\234\001\377" "c\232\002\377^\226\001\377f\245\001\377e\236\001\377s\260\006\377h\243\003\377g\235\002" "\377o\247\001\377r\254\001\377j\236\002\377a\226\002\377g\236\001\377n\251\001\377l\246" "\002\377j\247\001\377o\260\003\377i\251\001\377m\254\005\377z\267\020\377g\246\001\377" "g\243\001\377j\246\001\377n\244\001\377t\247\002\377{\262\001\377u\245\001\377r\245\002" "\377r\251\001\377m\243\001\377i\240\001\377n\250\001\377r\253\001\377|\264\002\377u\256" "\002\377m\241\001\377g\235\002\377i\245\001\377X\227\001\377Y\221\002\377`\237\001\377" "b\240\002\377\\\222\001\377^\230\000\377`\235\001\377^\225\001\377Y\217\002\377_\226" "\002\377_\230\002\377`\234\003\377a\230\001\377d\233\002\377g\237\004\377`\233\004\377" "g\245\020\377b\243\011\377[\232\002\377[\221\001\377_\232\001\377j\250\004\377k\253" "\004\377b\240\001\377b\241\001\377c\241\002\377d\234\002\377m\256\003\377e\244\002\377" "W\215\001\377[\222\001\377b\235\002\377c\241\001\377f\251\004\377f\246\005\377`\233\001" "\377f\235\002\377m\243\001\377w\263\001\377n\251\002\377Z\223\001\377a\220\032\377\335" "\317\305\377\377\362\365\377\373\370\361\377\225\273W\377S\220\000\377V\222" "\001\377W\225\002\377Y\225\002\377X\217\002\377Z\232\002\377\\\235\007\377{\272(\377" "d\240\010\377`\226\002\377l\245\002\377p\251\001\377j\236\002\377n\246\001\377j\251" "\001\377f\245\007\377i\251\022\377h\250\014\377[\233\001\377^\240\001\377`\242\002\377" "_\236\002\377i\251\010\377m\254\015\377Z\232\001\377]\235\002\377^\236\001\377_\240" "\001\377i\254\004\377`\241\001\377a\234\002\377g\234\002\377r\250\002\377o\240\002\377" "o\240\002\377k\234\001\377g\235\002\377h\246\002\377i\253\003\377h\243\001\377n\245\001" "\377p\246\002\377q\253\001\377j\235\002\377f\236\002\377c\227\002\377b\231\001\377i\245" "\001\377g\241\001\377n\256\005\377d\244\004\377f\240\002\377n\243\001\377s\246\001\377" "m\242\001\377f\230\002\377h\233\002\377m\246\001\377m\245\001\377p\256\001\377m\251\002" "\377m\247\001\377l\250\003\377v\267\014\377e\247\002\377a\243\001\377`\241\002\377c" "\233\002\377l\244\001\377s\253\001\377r\252\001\377v\254\002\377o\244\001\377j\234\001" "\377h\241\002\377o\256\001\377t\264\003\377t\261\002\377q\253\002\377l\246\001\377f\235" "\002\377q\254\010\377[\230\001\377X\220\001\377[\227\001\377^\230\001\377_\225\001\377" "a\231\001\377c\242\001\377_\232\002\377]\224\001\377\\\225\000\377e\243\010\377g\250" "\010\377^\224\001\377e\236\002\377a\232\001\377e\237\004\377l\247\012\377c\236\002\377" "e\235\001\377f\235\002\377i\242\002\377j\246\002\377k\253\003\377f\246\001\377k\252\002" "\377j\244\001\377m\243\001\377r\255\002\377d\233\002\377Z\217\003\377W\214\001\377n\242" "\001\377o\251\001\377g\245\001\377d\240\001\377g\234\002\377o\252\002\377t\255\002\377" "v\256\001\377n\243\001\377Z\224\000\377\243\264}\377\344\326\333\377\366\352\356" "\377\373\363\366\377\373\362\363\377\257\311\177\377N\210\000\377Y\225\002\377" "[\227\001\377Y\226\001\377Z\235\001\377f\250\014\377x\264\034\377r\257\017\377d\237" "\002\377q\254\002\377s\255\002\377k\236\002\377f\233\002\377d\240\001\377f\250\005\377" "\\\235\002\377`\241\006\377Y\226\001\377_\237\001\377c\240\002\377d\236\001\377h\250" "\003\377a\241\002\377]\237\003\377\\\236\002\377W\225\001\377_\233\002\377g\247\002\377" "d\243\001\377[\222\001\377b\227\002\377i\236\002\377c\227\001\377e\232\000\377f\237\000" "\377p\253\010\377g\250\003\377g\244\001\377l\243\001\377l\243\002\377o\254\001\377k" "\251\001\377e\235\002\377d\241\001\377b\240\002\377a\234\002\377j\246\001\377i\237\001" "\377m\250\001\377h\243\001\377j\243\002\377n\242\001\377p\244\001\377s\251\001\377l\237" "\001\377m\241\001\377p\245\002\377r\247\001\377w\263\001\377s\250\001\377q\247\002\377" "o\246\001\377v\267\006\377f\247\003\377]\240\001\377[\241\002\377\\\233\001\377l\246" "\001\377s\253\002\377s\255\002\377q\254\003\377j\246\003\377c\235\002\377d\242\001\377" "g\250\002\377k\251\003\377k\245\003\377t\255\011\377s\253\014\377l\247\013\377u\260" "\021\377b\234\001\377`\234\001\377^\227\002\377a\231\002\377f\241\001\377f\237\001\377" "i\243\001\377a\227\001\377X\217\000\377e\243\021\377~\271/\377a\233\001\377^\222\002" "\377a\231\001\377g\235\003\377q\244\007\377m\241\002\377k\236\002\377m\232\001\377n\236" "\001\377i\233\002\377_\222\001\377_\232\002\377d\246\002\377o\255\004\377r\252\001\377" "}\261\001\377y\254\002\377^\223\002\377X\216\002\377T\212\002\377u\246\002\377w\255\002" "\377e\237\001\377e\240\002\377i\237\002\377p\252\001\377h\234\002\377o\244\002\377l\242" "\001\377V\222\000\377\331\320\317\377\324\303\313\377\341\317\331\377\363\345" "\354\377\353\332\332\377\364\355\351\377y\251\061\377V\231\000\377W\221\001\377" "_\236\004\377n\257\023\377p\257\023\377l\254\014\377p\260\013\377j\251\002\377p\252" "\002\377s\245\001\377l\237\001\377b\226\001\377f\243\001\377j\254\003\377\\\236\001\377" "Z\234\002\377Y\232\001\377]\234\001\377f\242\001\377e\234\001\377d\244\002\377`\243\002" "\377_\243\005\377V\234\001\377T\220\002\377f\243\001\377k\250\001\377g\247\001\377]\224" "\001\377_\227\000\377t\246\027\377\223\272G\377\242\277b\377u\215\071\377^\234" "\011\377^\237\001\377f\240\002\377m\250\001\377m\252\001\377h\247\002\377d\245\001\377" "c\243\001\377b\244\001\377]\234\001\377b\244\001\377e\244\002\377g\235\002\377p\251\001" "\377m\244\001\377l\246\001\377j\237\002\377o\244\001\377t\255\001\377q\250\001\377q\252" "\001\377p\244\002\377t\253\002\377y\263\002\377y\263\001\377u\253\001\377r\245\002\377" "r\255\001\377h\240\001\377c\242\001\377e\247\004\377a\234\002\377q\252\002\377v\245\002" "\377z\260\001\377n\254\004\377m\257\011\377d\243\001\377g\247\001\377g\245\001\377i" "\245\001\377j\242\004\377~\263\032\377\203\263\040\377\202\264\037\377s\254\013\377" "g\232\002\377n\245\007\377`\226\002\377`\224\002\377f\242\001\377f\240\001\377g\243\002" "\377f\236\001\377f\240\003\377\205\274\071\377\200\270\066\377d\233\002\377b\230" "\002\377`\226\002\377d\233\001\377z\260\015\377m\241\003\377g\226\001\377k\231\002\377" "n\234\001\377j\234\001\377e\231\001\377`\225\001\377c\240\002\377e\243\002\377h\237\002" "\377\210\267\001\377y\251\001\377_\225\002\377^\225\001\377X\214\001\377v\247\002\377" "y\256\001\377e\232\002\377f\233\002\377m\240\002\377l\237\001\377]\221\002\377t\251\001" "\377t\257\000\377k\233\034\377\326\301\315\377\313\267\301\377\347\331\343\377" "\367\356\364\377\362\345\352\377\354\332\332\377\357\354\326\377W\221\011" "\377Q\221\000\377c\244\011\377q\260\030\377e\246\006\377_\240\002\377[\225\002\377" "d\244\001\377n\247\002\377u\251\001\377u\247\002\377k\236\002\377n\252\002\377l\251\002" "\377Z\231\002\377T\225\002\377[\234\004\377a\236\003\377f\244\001\377f\240\001\377e\247" "\002\377^\237\002\377Y\230\001\377U\221\001\377Y\225\002\377d\243\002\377d\241\002\377" "b\240\002\377X\225\000\377\253\306s\377\363\357\336\377\353\335\332\377\345\326" "\322\377\311\273\261\377e\212\"\377^\237\000\377d\240\001\377m\250\001\377l\246" "\001\377b\232\002\377^\226\001\377^\236\001\377k\255\010\377o\257\007\377d\245\000\377" "c\240\002\377g\234\002\377r\254\002\377k\243\002\377i\243\001\377e\234\001\377l\244\001" "\377t\260\001\377v\257\001\377v\253\002\377o\240\002\377u\247\002\377z\262\001\377|\262" "\001\377z\255\002\377r\243\003\377t\250\001\377n\243\002\377j\243\002\377p\254\003\377" "l\245\001\377v\257\004\377q\245\001\377u\255\002\377t\263\007\377i\252\002\377e\245\002" "\377o\254\001\377q\252\001\377o\247\002\377l\241\002\377{\257\014\377\204\263\027\377" "\201\261\022\377r\252\004\377m\245\001\377f\236\002\377b\236\002\377d\236\002\377j\246" "\002\377i\242\001\377i\242\001\377e\237\002\377t\255\030\377\220\304E\377u\254\032" "\377g\233\001\377f\226\001\377f\225\001\377f\226\001\377p\252\001\377m\247\002\377c\230" "\002\377d\232\001\377j\241\002\377g\233\002\377e\236\002\377d\236\001\377e\241\001\377" "f\241\001\377d\227\002\377\201\261\002\377y\252\002\377g\233\001\377b\232\002\377c\230" "\001\377u\250\002\377|\260\001\377j\236\002\377g\233\002\377i\235\002\377k\236\001\377" "e\231\002\377{\256\001\377\203\273\014\377\230\262`\377\317\266\304\377\271\236" "\252\377\350\332\344\377\360\342\353\377\341\316\325\377\335\307\307\377" "\334\304\303\377\326\340\261\377`\236\020\377n\255\035\377x\267$\377e\244\004" "\377a\233\001\377[\220\002\377e\235\001\377n\244\001\377q\245\002\377r\243\001\377t\254" "\002\377s\253\001\377s\247\001\377d\234\001\377P\221\002\377m\251\003\377l\244\002\377" "d\234\001\377e\244\003\377f\247\003\377`\240\002\377\\\222\002\377\\\225\002\377^\234" "\002\377_\241\003\377X\227\000\377s\242\026\377\336\343\246\377\352\326\322\377" "\335\306\276\377\330\303\273\377\314\271\260\377\330\311\306\377\236\245" "r\377[\234\000\377g\250\002\377e\243\001\377d\234\001\377b\231\001\377_\230\001\377^" "\224\001\377b\232\001\377l\251\004\377n\255\003\377h\242\001\377n\243\001\377w\261\001" "\377m\241\002\377k\242\002\377e\233\001\377m\251\002\377u\260\002\377v\254\002\377u\250" "\001\377q\243\002\377w\250\002\377}\264\002\377~\262\001\377z\255\001\377n\240\001\377" "o\247\002\377n\245\002\377j\242\002\377n\252\002\377s\257\007\377n\250\003\377l\244\002" "\377p\253\001\377s\260\003\377n\256\001\377l\246\001\377n\251\002\377t\256\001\377p\251" "\001\377o\246\002\377|\262\014\377w\255\010\377m\246\001\377k\245\001\377h\243\002\377" "e\243\001\377c\235\001\377f\236\001\377x\263\006\377n\242\002\377l\240\001\377d\234\001" "\377d\231\014\377\212\301B\377l\253\020\377h\244\004\377i\240\002\377l\241\002\377" "g\230\001\377r\253\001\377l\246\001\377h\247\002\377e\241\001\377o\250\004\377e\234\002" "\377o\251\011\377s\256\017\377k\251\003\377h\240\001\377k\234\001\377\202\262\002\377" "s\242\002\377h\234\001\377g\245\002\377j\250\002\377p\252\001\377x\260\002\377r\246\001" "\377]\225\002\377g\235\003\377f\232\003\377q\244\002\377q\246\001\377i\245\000\377\245" "\263z\377\321\275\307\377\323\277\313\377\360\345\357\377\362\347\360\377" "\357\345\352\377\342\314\320\377\341\314\314\377\346\315\303\377\231\272" "T\377i\246\014\377g\245\005\377_\224\002\377`\227\001\377d\230\001\377i\234\002\377" "p\243\002\377o\241\002\377o\243\002\377s\255\001\377r\251\001\377q\245\002\377j\236\002" "\377T\223\002\377v\255\002\377j\242\001\377e\241\002\377i\246\004\377f\242\001\377f\236" "\002\377d\227\001\377g\235\001\377d\241\002\377]\234\002\377{\254\060\377\365\362\337" "\377\332\305\277\377\317\265\260\377\330\302\275\377\322\274\272\377\311" "\262\255\377\301\257\251\377\276\264\242\377U\223\002\377]\236\002\377a\237\002" "\377c\234\002\377a\234\002\377a\236\002\377`\225\002\377c\237\001\377c\236\001\377g\244" "\001\377l\243\002\377s\251\001\377{\263\001\377n\240\002\377m\241\001\377j\241\001\377" "k\242\002\377o\251\001\377o\247\001\377k\243\002\377j\240\001\377p\251\001\377y\262\001" "\377x\262\001\377x\262\001\377k\237\002\377j\242\001\377j\240\001\377j\243\002\377j\243" "\002\377m\247\001\377k\242\002\377o\242\001\377s\252\002\377t\244\002\377t\255\002\377" "u\257\001\377k\242\001\377m\247\001\377i\242\001\377l\245\001\377s\255\005\377f\241\001" "\377e\243\001\377i\247\001\377g\243\001\377e\243\002\377d\232\001\377o\247\005\377u\261" "\011\377n\250\001\377n\244\001\377l\242\001\377a\226\002\377u\260\021\377d\245\005\377" "_\241\003\377k\252\022\377n\252\021\377k\243\005\377o\246\001\377t\252\002\377p\251" "\001\377k\243\001\377m\244\002\377k\233\002\377j\235\002\377i\235\002\377q\253\003\377" "y\257\001\377\177\255\001\377\207\264\001\377o\235\002\377e\231\002\377_\222\001\377" "b\234\001\377m\252\001\377u\257\002\377j\234\002\377_\223\001\377n\242\001\377j\237\001" "\377k\242\001\377c\227\002\377_\234\000\377\262\272\213\377\303\254\266\377\324" "\277\312\377\336\316\333\377\372\365\371\377\355\341\351\377\344\321\331" "\377\335\304\310\377\327\277\277\377\347\324\276\377t\247\025\377[\220\001\377" "W\216\000\377a\233\000\377e\234\000\377k\241\000\377p\246\000\377n\243\001\377n\250\001" "\377l\246\002\377n\246\002\377h\235\002\377d\236\001\377_\233\002\377z\260\001\377h\241" "\002\377k\251\006\377x\261\031\377i\245\006\377i\246\003\377b\231\001\377c\235\002\377" "^\233\000\377\217\265?\377\356\344\327\377\323\273\265\377\313\257\246\377" "\341\315\315\377\334\313\315\377\307\263\265\377\266\240\235\377\272\244" "\236\377\313\273\261\377Y\226\011\377_\237\003\377a\232\002\377_\226\001\377]\225" "\002\377b\241\002\377c\237\001\377e\243\001\377c\234\002\377i\240\001\377k\236\002\377" "{\255\002\377\201\263\001\377q\243\002\377q\250\002\377m\245\001\377l\240\001\377n\245" "\001\377p\250\001\377h\235\002\377i\240\002\377q\254\002\377|\264\002\377}\263\002\377" "z\263\001\377n\246\001\377m\245\001\377l\250\000\377h\245\000\377o\247\011\377\203\266" "\035\377|\260\023\377n\241\001\377v\254\001\377x\247\002\377}\256\001\377|\257\001\377" "s\246\002\377t\256\001\377n\245\001\377m\243\001\377j\243\001\377e\234\002\377e\237\002" "\377j\243\001\377q\251\002\377c\236\001\377c\237\001\377k\251\004\377d\242\002\377o\255" "\005\377o\250\002\377p\245\001\377o\243\003\377x\263\012\377g\241\000\377e\246\006\377" "_\240\023\377d\243\024\377g\241\007\377w\255\005\377{\256\001\377z\261\001\377v\257" "\003\377s\254\006\377o\243\005\377k\237\002\377h\234\001\377t\253\003\377\200\265\002\377" "\211\264\001\377\214\263\002\377w\245\001\377k\242\002\377c\227\001\377_\227\001\377" "i\247\001\377p\254\001\377d\225\002\377f\230\002\377o\241\002\377p\247\001\377h\235\002" "\377a\230\001\377U\216\000\377\275\301\232\377\305\247\252\377\303\242\243\377" "\347\325\335\377\364\352\362\377\364\347\357\377\346\323\334\377\312\257" "\264\377\340\313\315\377\316\265\261\377\316\316\230\377Z\214\022\377a\225" "\035\377`\222\"\377`\217&\377m\235\060\377p\242%\377e\235\016\377f\240\012\377" "h\243\010\377c\234\004\377a\223\001\377d\237\001\377e\237\003\377x\261\005\377f\234" "\000\377y\261\040\377r\257\040\377o\257\025\377f\247\005\377e\242\001\377e\235\001\377" "\226\273G\377\354\336\316\377\304\251\245\377\342\311\310\377\327\301\277" "\377\333\310\312\377\327\310\316\377\313\275\302\377\277\256\257\377\271" "\246\242\377\320\276\270\377c\232\013\377d\241\001\377c\227\001\377c\226\002\377" "h\241\001\377e\236\001\377o\256\010\377c\240\001\377a\231\001\377g\242\002\377j\235" "\002\377}\257\001\377\201\263\001\377w\252\002\377q\246\001\377n\246\002\377l\240\002\377" "r\247\001\377r\252\001\377f\232\002\377g\241\002\377v\260\001\377~\261\002\377{\252\001" "\377|\257\001\377u\252\001\377v\263\010\377t\256\014\377x\263\031\377\211\300\063" "\377\205\273+\377~\266\032\377r\254\002\377w\261\001\377|\262\002\377{\255\002\377" "v\250\002\377p\237\001\377y\253\002\377z\252\002\377n\236\001\377l\237\001\377j\241\001" "\377l\243\001\377n\241\002\377p\250\002\377g\235\002\377j\244\004\377m\250\006\377`\232" "\001\377l\251\006\377r\251\001\377{\256\001\377p\241\001\377t\253\004\377o\241\001\377" "m\246\003\377g\245\030\377Y\230\013\377l\246\027\377\211\273%\377\204\267\015\377" "}\262\002\377}\266\012\377z\264\024\377v\260\031\377t\255\026\377\205\274'\377" "\205\275\035\377t\246\001\377\201\255\001\377\221\271\001\377o\237\002\377f\231\002" "\377i\242\001\377d\240\002\377d\243\002\377n\254\002\377j\235\001\377j\231\002\377r\241" "\002\377s\245\001\377i\235\001\377a\230\002\377S\214\000\377\321\316\265\377\316\260" "\263\377\332\277\306\377\366\352\362\377\373\367\372\377\365\355\362\377" "\354\337\345\377\337\311\315\377\317\266\267\377\302\252\244\377\324\271" "\253\377\270\266\232\377emd\377aff\377^bg\377_ej\377gpo\377o\200l\377s\216" "b\377n\217M\377w\240G\377f\226(\377`\225\030\377b\225\021\377v\254\017\377k" "\240\012\377o\246\031\377k\247\030\377h\244\017\377a\233\001\377i\244\000\377\272" "\321u\377\331\304\264\377\301\241\227\377\302\242\232\377\337\313\310\377" "\336\314\316\377\333\313\321\377\341\324\335\377\312\275\310\377\273\254" "\265\377\274\251\253\377\310\266\262\377^\227\014\377c\236\002\377b\226\001\377" "l\242\002\377r\253\001\377l\246\001\377j\252\004\377c\246\002\377`\230\001\377i\243\002" "\377m\237\001\377\200\262\001\377w\247\002\377r\246\002\377p\247\001\377o\244\001\377" "o\241\002\377v\256\002\377x\261\001\377e\233\002\377g\236\001\377u\251\002\377\200\263" "\002\377z\252\001\377\206\272\016\377\203\271\031\377\211\301-\377\200\274-\377" "o\256\027\377k\250\012\377p\255\004\377o\246\003\377m\241\001\377p\245\001\377q\246" "\002\377r\253\002\377p\253\002\377m\236\001\377~\260\001\377|\251\001\377p\234\001\377" "u\250\001\377s\250\001\377q\245\001\377u\252\000\377f\232\001\377r\251\006\377o\247\004" "\377j\241\002\377g\237\001\377i\242\003\377q\250\002\377\205\265\001\377|\256\001\377" "o\246\002\377i\237\001\377i\243\002\377m\252\035\377d\242\040\377o\254*\377l\250" "\027\377\203\265\017\377\204\265\005\377z\255\002\377s\253\006\377v\261\027\377|\266" "!\377~\267\040\377|\267\025\377s\250\000\377x\246\001\377\206\262\002\377z\252\002" "\377i\234\001\377q\253\001\377l\246\001\377k\245\002\377o\252\001\377r\253\001\377o\240" "\001\377u\250\001\377n\240\001\377j\234\002\377e\235\002\377Y\212\000\377\321\311\261" "\377\321\266\271\377\327\277\307\377\347\325\337\377\345\323\336\377\336" "\314\326\377\341\317\327\377\314\271\274\377\271\241\240\377\301\251\250" "\377\212gf\377\306\250\226\377xei\377~ow\377XI\\\377XKa\377[Ne\377XLd\377" "i`r\377tn}\377}{\206\377\201\206\210\377z\205x\377x\212d\377p\222C\377n\226" "?\377f\225,\377l\236.\377d\227\034\377d\226\016\377\271\314q\377\301\254\237" "\377\251\215\213\377\313\261\254\377\330\304\300\377\341\320\320\377\362" "\350\353\377\324\306\316\377\346\332\343\377\311\274\306\377\275\251\262" "\377\271\243\243\377\330\302\274\377i\237\022\377i\240\001\377o\241\002\377y\257" "\001\377n\241\002\377a\227\001\377c\244\001\377g\251\003\377e\235\002\377p\246\001\377" "t\245\002\377\202\263\001\377c\227\002\377f\235\001\377j\245\001\377m\245\002\377o\244" "\001\377z\265\006\377x\262\003\377p\247\002\377p\243\001\377w\252\001\377|\264\000\377" "\202\273\015\377\202\271\027\377v\261\016\377w\261\015\377]\225\000\377c\240\001" "\377i\240\001\377q\244\001\377r\242\002\377x\251\002\377q\240\002\377p\241\001\377l\242" "\002\377n\244\001\377y\253\001\377\201\262\001\377r\240\001\377s\241\002\377v\247\001\377" "q\244\002\377q\246\002\377\201\271\012\377_\222\002\377l\246\004\377l\245\001\377m\241" "\001\377q\251\001\377h\232\001\377s\250\002\377\177\260\001\377\201\263\004\377z\256" "\020\377n\244\014\377g\237\003\377l\246\022\377p\254$\377i\247\034\377c\242\005\377" "n\247\002\377z\253\002\377\200\260\002\377z\255\002\377u\257\006\377y\262\021\377\202" "\273\036\377t\257\013\377u\254\002\377z\252\001\377z\247\002\377\202\261\001\377y\251" "\002\377v\247\001\377t\250\001\377v\243\002\377\177\257\001\377v\251\001\377u\257\001\377" "l\245\001\377h\235\002\377c\226\002\377d\231\001\377[\216\000\377\332\325\272\377\342" "\307\311\377\335\302\307\377\361\345\354\377\375\375\374\377\367\354\363" "\377\357\336\347\377\333\306\312\377\312\260\261\377\262\225\223\377\312" "\260\252\377\317\264\254\377\307\256\254\377\332\306\306\377\321\277\304" "\377\324\312\321\377\260\237\257\377wf\177\377\200m\211\377\233\215\242\377" "\212|\222\377\207v\215\377\201r\206\377\226\207\220\377zoz\377e`h\377ggh" "\377t|r\377\216\227\200\377\313\320\253\377\330\300\271\377\254\214\210\377" "\312\260\254\377\330\303\277\377\323\272\272\377\357\337\343\377\332\311" "\321\377\360\344\355\377\343\330\345\377\311\274\311\377\302\257\267\377" "\305\260\257\377\320\277\264\377^\232\002\377m\243\001\377x\253\002\377z\252\002" "\377m\235\002\377h\240\001\377f\246\001\377d\241\002\377j\250\001\377s\253\001\377x\252" "\001\377~\255\002\377`\223\002\377a\225\001\377d\244\001\377l\252\001\377p\252\001\377" "t\257\002\377q\247\001\377v\256\002\377v\250\001\377\200\267\002\377\201\270\002\377" "\201\271\006\377s\251\002\377q\251\002\377\200\271\013\377e\232\001\377f\233\001\377" "l\243\001\377i\231\002\377o\240\002\377|\255\002\377y\246\002\377r\237\002\377n\236\001" "\377u\250\001\377{\260\002\377z\250\001\377w\241\002\377{\251\001\377w\250\001\377s\247" "\001\377y\260\001\377\202\271\005\377_\223\002\377i\243\002\377p\251\003\377n\237\001\377" "p\247\001\377l\236\001\377r\244\002\377x\252\001\377\177\261\003\377\210\267\027\377" "\221\276\060\377m\243\006\377j\243\003\377w\260\030\377d\243\015\377l\253\017\377" "s\255\006\377r\243\002\377v\253\001\377}\266\014\377k\247\005\377s\255\023\377\204" "\274(\377l\240\012\377q\240\002\377~\255\001\377{\250\002\377x\245\001\377\177\261" "\001\377y\250\002\377x\245\002\377|\247\002\377\204\260\001\377t\243\002\377p\247\002\377" "l\247\001\377d\236\002\377Y\215\001\377]\216\001\377X\217\000\377\326\320\262\377\343" "\303\302\377\354\321\325\377\341\321\333\377\363\353\360\377\353\335\344" "\377\360\335\337\377\340\312\307\377\337\310\304\377\347\317\312\377\353" "\326\323\377\345\323\321\377\360\341\346\377\361\347\354\377\365\360\363" "\377\366\356\363\377\344\327\341\377\340\322\334\377\346\332\343\377\370" "\361\365\377\360\351\355\377\336\322\333\377\303\256\267\377\322\276\275" "\377\312\262\255\377\271\233\220\377\260\223\214\377\232\202\177\377\263" "\235\226\377\301\241\220\377\215oh\377\272\233\226\377\307\256\252\377\333" "\310\310\377\347\327\333\377\364\352\360\377\327\307\321\377\337\320\332" "\377\325\310\326\377\303\263\300\377\277\251\260\377\305\253\252\377\310" "\274\253\377_\240\000\377h\241\001\377k\232\002\377z\251\002\377t\245\001\377n\244" "\001\377g\241\001\377f\236\001\377m\246\001\377u\254\002\377|\254\002\377w\247\001\377" "[\216\002\377^\223\001\377k\250\003\377u\264\004\377r\250\001\377t\252\001\377t\251\001" "\377u\256\001\377u\252\001\377{\260\001\377~\257\001\377\204\264\001\377\203\263\002" "\377\200\263\002\377|\264\003\377a\223\002\377j\242\002\377k\245\002\377m\244\001\377" "w\255\001\377t\245\002\377u\242\002\377w\246\002\377w\247\001\377v\254\001\377o\242\001" "\377r\244\002\377\216\271\016\377\210\264\011\377{\255\002\377\177\267\002\377\201" "\270\002\377~\261\002\377b\237\001\377c\233\002\377h\243\002\377o\252\004\377p\250\001" "\377o\246\002\377q\251\003\377t\245\001\377~\256\001\377\207\265\020\377\226\277." "\377l\240\003\377o\246\003\377t\255\007\377f\244\005\377r\257\022\377m\243\003\377|" "\257\002\377w\255\001\377m\247\003\377\213\270\064\377p\251\033\377\200\271)\377" "d\231\003\377k\235\002\377r\251\002\377r\251\002\377o\243\001\377u\253\000\377x\257\001" "\377v\255\001\377w\256\001\377u\254\002\377m\241\001\377n\237\001\377r\255\002\377`\226" "\002\377Y\214\002\377_\220\002\377[\220\000\377\305\307\223\377\344\277\273\377\352" "\313\312\377\337\312\322\377\356\341\351\377\354\336\340\377\362\337\334" "\377\362\343\337\377\367\356\355\377\342\316\317\377\277\245\247\377\344" "\320\324\377\365\356\360\377\271\244\260\377\364\355\360\377\324\300\314" "\377\342\322\334\377\360\346\354\377\357\345\352\377\305\263\272\377\352" "\340\344\377\353\336\341\377\314\262\262\377\334\305\301\377\347\323\315" "\377\342\313\302\377\334\300\263\377\343\303\257\377\333\274\243\377\303" "\242\212\377\312\254\232\377\306\251\237\377\277\244\236\377\354\331\332" "\377\332\305\314\377\356\342\352\377\332\311\323\377\320\276\311\377\306" "\266\302\377\276\254\265\377\302\252\256\377\321\270\263\377\267\270\214" "\377b\244\000\377i\243\001\377j\232\001\377|\255\002\377w\253\002\377l\235\001\377h\231" "\002\377i\235\001\377l\237\001\377u\251\002\377{\256\001\377k\233\002\377Z\216\001\377" "m\250\010\377x\266\014\377p\252\002\377r\246\002\377s\247\002\377v\260\002\377}\270" "\013\377u\253\002\377{\256\001\377|\255\002\377z\246\002\377\203\261\002\377\216\275" "\002\377u\260\002\377c\225\002\377l\245\001\377q\255\002\377o\247\001\377x\264\002\377" "j\240\001\377k\235\002\377m\236\002\377t\251\001\377s\254\002\377n\245\001\377o\243\001" "\377\221\302\036\377\203\266\011\377\202\267\003\377\202\272\004\377|\260\001\377" "|\254\002\377g\243\002\377h\241\002\377e\236\001\377\200\270\016\377w\257\004\377o\247" "\002\377q\250\002\377r\246\001\377|\253\002\377\211\267\016\377\177\255\013\377p\242" "\003\377k\242\001\377i\243\002\377k\251\005\377j\250\005\377k\243\003\377u\254\001\377" "r\246\001\377\204\256\022\377\260\277]\377q\250\"\377\202\271/\377n\246\006\377" "o\245\001\377o\245\001\377u\254\012\377z\261\025\377r\254\015\377l\251\006\377k\247" "\002\377u\262\011\377o\255\006\377i\244\001\377l\245\001\377l\245\002\377g\240\001\377" "c\227\001\377^\217\002\377V\215\000\377\255\271x\377\340\270\262\377\337\275\266" "\377\360\334\334\377\360\340\342\377\361\342\341\377\340\313\315\377\355" "\340\341\377\370\360\360\377\272\230\234\377\311\255\261\377\367\355\356" "\377\334\310\312\377\344\323\331\377\345\330\336\377\320\276\311\377\352" "\334\345\377\330\307\315\377\261\233\243\377\317\276\276\377\375\373\373" "\377\336\314\316\377\221wx\377\317\272\264\377\347\333\327\377\353\333\327" "\377\353\326\314\377\336\303\261\377\344\304\254\377\351\314\262\377\357" "\327\302\377\325\273\254\377\332\304\274\377\346\324\325\377\326\302\312" "\377\353\335\350\377\350\334\345\377\275\254\270\377\276\255\273\377\274" "\250\263\377\317\267\271\377\340\304\303\377\235\261_\377a\237\000\377f\233" "\002\377u\251\002\377\204\265\002\377o\237\001\377l\236\001\377i\232\001\377k\241\002\377" "o\244\001\377p\246\001\377p\246\001\377i\231\001\377l\242\006\377y\264\022\377p\254" "\003\377q\255\001\377o\245\001\377q\253\001\377u\262\004\377m\246\002\377n\245\001\377" "z\257\001\377w\246\002\377i\223\002\377|\255\001\377\206\272\003\377\200\272\023\377" "h\234\000\377k\242\002\377w\262\002\377\201\271\001\377o\252\002\377q\260\014\377i\241" "\002\377o\243\001\377s\247\001\377t\254\001\377p\243\001\377v\246\001\377\212\275\004\377" "\206\272\003\377\177\264\001\377|\257\001\377w\245\002\377u\247\001\377b\235\002\377" "e\237\002\377d\231\002\377\200\267\022\377z\260\011\377i\237\001\377k\244\002\377s" "\253\003\377u\251\001\377~\261\005\377q\247\001\377g\235\001\377h\235\002\377e\231\001" "\377j\246\004\377g\245\005\377f\245\010\377g\244\005\377c\231\001\377\241\263,\377" "z\244\036\377e\241\016\377u\260\027\377w\254\010\377w\244\001\377\204\264\021\377" "\213\272%\377\203\265\032\377i\242\005\377Z\224\001\377b\233\001\377r\253\001\377" "s\247\001\377l\240\001\377h\243\001\377d\234\002\377f\241\001\377f\236\005\377\\\216" "\006\377W\205\023\377\224\243p\377\352\310\312\377\312\245\250\377\356\330\330" "\377\355\334\332\377\332\312\307\377\307\263\261\377\370\362\362\377\320" "\272\266\377\247zr\377\365\341\335\377\344\315\313\377\335\311\311\377\343" "\326\334\377\345\327\340\377\351\333\343\377\360\346\354\377\355\343\350" "\377\336\317\320\377\374\361\357\377\374\367\365\377\334\307\277\377\207" "g[\377\303\246\233\377\367\360\355\377\333\312\305\377\311\262\254\377\331" "\301\267\377\342\310\267\377\316\261\236\377\341\305\261\377\363\346\327" "\377\356\340\327\377\356\341\336\377\345\325\330\377\342\323\334\377\325" "\306\324\377\267\245\265\377\274\251\271\377\277\255\272\377\337\306\304" "\377\347\315\306\377p\236\026\377e\234\000\377j\241\001\377{\262\001\377\200\264" "\001\377t\245\002\377o\242\002\377k\243\001\377q\256\002\377p\256\001\377m\247\001\377" "p\251\001\377q\246\002\377\202\264\011\377m\241\004\377m\246\001\377m\252\002\377g\242" "\002\377k\247\002\377k\250\002\377d\234\002\377t\253\003\377\177\264\002\377w\247\001\377" "p\240\001\377z\257\002\377y\256\001\377\177\270\010\377r\244\002\377|\256\001\377\206" "\273\002\377\203\263\000\377q\246\002\377u\261\011\377k\247\001\377p\251\001\377t\247" "\001\377u\254\001\377z\253\001\377\205\271\001\377\205\272\002\377~\263\001\377|\260" "\001\377}\260\002\377r\243\001\377s\247\002\377_\236\004\377c\232\001\377l\243\001\377" "u\254\006\377~\265\024\377b\232\002\377e\237\001\377m\253\007\377r\255\005\377y\263" "\011\377i\242\002\377e\235\003\377d\232\002\377i\235\001\377q\252\006\377k\246\004\377" "b\236\002\377k\245\013\377h\234\002\377\213\247\015\377e\227\000\377c\232\002\377h" "\241\003\377v\255\007\377u\252\002\377x\256\011\377k\240\002\377~\261\001\377t\247\001" "\377]\226\001\377b\232\001\377m\247\001\377u\251\001\377v\253\001\377n\245\001\377c\235" "\002\377g\244\024\377i\234*\377b\220\063\377r\212b\377\200\203~\377\340\303\304" "\377\347\325\324\377\371\363\363\377\366\356\357\377\352\331\336\377\365" "\353\354\377\376\373\372\377\344\323\313\377\252\200i\377\354\322\310\377" "\340\310\305\377\341\315\317\377\353\336\345\377\341\322\334\377\350\333" "\343\377\346\330\340\377\332\311\322\377\323\277\301\377\332\302\277\377" "\361\335\321\377\341\307\265\377\204V;\377\330\275\252\377\375\375\375\377" "\354\340\334\377\270\234\233\377\302\246\243\377\317\270\261\377\351\323" "\304\377\340\305\257\377\347\314\272\377\365\352\341\377\353\340\332\377" "\373\364\363\377\360\350\353\377\303\255\274\377\263\235\255\377\267\242" "\261\377\315\262\270\377\336\275\257\377\316\277\230\377h\237\005\377i\237" "\001\377u\261\006\377s\252\002\377|\263\001\377x\253\002\377v\262\002\377q\254\002\377" "u\256\004\377|\261\003\377v\252\001\377r\252\001\377r\253\002\377n\242\001\377i\232\002" "\377n\245\002\377p\252\001\377l\247\002\377k\250\001\377l\251\001\377l\245\002\377u\254" "\003\377\200\265\004\377s\242\001\377r\241\001\377v\256\001\377u\255\004\377\216\304" "&\377x\255\016\377\224\303\034\377\204\265\000\377\177\260\001\377{\255\001\377y" "\257\002\377n\247\002\377s\253\002\377x\254\002\377\177\263\001\377\207\273\001\377\207" "\274\002\377\203\271\001\377}\260\001\377\203\272\003\377{\256\001\377o\242\001\377t" "\253\001\377r\254!\377f\232\000\377q\246\001\377q\247\001\377\202\271\031\377m\252" "\007\377c\241\003\377`\233\002\377i\247\003\377v\262\014\377n\246\002\377e\235\002\377" "l\244\010\377o\247\007\377r\252\005\377i\241\002\377\\\225\001\377p\247\027\377p\241" "\014\377u\243\011\377j\230\001\377k\241\002\377r\252\012\377o\251\007\377j\236\001\377" "n\240\002\377x\250\001\377\200\255\002\377\203\262\001\377Y\224\001\377Y\225\001\377" "d\241\001\377j\237\001\377o\241\001\377q\246\001\377i\242\002\377h\242\030\377r\222" "g\377{\214\200\377\177\201\210\377\254\235\246\377\346\321\315\377\374\367" "\365\377\370\361\361\377\277\250\250\377v[\\\377}db\377\272\241\225\377\363" "\325\307\377\345\301\253\377\364\332\313\377\343\306\275\377\344\313\311" "\377\336\311\316\377\334\313\323\377\344\322\332\377\316\271\301\377\326" "\302\305\377\315\266\264\377\323\273\260\377\353\320\301\377\316\255\226" "\377\216dD\377\372\357\346\377\365\354\347\377\344\330\326\377\331\307\307" "\377\352\332\327\377\320\267\261\377\340\311\274\377\331\277\250\377\335" "\301\255\377\361\341\323\377\346\322\311\377\361\341\337\377\346\324\327" "\377\317\273\306\377\263\233\250\377\274\240\246\377\340\276\263\377\352" "\306\275\377\231\261I\377m\247\002\377i\237\001\377n\250\001\377q\243\001\377\177" "\265\001\377z\256\002\377r\253\001\377o\237\001\377t\243\002\377\201\257\004\377\205" "\270\012\377y\261\010\377p\252\007\377c\234\002\377d\234\002\377s\255\001\377v\252" "\002\377z\261\001\377s\253\001\377p\250\002\377r\251\002\377}\263\007\377~\263\002\377" "|\255\003\377z\247\001\377\201\262\001\377z\257\002\377\200\271\037\377\210\276\063" "\377\230\310>\377z\256\000\377{\254\001\377r\240\002\377r\246\001\377r\255\001\377" "u\261\002\377v\256\003\377\205\271\001\377\213\276\001\377~\254\001\377\201\263\002\377" "\202\265\002\377\207\275\006\377{\256\001\377s\252\001\377n\247\001\377|\265\061\377" "m\250\013\377f\234\001\377o\250\006\377\204\272\040\377\177\266\033\377r\253\017" "\377l\245\012\377q\251\014\377z\261\016\377r\255\010\377d\237\001\377j\245\010\377" "{\260\024\377|\257\022\377s\246\020\377U\221\005\377h\235\020\377x\247\023\377r" "\243\014\377f\234\002\377o\247\001\377p\251\001\377l\241\001\377q\245\001\377t\242\002" "\377\200\262\003\377s\241\002\377x\252\003\377k\240\006\377c\230\001\377d\232\002\377" "j\244\001\377j\241\002\377g\236\002\377j\244\010\377s\240\060\377|\204\205\377||" "\207\377\212\177\220\377\326\307\313\377\370\353\351\377\372\361\354\377" "\310\244\224\377O/$\377#\011\003\377#\014\005\377\024\000\000\377D%\035\377\330\261\233" "\377\377\366\347\377\342\314\300\377\333\277\272\377\333\305\305\377\330" "\276\302\377\276\247\255\377\276\250\251\377\275\243\240\377\337\307\274" "\377\360\334\316\377\355\323\277\377\325\260\221\377\343\303\251\377\306" "\257\237\377\251\221\210\377\266\241\235\377\274\250\251\377\343\326\327" "\377\350\330\327\377\337\312\303\377\334\301\264\377\323\272\246\377\324" "\271\247\377\372\362\354\377\357\337\340\377\315\266\303\377\266\233\254" "\377\257\220\234\377\323\253\236\377\341\270\250\377\333\306\255\377f\235" "\000\377m\247\002\377l\244\001\377q\254\001\377v\256\001\377x\262\002\377o\247\001\377" "j\243\001\377o\242\001\377q\237\001\377u\243\001\377v\254\002\377\200\271\033\377~\267" "-\377f\237\004\377e\233\001\377s\252\002\377|\257\001\377~\260\001\377|\256\001\377y" "\254\001\377}\257\001\377\203\270\005\377}\260\001\377}\256\002\377\202\263\001\377\177" "\256\002\377t\247\006\377\207\300/\377\205\276\066\377\210\302\062\377r\252\000\377" "s\254\001\377s\255\003\377m\250\001\377p\255\004\377n\247\002\377\203\266\001\377\211" "\275\002\377\205\267\001\377|\252\002\377\201\264\001\377\204\267\006\377|\257\001\377" "{\260\002\377v\262\002\377q\251\001\377~\267!\377u\260\015\377`\225\001\377c\234\003" "\377\201\266\031\377\214\276'\377\200\265\034\377\177\264\036\377\200\265\035" "\377\200\266\030\377t\256\011\377c\235\002\377_\230\001\377d\241\002\377w\254\034" "\377w\251)\377V\217\016\377p\241\030\377\205\255\034\377\217\265,\377o\245\020" "\377t\256\012\377q\254\002\377m\244\002\377w\253\006\377~\257\004\377x\255\003\377m" "\246\010\377m\245\015\377\202\263\"\377\203\262$\377d\226\002\377g\240\003\377" "X\215\001\377f\235\024\377d\226'\377\177\217w\377\200|\207\377\210}\216\377" "\241\225\237\377\362\342\333\377\350\321\313\377\355\323\306\377\236n[\377" "\071\025\020\377bA+\377\320\307\317\377\244\241\267\377bLB\377M(\032\377\376" "\354\330\377\355\317\271\377\373\352\337\377\326\254\225\377\347\315\277" "\377\323\270\260\377\340\305\272\377\325\270\254\377\372\365\357\377\375" "\372\364\377\365\336\307\377\300\241\207\377;&\035\377\034\006\014\377\"\012\017" "\377\040\010\010\377<&#\377\234\204}\377\346\326\324\377\350\332\326\377\310" "\256\241\377\352\322\302\377\334\303\256\377\363\344\327\377\371\363\360" "\377\340\317\325\377\267\234\254\377\323\262\264\377\345\277\264\377\354" "\317\304\377pdJ\377s\255\000\377q\246\001\377w\255\003\377z\266\005\377s\256\001\377" "u\261\005\377i\243\002\377l\241\002\377x\253\001\377{\251\002\377~\253\002\377z\253\001" "\377n\246\002\377n\252\034\377\207\275\071\377j\240\002\377{\257\001\377\207\267" "\002\377\177\255\001\377z\255\002\377|\261\002\377~\260\001\377\212\276\010\377|\257" "\002\377{\254\001\377\203\270\004\377y\257\007\377t\256\016\377\217\306?\377\207\300" ";\377x\265\036\377r\261\001\377p\254\001\377r\260\007\377m\254\007\377i\243\001\377" "~\260\001\377\230\303\002\377\177\256\001\377u\246\001\377t\252\004\377\212\301\036" "\377\211\300\034\377y\260\003\377{\257\001\377z\261\001\377s\250\001\377x\257\013\377" "|\262\005\377e\236\002\377c\235\003\377w\255\011\377\206\270\030\377}\262\017\377" "s\253\011\377g\237\002\377z\260\022\377~\264\025\377e\240\006\377^\234\003\377b\237" "\004\377h\240\011\377e\236\021\377R\217\006\377a\230\016\377\221\264\063\377\204" "\252*\377{\253\"\377v\255\031\377c\240\006\377m\251\022\377\203\266\"\377\210" "\267\026\377w\250\002\377m\242\002\377r\251\013\377\203\266)\377\206\265\063\377" "Y\211\001\377h\236\025\377h\232\034\377w\243<\377x\222\\\377\201{\206\377\205" "w\210\377\226\203\223\377\301\260\265\377\267\237\245\377\372\362\357\377" "\371\361\355\377\241\200}\377nP:\377\273\253|\377\307\305\260\377\247\251" "\276\377\322\322\324\377\204dN\377\374\342\313\377\366\327\271\377\366\323" "\260\377\366\320\252\377\370\334\276\377\366\335\306\377\357\317\275\377" "\367\342\321\377\375\377\375\377\372\352\330\377\225t\\\377\065\034\015\377" "B/'\377\201u{\377ymy\377^H@\377/\023\014\377\067\033\025\377\251\210u\377\333" "\311\300\377\350\327\314\377\315\260\242\377\332\301\263\377\354\336\321" "\377\340\317\310\377\351\331\331\377\322\261\262\377\353\303\261\377\373" "\343\326\377\254zc\377\201tC\377y\261\011\377w\250\001\377{\257\003\377z\263\004" "\377v\260\002\377t\257\001\377q\253\001\377o\246\002\377w\253\001\377\177\260\000\377" "\177\255\001\377w\247\001\377n\245\001\377b\233\001\377\202\271)\377w\256\007\377~" "\261\001\377\204\265\001\377\177\260\001\377{\254\001\377\200\262\001\377\200\262\001" "\377\211\300\010\377u\250\002\377t\245\001\377\202\271\001\377}\267\015\377\204\276" "\071\377\244\325`\377\217\307C\377x\265\024\377t\262\003\377y\265\006\377|\266" "\015\377p\247\002\377s\251\001\377\206\270\002\377\204\264\002\377x\253\000\377{\262" "\023\377\222\306=\377~\272\025\377q\252\001\377u\246\001\377~\257\001\377}\263\001" "\377v\253\001\377p\247\007\377\202\265\007\377g\242\012\377c\240\007\377r\253\007\377" "y\260\012\377\202\265\022\377p\247\006\377d\235\003\377v\255\025\377\200\266\036" "\377t\254\017\377q\251\016\377b\231\001\377c\231\002\377^\233\007\377J\214\003\377" "X\225\017\377\204\257\061\377e\226\024\377l\237\035\377v\252(\377`\233\016\377" "t\257'\377|\262\"\377}\260\025\377r\243\002\377m\237\002\377r\247\005\377\203\270" "\040\377w\255\031\377Y\222\005\377k\240#\377l\235/\377v\225^\377\200\200\211" "\377~v\203\377\213|\215\377\234\210\225\377\271\241\246\377\321\272\270\377" "\344\316\314\377\363\354\354\377\336\322\321\377\231\210\201\377\334\330" "\273\377\346\340\303\377\355\347\314\377\333\321\303\377\265\225\215\377" "\362\320\274\377\366\331\276\377\364\321\262\377\363\310\250\377\362\313" "\250\377\362\316\261\377\365\326\303\377\341\271\251\377\357\320\265\377" "tTJ\377)\031\034\377kZ\070\377\275\265\237\377\315\316\341\377\265\266\322\377" "\325\327\333\377\213mK\377\067\031\024\377tL?\377\313\252\231\377\307\251\224" "\377\262\217{\377\320\263\242\377\343\315\301\377\346\330\322\377\332\302" "\303\377\306\225\203\377\330\246\211\377\371\343\323\377\303\242\223\377" "\225\224y\377x\246-\377u\246\012\377\177\263\013\377{\257\004\377z\255\002\377" "z\263\002\377x\263\003\377p\251\001\377s\253\001\377z\254\001\377}\255\001\377x\247\001" "\377q\242\001\377u\247\001\377{\260\003\377~\263\003\377\177\261\001\377\203\264\001" "\377\202\263\001\377~\250\002\377\203\263\002\377\201\264\001\377\205\273\004\377u" "\250\001\377z\253\002\377\202\265\002\377\202\270\007\377\214\304K\377\227\314W\377" "{\272$\377r\261\006\377r\255\001\377u\260\003\377t\254\002\377\177\257\001\377\212" "\275\002\377\177\262\001\377v\256\001\377w\262\012\377\224\310D\377\213\300\064\377" "u\260\000\377u\254\001\377z\256\001\377~\255\001\377\201\266\001\377v\250\002\377v\244" "\002\377\207\267\022\377o\250!\377W\225\003\377h\245\016\377l\247\014\377|\263\037" "\377m\247\024\377l\247\033\377j\246\026\377\200\266%\377n\243\006\377\203\264" "\037\377l\241\013\377b\233\007\377^\233\023\377P\220\004\377Z\231\005\377n\243\017" "\377y\253\036\377n\237\025\377x\253,\377d\234\034\377\205\274;\377{\262\"\377" "x\256\032\377`\226\003\377[\213\001\377V\210\001\377y\254\060\377\177\264!\377\177" "\263(\377{\253=\377}\247V\377\177\213\177\377\203~\213\377\207{\214\377\214" "|\215\377\305\257\260\377\256\221\221\377\301\243\231\377\275\235\220\377" "\303\247\236\377\325\300\273\377\353\334\332\377\370\364\360\377\374\373" "\365\377\364\346\330\377\326\254\235\377\310\241\242\377\332\262\261\377" "\341\273\274\377\333\265\267\377\314\247\254\377\266\216\217\377\323\240" "\177\377\341\301\262\377\277\232\225\377\220b_\377oCF\377\213cP\377\264\241" "\202\377\333\324\262\377\330\327\312\377\341\342\331\377\343\345\333\377" "\276\270\235\377\202hW\377\347\332\321\377\364\351\340\377\312\262\250\377" "\245\215\203\377\317\265\247\377\324\274\261\377\344\322\313\377\347\324" "\320\377\305\236\216\377\331\271\247\377\345\311\267\377\264\201l\377\223" "\220\221\377\207\237|\377v\242\067\377t\251\017\377r\250\002\377p\246\001\377r" "\257\002\377m\251\004\377q\256\002\377x\256\001\377y\245\002\377}\251\001\377|\252\001" "\377{\247\002\377{\252\002\377{\253\001\377\177\266\003\377\207\301\015\377\207\273" "\005\377\203\262\000\377\201\254\001\377\200\257\002\377~\261\001\377\177\270\003\377" "t\247\001\377{\254\001\377\201\265\002\377\227\311/\377\203\273\063\377\210\301" "E\377v\265\037\377q\255\003\377s\255\001\377u\261\002\377}\265\001\377\214\276\001\377" "\203\264\001\377w\254\001\377y\267\016\377\233\320R\377\203\273)\377s\257\005\377" "r\256\001\377w\261\001\377v\245\001\377~\256\001\377~\257\001\377{\252\002\377\221\263" "\001\377\200\256\012\377\203\267D\377V\225\016\377Y\230\014\377_\236\016\377s\256" "&\377y\256\032\377\206\270\040\377q\246\006\377}\263\033\377r\250\022\377~\262" ".\377\210\272G\377\214\275U\377\204\270N\377Z\226\007\377Z\223\001\377a\231\001" "\377t\247\030\377y\247\036\377y\252'\377m\242!\377\177\266.\377i\240\011\377" "v\254#\377`\227\020\377U\206\001\377Q\177\004\377\210\264X\377r\245\023\377\205" "\266$\377~\254\071\377{\226n\377{v\204\377\214\177\216\377\214{\213\377\270" "\242\251\377\260\224\225\377\260\217\216\377\303\241\230\377\324\265\253" "\377\323\265\260\377\326\272\266\377\340\310\304\377\354\326\320\377\360" "\340\330\377\357\334\314\377\366\335\302\377\302|]\377\304\177a\377\362\253" "\227\377\365\263\241\377\314\222\202\377\231f`\377\227fW\377\310\240\211" "\377\261\220\206\377\265\224\215\377\246gY\377\376\354\315\377\373\367\346" "\377\356\352\330\377\357\350\323\377\360\350\325\377\347\340\321\377\324" "\316\307\377\366\361\360\377\351\335\335\377\343\321\317\377\353\332\327" "\377\240\214\214\377t]_\377\326\301\274\377\343\323\315\377\330\305\277\377" "\257\214\201\377\265\224\202\377\241xa\377\216jW\377\201y\201\377\235\240" "\247\377\224\251\207\377{\253\063\377n\244\015\377r\253\010\377q\255\007\377f" "\236\005\377l\242\001\377{\256\002\377y\245\001\377\201\255\001\377~\250\001\377|\251" "\001\377z\250\001\377|\254\001\377\203\274\013\377\207\300\032\377\200\270\013\377" "\203\272\013\377\200\262\002\377x\250\001\377x\255\001\377q\245\001\377p\240\001\377" "\202\264\017\377\233\312\062\377z\257\005\377{\263\026\377g\245\034\377\177\272" "\067\377s\260\016\377u\260\002\377y\263\002\377\200\266\002\377\206\270\002\377|\254" "\002\377t\251\002\377w\263\013\377\211\277(\377\217\302)\377|\263\012\377x\262" "\001\377u\254\002\377u\252\002\377z\257\002\377}\262\001\377\177\261\002\377\234\277" "\002\377\177\261\027\377\210\274K\377a\234'\377m\244\061\377~\264\065\377\210" "\272\013\377\222\276\003\377\210\263\001\377\177\256\017\377\203\264\033\377u\251" "\011\377p\246\011\377x\257\022\377\202\266\034\377v\253\012\377h\236\001\377g\236" "\002\377h\243\002\377r\251\013\377v\254\017\377s\252\021\377~\265)\377{\263'\377" "^\227\004\377n\243\035\377\177\264\070\377[\213\003\377Nx\007\377t\246\070\377n\240" ")\377\177\253\064\377\203\245R\377\215\224\221\377\202v\202\377\217\200\217" "\377\251\227\237\377\317\273\274\377\246\221\226\377\302\252\251\377\340" "\306\277\377\355\327\317\377\324\271\261\377\306\250\244\377\340\313\305" "\377\354\331\316\377\361\341\324\377\375\374\366\377\372\365\357\377\215" "fX\377X%\033\377\310eE\377\260aF\377\033\005\001\377@\033\013\377B\034\027\377\316" "\237~\377\361\323\265\377\366\347\324\377\326\265\253\377\354\332\316\377" "\366\351\343\377\361\344\336\377\356\337\330\377\341\320\311\377\326\301" "\271\377\311\260\251\377\243\204\200\377\211lg\377\252\217\210\377\301\252" "\236\377\301\255\242\377ubd\377\262\234\226\377\336\313\304\377\343\317\311" "\377\242\206\204\377mUK\377Q\065$\377`J=\377\203v\203\377\237\230\246\377" "\246\245\255\377\237\254\227\377\206\250R\377r\242\021\377~\260\021\377n\240" "\003\377q\241\001\377z\257\001\377x\246\002\377~\257\001\377t\242\001\377s\246\001\377" "|\261\002\377\204\267\004\377\205\270\004\377\216\300\027\377{\257\006\377\201\267" "\017\377~\262\001\377}\257\001\377{\255\002\377s\243\000\377~\264\023\377\242\317a" "\377\212\270\036\377|\256\000\377m\243\002\377\\\226\000\377w\264\040\377\200\273" "%\377}\265\006\377\203\271\001\377\203\265\001\377|\255\001\377w\250\002\377u\253\001" "\377s\255\001\377{\266\005\377~\264\005\377}\260\001\377\177\263\001\377v\247\002\377" "s\247\002\377z\253\001\377\202\261\000\377}\254\002\377\231\276\001\377\200\267\062" "\377q\251#\377\205\272/\377\225\305!\377\210\272\011\377x\255\002\377r\246\001" "\377}\257\013\377\207\270%\377\204\265\030\377\201\263\013\377\215\274\032\377" "\233\307\065\377\227\304/\377\217\300\"\377\202\264\014\377r\251\000\377q\255" "\014\377{\265(\377k\245\025\377e\235\021\377\206\272<\377\213\276G\377i\241" "\035\377l\240\033\377\225\303V\377o\240\037\377i\223%\377z\243K\377\201\247" "Y\377\201\244a\377\212\230\210\377\212\204\217\377\237\222\236\377\212y\204" "\377\266\243\245\377\313\267\264\377\253\216\221\377\272\240\233\377\266" "\234\224\377\265\235\225\377\271\241\235\377\306\255\250\377\364\346\333" "\377\336\276\261\377\361\330\305\377\371\365\361\377\375\375\376\377\377" "\377\377\377\240ru\377\230J\067\377\232VD\377uZc\377\320\300\276\377\375\365" "\353\377\377\377\370\377\372\363\340\377\370\347\322\377\360\334\313\377" "\351\323\312\377\351\324\317\377\300\244\245\377\271\235\235\377\317\265" "\261\377\320\265\257\377\301\245\240\377\306\253\242\377\260\222\206\377" "\236\202u\377\233\201u\377\245\214\177\377\226~y\377yab\377\307\262\256\377" "\310\262\257\377\315\270\263\377\231\203}\377_HB\377eOE\377\224\203\210\377" "\214\177\215\377\232\223\242\377\237\237\250\377\223\236\225\377\206\247" "T\377\205\265%\377z\253\002\377{\251\002\377\177\261\001\377~\261\001\377{\261\002" "\377q\245\001\377s\253\003\377|\264\003\377\204\266\003\377\204\262\002\377\205\265" "\005\377\177\262\005\377|\262\012\377~\270\017\377{\256\002\377z\252\002\377w\256\000" "\377\177\267!\377\220\302<\377z\246\000\377|\256\001\377e\226\001\377j\237\001\377" "|\267\012\377\201\271\024\377\204\274\013\377\204\266\001\377}\261\001\377t\252" "\001\377q\246\001\377v\257\001\377x\261\001\377x\255\001\377|\254\001\377\204\270\001\377" "\201\262\001\377y\250\001\377r\242\001\377\216\275\030\377\217\275\015\377t\246\001" "\377\221\274\001\377t\255\035\377\214\300\060\377\202\266\032\377o\250)\377\\" "\227\033\377Y\225\024\377k\243\022\377r\251\011\377y\253\002\377\202\263\002\377" "\216\275\021\377\210\272&\377\207\266\067\377\204\263\066\377\210\267:\377~" "\257\037\377|\256\000\377~\261$\377t\246)\377w\246\063\377u\243/\377\202\256" "\062\377\230\301T\377\210\264>\377\205\266=\377\213\274H\377s\250\064\377h" "\232,\377w\231\\\377\203\232z\377\213\226\213\377\217\212\225\377\227\212" "\232\377\232\214\230\377ucq\377\276\254\251\377\271\243\244\377\264\236\240" "\377\321\271\254\377\257\227\222\377\256\225\222\377\244\214\211\377\370" "\360\355\377\374\367\356\377\367\344\321\377\342\303\261\377\371\364\363" "\377\375\375\375\377\376\376\376\377\304\266\300\377T/<\377\202cr\377\363" "\360\363\377\376\376\376\377\375\375\375\377\376\375\373\377\336\303\257" "\377\323\250\217\377\301\235\222\377\331\275\264\377\371\356\350\377\337" "\313\311\377\267\240\242\377\302\247\245\377\274\242\237\377\322\274\272" "\377\310\262\260\377\264\232\224\377\241\204}\377\273\247\227\377\212qi\377" "\220uq\377nXX\377\262\236\226\377\260\233\226\377\325\300\275\377\331\305" "\303\377\241\211\212\377\202jg\377\217{z\377\217\200\213\377\220\205\226" "\377\220\212\232\377\222\222\234\377\221\235\225\377\207\256T\377z\252\017" "\377v\244\001\377\177\260\003\377{\253\001\377y\256\001\377t\245\001\377w\254\001\377" "|\254\002\377\203\263\001\377\201\260\001\377~\255\001\377\210\271\012\377\222\303" "-\377}\264\032\377z\261\016\377~\256\001\377{\257\001\377\202\273\017\377}\257\011" "\377|\253\001\377z\251\002\377Z\222\001\377q\251\001\377\200\267\002\377\200\265\001" "\377~\261\001\377y\254\002\377v\257\001\377r\251\001\377q\243\002\377|\254\002\377\201" "\262\001\377~\253\002\377\202\263\001\377\202\264\001\377\177\264\001\377z\260\001\377" "t\251\001\377\200\271\024\377z\262\007\377t\247\002\377\215\273\007\377|\267)\377" "b\236\022\377j\242,\377u\253@\377a\231*\377q\246\071\377\205\266+\377v\250" "\017\377f\227\001\377\201\257\001\377\214\275\003\377{\260\015\377_\230\014\377m\244" "!\377k\241\032\377Y\217\004\377\211\267\000\377\210\270\070\377\203\262?\377\234" "\310\\\377\221\275@\377\201\260,\377\213\270?\377\204\263B\377q\244\064\377" "p\244\060\377l\237*\377p\235?\377p\206m\377\205\214\216\377\222\217\235\377" "\221\206\227\377\233\217\236\377\207{\207\377\224\206\215\377\312\270\263" "\377\264\243\240\377\314\263\250\377\320\261\235\377zYN\377yc^\377\352\342" "\341\377\344\316\303\377\337\300\261\377\345\310\274\377\367\351\344\377" "\375\375\374\377\373\367\370\377\374\371\373\377\344\334\347\377\200g|\377" "\314\276\312\377\360\353\355\377\374\371\372\377\373\371\372\377\363\346" "\342\377\324\261\233\377\354\314\261\377\312\245\221\377\325\271\247\377" "\321\261\237\377\353\326\316\377\325\306\307\377\232\205\204\377\264\237" "\234\377\251\224\222\377\250\221\213\377\265\234\224\377\266\233\216\377" "\246\213~\377\234\202y\377\225~y\377kWV\377\231\207\200\377\256\233\227\377" "\321\277\274\377\316\274\272\377\262\237\240\377\243\216\220\377\247\224" "\226\377\241\217\230\377\212}\215\377\201x\211\377\210\203\223\377\213\213" "\230\377\225\244\233\377x\243J\377n\245\036\377\204\271/\377t\251\002\377x\247" "\002\377w\246\002\377\177\262\001\377\201\260\001\377\201\263\001\377{\257\002\377u\253" "\002\377\213\276\037\377\222\301*\377n\245\001\377t\260\010\377\200\266\001\377\201" "\256\001\377\205\265\002\377\177\253\001\377\200\253\001\377x\246\000\377\202\256\026" "\377\200\260\006\377\204\271\001\377\201\265\001\377}\261\003\377v\255\006\377p\252" "\001\377w\254\001\377\207\270\002\377~\254\002\377\201\261\001\377~\254\002\377\201\262" "\001\377\201\265\002\377y\252\001\377{\261\002\377w\260\002\377u\261\000\377u\255\001\377" "s\246\002\377\206\276\062\377p\256\"\377p\254)\377l\246'\377o\247-\377s\252" ".\377c\233\016\377g\236\006\377Z\220\003\377]\222\001\377u\250\001\377\207\272\007\377" "z\260\007\377n\244\023\377f\231\022\377]\221\004\377W\215\007\377\211\264\002\377q" "\246\030\377\225\304_\377\214\275G\377y\252\027\377y\251\027\377|\253%\377\223" "\277G\377]\222\032\377o\241+\377r\241+\377\203\243a\377\243\244\252\377\260" "\253\270\377\251\237\257\377\241\230\250\377\220\207\226\377wiz\377\215y" "\206\377\270\244\242\377\272\241\237\377\341\307\271\377\277\232\204\377" "\257\214x\377\344\321\311\377\375\371\366\377\372\370\365\377\367\350\341" "\377\352\315\307\377\364\334\333\377\367\356\360\377\374\366\370\377\371" "\364\370\377\275\256\274\377\204v\204\377\265\243\261\377\321\273\302\377" "\335\307\311\377\362\344\346\377\372\363\363\377\371\356\352\377\261\207" "r\377\266\210k\377\326\262\225\377\376\366\354\377\373\370\365\377\373\364" "\363\377\266\241\237\377\231\205\200\377\307\265\251\377\276\252\227\377" "\275\250\225\377\314\265\250\377\264\234\217\377\276\252\240\377\247\223" "\216\377}eh\377\212tq\377\247\217\213\377\266\241\235\377\307\265\260\377" "\305\262\256\377\235\205\207\377\245\216\222\377\263\244\253\377\232\214" "\232\377\200u\206\377\200x\213\377\212\205\226\377\212\213\227\377\206\226" "\211\377~\251V\377{\257\062\377x\253\000\377\200\260\001\377v\245\002\377}\255\001" "\377\200\263\001\377}\264\001\377w\257\002\377r\254\007\377\222\306F\377\200\264" "\032\377q\247\001\377q\251\003\377w\262\003\377\200\260\001\377\203\256\001\377\177" "\250\001\377~\251\002\377x\246\001\377\207\267\010\377\207\267\004\377\205\267\002\377" "}\257\001\377\204\266\013\377\227\302&\377u\250\000\377|\255\001\377\215\300\001\377" "\204\262\001\377\207\264\001\377\205\260\001\377\210\267\001\377\203\262\001\377x\246" "\002\377\177\257\001\377w\246\002\377z\257\001\377~\263\001\377s\243\001\377`\241\021" "\377N\214\000\377c\240\011\377y\262\037\377d\240\005\377b\237\006\377p\253\017\377" "l\243\002\377g\225\003\377w\246\014\377q\241\000\377\177\263\004\377u\255\005\377~\263" "\"\377\\\204\000\377e\216\007\377d\217\012\377}\253\003\377\216\303\064\377\177\262" "@\377g\236\026\377[\216\005\377h\231\006\377~\254!\377\220\274A\377u\241)\377" "\206\255S\377\232\260\204\377\243\250\243\377\234\230\244\377\210\200\225" "\377\222\211\233\377\217\204\226\377\224\204\225\377\215\200\216\377\242" "\224\231\377\240\217\223\377\320\274\271\377\336\311\302\377\262\226\217" "\377\267\233\231\377\363\357\360\377\373\370\365\377\351\324\312\377\351" "\317\311\377\366\346\346\377\365\353\355\377\360\352\356\377\361\347\356" "\377\322\306\322\377}i|\377P\070H\377\216\200\215\377\313\275\306\377\340" "\322\330\377\364\347\353\377\345\316\313\377\347\320\312\377\327\265\250" "\377\336\304\265\377\320\262\242\377\311\252\233\377\332\306\275\377\352" "\342\342\377\331\314\316\377\216\200|\377`TR\377ygb\377iXT\377\231\200t\377" "\314\270\255\377\301\254\247\377\262\237\236\377\234\212\211\377\202on\377" "\266\243\237\377\234\206\205\377\267\242\240\377\302\256\256\377\253\226" "\233\377\237\211\221\377\315\300\306\377\322\306\313\377\241\225\242\377" "\210~\220\377\214\205\227\377\214\207\226\377\211\211\224\377\210\233\204" "\377x\250\066\377l\232\000\377\203\257\002\377\205\265\001\377\202\265\002\377\203" "\267\002\377|\257\002\377x\255\001\377w\262\007\377w\261\022\377n\237\001\377q\245\001" "\377r\251\001\377t\256\002\377\202\266\004\377\204\262\002\377\205\257\002\377~\251" "\001\377{\247\002\377\204\265\001\377\206\265\002\377\204\266\001\377~\257\001\377\205" "\261\002\377\222\274\002\377\206\264\001\377y\251\002\377\177\261\001\377\205\271\002" "\377\201\256\001\377\205\261\001\377\212\272\002\377\206\265\001\377{\252\002\377}" "\253\002\377y\250\001\377}\260\001\377{\255\001\377r\241\002\377d\236\002\377[\223\002" "\377l\250\025\377|\264\031\377t\255\011\377^\225\001\377l\246\004\377w\252\003\377" "b\215\001\377^\215\002\377q\242\001\377\205\266\005\377v\253\007\377\216\305+\377p" "\236\003\377f\214\001\377Ry\001\377t\254\020\377z\264$\377Q\205\010\377~\260\040\377" "T\206\003\377_\217\005\377\177\253\040\377\260\317t\377q\230B\377k\222E\377\213" "\230\206\377\252\250\262\377\254\245\263\377\232\216\242\377rdx\377\201m" "}\377\205q|\377\220|\201\377\221}|\377\202ge\377\334\300\254\377\340\274" "\242\377\255~d\377\353\333\324\377\372\367\367\377\344\325\315\377\356\337" "\325\377\351\324\312\377\275\245\242\377\307\265\267\377\342\331\337\377" "\307\301\310\377zq~\377\070\036+\377N\067D\377UBN\377\232\217\233\377\325\312" "\323\377\312\273\302\377\331\310\306\377\343\320\315\377\343\313\304\377" "\277\236\220\377\310\237\203\377\370\355\345\377\357\341\331\377\332\312" "\306\377\327\320\315\377\274\253\246\377\273\246\235\377\271\242\232\377" "\225}w\377mOB\377\271\233\210\377\335\312\301\377\251\216\207\377\250\220" "\212\377nVS\377\253\227\216\377\302\257\251\377\247\220\217\377\261\234\235" "\377\254\232\237\377\240\213\227\377\260\237\252\377\314\277\305\377\323" "\306\314\377\235\221\241\377\214\203\226\377\205~\220\377\202\177\215\377" "\211\217\224\377v\233U\377i\234\010\377\200\261\001\377\203\262\002\377\202\262" "\001\377\201\262\001\377}\253\001\377}\257\002\377~\262\001\377v\247\002\377x\246\001\377" "x\247\002\377s\241\002\377x\260\003\377y\254\001\377\177\256\001\377\202\263\001\377" "\202\256\002\377\200\254\002\377\206\264\001\377\207\264\002\377\211\271\002\377\204" "\255\001\377\223\267\001\377\245\307\001\377\212\263\002\377{\256\001\377s\245\002\377" "~\260\001\377}\254\002\377\203\262\002\377\207\267\001\377\211\271\001\377\177\256" "\001\377~\256\002\377\177\263\002\377}\257\001\377u\244\001\377z\252\002\377g\236\001\377" "f\236\001\377s\256\026\377\210\276.\377z\262\020\377t\256\011\377i\235\001\377s" "\246\002\377p\240\001\377o\235\001\377w\242\000\377\223\301\071\377w\254\040\377\214" "\301/\377y\256\013\377m\240\002\377a\222\000\377z\262\025\377k\242\012\377h\226" "\020\377x\250\024\377Gy\000\377d\214\022\377\212\246\064\377\262\316\213\377\204" "\245l\377\207\224\211\377\220\216\234\377\207\177\222\377\205{\220\377\204" "w\212\377l_r\377\211w\203\377\234\206\217\377\266\243\250\377\254\233\235" "\377\277\256\255\377\367\347\340\377\326\271\243\377\314\262\244\377\336" "\324\322\377\354\342\336\377\367\356\347\377\324\300\262\377\236\214\207" "\377\261\247\242\377\271\262\264\377\267\260\266\377l^g\377XIP\377ven\377" "cNZ\377eS\\\377aNY\377\210z\210\377\277\271\301\377\316\301\306\377\252\220" "\215\377\273\240\231\377\337\306\277\377\314\270\255\377\320\267\243\377" "\336\314\276\377\363\353\346\377\327\313\312\377\335\317\322\377\355\341" "\342\377\364\355\355\377\341\323\315\377\234\205z\377\243\213\201\377\316" "\272\262\377\305\256\242\377\270\241\225\377\207sm\377zd`\377\261\237\225" "\377\254\231\225\377\262\240\235\377\262\243\247\377\227\205\222\377\221" "~\216\377\304\266\276\377\326\311\316\377\315\300\310\377\241\226\246\377" "\213\205\227\377\205\201\221\377\216\215\233\377z\216x\377\223\273S\377\205" "\265\016\377\210\266\005\377\213\271\002\377\205\261\001\377~\257\001\377y\257\001\377" "s\245\001\377z\254\001\377\200\257\002\377}\254\002\377}\253\010\377}\261\013\377y" "\254\004\377\177\257\002\377}\253\002\377~\253\001\377v\246\002\377u\245\002\377\200" "\260\001\377\205\265\002\377\203\253\001\377\225\275\001\377\223\267\001\377\200\253" "\003\377\200\262\001\377{\261\002\377t\246\002\377x\251\002\377\203\264\001\377\212\267" "\001\377\221\301\001\377\206\262\001\377\200\257\001\377\201\263\001\377z\251\002\377" "w\246\001\377\204\264\001\377\226\275\002\377q\247\001\377m\246\002\377u\256\011\377" "o\247\001\377y\255\001\377~\260\002\377~\256\002\377z\247\001\377}\253\001\377\220\271" "#\377\227\302a\377Y\227\003\377\205\272\060\377}\260\026\377o\242\002\377s\253" "\004\377v\254\003\377y\252\003\377z\247\010\377\210\263I\377}\243F\377\201\242\066" "\377\232\264g\377\242\276\202\377z\215u\377|}\205\377\203|\214\377\204x\211" "\377\213~\220\377~p\201\377|n{\377\265\245\252\377\327\313\317\377\354\344" "\351\377\326\320\326\377\343\326\327\377\342\315\277\377\310\265\251\377" "\341\324\317\377\344\326\323\377\350\335\334\377\352\343\340\377\262\243" "\217\377\234\207l\377wjX\377\204\201v\377smk\377iZ^\377\212x\177\377\235" "\212\226\377\221~\213\377\204nx\377\214x\200\377eV\\\377kci\377\244\237\242" "\377\267\252\252\377\240\217\215\377\212tf\377\301\255\235\377\333\311\275" "\377\331\316\307\377\333\312\277\377\365\357\355\377\351\336\334\377\335" "\317\310\377\303\250\224\377\301\250\226\377\326\310\301\377\223\203\177" "\377\270\250\242\377\333\311\303\377\264\237\221\377\220zv\377`LN\377\221" "~z\377\236\215\206\377\226\203\204\377\247\227\233\377\234\213\230\377\232" "\214\234\377\255\235\252\377\311\273\303\377\323\310\317\377\314\303\312" "\377\243\233\252\377\205\177\220\377\215\212\230\377\202\207\213\377v\233" "?\377\214\272&\377\210\264\001\377\213\257\001\377\215\264\001\377\201\260\001\377" "t\250\001\377q\243\001\377u\244\002\377z\245\002\377z\250\001\377z\255\010\377\200\265" "!\377\202\271'\377x\253\001\377\206\263\003\377\200\260\002\377w\247\002\377u\245" "\001\377\201\260\002\377\207\265\002\377\206\260\001\377\214\267\001\377\214\273\004" "\377\201\255\003\377}\262\001\377p\255\002\377k\242\002\377h\234\001\377}\257\002\377" "\202\256\002\377\217\274\002\377\221\276\001\377\200\257\002\377{\256\002\377w\247" "\001\377~\261\001\377\202\265\001\377\215\265\001\377\202\264\003\377x\253\006\377i\227" "\001\377y\256\001\377w\251\001\377d\224\001\377g\226\002\377\200\255\002\377\205\262" "\000\377\204\266\"\377f\232\020\377Z\231\001\377p\250\034\377\203\264%\377}\261" "\035\377v\255\013\377r\251\003\377{\254\001\377\201\257\001\377\211\260i\377\216" "\265q\377\234\276i\377|\246C\377n\225J\377\201\211\205\377\203\177\213\377" "\203y\213\377\204w\212\377\204w\211\377\242\231\244\377\253\242\252\377\307" "\275\306\377\342\325\336\377\313\277\306\377\277\265\276\377\323\310\314" "\377\312\261\247\377\306\247\217\377\327\301\255\377\317\274\265\377\325" "\307\277\377\312\266\242\377\272\242\202\377\234\202a\377oaM\377ph\\\377" "tol\377\247\234\237\377\275\261\271\377\252\233\247\377\226\204\220\377\245" "\225\240\377\246\230\241\377\242\230\234\377pfl\377ife\377\213\211\201\377" "|rh\377\242\232\222\377\204t_\377\257\237\214\377\321\304\264\377\344\336" "\331\377\365\363\364\377\343\335\330\377\317\275\257\377\250\205e\377\260" "\217r\377\247\205j\377\230xb\377\223vi\377\332\314\311\377\316\301\277\377" "\277\254\245\377^FD\377}ib\377\225\200|\377\222\177~\377\230\206\213\377" "\227\210\224\377\260\244\261\377\252\232\247\377\262\243\256\377\312\274" "\306\377\313\277\307\377\307\273\305\377\233\221\242\377\211\200\222\377" "\203~\215\377u\203h\377u\246\022\377~\257\007\377\177\247\002\377\225\264\001\377" "\202\253\002\377j\231\002\377z\251\002\377\201\252\001\377\201\254\001\377r\234\001\377" "~\257\002\377}\260\007\377w\255\010\377w\251\003\377\223\277\031\377\207\270\011\377" "\200\261\013\377x\245\001\377\206\262\001\377\213\267\001\377\206\257\002\377\212" "\262\001\377\220\276\016\377\214\276\026\377{\264\002\377m\250\001\377n\251\001\377" "h\235\001\377|\262\001\377}\255\001\377\210\264\001\377\221\275\002\377\203\263\002\377" "q\245\001\377u\252\001\377|\267\003\377~\270\001\377\207\260\001\377\206\256\001\377" "\211\266\007\377r\236\002\377y\251\002\377j\234\001\377P\202\002\377\\\216\002\377x\245" "\001\377\210\267\026\377\206\266\021\377|\250\002\377g\237\012\377c\232#\377\200" "\264\060\377t\253\034\377l\247\017\377m\244\014\377\206\264\032\377\227\274\070" "\377w\240\037\377z\246\064\377\216\267X\377}\252S\377q\217`\377\212\207\217" "\377\215\205\222\377\230\214\234\377\230\215\235\377\241\223\242\377\210" "z\210\377\302\263\275\377\335\321\331\377\353\340\347\377\325\314\324\377" "\266\245\255\377\247\217\214\377\243\213\200\377\330\312\302\377\326\310" "\300\377\303\255\241\377\302\245\210\377\301\246\210\377\263\227z\377\221" "v\\\377`M@\377pgb\377\252\247\251\377\312\304\310\377\305\276\305\377\313" "\306\314\377\260\250\262\377\275\264\275\377\274\263\274\377\254\245\254" "\377\255\250\256\377xss\377ysl\377ri`\377uja\377\242\232\212\377\265\257" "\231\377\302\275\252\377\317\310\271\377\331\325\320\377\340\332\332\377" "\325\305\276\377\303\252\224\377\322\272\246\377\277\244\212\377\237}a\377" "tS@\377\275\243\216\377\333\313\277\377\343\331\326\377\214{}\377xd`\377" "ve\\\377\204rq\377\210w\200\377\224\204\221\377\251\232\247\377\274\256\270" "\377\273\253\266\377\265\242\256\377\313\275\306\377\330\313\323\377\321" "\305\314\377\217\201\225\377\211\200\221\377x~y\377o\234<\377w\253.\377p" "\245\001\377\201\256\001\377\177\250\001\377r\236\002\377\177\254\002\377\206\261\002" "\377\201\253\001\377{\245\001\377\177\250\001\377~\247\001\377\202\261\001\377r\242" "\002\377|\253\001\377\202\262\002\377|\254\003\377|\252\001\377\211\267\001\377\206\262" "\001\377\203\256\001\377\206\257\001\377\202\253\001\377\201\264\000\377\201\272\006" "\377n\244\001\377q\252\001\377l\240\002\377~\261\004\377\206\266\012\377\215\266\002" "\377\213\264\001\377\211\265\001\377p\251\001\377s\254\001\377{\257\001\377\202\265" "\001\377e\221\001\377\203\255\002\377\216\270\001\377\207\261\001\377j\232\001\377_\217" "\002\377X\210\002\377s\241\002\377z\254\001\377\214\274A\377g\230\020\377g\223\002\377" "y\251\010\377\203\265'\377\215\277@\377y\256\063\377\233\303i\377\236\302a" "\377\177\254\015\377{\254\003\377l\236\011\377n\236#\377o\235\071\377p\230L\377" "}\205\202\377\217\210\226\377\242\234\247\377\221\210\230\377\214\177\220" "\377\226\206\224\377\256\234\245\377\320\303\311\377\334\322\332\377\311" "\271\302\377\277\257\270\377\252\226\241\377\245\226\227\377\177j_\377\320" "\300\264\377\320\276\257\377\320\275\252\377\312\271\244\377\301\257\234" "\377\274\254\232\377\274\256\241\377\236\224\216\377\250\244\243\377\302" "\300\304\377\277\275\301\377\311\307\314\377\317\315\322\377\300\270\301" "\377\303\300\306\377\311\307\314\377\266\263\270\377\315\314\320\377\271" "\271\274\377\235\234\230\377~ys\377TFB\377YOS\377\215}e\377\277\264\237\377" "\266\253\223\377\273\253\220\377\300\264\243\377\336\325\323\377\337\324" "\322\377\330\311\301\377\321\275\255\377\273\240\206\377vVA\377\225sZ\377" "\336\316\300\377\350\336\327\377\260\244\250\377\227\210\221\377\224\207" "\205\377ucd\377\206x\202\377\223\205\222\377\243\225\242\377\262\242\256" "\377\300\261\272\377\273\252\263\377\301\261\271\377\312\273\301\377\317" "\300\306\377\321\306\312\377\233\222\240\377z{\203\377w\221o\377\211\264" "c\377p\247\004\377t\250\002\377y\247\002\377}\253\001\377~\251\001\377\201\257\001\377" "{\247\001\377z\244\002\377v\237\002\377\203\252\001\377\204\261\002\377w\245\002\377" "s\243\001\377z\253\001\377}\261\001\377\201\262\002\377\203\255\002\377\207\263\002\377" "\203\256\002\377\201\254\002\377\204\255\001\377\206\267\002\377z\257\002\377s\251" "\001\377t\254\002\377j\233\001\377n\237\002\377\220\300\024\377\212\270\000\377\211" "\264\001\377{\251\002\377{\260\002\377v\252\002\377\177\257\001\377\202\263\001\377b" "\221\002\377g\225\001\377\213\267\001\377\224\276\012\377\205\261\005\377l\232\001\377" "d\221\002\377w\246\002\377{\253\001\377r\247\000\377T\211\000\377P\207\002\377O\210\002" "\377{\255#\377\227\303K\377\231\304Z\377|\256\067\377z\251\037\377\204\260" "\002\377\210\261\007\377\202\253*\377q\237.\377o\233>\377q\217[\377\231\227\241" "\377\226\216\236\377\200u\211\377\207}\221\377\200t\206\377\235\215\232\377" "\303\267\273\377\306\267\276\377\251\221\233\377\266\240\253\377\315\276" "\306\377\311\274\304\377\241\216\217\377\264\237\226\377\275\243\214\377" "\272\241\206\377\264\235\177\377\242\210j\377\254\225}\377\304\264\253\377" "\331\323\322\377\316\313\315\377\300\277\300\377\300\300\300\377\271\271" "\272\377\311\311\315\377\312\311\316\377\302\301\306\377\304\303\311\377" "\310\307\315\377\312\311\316\377\273\272\276\377\320\320\324\377\276\301" "\277\377\266\266\263\377\233\232\223\377\210\177\204\377jY]\377\212pV\377" "\305\273\247\377\276\263\235\377\301\262\232\377\301\265\241\377\327\317" "\310\377\316\277\261\377\301\261\237\377\304\261\237\377\261\231~\377|\\" "C\377\312\263\232\377\342\325\312\377\300\262\257\377l`g\377\200t~\377\243" "\232\241\377\215\201\213\377\230\213\227\377\217\201\221\377\243\225\244" "\377\271\252\264\377\310\271\277\377\261\240\251\377\274\255\264\377\276" "\255\265\377\323\306\311\377\315\303\311\377\205\202\216\377m|q\377\234\272" "\220\377z\261\034\377l\241\000\377r\247\003\377\177\256\015\377s\246\001\377|\262" "\012\377{\256\006\377v\244\001\377x\246\001\377\177\256\001\377s\241\002\377p\236\001" "\377z\251\002\377z\250\001\377w\251\001\377y\260\001\377|\256\002\377\201\262\001\377" "\177\252\001\377{\245\002\377\206\261\001\377\207\271\002\377o\242\001\377r\255\006\377" "u\256\005\377p\240\001\377j\233\002\377\204\270\025\377\231\304.\377\207\270\004\377" "x\252\002\377k\240\001\377|\254\001\377~\255\001\377~\261\001\377k\235\001\377m\235\002" "\377{\252\003\377\223\277\031\377}\253\002\377}\254\001\377~\256\002\377|\261\003\377" "m\236\002\377p\245\001\377i\245\004\377u\257&\377_\232\037\377\214\275f\377\215" "\277T\377z\255\013\377m\237\002\377p\230\002\377\211\262\001\377\213\262\004\377u" "\237\003\377]\214\000\377h\224\062\377\213\225\214\377\222\213\227\377~t\204\377" "xp\200\377}u\207\377\265\254\271\377\276\260\264\377\271\244\250\377\267" "\240\244\377\270\243\253\377\303\264\276\377\277\256\266\377\273\254\264" "\377\241\217\223\377\227{p\377\276\241\213\377\267\235\210\377\272\246\223" "\377\275\253\227\377\271\246\222\377\314\276\267\377\340\333\334\377\333" "\331\335\377\307\307\307\377\275\276\271\377\261\262\254\377\275\302\276" "\377\276\301\300\377\271\272\272\377\272\273\274\377\303\304\307\377\301" "\303\304\377\257\262\255\377\274\300\273\377\307\314\312\377\311\313\312" "\377\302\302\303\377\274\273\276\377\264\257\260\377\256\241\234\377\326" "\321\312\377\265\256\234\377\305\274\253\377\266\246\223\377\314\300\261" "\377\301\270\261\377\320\306\272\377\306\272\254\377\304\253\226\377\216" "pS\377\242\207l\377\312\271\243\377\256\231\216\377yjs\377tdj\377rek\377" "\245\234\246\377\264\251\261\377\223\204\223\377\237\215\235\377\256\236" "\250\377\301\262\271\377\277\256\267\377\265\242\254\377\271\250\260\377" "\273\253\257\377\343\327\327\377\236\225\242\377sux\377\215\243\203\377\177" "\260)\377p\243\000\377n\236\002\377r\244\003\377j\231\003\377|\262\016\377p\244\001" "\377u\253\003\377\200\264\021\377t\247\002\377k\232\002\377t\242\002\377y\246\001\377" "\204\262\002\377u\244\002\377t\250\001\377m\237\001\377x\257\002\377|\255\001\377~\251" "\002\377\213\273\007\377\201\264\002\377q\246\003\377u\260\011\377n\240\001\377s\243" "\001\377y\250\002\377\201\266\011\377\243\310B\377\201\261\006\377j\237\002\377a\227" "\002\377\217\274\002\377\206\260\001\377\201\262\001\377h\227\002\377u\246\001\377l\234" "\002\377p\243\017\377{\256\030\377e\234\006\377Y\220\003\377v\257\024\377l\242\004\377" "p\246\002\377u\255\010\377\211\277:\377\177\263:\377r\250\065\377\200\264B\377" "`\227\022\377[\216\001\377|\253\001\377\215\266\002\377\210\257\006\377\227\265\065" "\377\201\246@\377w\230a\377\211\216\210\377\214\207\217\377\217\204\222\377" "\226\217\236\377\253\241\256\377\233\207\222\377\264\240\244\377\303\261" "\263\377\270\244\252\377\270\246\260\377\246\223\236\377\275\256\267\377" "\315\300\311\377\244\217\225\377\206ig\377\250\216{\377\265\236\213\377\252" "\223\203\377\261\232\206\377\261\235\214\377\317\306\301\377\327\322\322" "\377\327\325\326\377\313\314\311\377\263\266\251\377\243\251\231\377\254" "\261\244\377\261\266\253\377\263\270\256\377\265\272\262\377\273\301\272" "\377\271\300\271\377\265\272\262\377\263\271\261\377\301\306\300\377\305" "\312\310\377\316\320\322\377\317\317\323\377\316\315\321\377\326\326\330" "\377\315\312\305\377\302\274\263\377\273\261\243\377\270\256\233\377\255" "\237\211\377\277\265\244\377\315\305\273\377\307\275\257\377\301\263\241" "\377\227~h\377gK\063\377\245\215u\377\211xk\377XLL\377h`c\377\204z\177\377" "\204u\200\377\255\242\252\377\261\242\254\377\236\214\232\377\252\231\244" "\377\273\253\264\377\277\260\266\377\271\250\256\377\267\246\255\377\272" "\251\254\377\325\307\310\377\304\270\300\377\177y\203\377\214\232{\377\211" "\265\067\377r\250\002\377k\233\001\377q\246\003\377s\247\004\377r\247\004\377j\237\002" "\377^\217\001\377z\246\004\377~\253\007\377r\234\001\377\177\253\000\377\203\257\002" "\377~\253\002\377z\251\002\377p\245\002\377k\235\001\377n\237\001\377|\257\004\377\201" "\263\011\377{\256\003\377\202\267\014\377\201\270\023\377o\246\000\377o\241\001\377" "o\240\002\377~\254\001\377y\251\000\377\212\267\016\377w\246\002\377h\235\002\377Y\221" "\002\377|\260\001\377\217\272\002\377\212\265\000\377z\246\002\377v\245\001\377f\225" "\002\377_\215\001\377v\251\014\377\205\266$\377e\233\003\377_\224\001\377w\254\032" "\377\204\267+\377\207\273)\377~\262\020\377z\254\000\377{\255\014\377\205\263" "\067\377\205\265\063\377U\216\003\377f\234\005\377\207\262\013\377\211\260\030\377" "\301\301\247\377\212\217x\377{\204y\377\204\204\210\377\221\207\223\377\242" "\227\247\377\233\222\240\377\220~\216\377\232\207\221\377\302\260\267\377" "\311\272\301\377\271\250\260\377\254\227\242\377\265\243\255\377\276\257" "\271\377\301\264\274\377\274\256\265\377\222\200}\377p_R\377\256\223~\377" "\250\215r\377\253\216s\377\306\262\244\377\277\261\253\377\322\314\311\377" "\324\324\323\377\305\307\301\377\266\271\252\377\247\255\224\377\237\251" "\212\377\242\253\215\377\242\256\220\377\240\256\221\377\252\266\237\377" "\255\272\245\377\255\271\243\377\255\270\245\377\266\277\262\377\303\310" "\302\377\313\316\316\377\316\322\325\377\330\331\335\377\332\332\334\377" "\317\314\307\377\301\271\255\377\275\261\240\377\273\257\241\377\217zc\377" "~iP\377\214{c\377\233\213x\377\311\273\261\377\241\211w\377kVH\377q\\J\377" "aNB\377UF?\377bUU\377xlt\377\212~\212\377\225\207\221\377\245\227\240\377" "\232\213\230\377\260\242\255\377\257\236\252\377\312\272\302\377\300\261" "\267\377\260\236\250\377\301\261\267\377\321\302\305\377\312\275\301\377" "\200x\207\377\213\225l\377\217\262=\377i\241\003\377r\250\012\377t\247\004\377" "o\234\001\377g\224\002\377[\213\001\377m\231\002\377\206\253\002\377\206\261\002\377" "~\250\005\377\232\301\020\377\203\256\001\377|\250\002\377w\247\002\377s\250\001\377" "k\233\002\377n\236\002\377s\246\000\377\216\275/\377{\260\005\377x\254\002\377{\263" "\006\377v\254\004\377r\244\002\377v\245\002\377\202\256\002\377n\235\002\377l\235\001\377" "s\244\001\377j\237\001\377c\234\001\377\202\273\031\377\210\270\001\377\207\262\002" "\377\206\260\001\377i\226\002\377U\206\001\377_\215\002\377i\232\001\377e\227\006\377" "y\257\026\377z\255\035\377\220\277\066\377\206\265\037\377m\237\002\377p\240\000" "\377\177\256\000\377\221\270\000\377\214\267\061\377v\241G\377^\177\070\377awE" "\377q{c\377c_h\377G>N\377TLY\377olt\377\227\223\235\377\241\231\247\377\224" "\210\230\377\203t\206\377\222\200\216\377\256\236\245\377\310\273\300\377" "\243\217\231\377\266\243\253\377\231\204\220\377\300\262\271\377\300\262" "\273\377\246\225\234\377\261\240\242\377\265\246\243\377\211so\377rQF\377" "nK>\377\227xe\377\252\212{\377\276\253\241\377\306\272\264\377\273\263\253" "\377\265\262\244\377\251\251\222\377\245\251\207\377\233\244y\377\227\241" "q\377\221\235i\377\214\231g\377\222\236p\377\222\240u\377\225\243|\377\244" "\261\222\377\250\265\235\377\267\301\262\377\306\315\306\377\306\313\311" "\377\323\327\327\377\327\330\332\377\323\322\322\377\311\304\277\377\304" "\274\266\377\271\254\243\377\302\272\264\377\271\256\245\377\252\232\214" "\377\252\223{\377\270\245\217\377\264\237\217\377\203ie\377t`\\\377[EC\377" "wfd\377vkm\377znu\377\202t}\377\217\201\212\377\231\211\224\377\232\207\222" "\377\222\200\216\377\255\236\252\377\274\255\264\377\304\265\273\377\252" "\230\242\377\233\207\222\377\257\233\237\377\320\303\305\377\271\254\267" "\377\240\236{\377\225\260H\377\202\261\064\377\200\260\036\377x\251\003\377u" "\243\003\377l\226\001\377j\223\002\377{\242\001\377\205\256\007\377\206\264\016\377" "\206\260\031\377\255\315;\377\201\263\002\377\177\250\002\377\177\247\002\377{\244" "\002\377y\242\002\377w\237\000\377\215\273$\377\227\304\067\377z\252\003\377z\247" "\001\377\200\257\002\377~\255\004\377o\236\002\377\203\256\002\377\204\255\003\377q\236" "\002\377m\235\002\377y\252\001\377u\247\002\377k\237\003\377t\252\003\377y\247\002\377" "\206\263\002\377z\246\002\377o\237\002\377Hw\002\377\\\213\001\377d\225\003\377c\226" "\014\377\224\306<\377\214\276\065\377\210\264\026\377\201\247\003\377q\241\002\377" "p\237\003\377c\204,\377Yo\064\377alI\377KDN\377=\064?\377G=J\377RET\377@\060=" "\377-\033(\377NAL\377\216\207\225\377\232\221\236\377\217\205\223\377\213" "~\217\377\203u\205\377\221\200\216\377\312\273\301\377\272\251\254\377\272" "\247\250\377\254\230\235\377\236\212\224\377\276\257\265\377\262\241\251" "\377\252\227\240\377\257\235\236\377\242\224\222\377\266\250\237\377\213" "uh\377qWM\377pTC\377\206gO\377\207jV\377\216q`\377\241\215|\377\234\215z" "\377\230\222v\377\230\227t\377\211\211`\377}}N\377oo=\377tvE\377\203\211" "W\377\205\216\\\377\210\220d\377\232\245~\377\250\261\224\377\251\262\233" "\377\266\274\255\377\276\304\273\377\303\306\300\377\307\307\306\377\326" "\326\326\377\316\312\311\377\310\301\275\377\273\256\246\377\257\235\217" "\377\264\246\234\377\247\222{\377\275\250\235\377\242\205e\377\231}d\377" "\215wo\377\202pk\377\203wt\377\201xu\377shj\377\211z\200\377\207w|\377\212" "x~\377\235\213\222\377\256\240\250\377\217\200\212\377\252\232\244\377\261" "\243\254\377\261\241\251\377\253\232\242\377\237\216\226\377\240\216\226" "\377\263\242\250\377\324\310\314\377\257\245\216\377\236\257[\377r\243(\377" "\226\303N\377s\246\010\377o\235\001\377n\233\002\377e\220\002\377\177\242\002\377" "\221\270\034\377\207\266#\377\247\311J\377\233\277'\377|\252\000\377\211\263" "\001\377\207\256\000\377\230\265\024\377\231\266\034\377|\244\000\377\177\253\011" "\377}\254\002\377~\250\002\377z\245\001\377\204\264\003\377\201\261\003\377t\241\002" "\377y\243\002\377z\245\002\377o\234\002\377w\250\001\377z\254\001\377}\260\003\377r\241" "\001\377u\250\001\377n\236\002\377\204\261\001\377x\245\001\377k\232\002\377k\232\001\377" "t\244\005\377\214\274\"\377\220\300&\377s\252\016\377b\235\005\377\215\272\017" "\377x\252\005\377k\240\020\377\177\226V\377\200\201\203\377\237\234\242\377" "\220\207\217\377ymt\377XHT\377=+\071\377;)\063\377cT]\377qfn\377gZe\377{p\200" "\377\203w\207\377\207y\212\377\214~\217\377\213}\215\377\246\227\241\377" "\305\265\264\377\254\230\232\377\241\215\217\377\220}\206\377\270\250\254" "\377\274\254\261\377\254\233\242\377\266\243\251\377\262\242\242\377\236" "\216\217\377\214\177~\377\245\227\221\377\227\203t\377jL\063\377iO\067\377" "ubO\377\\H;\377dND\377^K=\377XF\064\377P@)\377aV\070\377bV\064\377uoI\377\207" "\207\\\377\212\214b\377\221\224k\377\214\216i\377\224\227w\377\244\247\217" "\377\235\237\212\377\245\247\224\377\237\234\214\377\265\262\251\377\271" "\263\257\377\304\276\276\377\273\256\250\377\265\241\220\377\255\227\203" "\377\240\203h\377\241\207v\377\222va\377\231|a\377\214wh\377nS<\377n[M\377" "zfb\377\244\226\223\377}qs\377{po\377\212{|\377\223\204\207\377\221\203\206" "\377\220~\201\377\220~\203\377\242\222\234\377\240\220\232\377\232\210\222" "\377\267\253\260\377\266\251\257\377\226\205\217\377\241\220\227\377\255" "\232\236\377\313\300\300\377\274\267\252\377\227\253i\377i\234\027\377t\246" "\032\377y\253\034\377n\241\003\377o\241\002\377g\221\001\377y\240\001\377\226\274\064" "\377t\243\012\377\262\320M\377\205\256\010\377\200\255\003\377}\254\001\377{\250" "\002\377\201\251\002\377\212\255\004\377~\245\002\377i\230\001\377s\243\001\377\200\254" "\002\377u\243\001\377y\250\002\377w\252\001\377r\242\002\377w\250\002\377w\246\002\377" "u\244\001\377u\250\002\377{\257\012\377w\246\013\377{\253\007\377v\247\002\377w\251" "\003\377v\243\001\377y\251\001\377_\214\001\377v\251\012\377\224\306%\377\206\267" "\022\377{\253\001\377]\221\001\377W\215\001\377_\227\004\377d\200>\377LJR\377\063#" "\065\377\067!\065\377_KZ\377\226\214\224\377\270\261\267\377\206|\203\377E\066" ":\377/\033'\377P,\377j_O\377uog\377pjf\377aZZ\377" "i^Z\377\217~y\377\204qj\377\211ys\377\220\200|\377\233\213\207\377\220}z" "\377\264\245\242\377\231\214\225\377\206w\202\377\243\227\236\377\260\246" "\252\377\232\213\224\377\226\204\216\377\246\221\225\377\310\273\274\377" "\273\265\265\377~\232j\377t\234:\377o\236\021\377r\241\007\377v\245\003\377u\245" "\001\377r\242\013\377~\260\071\377l\234\000\377\253\303\062\377\206\252\010\377s" "\240\001\377s\241\002\377p\242\001\377q\236\001\377~\250\002\377\213\261\002\377\211" "\255\001\377\202\251\002\377v\242\002\377z\244\002\377~\252\011\377y\247\004\377t\243" "\001\377u\250\001\377q\243\001\377j\235\002\377o\250\002\377u\250\001\377s\242\001\377" "g\230\002\377\\\220\001\377\200\257$\377r\244\013\377\206\265\021\377\222\302\035" "\377\203\265\024\377[\213\000\377x\242\001\377\205\260\001\377t\241\002\377p\243\010" "\377Ts\071\377?V\061\377$%\031\377\017\013\006\377\015\011\003\377\007\006\002\377\000\000\001\377" "!\037!\377\000\000\000\377\000\000\000\377%!$\377\012\005\011\377$\040&\377'\024#\377i[h\377" "zk{\377\203u\204\377\203w\204\377\223\206\225\377\246\232\242\377\317\302" "\305\377\313\274\275\377\230\200\206\377yem\377\212v\200\377\264\244\253" "\377\250\227\236\377\253\225\233\377\272\244\241\377\232\203\200\377\241" "\212\205\377\226\203}\377uc_\377M\071\061\377o^V\377\250\242\232\377\265\260" "\247\377\210~h\377{oO\377ta?\377[@\027\377U<\022\377dR-\377aN*\377bN*\377l" "`\070\377\204\202Z\377\206\206_\377\217\217m\377\233\234\177\377\227\227z" "\377\213\206h\377\202yY\377\221\204c\377\201pQ\377udA\377\212y\\\377~nN\377" "^I-\377W>#\377u]M\377gJ\060\377v`G\377\245\234\224\377\231\221\201\377\204" "zi\377mbS\377cXQ\377i`\\\377od^\377~lg\377|jg\377\204sr\377\225\205\203\377" "\203qp\377\206qo\377\254\235\233\377\203u\201\377\221\206\220\377\231\213" "\223\377\252\235\241\377\234\215\224\377zju\377\215|\204\377\270\253\256" "\377\302\272\276\377\177\225n\377~\240A\377}\245\026\377o\234\001\377r\240\001" "\377z\255\001\377u\244\010\377z\255\026\377{\250\007\377\201\250\004\377z\241\001\377" "\214\261\033\377r\236\003\377w\247\002\377w\243\002\377\213\264\001\377\215\261\002" "\377\212\255\002\377\207\256\002\377\202\252\001\377p\233\002\377x\242\014\377\212" "\265,\377|\253\014\377y\251\001\377|\257\001\377s\240\001\377z\251\003\377x\245\001" "\377q\236\001\377g\227\002\377i\233\006\377\211\265%\377\213\271\060\377~\261\026" "\377h\233\005\377i\231\001\377p\234\001\377\201\253\002\377\220\271\003\377s\237\035" "\377Zz+\377?S(\377\061@\036\377\012\012\003\377\000\000\000\377\000\000\000\377\000\000\000\377\015" "\015\016\377\004\004\004\377\000\000\000\377\001\001\001\377\001\000\001\377\020\015\020\377\002\002\003\377" "\001\000\000\377;-\071\377xgv\377zlz\377\217~\221\377\217}\216\377\243\222\235\377" "\324\312\312\377\264\240\241\377\220{{\377lZb\377\212z\201\377\266\246\254" "\377\261\240\243\377\303\263\261\377\301\255\250\377\266\235\221\377\231" "~s\377\226|q\377}fa\377l[U\377g]T\377k`M\377{nU\377\177pT\377\215~b\377\210" "y^\377q_?\377]G!\377\\D\032\377aK\037\377iV&\377m^,\377\200xI\377|tC\377wp" "B\377\177xQ\377~xS\377\214\205`\377\206~X\377xkC\377\201uN\377\203zW\377" "\205}^\377\233\226~\377\236\231\206\377\224\212w\377\235\223\200\377\232" "\214u\377\237\224~\377\227\213x\377yl[\377bSA\377^UC\377PC/\377XJ;\377cV" "L\377xmg\377\203tr\377wdd\377vef\377\204qs\377\224\200\201\377\256\240\240" "\377\204x\201\377\204y\203\377\233\216\226\377\240\221\232\377\232\212\224" "\377veq\377\223\201\207\377\263\247\247\377\310\304\305\377\221\241\201\377" "m\227)\377n\231\010\377s\236\003\377t\232\003\377\213\267\032\377x\250\003\377{\251" "\001\377k\237\002\377h\233\001\377w\247\000\377\260\277\061\377\216\246\024\377q\232" "\001\377\177\252\002\377\203\261\002\377}\242\002\377\220\264\002\377\213\261\001\377" "{\244\002\377u\240\003\377z\245\010\377x\246\012\377k\234\002\377z\257\002\377y\250" "\002\377s\236\002\377\177\253\003\377z\246\001\377i\230\002\377j\231\002\377w\241\003\377" "y\244\002\377u\246\004\377v\256\006\377e\220\002\377q\230\001\377\205\253\001\377\214" "\261\000\377\211\265\017\377f\215\037\377Vi\063\377+>\015\377\040\062\012\377\000\000" "\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000" "\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377!\027\036\377h[g\377\200q\177\377" "\237\223\241\377\204v\206\377\251\233\243\377\303\267\270\377\244\216\221" "\377\204pv\377mYe\377\224\204\211\377\270\251\254\377\264\242\246\377\263" "\233\232\377\271\242\231\377\267\237\224\377\237\203|\377\235\204\177\377" "\200h`\377aH>\377ZA\064\377n_E\377sfG\377\204zX\377\242\237\204\377\243\241" "\210\377\227\224w\377\200uT\377\203vS\377\210|U\377\203vL\377\213\204Z\377" "\212\205Z\377\213\205]\377\213\204_\377\177vO\377xk?\377m]/\377iY.\377pb" "\067\377skB\377}zZ\377\226\227\204\377\223\216|\377\237\235\213\377\250\246" "\224\377\240\235\210\377\213\200k\377Q<'\377VD\066\377eVO\377O>'\377XH+\377" "^O/\377]J,\377eT:\377\217\200x\377\210xx\377\177nl\377\202pn\377\214|x\377" "\210sq\377\236\215\212\377~pz\377\215\201\212\377\246\231\241\377\221\201" "\213\377\237\222\231\377rdn\377\215}\207\377\256\241\244\377\274\266\267" "\377\226\243\205\377g\223\033\377e\224\003\377j\222\002\377p\225\004\377\224\272" "\061\377\204\256\020\377~\251\002\377i\237\001\377b\230\000\377n\237\001\377\210\247" "\021\377\276\311P\377|\241\004\377x\243\002\377{\253\002\377v\240\001\377\215\264" "\002\377\202\250\002\377\200\246\002\377\204\254\002\377s\232\001\377y\245\001\377t\241" "\001\377r\240\002\377o\234\002\377o\237\002\377y\250\002\377v\242\002\377s\237\002\377" "l\232\002\377i\224\002\377u\242\001\377q\236\001\377r\244\002\377m\233\002\377\203\257" "\003\377\211\262\003\377\222\272\021\377\213\263E\377\221\266V\377w\215d\377a" "\202E\377\065M\"\377\034.\020\377\016\032\002\377\000\000\000\377\000\000\000\377\000\000\000\377" "\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\"" "\035!\377b[c\377sds\377\230\212\235\377\204u\211\377\246\227\241\377\302\267" "\266\377\232\210\210\377\213y\200\377s`m\377|jv\377\247\225\233\377\216z" "\203\377\260\234\230\377\254\225\216\377\262\232\215\377\224zo\377\207le" "\377oVO\377s\\S\377fRE\377\\N\066\377h^=\377ld@\377}wW\377\215\211k\377zu" "Z\377}x\\\377\206|^\377|mF\377\215\202]\377\215\203\\\377|qI\377\212\200" "\\\377\224\215n\377\203uP\377{kA\377m\\.\377hW/\377gW\061\377ujI\377\203|" "b\377\203}c\377\202y]\377|rO\377xkH\377YF)\377maG\377K<\034\377RE'\377zqf" "\377SC&\377P@\036\377VA!\377P:\031\377aO\063\377\204tc\377\226\213\213\377u" "cb\377\213zw\377\220\177y\377\216xt\377\230\205\203\377wep\377\206u\201\377" "\246\234\241\377\220\206\216\377\227\214\223\377k^h\377qcm\377\244\227\232" "\377\274\270\272\377\212\227w\377e\222\035\377`\216\001\377p\233\002\377r\231" "\003\377\225\273-\377|\243\001\377\201\257\003\377r\244\007\377r\247\017\377g\230" "\001\377t\231\002\377\270\306S\377\214\256\033\377y\245\001\377o\234\002\377r\237" "\001\377{\246\001\377\201\250\001\377\201\246\002\377\206\250\002\377}\240\003\377\211" "\257\002\377\177\247\001\377w\241\001\377q\240\001\377w\252\002\377f\232\002\377w\244" "\001\377~\244\001\377j\223\002\377q\227\002\377t\243\002\377v\246\002\377y\251\005\377" "g\227\001\377s\243\002\377|\252\002\377u\241\002\377l\232\010\377c\215+\377=]$\377" "Ms,\377Jb>\377WlL\377[mX\377XlW\377NcM\377N`H\377AR?\377-\066,\377\012\016\011" "\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377'#'\377JAI\377ses\377\225\207" "\230\377\205v\210\377\270\255\262\377\271\254\254\377\245\224\224\377}kt" "\377k[g\377\201t}\377\253\232\236\377\240\215\220\377\251\222\216\377\255" "\222\213\377\274\242\225\377\220ra\377\225xj\377\220ti\377x^R\377iRA\377" "N\071\036\377=,\013\377dW\064\377{rO\377\221\213m\377\214\204j\377}qW\377|lP" "\377\203sU\377\210wZ\377\201pO\377\212yX\377\204rW\377\234\224\200\377\231" "\221{\377\211|b\377\221\207m\377\211\201h\377\207\201h\377\212\206m\377\203" "ze\377\204{e\377\200xZ\377YO$\377_R(\377reF\377gZ\064\377[M%\377Q@\030\377" "rdP\377@/\013\377VC\040\377ZD#\377\\E(\377yfP\377wdT\377~oj\377\210yy\377~" "je\377\206pi\377\224\200y\377\227\203\201\377wfr\377\215~\207\377\252\236" "\244\377\235\220\227\377\235\217\227\377l^j\377\203u}\377\230\213\215\377" "\265\256\264\377u\211X\377_\212\022\377_\214\001\377x\244\022\377u\243\011\377" "|\247\013\377y\244\001\377u\245\000\377\203\265)\377\201\265\063\377g\236\017\377" "}\250\004\377\207\252\014\377\240\270\035\377\214\256\002\377\201\245\002\377|\243" "\001\377k\231\002\377x\243\002\377}\243\002\377\217\261\002\377\215\255\002\377p\230" "\002\377u\236\002\377~\252\001\377~\254\001\377u\245\002\377Y\216\002\377l\230\002\377" "v\235\001\377q\230\002\377u\234\002\377z\252\003\377p\231\003\377x\240\001\377g\236\003" "\377t\247\001\377z\246\002\377u\234\002\377x\240\002\377W\201\003\377;a\003\377#H\002\377" "\036\064\004\377\013\013\006\377\004\004\003\377\001\001\001\377\001\000\001\377\000\001\001\377\003\011\003\377" "(\067%\377b{^\377w\222y\377AQ?\377\015\023\011\377\035\037\023\377==\066\377<:<\377" "kbn\377\215\177\220\377\216\177\217\377\264\251\255\377\270\254\253\377\240" "\216\217\377\211w~\377m\\g\377\202v|\377\267\252\254\377\234\206\216\377" "\244\215\214\377\226yq\377\256\225\211\377\237\201p\377\221qa\377~bO\377" "rWB\377fN\066\377UB\"\377C\064\020\377?\061\017\377J\066\026\377kV\070\377n\\B\377" "\213\205v\377\230\224\205\377\211\200m\377\215\200k\377\231\214w\377\241" "\226\201\377\232\216w\377\215\201i\377\241\234\210\377\243\241\215\377\235" "\234\207\377\223\220y\377\224\216t\377\205}[\377ykC\377kZ\061\377sd<\377]" "H\033\377Q\071\014\377sb=\377l^\064\377VC\033\377G\063\020\377]L\070\377G.\024\377" "K\063\025\377M\066\027\377q^D\377zfT\377r]Q\377\202oi\377zgb\377\230\207|\377" "\223\200u\377\205qn\377\226\207\210\377udp\377\200q|\377\223\210\216\377" "\232\220\224\377\220\205\214\377fYe\377o`k\377\240\224\231\377\261\254\260" "\377s\211X\377a\212\037\377f\222\027\377i\230\010\377x\250\010\377v\244\003\377" "v\241\001\377{\246\007\377\200\261\032\377t\250\013\377l\243\006\377e\234\002\377q" "\237\002\377\200\247\011\377\230\267\013\377\231\267\002\377\225\265\002\377m\232" "\002\377u\241\002\377\205\260\002\377\223\270\002\377\212\254\001\377i\225\002\377x\242" "\002\377\206\256\001\377\202\254\002\377u\242\002\377l\232\002\377c\222\001\377y\245" "\003\377u\237\001\377p\233\002\377t\237\002\377w\233\001\377}\241\003\377g\240\006\377" "u\250\001\377y\245\002\377}\247\001\377z\242\002\377]\211\001\377Ak\004\377%@\002\377." "\063#\377!%\026\377\016\016\013\377\000\000\000\377\000\000\000\377\000\000\000\377\000\000\000\377\000" "\000\000\377\000\000\000\377\000\000\000\377\011\015\011\377:J#\377`l;\377@G*\377;<\064\377l" "gn\377\224\210\224\377\207}\210\377\255\244\250\377\272\257\260\377\233\214" "\216\377xjo\377^PZ\377{ms\377\260\244\245\377\246\225\230\377\243\220\213" "\377\212ql\377\272\245\235\377\240\202q\377~XB\377yT?\377V\063&\377Z=,\377" "s^E\377mW\070\377S<\033\377G/\013\377K\064\023\377xhR\377\202ub\377wiV\377\225" "\217}\377\220\211v\377\230\221\200\377\235\226\204\377\250\241\216\377\243" "\236\211\377\223\214q\377\221\212m\377\215\204d\377ykE\377xgB\377vb\064\377" "lV%\377gP#\377\\D\031\377aK!\377ziB\377\177qF\377fP&\377B*\013\377\060\027\003" "\377ZA\063\377W=,\377pYC\377\202nX\377\213xj\377fMC\377pYP\377nUN\377\206" "oj\377\237\212\202\377\202ka\377\231\206}\377\235\220\214\377~v{\377\212" "\201\210\377\235\221\230\377\241\226\234\377\205x\202\377`S`\377l_j\377\241" "\232\240\377\244\243\246\377i~\071\377`\202\025\377x\244@\377`\216\002\377|\254" "\005\377v\243\001\377y\247\002\377z\250\006\377w\246\004\377t\241\000\377}\256\005\377" "i\235\001\377k\225\001\377|\242\001\377\210\251\002\377\214\255\001\377\214\257\001\377" "y\241\001\377v\240\001\377\201\250\002\377\203\247\002\377\205\250\002\377s\233\001\377" "~\247\002\377\210\264\002\377}\250\001\377x\242\002\377x\245\001\377m\232\001\377{\245" "\004\377}\250\020\377x\245\002\377s\232\002\377l\217\002\377\207\246\002\377n\251\003" "\377k\237\002\377k\232\002\377k\233\001\377g\231\003\377]\221\001\377Hx\003\377\031+\005" "\377'\060\030\377MOE\377,**\377\012\007\010\377\000\000\000\377\000\000\000\377\000\000\000\377\000" "\000\000\377\000\000\000\377\001\002\000\377\011\027\002\377,D\010\377\060I\010\377\062E\020\377=" "H)\377rtq\377\232\221\231\377\221\206\216\377\221\205\214\377\261\245\244" "\377\211y\177\377hX`\377UHP\377\221\206\212\377\252\240\243\377\243\224\227" "\377\235\212\204\377\215yl\377\253\227\212\377\257\224\206\377\203]I\377" "\222o^\377\205h\\\377t]R\377nWA\377xaD\377u`;\377hR*\377aI#\377eN+\377Y:" "\030\377wbK\377xiS\377\177pZ\377\215\177h\377\177pT\377\230\216t\377\226\212" "n\377\204vR\377\205wU\377\222\210i\377\216\206f\377\214\203e\377\221\206" "h\377\223\204f\377\202pW\377\224\203o\377\215}a\377xdB\377hM-\377;\040\006\377" "@'\015\377A'\015\377{fR\377s\\@\377jR<\377oZK\377yf[\377aKG\377eOI\377oWQ\377" "\241\213\203\377\210lg\377\203bZ\377\232\203\200\377\233\221\223\377\205" "\200\205\377\230\224\230\377\252\242\245\377\252\237\244\377\216\200\211" "\377VGR\377gXe\377\230\216\226\377\236\233\235\377{\210S\377g\201\060\377" "{\251F\377f\234\006\377p\236\002\377\177\252\002\377z\245\003\377y\244\002\377s\241" "\003\377t\237\002\377\200\250\001\377|\252\004\377j\226\002\377w\240\001\377}\241\003\377" "\177\243\001\377\200\244\002\377\177\245\001\377\177\246\003\377\201\251\002\377s\232" "\002\377\177\243\001\377\200\244\002\377\207\260\002\377|\251\002\377w\246\001\377r\234" "\001\377v\244\000\377\212\256\016\377l\222\002\377|\252\027\377r\240\002\377w\236\002" "\377|\237\002\377\177\241\002\377p\253\011\377U\200\000\377Z\204\002\377Y\206\002\377" "[\220\006\377\\\232\006\377S\211\004\377\020\034\003\377\000\000\000\377\023\022\016\377\"\034" "\025\377\013\012\006\377\000\000\000\377\000\000\000\377\000\000\000\377\001\001\001\377\007\014\001\377\027" ")\001\377&E\003\377\066^\011\377;e\020\377Mr&\377.A\030\377_[Z\377\220\203\215\377" "\214\200\211\377\231\216\224\377\276\263\262\377\220\200\203\377g[d\377O" "@K\377QDH\377\236\223\222\377\247\227\226\377\243\217\213\377\200kd\377\231" "\204y\377\262\235\221\377\237\201t\377\201aS\377mOA\377I*\036\377hO>\377r" "X?\377|gI\377v`;\377lW-\377r\\/\377oV)\377~iA\377u_\071\377~jH\377\210yY\377" "\231\217q\377\235\224x\377\241\231}\377\216\202b\377\215\200a\377\205z]\377" "\211~d\377\200s\\\377\177r`\377\213\177p\377\222\206z\377~oZ\377|jJ\377Q" "\071\024\377:!\005\377F.\020\377\\H#\377nX\066\377\231\210s\377{gK\377nYE\377v" "cQ\377]L>\377aQB\377bL>\377zaT\377\220{l\377|bT\377\200fW\377\273\262\252" "\377\222\205\210\377\200pz\377\210}\202\377\227\216\220\377\216\205\212\377" "i^d\377L?K\377d[d\377\226\214\225\377\207\206|\377l|M\377k\225>\377t\251" "M\377i\240\021\377i\227\001\377t\237\002\377x\241\002\377w\237\000\377\200\254\016" "\377v\236\003\377\206\250\002\377\230\274\026\377|\250\010\377t\240\002\377~\250" "\002\377y\237\001\377\220\256\002\377~\240\002\377\205\254\001\377\203\253\002\377\200" "\247\001\377\202\247\002\377}\243\001\377\204\255\002\377m\233\002\377z\256\016\377" "{\254\012\377m\226\002\377\207\251\011\377\210\254\015\377\177\254\017\377k\227" "\002\377\205\252\006\377r\226\002\377{\245\004\377b\243\013\377`\230\015\377Z\206\004" "\377_\215\001\377Y\212\002\377]\224\000\377[\231\012\377/O\006\377\022\035\002\377\001\002" "\001\377\000\000\000\377\000\000\002\377\002\003\001\377\006\011\002\377\005\007\002\377\012\030\002\377\031" ".\002\377;X\006\377:_\003\377\066_\003\377=j\016\377=_\032\377\071D*\377a[^\377\213\177" "\211\377\225\213\224\377\214\200\211\377\253\237\242\377\231\211\214\377" "pdk\377ZOW\377d^`\377\203{z\377\231\215\216\377\273\254\246\377\237\210\201" "\377\177rh\377\261\242\226\377\263\242\224\377\215r^\377v\\H\377|bT\377i" "O?\377s^O\377xdN\377\206sU\377\205pK\377\222\177W\377\200k?\377oU&\377\204" "oK\377zfF\377\205rV\377\201nU\377\236\224~\377\205tX\377\216\200`\377\222" "\204c\377\215|Y\377\230\210h\377\206wU\377\203pU\377t`G\377fS>\377\\K\066" "\377=*\026\377\061\035\004\377N\070\021\377eR%\377ub\066\377\215{U\377\217\177h\377" "T<\036\377zeN\377bJ\066\377bJ<\377}f\\\377xaW\377\247\223\216\377w]V\377`F" ";\377\204rj\377\274\264\254\377\212}\200\377\217\204\211\377\237\226\232" "\377\230\217\225\377\211\201\210\377`Xa\377F?J\377`Ya\377\243\236\240\377" "sx`\377l\216>\377\\\217\027\377u\252>\377j\235\006\377o\233\002\377k\225\001\377" "~\245\002\377z\241\002\377v\235\000\377w\237\002\377\217\264\021\377\212\255\006\377" "\207\256\010\377t\240\001\377y\244\002\377w\237\002\377\214\254\002\377\211\251\001" "\377\207\250\002\377\201\246\002\377y\240\002\377}\245\001\377w\236\002\377|\250\002" "\377b\222\002\377x\252\011\377t\234\001\377~\240\002\377\206\252\007\377}\242\005\377" "\200\252\014\377m\225\002\377x\236\001\377y\235\003\377\202\253\025\377U\223\001\377" "c\237\020\377a\232\012\377_\222\001\377c\223\001\377d\227\002\377Y\206\002\377\\\220" "\011\377R\177\014\377#\067\002\377\023$\002\377\013\031\002\377\034'\002\377?F\013\377;H" "\013\377\012*\002\377!E\003\377\067^\002\377Ly\003\377Nz\012\377t\246H\377\067R\021\377" "?O+\377]`Y\377\200z\177\377\201w}\377|px\377\247\233\234\377\233\215\216" "\377rfk\377SEQ\377LCK\377\214\205\204\377\252\236\234\377\273\256\246\377" "\250\215}\377pZJ\377\217|p\377\302\266\255\377\234\205r\377z[B\377rS<\377" "~`I\377oQ:\377mO?\377|cO\377\213wY\377\230\205d\377\222\200^\377\210uR\377" "\206qO\377\202jF\377|b<\377\212rQ\377\206oL\377\206oI\377\202jC\377za\070" "\377tZ\060\377\207uX\377\213|_\377vfL\377\\N\064\377\065\"\012\377\065\040\010\377" "\066\040\004\377Q:\026\377r_\070\377\214zW\377\223\202a\377\206rU\377\201kV\377" "kT;\377\\A*\377fK\067\377sZK\377|d[\377\233\213\204\377\232\213\205\377}h" "_\377hRI\377\240\221\207\377\263\253\250\377|ou\377\214\202\206\377\211\177" "\205\377\243\233\240\377\203\177\205\377UNX\377JAL\377h`g\377z|o\377kzV\377" "g\231\064\377c\225#\377\202\261A\377n\232\004\377n\232\002\377p\222\002\377\224" "\251\002\377\215\251\001\377x\231\001\377}\252\006\377{\241\003\377\205\253\002\377\205" "\255\002\377|\246\001\377y\242\002\377z\242\002\377\201\244\001\377~\236\001\377\207" "\252\002\377\202\247\002\377\177\244\001\377\202\251\002\377\215\270\003\377x\244\002" "\377`\217\002\377o\237\002\377r\231\002\377\207\254\002\377p\225\003\377n\223\002\377" "y\245\010\377c\215\002\377s\234\002\377\204\256\007\377x\237\002\377d\230\010\377c" "\227\006\377\\\214\001\377Q}\002\377]\211\001\377^\206\001\377`\206\002\377Z~\001\377Y" "\210\005\377=a\003\377%G\002\377\060J\002\377/B\003\377/?\002\377GV\005\377-F\002\377*O\003" "\377\065\\\002\377Q|\002\377`\220\015\377X\205\031\377;c\006\377Ih\"\377m{_\377\205" "\206~\377\204~}\377\201vz\377\246\236\236\377\232\217\217\377pbf\377I:D\377" "<-\067\377WJM\377\201rq\377\241\223\216\377\266\243\225\377\201fV\377dNG\377" "\236\213~\377\250\221|\377\211lP\377pL/\377}]@\377\205hN\377\177dJ\377mL" "\061\377qR\066\377yaF\377\221\202g\377\226\213p\377\236\225z\377\247\236\204" "\377\232\215t\377\231\211n\377\215|_\377\222\201d\377\230\210l\377\210vV" "\377~jK\377vbG\377iS\070\377aJ/\377S>\036\377V@\033\377mY\067\377hP.\377[@\040" "\377\231\205e\377\243\223u\377zdM\377v^D\377\203nS\377w[B\377^@)\377\210" "pZ\377\237\211y\377\214uh\377\242\222\211\377]LF\377G\065\062\377\204rm\377" "\304\275\271\377\222\210\207\377\204y~\377\226\214\217\377\237\226\231\377" "\235\227\235\377oms\377@:E\377@\071F\377pmo\377ip\\\377m\206Y\377d\222\067" "\377a\224(\377m\244\036\377`\220\000\377~\252)\377y\232\006\377\232\260\004\377" "\215\251\002\377\200\245\002\377z\244\004\377n\224\002\377\200\245\002\377\205\251" "\002\377\215\263\002\377t\235\001\377t\235\002\377~\242\002\377\204\251\002\377z\237" "\002\377\207\256\001\377\177\244\002\377\211\260\001\377\204\251\001\377~\246\002\377" "q\236\002\377a\216\001\377s\234\001\377w\235\002\377f\214\002\377l\221\000\377v\237\003" "\377l\223\002\377r\232\002\377\201\247\002\377{\241\000\377\204\260N\377}\246\060" "\377a\212\002\377Do\002\377Y|\002\377X{\002\377Rx\001\377\\\203\001\377Mv\002\377Jw\003\377" "Ky\004\377Cd\002\377\064O\003\377\035\067\001\377)=\001\377:M\002\377>V\002\377\062O\002\377" "\377e[b\377\204z" "y\377\210xw\377\307\267\256\377\221wj\377^H>\377\201i_\377\215uh\377\227" "{g\377\242\206j\377\234}[\377\207gE\377sS\066\377\201bH\377a@\"\377jL-\377" "\177jK\377\201pT\377\221\210u\377\243\235\212\377\234\226\202\377\245\236" "\216\377\240\233\214\377\243\233\214\377\235\223\204\377\223\206v\377\213" "|l\377\224\205w\377\206sa\377\200nV\377\206tX\377\207oR\377z_;\377\203hH" "\377\231\204b\377\251\234y\377}gP\377eI\065\377\216yb\377~iP\377fI-\377}a" "G\377\241\213q\377\250\227\204\377\226\205s\377\217\202u\377A,$\377\\J?\377" "\244\230\217\377\303\276\271\377\203\177\202\377\202{\200\377mck\377\213" "\203\207\377\203y\200\377NDN\377JDL\377MIP\377ppm\377ahR\377{\235j\377Y\207" "-\377d\225+\377a\226\014\377a\220\003\377p\232\012\377n\221\002\377{\244\002\377" "y\241\002\377z\243\003\377k\223\002\377o\230\002\377}\250\002\377\203\254\002\377\207" "\263\002\377p\241\003\377j\226\002\377\177\246\001\377\210\256\001\377\205\253\001\377" "\202\252\002\377~\246\002\377\206\255\001\377w\234\002\377\206\254\006\377u\233\002\377" "x\241\003\377w\240\002\377l\221\001\377m\224\001\377|\252\017\377\204\265\030\377|" "\256\016\377\211\272\035\377\224\300)\377\215\270\036\377^\222\005\377p\233\012" "\377r\226\007\377n\215\007\377`}\001\377_\177\001\377]\177\001\377Z\203\002\377Kt\002\377" "It\002\377J\177\003\377Cj\002\377\060O\001\377@X\004\377\032\070\002\377\037<\002\377,I\000\377" "\063W\001\377Mv\004\377\\\206\037\377Jj\002\377Yx\005\377]x\036\377Xe?\377poi\377\205" "{\201\377\205z\201\377\225\213\215\377\232\217\217\377~tz\377_S[\377A\071" ">\377\071\063\064\377]YU\377\177xv\377\272\255\245\377\253\227\203\377lP@\377" "T\066-\377\203m`\377\264\240\215\377\253\226y\377\252\224v\377\231\177c\377" "\207mS\377tU;\377fE$\377nP/\377bH%\377\201oP\377\215\177d\377\204uZ\377\210" "}f\377\224\217\201\377\222\216\200\377\212\201r\377\232\221\204\377\243\235" "\215\377\226\216z\377\227\211t\377\215|f\377\203pS\377q[\067\377\206nJ\377" "\240\214k\377\257\242\205\377\242\222t\377{dK\377^A.\377w^I\377zcK\377nR" "\070\377\200eM\377\237\213x\377\261\245\223\377\236\217|\377\220\201q\377" "S?\062\377P<\061\377\214zj\377\271\262\244\377\244\233\223\377\205|{\377\220" "\207\207\377\220\207\213\377\206{\200\377obl\377OEP\377F?G\377VRU\377qrg" "\377brQ\377r\241S\377\\\216-\377b\227%\377T\204\005\377f\222\010\377g\215\001" "\377k\223\001\377v\244\003\377\177\255\013\377y\246\004\377g\222\002\377x\243\002\377" "\177\254\002\377\201\255\002\377t\243\001\377m\243\002\377l\232\001\377|\244\001\377" "\210\256\001\377\201\247\002\377}\245\002\377|\247\002\377\177\247\001\377{\237\002\377" "\210\254\002\377{\237\002\377\206\262\005\377z\244\005\377\204\257\016\377\224\275" "$\377\225\277#\377\206\263\017\377\205\264\013\377y\243\004\377\207\265\033\377" "\214\270#\377V\213\003\377\\\215\002\377Z}\001\377x\222\021\377]v\003\377^x\001\377" "Zv\001\377^}\003\377Wu\002\377A`\002\377\"K\002\377<^\002\377Rk\003\377Z\003\377o\237I\377j\223>\377" "_\210\030\377Hm\003\377Or\007\377Tv\006\377g\207!\377g\205!\377b\177!\377u\206U" "\377_^O\377pmg\377vsn\377tqj\377\221\211\200\377\210\177x\377h^^\377g\\_" "\377dYa\377ZRY\377GAD\377]XP\377\200zl\377\270\261\227\377\214\177a\377G" "\060!\377V?.\377{fR\377\232\211{\377\212yh\377}jU\377\240\217y\377\227\204" "i\377\205iH\377\201e?\377\210oI\377\206pJ\377\217\200]\377\177oK\377\211" "{X\377\213\203^\377\200wU\377\215\206d\377\212\202a\377\222\215o\377\220" "\207n\377\226\212p\377\200oK\377\177hC\377~e=\377\207tO\377\210uT\377\232" "\211k\377\244\224z\377\257\242\214\377\267\253\232\377\252\235\213\377\231" "\210v\377\203sf\377scZ\377K\066(\377fN\067\377\246\231\204\377\252\244\223" "\377\203wk\377ynd\377ka[\377\177{v\377\213\211\207\377xtx\377d^g\377`Zc\377" "igk\377ddc\377w|t\377bwS\377]\216\066\377My\002\377R\203\005\377Y\215\011\377f" "\230\005\377|\244\004\377w\235\004\377u\234\002\377p\226\002\377r\231\002\377r\232\001" "\377j\226\002\377\202\252\002\377\201\252\002\377|\246\001\377u\243\007\377t\246\031" "\377\211\277U\377\201\255\021\377~\246\002\377n\230\002\377\177\251\004\377\207" "\251\002\377\250\275\025\377\230\263\016\377\205\240\002\377\221\260\023\377\210" "\257\010\377\204\251\020\377}\232\006\377\216\250\002\377\254\271\035\377\222\256" "\003\377\201\242\001\377\206\257\001\377\200\251\001\377z\246\001\377T\203\002\377V\206" "\006\377[\214\006\377@e\001\377[\207\005\377a\221\004\377Z\203\001\377`\207\006\377S~\002" "\377Z\202\001\377Op\004\377Vs\002\377Gf\002\377\063S\002\377Dd\002\377Ec\007\377h\243\060" "\377S\205\000\377Lq\003\377C`\001\377Tq\003\377Lg\001\377o\211'\377{\230F\377drC\377" "VYE\377yvn\377yvo\377||s\377rph\377\225\221\211\377~vu\377WLS\377ZNW\377" "nhm\377^[[\377_\\U\377\\XL\377\213\205r\377\250\241\207\377o_J\377M:\"\377" ">)\023\377{pe\377\222\212z\377kXC\377uaL\377\231\211r\377\243\223y\377\226" "\201b\377\202jB\377\207pI\377\210wN\377\212|T\377\221\206_\377~tN\377\207" "\200^\377\203~Z\377}uT\377\215\205j\377\204uX\377\217\200_\377\204qJ\377" "\210tL\377\204tK\377\242\224t\377\250\232~\377\264\252\226\377\234\215u\377" "\264\250\225\377\237\222\200\377\221\202n\377\226\210w\377{na\377G\061+\377" "T?\071\377\236\217}\377\265\253\235\377\233\220\207\377\177rl\377ped\377~" "wu\377}xy\377c^e\377D;I\377H?L\377c`e\377||z\377lri\377s\201n\377f\213U\377" "X\177!\377Z\213\010\377X\211\003\377`\216\003\377t\240\002\377\206\253\017\377r\232" "\002\377x\244\002\377f\215\002\377\202\246\007\377\201\243\002\377|\236\003\377\205\257" "\002\377\177\245\002\377\200\250\001\377p\237\007\377n\236\015\377\225\304n\377}\246" "\024\377x\240\002\377y\245\002\377~\247\002\377\201\247\002\377\246\300\022\377\262" "\265/\377~\230\004\377\236\277&\377}\253\006\377{\246\015\377\201\242\003\377\221" "\252\001\377\242\250\025\377\226\257\011\377\206\251\002\377\204\257\001\377\203" "\253\000\377\177\245\000\377a\222\001\377q\242\023\377V\207\002\377M~\002\377L|\003\377" "Y\216\002\377W\214\002\377Y\217\016\377Y\213\026\377T~\023\377X|\006\377Kf\001\377b" "\223\015\377En\001\377Z\201\012\377f\223*\377V\213\010\377W\215\003\377Hn\001\377" "Pp\002\377Qp\004\377Oo\007\377t\223H\377[|\060\377Le%\377HT.\377Y[J\377kl_\377d" "dZ\377{ws\377\207~|\377vlm\377ZMV\377f[c\377i^f\377\\RT\377kea\377}yn\377" "xth\377\224\217z\377\251\244\210\377{mR\377cSF\377P>\065\377via\377\206ug" "\377\202o_\377\207ue\377\245\226\202\377\250\234\201\377\224\204c\377\220" "}Z\377\205sL\377\212|U\377\212\201[\377\211\201\\\377\212\177]\377\216\205" "b\377\203vT\377\220\204f\377\206wW\377\223\205b\377\215}Z\377\235\216l\377" "\237\220m\377\232\215k\377\245\230|\377\243\227\200\377\222\202j\377\221" "\201j\377~l[\377\214~m\377uf]\377Q:\070\377bN@\377\227\211o\377\246\232\205" "\377\237\226\205\377\200yn\377\200}v\377vtr\377xtu\377khi\377okn\377a^e\377" "KIO\377\\]^\377pzo\377n}j\377bwZ\377e\234K\377Hz\004\377V\211\003\377b\226\005" "\377g\231\002\377k\232\001\377t\244\005\377q\240\002\377t\245\004\377j\227\001\377|\251" "\003\377|\234\000\377|\237\002\377v\236\002\377z\246\002\377n\232\002\377p\235\001\377" "y\250\062\377\240\313\213\377z\250%\377\177\252\000\377m\232\002\377\200\246\002" "\377\213\260\002\377\224\265\003\377\243\267!\377\227\265\036\377w\237\000\377u" "\236\001\377z\247\002\377\201\252\002\377\204\247\002\377\216\255\016\377\224\266" "\012\377\211\262\002\377|\244\002\377\235\273\026\377\212\246\001\377b\232\006\377" "g\233\020\377]\215\002\377X\214\002\377Y\217\004\377^\222\004\377X\211\001\377N|\003\377" "S\200\007\377a\216\020\377p\221\"\377Ru\002\377Py\002\377e\230\004\377k\226\024\377" "m\236\035\377e\225\002\377\\\215\001\377V\212\001\377Hr\002\377Jq\003\377`\204,\377" "Y\177)\377Qq!\377Og#\377EU\040\377bfJ\377ij[\377xvl\377}tp\377\210~|\377n" "ad\377eW]\377pdl\377S@N\377WJO\377j`^\377rog\377||r\377ywk\377\224\216v\377" "\236\225v\377{qY\377L<\060\377D\060-\377mZV\377\230\215z\377\216{k\377\215" "{h\377\235\216z\377\241\224{\377\240\222u\377\224\206e\377\222\203d\377\205" "vY\377\217\203g\377\217\204g\377\223\210k\377\207y\\\377\224\214q\377\213" "\203e\377\234\222u\377\215\177]\377\243\225s\377\225\206b\377\212xR\377\204" "pK\377\205qO\377\222\201i\377\235\220|\377\217\203w\377vi]\377O\071\061\377" "M/(\377\235\216|\377\256\243\222\377\225\210y\377vk`\377xpk\377spn\377zz" "z\377utw\377^Y\\\377B;C\377TNU\377baf\377pss\377qys\377m\201l\377_\203R\377" "X\215\"\377T\205\001\377\\\217\004\377Y\211\003\377b\231\007\377c\227\006\377u\236" "\020\377q\236\002\377t\242\003\377s\240\002\377p\233\001\377\207\260#\377v\237\002\377" "i\224\001\377s\241\002\377c\223\002\377p\237\002\377e\226\002\377\222\302z\377\200" "\264\065\377y\243\000\377\204\250\001\377\220\262\003\377\221\270\001\377\207\260" "\002\377\207\253\006\377\256\302C\377\204\245\014\377w\233\001\377\204\254\002\377" "\212\257\002\377\217\255\012\377\211\245\004\377\217\262\001\377|\241\002\377\177" "\244\004\377\215\242\016\377\214\245\001\377_\227\004\377a\230\004\377]\223\002\377" "R\206\001\377U\211\002\377]\221\003\377W\204\001\377Ot\002\377Y\200\002\377Z}\002\377U" "w\001\377Nr\001\377Qw\002\377Ow\000\377a\224\007\377\\\216\002\377s\241\002\377e\224\002" "\377`\222\002\377X\220\004\377l\233=\377Y\177'\377X}\032\377b\206*\377Zx&\377" "Xo,\377aoC\377tzd\377vvl\377xso\377|sq\377znn\377|tt\377zps\377jad\377ul" "g\377H>?\377\\XT\377lja\377oh^\377_TB\377\205}`\377~sR\377\177pV\377n\\L" "\377YIE\377D\062*\377rdV\377ygV\377\177kX\377\235\220\177\377\236\224|\377" "\235\220u\377\236\220p\377\206wW\377\207vY\377\207x\\\377\211y\\\377\177" "lO\377\221\203e\377\223\207f\377\212|[\377\214|Z\377\210wR\377xe:\377wb\066" "\377\217\177\\\377\230\210l\377\222\207q\377\231\220\200\377i^Y\377QCA\377" "H\067\061\377\217\177b\377\261\246\221\377\235\221\202\377\215\203x\377\216" "\205~\377jaa\377zyy\377wxx\377]^a\377C>D\377*\040*\377RQQ\377]_[\377rxv\377" "{\206|\377s\216l\377b\230G\377Jz\000\377b\230\006\377^\223\003\377\\\221\001\377" "e\234\016\377h\233\021\377u\241!\377n\233\002\377\177\253\004\377w\246\003\377[\204" "\000\377v\247\034\377m\232\005\377l\227\004\377p\236\001\377v\250\020\377{\252\002\377" "o\233\001\377n\241\014\377y\255\022\377u\236\001\377\211\251\002\377\231\263\010\377" "\244\300\010\377\204\246\000\377\221\260\026\377\215\250\"\377\270\315n\377\201" "\243\006\377\202\246\002\377\210\250\000\377\232\252\017\377\213\235\001\377\233\273" "\001\377\211\254\001\377\204\252\002\377v\231\001\377\206\250\002\377e\234\014\377c" "\231\012\377^\222\002\377Z\214\002\377a\221\003\377b\216\003\377e\212\003\377_\200\002" "\377Su\002\377[y\002\377Vr\001\377[{\002\377`\210\002\377i\223#\377]\213\016\377Nu\005" "\377v\241\005\377T\202\002\377\067d\000\377^\212'\377o\234\064\377S\200\000\377v\241" "\020\377\223\265\022\377\237\301(\377\214\260Q\377g\202:\377`tB\377~\200v\377" "\201\200|\377\205\202\177\377\210\205\200\377\204\204~\377\214\222\210\377" "fa`\377?\060\066\377<\061\064\377PIK\377d[[\377phc\377uo`\377rjQ\377\201zZ\377" "\247\237\204\377\227\211l\377fVC\377SB\065\377\067$\036\377QA;\377dSG\377uh" "[\377\217\202o\377\214zf\377\240\220w\377\215|[\377\210y[\377\200sX\377}" "oS\377\200pQ\377weB\377\203sO\377\212{W\377vbC\377\202oQ\377\207uZ\377\232" "\212r\377\224\204p\377\225\207v\377ti`\377bVT\377L=?\377jZN\377\231\211n" "\377\261\246\213\377\237\221w\377\215\201k\377\202{j\377ytk\377\204\205\200" "\377{\177|\377rtt\377YZZ\377QTQ\377\\dU\377bp[\377\\gX\377mzk\377o\210m\377" "Eu%\377P\207\036\377^\220\001\377[\221\002\377V\213\003\377c\227\002\377c\224\013\377" "g\227\010\377Z\201\000\377h\220\002\377\202\252\001\377~\251\020\377\201\262/\377" "\203\261\063\377\202\260\063\377\203\254\025\377\204\261\040\377v\244\021\377" "y\247\002\377h\227\002\377o\237\003\377|\264;\377}\257\026\377\214\264\002\377\204" "\246\002\377\225\261\001\377\246\267\002\377\212\245\013\377\233\267<\377\244\302" "U\377\223\267*\377z\237\000\377\216\257\005\377\246\263$\377\247\274/\377\231" "\266\000\377\216\254\002\377\203\246\003\377\203\251\003\377\217\263\002\377_\220\001" "\377Y\212\000\377M~\000\377U\204\000\377V\205\000\377a\217\003\377c\214\002\377_\203" "\000\377_\204\002\377b\212\003\377X\177\002\377Y\204\003\377d\223\023\377Oy\033\377C" "o\000\377h\212\036\377~\246\024\377O\200\000\377Z\211)\377Kp&\377U\202\001\377f\225" "\003\377x\241\003\377\211\254\002\377o\220\010\377\216\260R\377u\226;\377Yv,\377" "\205\213\203\377\227\236\225\377\221\227\217\377\221\224\217\377\201{\200" "\377\217\207\211\377cW`\377B\062<\377NAH\377i^e\377\202v~\377\223\214\217" "\377\206\204z\377yq^\377rkP\377\233\224x\377\246\240\203\377\234\221s\377" "\213yb\377cQC\377\070&\040\377=+(\377K=:\377RD>\377]J@\377vgX\377m^O\377qe" "W\377wiX\377\177p[\377\210wa\377\225\206l\377\220\201i\377\227\212t\377\206" "xf\377{l^\377\211~q\377\207|s\377i[Q\377S?:\377I\066\065\377]LG\377\205vi\377" "\240\223~\377\255\242\216\377\245\231\204\377\203qX\377\202t_\377vnd\377" "\203~|\377\221\221\221\377~~\202\377NOT\377MVL\377Q^N\377BP@\377\070D\064\377" "VfO\377bzU\377Sz?\377H~\032\377P\204\012\377V\213\003\377P\202\003\377W\206\002\377" "i\225\002\377g\222\012\377a\217\001\377p\232\003\377l\226\002\377p\235\002\377k\225" "\002\377z\247\007\377p\236\006\377h\235\010\377\207\262\067\377r\244\020\377j\234" "\003\377\202\261\013\377m\236\003\377s\243\006\377\204\266:\377s\255\061\377\202" "\253\001\377\201\242\002\377\216\256\002\377\212\250\002\377\256\267\007\377\244\300" "M\377\215\260\031\377\210\260\040\377~\247\007\377\206\253\013\377\242\273,\377" "\230\271\037\377\222\264\002\377\211\251\002\377\216\262\004\377\205\250\001\377\207" "\251\001\377u\235\065\377\236\277v\377\302\333\270\377t\244\066\377\220\266M" "\377m\225\025\377_\216\006\377\\\217\024\377S\205\005\377\\\221\004\377_\225\003\377" "Y\211\001\377e\227\031\377En\000\377Ks\001\377U~\002\377n\241\003\377Y\211\030\377O|" "\017\377W\211\005\377g\231\015\377V\203\001\377]\210\000\377V\177\000\377]\207\033\377" "y\244<\377h\224-\377c\211\066\377o\212U\377~\205x\377\231\231\227\377\235" "\233\233\377\233\230\230\377\223\216\214\377qim\377\\QV\377h`b\377ztw\377" "\211\203\210\377\233\226\224\377\210\202x\377\212\203s\377\200u_\377\207" "}b\377\233\223t\377\246\240\203\377\242\231{\377\240\221u\377|iX\377eOH\377" "`MJ\377A*(\377G\060.\377L;\070\377A/*\377WG>\377M\071,\377^L>\377{jZ\377\220" "\201r\377\207zj\377\202wj\377pe[\377UIB\377WHC\377^LB\377wfW\377q`Q\377}" "j]\377\243\232\205\377\260\251\226\377\260\250\224\377\235\222~\377\201s" "]\377\201s`\377\202ym\377\177vo\377\204~{\377\250\247\246\377\221\225\222" "\377q}n\377PUO\377AEE\377ENE\377:L/\377>?\377>A=\377UjH\377W\177:\377N\203&\377F\210\016\377T\223\025" "\377t\231\023\377c\216\004\377Q\201\001\377`\214\002\377_\211\002\377c\210\001\377o" "\233\026\377\201\253\064\377\205\245\012\377e\222\002\377n\235\002\377j\236\014\377" "l\243\002\377c\225\010\377^\221\005\377r\244\005\377x\247\012\377q\235\001\377w\253" "\003\377t\251\030\377x\237\013\377\226\270W\377u\235\040\377|\251E\377\217\255" "\005\377\223\262\003\377\204\244\002\377\203\244\003\377\253\312J\377\222\266\023" "\377~\244\000\377\211\266\034\377\213\271#\377\207\262\010\377\215\270\007\377" "\231\274\017\377\233\266\003\377\247\275\011\377\223\253\001\377\233\270\002\377" "o\257\061\377[\221\000\377d\230\001\377Q\203\002\377Y\223\017\377O\205\002\377Ix\001" "\377[\214\002\377W\213\002\377V\212\003\377T\205\004\377\\\225\013\377\\\217\016\377" "S}\003\377Tz\001\377^\216\007\377_\221\005\377U\200\005\377Z\201\020\377o\236\016\377" "h\227\005\377\207\241\017\377e\206\005\377f\210\002\377p\227\002\377Z}\001\377Y|\012" "\377\067R\000\377Fb\030\377izN\377t}f\377\231\232\224\377\227\227\217\377\220" "\217\207\377\202{x\377h]d\377lfl\377lgm\377\231\220\225\377\262\253\247\377" "\252\244\234\377\221\216\201\377\220\213y\377\211\202j\377|tS\377\216\202" "`\377~qI\377\213{Y\377\253\243\211\377\263\255\230\377\252\243\211\377\256" "\244\216\377\241\222}\377\233\212w\377\237\216{\377\250\232\207\377\231\213" "x\377\225\207u\377\212{m\377\240\222\203\377\247\232\213\377\234\215}\377" "\245\227\200\377\254\234\202\377\251\230~\377\255\236\206\377\273\255\233" "\377\277\266\244\377\262\252\226\377\235\225\201\377\224\212u\377\211}e\377" "\227\216x\377\220\210u\377\224\217\201\377\211\207}\377\217\216\207\377\247" "\250\242\377\235\252\224\377gw`\377bkT\377ScN\377bv`\377Hc\065\377\030=\000\377" "+Z\005\377E\203\013\377B}\007\377\\\222\003\377\227\240\011\377X\204\002\377c\215\002" "\377_\207\002\377a\211\001\377e\224\014\377R\200\003\377\206\256(\377c\214\001\377" "s\237\001\377k\233\002\377l\243\023\377g\231\010\377d\223\003\377q\242\003\377p\237" "\002\377\177\260\005\377k\233\002\377k\235\000\377\201\250\006\377w\246\034\377\203" "\253\066\377\251\273W\377\215\253\037\377\250\267\040\377\215\251\004\377\210" "\252\002\377\212\263\010\377\214\263\016\377\177\242\001\377\246\273\005\377\217" "\274B\377\213\271\040\377\214\271\013\377\205\251\001\377\243\276\006\377\230\260" "\002\377\220\252\003\377\231\263\002\377Z\220\001\377g\233\002\377W\211\002\377Y\216" "\005\377_\224\003\377b\223\002\377V\210\002\377_\222\003\377^\220\003\377b\235\002\377" "d\235\003\377]\227\002\377^\230\003\377R\210\003\377[\221\003\377d\231\012\377b\227" "\006\377g\226\023\377e\227\007\377l\232\011\377\215\253&\377_z\002\377Z}\004\377q\213" "\003\377p\212\002\377Ts\003\377\065W\002\377Cg\004\377-Q\001\377X\202\004\377_tD\377t|n" "\377y{r\377\212\211\201\377\222\220\213\377\217\214\210\377XQY\377b[c\377" "~w\177\377\230\223\223\377\270\263\254\377\237\232\217\377\217\210w\377\224" "\215x\377\220\212q\377\215\202b\377\215}Z\377\221\203_\377\217\202a\377\227" "\214p\377\245\234\202\377\253\244\215\377\267\255\230\377\260\244\217\377" "\264\250\225\377\264\252\230\377\262\247\226\377\253\240\217\377\254\241" "\222\377\251\235\216\377\232\213z\377\262\245\223\377\262\244\215\377\255" "\235\205\377\264\242\211\377\247\223z\377\267\250\225\377\272\255\233\377" "\232\215v\377\221\207r\377\231\222~\377\215\206p\377\223\215y\377\231\223" "\204\377\221\220\205\377\213\213\203\377\211\211\200\377\241\246\226\377" "\201\217u\377|\211d\377\201\177U\377Q[I\377)E\035\377.T\040\377p\227Z\377J" "\200\021\377=p\002\377P\210\003\377[\222\003\377o\212\016\377c\203\003\377b\210\002\377" "h\224\002\377e\222\001\377R\202\001\377M\200\002\377\204\254/\377b\212\000\377q\237" "\002\377i\236\013\377p\247\023\377j\235\005\377s\241\011\377u\243\001\377t\246\002\377" "q\242\003\377h\232\002\377m\236\004\377|\247\003\377n\245\024\377\210\266G\377\260" "\313P\377o\234\010\377\234\263\022\377\250\265!\377\222\256\017\377\212\261" "\024\377\221\271'\377\203\245\001\377\236\243\003\377\221\271\060\377\216\272:" "\377\223\300\017\377\222\272\004\377\220\256\002\377\213\247\002\377\221\255\002\377" "\222\257\002\377_\223\002\377m\235\001\377g\230\010\377p\230\011\377n\221\012\377" "z\234\033\377_\213\005\377W\206\003\377\\\220\002\377^\225\002\377c\234\011\377Z\210" "\001\377j\226\026\377s\244'\377^\220\026\377V\204\014\377_\217\002\377]\206\001\377" "o\227\002\377{\230\023\377\205\243\030\377Mp\001\377Ln\002\377f\202\002\377m\206\002" "\377Uo\002\377\065J\001\377;T\002\377Ff\003\377?c\001\377If\040\377Sa@\377ei^\377\177" "~v\377\220\221\204\377\201\201w\377gfc\377cac\377a]a\377\233\227\225\377" "\252\245\235\377\223\214\201\377\224\220\203\377\215\213{\377\231\226\205" "\377\212\205j\377\206\200]\377\216\205c\377\217\205g\377\222\206j\377\204" "uU\377\217\201a\377\241\225x\377\260\244\211\377\250\235\202\377\235\220" "w\377\252\240\207\377\237\224\177\377\260\247\224\377\227\211u\377\224\203" "o\377\247\233\206\377\266\252\223\377\250\231\201\377\260\243\214\377\254" "\237\210\377\247\232\204\377\250\233\205\377\230\213u\377\221\205r\377\212" "\201o\377\235\227\207\377\234\233\216\377\232\232\221\377\202\202|\377\207" "\207\177\377\177\202t\377\216\241w\377\177\217d\377r\177^\377eoc\377G\\>" "\377&K\015\377)V\003\377Fy\021\377n\232H\377J\201\014\377d\235\002\377[\217\020\377" "x\241=\377o\217\012\377o\220\005\377d\222\003\377_\217\001\377Z\215\003\377Y\212\002" "\377t\241#\377p\235\017\377p\243\010\377k\234\015\377o\243\010\377f\223\005\377" "p\236\005\377w\245\002\377}\254\002\377{\255\004\377n\237\002\377\\\217\000\377\205\256" "\000\377f\241\017\377\210\270G\377\224\273(\377x\243\002\377u\245\013\377\202\244" "\000\377\225\257#\377\224\263#\377\223\271\063\377\203\250\001\377\222\246\004\377" "\200\246\006\377\206\256+\377\207\262\011\377\204\251\001\377\217\264\002\377\214" "\256\002\377\214\264\007\377\215\261\006\377a\225\003\377e\230\002\377Y\220\004\377U" "\213\002\377U\205\001\377`\215\001\377Sq\001\377]\211\002\377^\216\003\377]\222\003\377" "_\220\030\377\212\264T\377k\234\025\377`\222\001\377U\203\001\377Fp\001\377b\217" "\002\377S|\003\377[\210\005\377\223\257>\377f\217\006\377W\201\002\377Z~\005\377i\223" "\002\377Yw\003\377Xi\001\377do\007\377Vg\004\377Rm\006\377Km\011\377:\\\000\377Qr\016\377" "\067H\040\377]kK\377|\206l\377\214\216\200\377\227\230\217\377kih\377ead\377" "\221\213\213\377\205{w\377\216\204{\377\214\203y\377\206\177t\377\217\214" "|\377\217\214u\377\222\215o\377\200zX\377\203~]\377\226\215k\377\204vP\377" "\216~Z\377\216\200]\377\230\213h\377\230\215j\377\241\230u\377\234\222t\377" "\242\227}\377\245\233\207\377\222\205s\377\226\212t\377\250\236\211\377\256" "\241\213\377\253\235\205\377\253\235\205\377\227\213u\377\240\224\202\377" "\207\177m\377\214\200o\377\215\205t\377\245\241\220\377\227\226\211\377\216" "\217\205\377\213\213\203\377\177~y\377}}t\377\220\236x\377y\221V\377^qK\377" "Q^K\377L]D\377.N\024\377+Q\004\377@m\005\377M\203\011\377?s\001\377\202\260R\377" "a\232\001\377Bm\025\377\203\255O\377k\217\035\377X\177\006\377O\177\001\377_\216" "\001\377c\225\002\377V\202\002\377V\202\000\377\231\302J\377j\231\010\377i\230\002\377" "t\246\002\377w\245\002\377t\243\003\377l\232\002\377\200\261\003\377l\236\003\377l\241" "\001\377W\217\017\377\222\270&\377r\246\024\377\215\276Y\377\224\273,\377\201" "\246\000\377}\247\002\377\200\255\002\377\177\245\002\377\200\246\005\377\242\302G" "\377\177\251\006\377w\235\001\377\220\261#\377\220\256.\377\206\261.\377v\237" "\000\377\212\265\001\377\205\253\002\377\224\271\005\377\243\305#\377O\204\001\377" "Z\217\003\377Z\221\003\377P\210\002\377Z\224\004\377W\213\003\377\\\215\000\377e\226" "\007\377d\226\005\377Vx\005\377t\222\024\377c\217\006\377\202\240>\377a\205\010\377" "_\207\004\377U~\002\377f\227\006\377t\251\036\377\204\261#\377\202\252'\377q\235" "\010\377^\214\001\377a\214\015\377\\\212\000\377T~\001\377]\205\003\377_\206\002\377" "[|\002\377Xw\004\377Ss\004\377Db\001\377;Z\001\377\063P\003\377p\224D\377`fS\377tvh\377" "~\177t\377d^]\377tps\377uoq\377\240\230\223\377\254\245\234\377\243\236\222" "\377\230\222\205\377{yk\377\204\203r\377\215\213r\377\217\212k\377\211\202" "`\377\207\200[\377\212\203]\377\227\217j\377\230\220l\377\242\232{\377\223" "\211j\377\232\224u\377\247\240\204\377\223\215s\377\234\224\177\377\206y" "f\377\220\203p\377\226\212t\377\244\230\177\377\254\240\207\377\247\234\206" "\377\222\207u\377~ua\377\226\221\200\377\234\232\213\377\230\230\212\377" "\230\231\216\377\206\206}\377\226\225\217\377\212\212\204\377\177~y\377\201" "\201z\377\215\244h\377k\177Q\377ERA\377/B&\377\062M\031\377,N\007\377Af\007\377" "Fr\002\377V\214\007\377F~\003\377_\237\002\377d\230,\377[\204\023\377s\235D\377Mv" "\000\377p\216-\377Ft\000\377]\213\002\377Y\211\001\377W\207\003\377`\222\002\377\226" "\300H\377c\215\007\377h\227\001\377i\232\003\377u\244\002\377g\230\002\377i\230\002\377" "x\254\002\377m\235\015\377b\231\000\377{\257<\377\234\306h\377\213\266\063\377" "\217\272\061\377\206\260\022\377\212\257\031\377\244\300=\377~\251\035\377\216" "\261\006\377\201\244\000\377\255\310N\377\317\326\217\377n\222\000\377\220\244" "'\377\246\276M\377r\240\006\377\211\256\023\377\223\264\002\377\206\246\002\377" "\214\257\001\377\227\273\030\377h\236\013\377P\206\001\377W\215\003\377_\226\003\377" "T\212\002\377^\226\000\377q\243\034\377q\245\037\377\\\216\001\377}\223\027\377\257" "\257F\377Z\203\001\377\\\212\017\377Rz\002\377d\217\015\377c\215\007\377s\244\003\377" "q\234\002\377\233\272\066\377h\206\001\377l\220\003\377V}\001\377o\233\004\377m\232" "\016\377h\216\013\377b\203\001\377]|\002\377Zw\002\377Vr\002\377Ee\002\377?a\002\377Cb" "\002\377[~\004\377Lk\004\377!\063\006\377?K,\377prd\377\214\213\204\377\212\213\204" "\377\210\203\201\377\232\223\224\377\225\222\220\377\235\234\226\377\203" "\201x\377|{r\377ccZ\377\214\214|\377\221\216y\377\220\214q\377\224\217q\377" "\206~\\\377\214\204`\377\222\214j\377\251\245\214\377\223\220t\377\222\220" "s\377\217\214p\377\222\213n\377\225\211n\377\210y`\377\203r[\377\216~h\377" "\245\232\200\377\240\230\177\377\232\223\177\377\230\221\200\377\240\233" "\212\377\243\243\227\377\224\224\211\377\214\215\203\377yyr\377lkh\377on" "o\377xxw\377YYY\377x\177h\377p\213N\377DND\377ES?\377)C\030\377+M\012\377?" "k\017\377/Z\002\377=l\002\377M\202\010\377G\201\002\377\\\226\004\377Bl\022\377{\253" "F\377y\244H\377Cn\000\377\245\274m\377d\222\017\377b\225\003\377^\222\012\377^" "\226\020\377\\\222\002\377n\232\026\377\201\255'\377_\221\001\377l\236\002\377d\227" "\003\377]\220\002\377o\242\002\377o\242\006\377\214\257G\377\223\277]\377i\235\014" "\377\\\216\003\377~\262\030\377p\230\004\377\204\263\035\377\212\273L\377\227\305" "h\377\231\304r\377\265\327\215\377\240\307F\377\221\267\065\377\245\272\071" "\377\223\251,\377\250\272I\377\271\313`\377\276\320`\377\233\271/\377\211" "\245\001\377x\226\002\377\212\254\001\377\242\301)\377V\210\003\377V\211\003\377c\223" "\004\377\\\225\004\377]\223\005\377Y\217\000\377`\222\004\377e\224\001\377d\216\004\377" "n\223\003\377h\214\002\377n\232\002\377h\231\003\377u\247\040\377\216\274J\377\201" "\256\015\377}\244\002\377\224\264\022\377\224\261A\377Z\201\000\377a\215\002\377" "p\232\012\377u\227\004\377w\222\003\377l\201\002\377l\200\002\377_p\002\377`p\001\377" "J_\002\377So\003\377+C\000\377Jp\013\377z\237\036\377<]\002\377\031\065\000\377Oi%\377" "dv?\377np_\377kjb\377}wz\377xmu\377ndn\377qfn\377^SZ\377>\070<\377>\071=\377" "efc\377]^V\377\207\210t\377\227\225z\377\206\202`\377\213\210d\377\216\211" "g\377\230\227|\377\177\207]\377\236\235\177\377\226\224v\377\234\226y\377" "\201uX\377\207z\\\377\222\207j\377\237\230}\377\230\222{\377\233\225\201" "\377\235\232\206\377\231\230\205\377\231\231\212\377\226\227\213\377\231" "\232\220\377wwr\377^]]\377>==\377B?A\377JFI\377B>B\377\231\245v\377]bQ\377" "-<\"\377(A\025\377(I\011\377\062Y\017\377*S\001\377\063d\006\377\067k\005\377Cx\002\377" "U\224\005\377Cl\002\377Nt\000\377o\233+\377y\245M\377Q\203\000\377Y\212\024\377\214" "\266Q\377S\205\002\377R\202\002\377S\201\002\377b\222\002\377e\226\015\377{\252&\377" "j\236\001\377k\235\001\377`\217\003\377i\227\000\377v\246\001\377s\250\026\377s\247" "\062\377f\233\015\377p\237\003\377m\232\005\377y\255\040\377v\232\003\377t\245\012" "\377o\236\000\377\225\273?\377\300\341\224\377\215\271<\377\204\260\027\377" "\204\263)\377\212\261(\377\254\306]\377\247\264?\377\214\257\030\377\207\256" "\023\377\232\303B\377\217\267(\377u\217\001\377\202\236\002\377\236\301/\377`" "\221\003\377\\\221\003\377\\\227\004\377`\231\005\377n\237\012\377\200\245\035\377" "g\227\001\377Y\215\002\377X\212\001\377q\231\004\377p\227\002\377f\224\002\377g\231\002" "\377u\251\"\377f\224\005\377m\230\001\377u\236\001\377s\233\006\377b\221\004\377a\221" "\004\377a\212\003\377m\217\006\377k\212\002\377l\210\001\377x\225\003\377j\212\002\377" "a\203\002\377a\201\003\377`\201\007\377h\216\007\377\\\203\011\377o\244\026\377Lk\012" "\377Hf\020\377Xy\034\377" "\071;\377\063.\060\377zx[\377\210\230h\377XdV\377\064K*\377\036@\010\377-V\016\377" "\037L\002\377,\\\005\377:m\007\377F{\004\377X\204\012\377`\215\021\377Lu\010\377`\207" "\013\377U\202\024\377~\261C\377U\210\000\377U\204\000\377\210\270G\377Z\213\004\377" "Q\204\001\377c\226\003\377W\205\002\377`\215\012\377|\252\032\377l\242\002\377[\215" "\002\377g\232\007\377\207\264\064\377|\254\012\377h\237\014\377`\227\020\377r\245" "\004\377}\246\001\377z\252\037\377p\244(\377\202\235\001\377\204\264\060\377\242" "\312^\377\260\323k\377\200\243\020\377v\244\004\377\214\275@\377\230\303T\377" "\255\320g\377\204\261.\377\240\274\040\377\210\255\020\377\235\253\040\377\224" "\273@\377\230\274G\377\225\264\021\377\217\260\002\377\227\302\070\377V\206\003" "\377Y\223\011\377O\216\002\377W\222\002\377U\220\003\377Q\212\003\377U\214\002\377Z" "\220\001\377\\\221\002\377^\222\001\377n\236\006\377t\247\024\377[\211\001\377i\233" "\015\377g\224\002\377h\217\002\377b\206\000\377}\240\022\377\224\267\011\377r\237" "\001\377j\226\002\377r\237\003\377i\221\002\377l\227\002\377q\225\002\377k\203\003\377" "`l\002\377eo\002\377{\230\002\377u\235\002\377Sl\010\377l\217\034\377o\213,\377Ei\005" "\377?`\001\377Np\022\377Pc\037\377!\060\006\377FN\071\377Z[S\377XUS\377skk\377\211" "\203\200\377ka]\377H\071\061\377TF>\377L?\071\377_VO\377e^R\377uo\\\377\211" "\210k\377\211\215g\377{\204S\377\210\212a\377~|V\377\222\222m\377\224\221" "m\377\177|T\377\203\177W\377\233\232x\377\213\211k\377\220\220v\377\221\222" "~\377\222\227\200\377\227\240\204\377\210\213x\377\226\226\211\377\233\234" "\223\377\216\214\205\377zri\377tk_\377zqe\377~yn\377kh]\377\224\245r\377" "ViB\377\067O\060\377>Y,\377\064T\024\377\032C\002\377\037Q\001\377;s\020\377A{\006\377" "X\213\020\377p\231#\377Nz\031\377M\204\015\377I{\000\377b\226\031\377v\253\062\377" "_\220\002\377W\201\001\377t\245\023\377^\220\004\377`\224\005\377K}\003\377U\203\002\377" "m\235\011\377\203\262%\377c\224\000\377[\214\000\377i\233\006\377j\227\004\377t\247" "\002\377i\240\012\377c\226\002\377\206\260\004\377~\246\012\377l\236\017\377i\242" "/\377\226\276K\377\221\272I\377\201\256&\377\212\237\020\377\211\241\005\377" "o\230\005\377\235\275O\377\221\257:\377\206\252!\377}\252\022\377\214\242\006" "\377\203\236\004\377\213\254\011\377\325\336q\377\272\311>\377\224\260\005\377" "\221\260\002\377\225\300=\377Bx\002\377T\217\004\377S\214\002\377U\215\002\377Y\221" "\003\377]\223\002\377X\213\002\377p\236\027\377p\233\010\377f\225\003\377b\221\007\377" "a\220\002\377\\\212\002\377W\205\001\377f\220\002\377X\202\002\377_\211\002\377\210\256" "\005\377\221\263\002\377l\222\002\377r\232\003\377v\236\001\377z\245\002\377q\216\003\377" "k\200\002\377hz\004\377jw\002\377x\206\005\377{\225\001\377g\210\011\377m\230\024\377" "q\226\040\377Zl\016\377Tl\012\377Uy\012\377Oi\034\377\040.\000\377\061<\025\377js[" "\377\202\204u\377{zn\377qh_\377}tm\377\230\220\200\377\225\210p\377\230\210" "m\377\230\211l\377\213|c\377\202u]\377|w\\\377tuO\377v\200N\377\206\214b" "\377\216\220n\377~}W\377\203\201Z\377\213\212b\377\203\201R\377\212\210^" "\377\224\223q\377\214\214l\377\213\213n\377\201\201j\377\223\226\200\377" "\227\236\201\377\224\232\200\377\225\226\211\377\230\231\220\377\211\206" "|\377\216\206y\377\226\216|\377\235\227\206\377\214\211|\377\201\205o\377" "\212\262j\377BY\064\377&?\031\377+K\021\377$H\010\377\"J\002\377J\001\377x\005\377;p\001\377e\230\027\377Z\217\003\377Q\204\001\377" "Hz\002\377I{\001\377Z\212\002\377a\224\022\377l\226\021\377n\225\004\377`\212\002\377" "l\227\003\377p\230\004\377i\231\007\377j\227\014\377\205\265H\377o\240\031\377\211" "\265/\377c\227\014\377Z\211\002\377m\232\004\377l\231\002\377\220\257\003\377z\251" "\002\377u\236\026\377q\240\035\377\212\271F\377s\242\032\377h\230\001\377q\240\001" "\377\251\315\065\377n\237\016\377u\244\006\377k\230\003\377\204\251\001\377\230\262" "\017\377u\242\016\377\225\270\066\377v\247+\377\230\271*\377\246\276#\377\245" "\270\013\377\240\252\001\377\241\274\014\377\215\264\007\377\207\260\000\377\202" "\257\003\377\\\233\003\377T\221\004\377O\206\003\377T\200\003\377Ao\001\377c\225\004\377" "c\226\004\377d\221\005\377d\224\002\377X\205\002\377O|\001\377s\232\033\377g\221\012" "\377a\211\001\377^\212\002\377`\212\001\377i\223\002\377}\247\004\377\205\244\004\377" "z\224\003\377p\214\003\377h\205\001\377\221\234\023\377v\212\004\377z\231\002\377s\234" "\005\377}\234B\377[x\011\377e\225\006\377Z\205\003\377[\210\007\377h\220\020\377_\203" "\005\377Qo\002\377Sn\001\377^{\005\377H_\002\377:M\001\377FU\003\377\066F\006\377aj@\377z" "{k\377\177\201t\377{zr\377gdW\377\201xe\377\223\214i\377\234\232e\377\237" "\234_\377\236\217a\377\226\201[\377\203qL\377q`@\377wlO\377\202|_\377\205" "\201b\377|yQ\377\201\200Y\377\220\220j\377\217\216i\377\221\220r\377\211" "\211m\377\217\221|\377\221\222\204\377\224\226\211\377\222\236|\377ztn\377" "qj_\377nf[\377WNI\377JFF\377B@B\377ivU\377^\205D\377}\253R\377Iv#\377\062" "\\\016\377Kx\030\377\065f\005\377/e\004\377\066l\002\377N\201\006\377]\220\003\377P\203" "\002\377Q\201\003\377[\216\002\377\\\220\002\377F{\001\377]\225\023\377_\221\012\377" "_\217\004\377Fv\001\377t\235\003\377]\206\002\377^\221\015\377\216\274R\377n\235\004" "\377T\200\002\377\212\271\067\377a\227\010\377m\227\010\377l\227\002\377\240\264" "\014\377r\231\002\377p\243\012\377`\225\011\377t\247\040\377p\231\014\377\177\251" "\002\377n\234\003\377e\226\001\377\230\300\040\377\221\271\064\377k\230\004\377k\230" "\003\377\177\244\002\377\240\270!\377\200\247\016\377\226\272'\377a\224\004\377" "\202\262\023\377\223\273\033\377\245\300\"\377\233\257\007\377\221\252\001\377" "\213\260\013\377\210\265\022\377\272\320H\377L\210\001\377T\217\003\377L\205\002" "\377R\213\005\377[\224\004\377b\223\006\377`\205\002\377o\222\004\377g\215\000\377X\203" "\000\377]\207\010\377[\177\001\377e\215\005\377f\217\016\377i\223\012\377W\202\001\377" "]\205\001\377x\241\013\377g\215\004\377j\212\003\377o\213\002\377n\215\002\377i\212" "\004\377~\234\010\377\204\250\013\377|\241\"\377x\222\027\377]\205\003\377g\225" "\002\377\\\201\001\377Ot\003\377^\213\023\377^\214\012\377^\204\010\377f\206\015\377" "Xr\002\377Pi\003\377Ga\002\377G_\001\377?W\003\377F[\021\377fnH\377rsb\377ii]\377kk" "`\377[[I\377s\200E\377\224\221e\377\241\224o\377\203pP\377nY\070\377ub<\377" "ziC\377yjH\377{pS\377yqT\377{vV\377|yU\377\202\177]\377\214\214k\377\215" "\216o\377\214\215t\377\225\226\204\377\213\213\200\377~}t\377\202\220f\377" "pmZ\377icZ\377]ZT\377JGF\377KJJ\377RRO\377r\212U\377ZoE\377t\244O\377Gv\036" "\377W\214&\377?u\024\377\067p\005\377M\212\004\377Q\210\004\377T\212\002\377W\212\002" "\377Dw\002\377O|\002\377Y\212\003\377V\211\003\377J~\002\377X\224\020\377N\202\007\377" "L}\003\377o\230\005\377o\233\004\377e\222\004\377s\242'\377m\230\016\377t\236\004\377" "h\220\012\377\207\265\062\377Y\205\000\377o\222\005\377\226\255\007\377o\217\003\377" "l\236\005\377s\247\063\377f\232\030\377d\230\007\377v\236\001\377z\240\003\377u\240" "\005\377s\241\003\377\212\270\013\377\200\254\026\377\\\212\002\377q\234\001\377\177" "\246\002\377\214\261\005\377\177\250\006\377l\234\005\377h\233\002\377}\257\015\377" "\223\274\"\377\246\273$\377\273\311R\377\236\264\023\377\216\261\011\377\203" "\257\027\377\216\261,\377H~\002\377M\205\004\377L~\003\377G\200\002\377j\246\015\377" "`\231\015\377h\241\023\377|\254*\377\213\267A\377\205\265/\377\202\253\065\377" "f\220\004\377j\224\004\377O|\002\377[\211\013\377X\207\001\377e\204\020\377\202\256" "\004\377Y\200\002\377Y~\002\377_\204\003\377V{\002\377_\202\003\377\200\245\004\377t\234" "\022\377~\222\032\377w\225\014\377e\216\003\377_\215\004\377\\\214\013\377S\202\003" "\377Jo\001\377W\203\017\377o\240'\377^\202\002\377]|\003\377_\202\002\377Uz\003\377" "X\177\005\377Oo\002\377Da\003\377DZ\016\377S[\060\377__H\377fiQ\377Yt\061\377ZXG\377" "tlY\377iYD\377qcE\377{lG\377\221\201U\377\233\211^\377\202oG\377\177mL\377" "m_B\377kbE\377\200z_\377{uX\377\202~a\377\222\220y\377\213\212v\377\207\207" "y\377{yn\377eaX\377ekP\377x\205X\377\\UN\377eb[\377ec_\377`^W\377`bQ\377" "q\215T\377w\233Y\377s\247C\377Ar\031\377\067i\020\377\066m\007\377D\201\005\377I" "\205\002\377Bz\002\377C}\001\377Av\002\377\067l\004\377a\227\010\377L\202\003\377I}\001\377" "K\201\001\377]\232\016\377>u\005\377k\230\005\377^\211\002\377T\205\003\377c\226\022" "\377x\243\061\377r\232\006\377\207\250\003\377j\213\004\377\220\267\066\377b\215" "\000\377\200\235\004\377\252\270\005\377f\224\002\377n\244\031\377\\\220\025\377v\253" "\060\377u\241\001\377\201\245\005\377{\235\002\377z\242\003\377z\254\006\377\205\262" "\002\377k\232\002\377`\222\003\377q\237\005\377\200\252\003\377\201\251\005\377}\252" "\005\377i\235\006\377i\237\004\377w\242\004\377\255\311V\377\311\322a\377\256\304" "@\377\234\271!\377\220\262\032\377\202\261$\377t\237\023\377v\244\030\377J~" "\004\377Av\002\377;v\004\377\\\217\015\377v\252\"\377\232\307h\377\234\307k\377" "\241\314v\377k\230.\377N|\000\377t\237\022\377f\226\007\377e\226\003\377b\230\003" "\377]\222\002\377`\223\002\377\231\275\004\377r\222\004\377h\206\002\377l\211\002\377" "s\221\003\377~\237\004\377o\231\017\377\205\247\037\377d\220\007\377g\224\014\377" "h\225\004\377]\215\002\377[\211\016\377\\\211\013\377Rw\002\377Qy\005\377\201\253N" "\377b\213\027\377f\210\024\377a\204\013\377Xv\001\377[y\004\377Ul\003\377GZ\001\377" "HZ\007\377GV\022\377w\203H\377~\210Q\377qpX\377ZVI\377`YJ\377nfL\377\221\207" "^\377\237\222e\377\222|P\377\222yN\377\217uM\377\217wS\377}jL\377aR<\377" "rkX\377wr[\377\204~i\377\213\206u\377\205\200t\377yri\377[QJ\377KC\070\377" "GD\066\377\205\227O\377wo_\377yrc\377yuh\377oma\377x\207^\377a\211=\377\204" "\256[\377Ai\025\377\065\\\023\377.\\\003\377P\210\003\377Dv\002\377F{\010\377Av\005\377" "N\202\002\377\061j\002\377L\201\003\377O\211\003\377O\210\002\377N\202\003\377K\201\002" "\377Y\221\007\377t\240\006\377g\221\002\377X\204\001\377^\220\002\377q\251/\377e\232" "\021\377x\236\004\377q\216\002\377l\207\014\377\221\272A\377c\215\000\377\253\267" "\011\377~\232\002\377q\241\000\377x\252,\377y\252\031\377{\255\"\377\224\270\010" "\377y\236\011\377\224\257\006\377\177\246\006\377\210\265\002\377\200\253\002\377" "m\233\010\377_\222\002\377b\222\004\377\177\247\013\377\222\271'\377\206\256\036" "\377q\236\003\377\243\270\027\377\232\260!\377\215\260.\377\200\244\022\377\233" "\272&\377\216\256\025\377\212\253\024\377\243\304Y\377\203\257\"\377[\217\002" "\377N\177\002\377I|\002\377Q\204\004\377L{\002\377o\235!\377x\250\061\377\234\304" "i\377p\226-\377Z\206\002\377\\\205\005\377^\210\005\377m\211\004\377X{\003\377^\212" "\005\377O\200\002\377X\221\014\377\242\305\002\377\202\240\026\377q\217\001\377z\231" "\002\377u\230\003\377h\221\003\377\\\211\004\377s\246\033\377d\233\016\377`\226\011" "\377v\244\035\377a\225\021\377e\222\036\377d\213!\377c\205\020\377]|\003\377k\217" "\037\377s\227\065\377n\223/\377n\227&\377a\204\017\377^\200\014\377Yr\021\377" "du\031\377@N\004\377Md\007\377r}\071\377\224\212h\377\205~b\377zx`\377xx^\377s" "pW\377zsW\377qbF\377w_?\377\231\200^\377\242\211f\377\234\202a\377\216v[" "\377XH;\377G<\061\377geX\377spd\377zsi\377|si\377wka\377fYK\377dXG\377woT" "\377\203\235+\377vzS\377qsT\377so_\377xtf\377\213\242m\377W\204%\377w\251" "K\377Ds\030\377Ar\016\377Z\217\004\377M|\001\377\070g\011\377Y\213\033\377L\177\002" "\377I\177\002\377M\201\003\377F}\001\377W\215\003\377P\210\002\377_\220\002\377Z\204" "\002\377s\240\005\377b\213\003\377f\216\003\377a\215\003\377[\215\002\377\\\231\025\377" "e\234\017\377\222\260\011\377j\222\014\377c\214\003\377|\255\060\377\224\252\007" "\377~\245\004\377m\233\002\377f\225\000\377\203\264\061\377|\253\013\377|\251\005\377" "\202\252\002\377\204\245\002\377\241\270\005\377v\247\003\377\201\255\003\377\203\256" "\002\377x\234\005\377i\224\007\377|\247\015\377}\244\003\377\205\255\000\377\210\261" "\000\377\216\257\013\377z\236\004\377v\245\040\377\203\252\012\377\203\247\007\377" "\252\300\031\377\215\252\004\377\230\263&\377\271\320w\377r\237\000\377`\205\014" "\377_\205\003\377\\\200\003\377Z|\004\377Rw\003\377V|\003\377t\221\005\377]\201\007\377" "e\212\004\377m\223\002\377e\215\010\377[\207\002\377e\213\006\377j\214\005\377r\232" "\003\377W\203\003\377Y\202\002\377\246\310\007\377\202\255(\377r\236\016\377k\227" "\002\377f\227\000\377k\233\012\377`\224\003\377`\221\014\377`\216\005\377i\225\010\377" "`\213\006\377u\231\015\377\\\205\014\377l\240\067\377j\245E\377b\230=\377^\223" "-\377\\\215\027\377Q\177\020\377Y\214\016\377X\177\020\377=V\002\377@P\001\377We" "\002\377e\210\010\377fx\030\377qp\070\377\202}U\377\225\220k\377\220\213i\377" "\204~c\377xk[\377|n]\377\212yb\377\244\217n\377\253\224r\377\242\213h\377" "\240\207h\377\222zc\377PH=\377\027\022\020\377<\067\070\377RLE\377maU\377\203" "vc\377vdO\377mWD\377]I:\377TB\066\377}\214=\377qrP\377am\071\377o\177E\377" "h}H\377d\221>\377Bf\023\377\204\263R\377P\207\026\377;g\002\377J~\005\377o\001\377K\177\001\377J\200\004\377H~\003\377F~\001\377M\203\003\377]\220\003" "\377c\222\003\377}\251\002\377g\233\031\377R\201\006\377U\200\003\377i\221\003\377t" "\247\001\377l\232'\377n\240\023\377\226\267\004\377f\237\022\377P\207\007\377s\242" "\017\377\241\254\013\377u\240\004\377n\232\002\377i\225\005\377\177\262\016\377w\251" "\011\377w\247\004\377l\226\003\377\224\264\003\377\201\250\003\377w\246\003\377u\244" "\003\377|\251\004\377d\216\007\377i\223\007\377\207\255\002\377\225\275\002\377\200\260" "\013\377v\252\017\377k\237\032\377k\236\003\377{\255;\377\203\243\000\377\202\254" "\021\377\211\255\006\377\221\261\013\377\246\303N\377n\222\005\377\207\250\012\377" "q\212&\377Hh\002\377Zz\003\377Hr\003\377Uy\002\377Xy\003\377Gc\002\377i\215\003\377a\207" "\003\377_}\003\377U\200\002\377U\202\006\377i\220\003\377k\212\002\377\202\245\002\377" "j\211\004\377j\223\010\377\230\276\011\377j\224\010\377p\241\001\377e\227\007\377" "w\250\065\377s\236\067\377k\225\033\377d\220\012\377c\205\003\377i\212\002\377q\222" "\006\377f\217\003\377U~\003\377[\212\010\377Z\215\021\377c\230!\377b\235$\377Y\226" "\030\377W\222\030\377N\206\012\377Y\220\020\377d\224\016\377f\212\016\377h\214" "\020\377cz\024\377Wj\013\377Zk\026\377\233\234m\377\236\227x\377\225\207s\377" "\241\220\201\377\236\215~\377\236\216}\377\226\205o\377\215y_\377\231\202" "e\377\251\224s\377\245\220r\377\224}g\377OJC\377\011\007\005\377'\035\032\377\\" "K?\377nXG\377x_J\377u[G\377zeU\377wh\\\377WJ?\377}\210T\377PY/\377DR$\377" "Zt<\377X\213\062\377S\212+\377Z\204\063\377n\240\062\377Ct\007\377Y\221\022\377" "\377w`<\377" "n]\061\377hc*\377n{\065\377^w!\377u\224)\377W\202\020\377Jz\004\377T\216\010\377" "V\215\024\377Z\227\033\377i\243\062\377f\242+\377`\230\034\377V\215\010\377W\214" "\001\377M\204\005\377H\200\006\377O\203\003\377L\177\002\377Y\210\006\377U|\003\377u\243" "\036\377S\213\003\377Q\204\002\377Q~\006\377y\242.\377\211\265D\377[\221\006\377n" "\224\005\377\204\251\013\377\202\256)\377g\230\002\377q\245\005\377\201\250\004\377" "\210\253\002\377`\217\002\377p\245\012\377\202\255\002\377z\246\001\377o\236\003\377" "`\221\002\377\254\270$\377\201\240(\377\241\274\017\377\231\262\004\377\205\250" "\003\377r\233\004\377f\215\001\377i\221\003\377{\242\006\377s\232\005\377t\233\002\377" "}\245\013\377{\242\022\377p\233\014\377|\250\002\377o\230\000\377x\236!\377|\233" "\010\377\213\254\023\377\224\276D\377\200\246\000\377\231\301Z\377\200\256\061" "\377\227\270\025\377\203\237\004\377Pk\002\377V|\003\377=c\001\377Sp\002\377Zk\002\377" "Nc\002\377]z\002\377j\202\003\377z\243\003\377o\224\005\377Z\220\012\377O\212\005\377" "{\253\003\377t\246\002\377_\217\004\377]\213\002\377\232\277\002\377u\240\025\377[\211" "\023\377e\227\013\377e\225\034\377t\243\037\377e\235\017\377m\237\"\377\206\260" "N\377~\254C\377c\226\021\377m\230\025\377e\230\002\377_\220\003\377a\224\004\377" "n\237\010\377m\231\022\377\221\262\067\377\260\303\064\377\254\275=\377c\231" "\021\377Z\225\024\377a\236\026\377Z\223\007\377_\230\012\377W\220\001\377V\216\002" "\377U\211\002\377b\217\011\377q\230\026\377g\221\015\377_\212\017\377b\205\034\377" "i\202)\377|\214J\377\212\220c\377\223\225o\377\213\213c\377\213\201a\377" "\215\177c\377W@\070\377zdR\377\250\225q\377}nC\377\207yL\377\205zG\377zx\071" "\377gn\037\377j\203+\377Qt\015\377[~\026\377Hl\013\377Is\003\377m\245,\377k\243" ".\377}\260J\377Q\210\040\377u\253A\377V\212\004\377Z\215\003\377M\177\002\377I~" "\004\377\377j\206;\377l\207\061\377y\214;\377w\211\071\377Rf!\377?S\016\377`t\025" "\377j\203\020\377o\213\011\377m\212\004\377~\225\004\377q\232\003\377\200\220\003\377" "{\230\061\377\201\252L\377j\227\004\377l\235\023\377\203\250\061\377]\222\031\377" "]\215\003\377m\232\030\377[{\034\377Fj\002\377@g\003\377By\005\377F}\012\377]\230'\377" "l\252\060\377r\250\014\377]\214\003\377l\234\011\377a\215\007\377b\226\006\377~\252" "\023\377n\234\014\377O~\002\377d\226\013\377W\207\025\377j\227\014\377\211\262\003" "\377\\\214\011\377b\235\025\377\226\260\003\377k\225\007\377z\247\002\377\234\261" "\025\377q\240\003\377f\221\005\377|\241\004\377x\236\002\377\\\206\002\377w\233\004\377" "\206\245\002\377\210\244\004\377\223\255\010\377y\232\003\377|\242\013\377s\235\004" "\377x\242\003\377\215\254\005\377e\217\002\377m\237\014\377u\233\000\377\242\264." "\377\204\223\016\377\221\261\003\377\201\255\002\377t\252\026\377t\247\023\377\207" "\270\060\377\205\260\037\377\211\267\021\377s\235\004\377\211\261\"\377\205\256" "\024\377q\231\002\377ct\004\377u\230\005\377p\241\004\377j\225\005\377Z\210\004\377l\235" ")\377v\237'\377_\202\005\377g\231\004\377L\201\001\377l\230\016\377Q\177\003\377N" "\202\002\377b\233\002\377O\212\002\377_\226\002\377p\250\003\377O\202\001\377X\211\005" "\377l\235\030\377p\236\063\377T\202\020\377T\203\007\377m\233*\377Z\224\007\377" "L\203\002\377b\227\003\377T\207\002\377`\231\002\377d\235\014\377S\213\003\377P\204" "\004\377J~\003\377V\213\003\377[\214\004\377`\212\002\377b\212\004\377w\236\012\377{\250" "\016\377a\217\024\377Nu\003\377Nf\011\377IW\007\377\\o\010\377z\211#\377\220\241" "I\377\207\252=\377\243\274X\377\201\222-\377x\213\036\377Qe\010\377`x\031\377" "n\220\064\377d\204\032\377]\205\012\377\\\213\012\377U\204\002\377Gp\001\377Pu\000" "\377R}\010\377q\232\023\377\223\265\061\377Rt\012\377`\214\000\377r\243-\377w\241" "\071\377h\237\017\377X\211\014\377\177\233%\377f\215&\377M\177\002\377:j\000\377" "Z\207!\377N|\012\377Cm\001\377H{\004\377a\001\377n\235@\377\223\304" "o\377s\252K\377Dk\013\377Is\004\377m\232\010\377`\212\002\377c\216\004\377m\223\004" "\377\224\265(\377\202\243\033\377\204\247\015\377d\237\026\377P\203\004\377V\206" "\011\377X\206\012\377u\236\010\377\206\256\002\377\210\262\031\377\214\264\007\377" "\214\240\015\377\211\252\005\377\237\267\034\377~\253\024\377\207\257\023\377m" "\224\004\377z\237\004\377r\227\011\377i\223\011\377u\242\004\377\212\257\003\377\203" "\247\003\377u\234\004\377z\242\002\377q\235\003\377u\236\002\377u\226\015\377u\235\006" "\377f\225\003\377o\236\003\377{\245\006\377\220\264\010\377\214\252\004\377\206\252" "\003\377\215\261\004\377\204\246\003\377[z\001\377\212\241\020\377\206\232\004\377\224" "\246\005\377\226\263\024\377x\247\034\377\200\255\036\377s\236\000\377i\233\006\377" "g\230\007\377_\227\005\377\\\214\004\377Fn\000\377V\206\003\377Aw\004\377R\207\003\377" "f\232\005\377Y\204\002\377\\\215\003\377V\220\002\377f\234\003\377^\223\004\377_\216" "\004\377p\237\004\377P{\003\377W\177\013\377b\221\004\377v\252\002\377P\204\003\377V\204" "\004\377a\213\010\377P\201\000\377k\236\037\377b\217\027\377o\242+\377U\213\000\377" "_\221\006\377Z\216\007\377X\223\007\377m\242\024\377o\241\013\377k\240\016\377z\254" ";\377y\254?\377a\230\027\377]\220\007\377f\222\031\377Wx\025\377Nk\002\377q\225" "\067\377o\241\065\377q\245\"\377z\254'\377Z\212\006\377]~\007\377]\210\022\377c" "\221\025\377c\226\017\377Y\177\022\377_\220\006\377_\223\003\377O}\007\377c\221\030" "\377v\243/\377\206\271/\377~\255'\377\223\266K\377\220\261K\377\227\276]" "\377\237\311o\377\230\306k\377m\236\020\377h\230\016\377\210\257-\377\\\226" "\012\377Y\216\023\377d\226\006\377Hs\000\377n\222/\377_\177\035\377Rl\004\377\202" "\255G\377|\256P\377}\256=\377Kt\015\377a\217$\377t\244\011\377X\212\006\377S" "\203\010\377a\222\012\377y\243\002\377\240\275\070\377Q\207\012\377j\237\013\377" "h\227\021\377Kx\003\377m\243\021\377Y\211\006\377w\241\003\377\203\262\011\377\203" "\260\005\377\177\246\014\377y\231\005\377\216\256\002\377o\222\003\377v\240\005\377" "g\221\002\377\200\251\004\377k\227\006\377m\240\013\377h\231\004\377y\244\005\377\231" "\300\010\377s\240\011\377n\230\004\377w\237\002\377e\221\002\377}\234\003\377\250\254" "\024\377l\224\003\377k\232\003\377e\226\002\377\202\247\030\377\204\246\002\377\212" "\253\003\377\217\262\004\377\227\270\003\377\221\262\003\377d\211\002\377k\223\003\377" "~\240\004\377\227\245\006\377\220\237\006\377\222\255\015\377\214\257\015\377y\250" "\007\377c\222\005\377[\213\004\377r\245\021\377q\253\030\377k\246\030\377Bv\001\377" "@s\002\377J}\002\377S\203\003\377U|\011\377U\211\003\377c\232\004\377f\225\002\377W\210" "\003\377h\230\002\377m\233\002\377Z\210\005\377c\222\023\377L\214\006\377p\244\003\377" "l\236\003\377R\201\004\377M\177\002\377_\217\002\377^\215\002\377a\216\024\377o\243" "=\377a\240\011\377h\241\034\377\205\270N\377b\236\026\377T\216\010\377i\233\016" "\377_\222\003\377x\254/\377\200\265;\377d\234\014\377g\240\005\377e\230\001\377" "X\207\013\377h\224\065\377f\222\026\377~\250\060\377~\255=\377}\255/\377q\234" "\021\377t\240\023\377e\231\005\377\\\216\003\377a\225\004\377p\243\016\377o\243\016" "\377}\265\061\377j\236\066\377\226\302v\377\220\276J\377q\243\035\377c\214\023" "\377Vt\014\377u\235\061\377\210\264\\\377V\204\037\377Z\221\031\377\201\266;" "\377z\257,\377w\250\025\377^\213\004\377V}\016\377l\223\012\377i\221#\377e\212" "%\377]\200\007\377\177\252O\377_\222-\377v\244(\377Rx\022\377m\237\061\377W\204" "\027\377m\232\003\377h\227\005\377W\203\006\377q\233\003\377\225\267\062\377s\227\020" "\377n\233\034\377h\234\014\377i\233\011\377f\233\015\377g\225\026\377j\231\013" "\377\205\266\003\377m\252\025\377s\245\010\377a\217\004\377o\237\004\377~\254\002\377" "m\234\004\377\211\256\007\377z\240\010\377i\224\004\377y\242\007\377r\236\020\377n" "\235\003\377\203\257\004\377t\244\007\377`\214\005\377s\236\003\377n\231\003\377l\226" "\005\377\225\247\002\377{\236\024\377k\223\002\377t\233\007\377g\217\002\377~\253\005" "\377p\233\004\377s\232\003\377\211\261\002\377\216\263\002\377\200\247\002\377\212" "\260\004\377p\227\002\377\204\244\003\377}\224\003\377\243\257\002\377\242\271'\377" "}\241\002\377t\241\006\377R\206\002\377Z\222\005\377O\205\006\377`\226\027\377w\247" "-\377v\246\062\377[\216\"\377M\202\000\377>r\001\377W\205\005\377N\204\003\377W\212" "\004\377o\240\010\377Kz\003\377d\230\004\377Y\213\004\377[\214\012\377Z\216\004\377F" "~\002\377O\205\003\377{\254\002\377i\226\003\377R{\003\377X\214\003\377]\217\004\377Y\212" "\013\377q\236'\377|\265;\377q\251-\377o\241+\377`\224\016\377L\202\003\377M\200" "\003\377M~\010\377V\205\003\377d\213\023\377y\231\030\377p\235\035\377d\231\026\377" "s\247)\377\\\220\021\377{\257<\377\201\260P\377]\224\010\377d\231\007\377a\231" "\004\377h\242\022\377^\220\011\377t\241\024\377\212\264'\377k\225\013\377o\227" "\024\377\207\252A\377\225\273P\377|\256\061\377k\235\035\377k\230\035\377}\245" "/\377\201\260E\377r\247>\377e\231!\377^\220\022\377m\234!\377[\215\016\377" "V\213\020\377b\227\026\377y\244\033\377~\247\064\377o\222\015\377u\243\066\377" "Z\207\037\377|\255H\377W\177\037\377s\252*\377Rz\005\377f\223\"\377T\204\015\377" "Gp\001\377[\204\002\377w\245+\377x\242\003\377v\236\023\377\204\250\024\377k\217" "\026\377h\230\006\377l\236\031\377o\236\007\377q\234\030\377p\240\007\377\206\266" "\010\377r\244\011\377h\246\036\377y\250\000\377i\226\002\377r\240\003\377\200\261" "\003\377q\235\003\377\201\242\020\377\215\261\032\377q\236\003\377Zs\002\377l\230\003" "\377r\231\003\377\214\263\004\377u\242\005\377n\230\006\377\203\245\013\377q\224\020" "\377p\234\011\377\201\247\036\377u\241\013\377v\236\006\377y\243\001\377s\241\001" "\377m\233\003\377]\212\004\377t\237\004\377{\245\003\377\222\267\004\377n\227\002\377" "\204\246\006\377\204\246\005\377\227\266\013\377\220\252\004\377\231\257\005\377\270" "\274@\377|\243\016\377p\233\001\377`\225\003\377a\225\002\377a\223\003\377T\206\004" "\377X\216\003\377Y\220\001\377b\220\031\377\225\275`\377c\211\030\377T\204\003\377" "H~\002\377V\212\003\377g\240\002\377M\205\003\377a\227\004\377Q\202\002\377Ky\002\377^" "\212\014\377S\204\002\377U\207\003\377m\236\003\377^\210\002\377h\235\002\377a\227\003" "\377L\205\003\377a\232\011\377h\240\021\377j\242\033\377}\260A\377\232\305h\377" "g\237\023\377Fw\002\377Z\215\003\377V\213\006\377V\206\003\377f\235\012\377V\216\004" "\377]\222\002\377U\213\002\377V\217\002\377^\226\003\377[\224\005\377R\213\005\377o\240" "\033\377r\241\016\377a\214\015\377f\232\"\377b\226\011\377i\236\013\377o\236\037" "\377{\250?\377e\221\027\377\225\267\065\377\200\244\031\377^\205\004\377t\241" "'\377i\237\037\377k\240\"\377l\232%\377Fg\006\377^\216\024\377z\241\063\377\212" "\235<\377b\220\031\377L~\003\377V\211\004\377p\235\016\377g}\035\377\207\264\065" "\377z\246*\377}\264B\377^\215'\377l\221\062\377d\214!\377k\222\033\377Mu\010" "\377@f\002\377Z\205\004\377v\244\"\377w\247\022\377m\224\001\377\241\272*\377h\213" "\017\377^\214\011\377j\230\005\377\216\257#\377k\227\004\377\207\260\005\377\204" "\255\007\377h\224\005\377f\235\021\377j\244\024\377|\252\002\377q\240\004\377\206\256" "\004\377u\241\002\377h\224\002\377r\217\007\377\211\251\013\377j\227\003\377]\201\003" "\377i\220\002\377\227\270\003\377p\227\002\377z\243\020\377u\242\001\377\177\255\031" "\377g\221\003\377g\217\010\377g\215\012\377n\230\005\377\177\252\007\377y\242\014" "\377{\243\025\377l\225\003\377f\221\004\377\210\261\004\377s\234\003\377\200\246\003" "\377}\240\003\377\207\247\003\377\214\250\003\377\200\241\006\377\220\267\021\377" "\202\245\003\377\242\261\031\377\226\260-\377u\236\002\377]\227\023\377Y\210\001" "\377Nv\000\377_\202\017\377U\210\005\377]\231\010\377a\236\010\377Y\216\000\377_\223" "\027\377W\214\006\377G\202\003\377b\226\023\377d\232\001\377[\223\007\377f\225\002\377" "Q}\005\377Hs\002\377`\212\013\377S\200\004\377W\212\003\377X\212\002\377^\206\003\377" "l\234\004\377n\245\004\377J\177\003\377i\235\007\377t\250\026\377|\260+\377u\253\065" "\377\207\266L\377\225\300T\377\207\264B\377r\225\066\377^\201\023\377`\220" "\021\377Iu\003\377L~\006\377R\205\004\377S\201\003\377]\221\015\377`\233\005\377M\201" "\000\377_\224\005\377d\225\013\377`\220\011\377h\231\021\377\\\214\004\377h\241\017" "\377b\227\012\377v\246\036\377k\231\035\377j\232\014\377u\245\022\377~\241\033" "\377r\230\030\377\204\260Q\377\200\255J\377i\210+\377}\227A\377}\251A\377" "\200\257I\377b\226)\377]\222\036\377\\\222\026\377F{\005\377\\\222\007\377v\245" "\005\377j\225\011\377t\254\024\377^\214\020\377p\245\035\377Y\213\014\377u\246(" "\377Z\212!\377R~\016\377F_\004\377JX\006\377h\216\007\377u\250#\377\214\261\001\377" "\207\244\012\377\177\251\026\377l\224\024\377i\232\002\377t\251\035\377h\231\011" "\377\204\260\005\377x\244\010\377\200\251\005\377`\216\011\377k\245.\377j\240\013" "\377w\246\002\377\205\260\004\377w\235\004\377i\225\002\377v\235\003\377l\221\005\377" "}\247\002\377u\245\006\377q\235\007\377r\232\003\377\224\265\010\377v\237$\377l\232" "\012\377l\231\004\377~\254\002\377w\247\030\377r\236\015\377w\236\007\377{\243\004\377" "u\232\004\377\212\261\032\377`\214\003\377\222\267)\377v\243\004\377\200\253\005\377" "p\225\000\377\210\255\004\377\203\246\005\377\207\247\007\377\215\245\001\377l\212" "\003\377~\225\004\377\204\242\010\377\222\261\010\377\211\247\024\377\220\262\016" "\377=u\005\377[\216\"\377\213\271G\377Fw\005\377Dp\002\377:j\001\377b\231\013\377" "Y\217\002\377Q\210\005\377o\250\005\377X\212\001\377p\244\030\377\\\225\001\377]\225" "\007\377a\223\003\377Ls\005\377V\203\005\377U\202\005\377Xv\010\377c\223\004\377N\201" "\003\377Y\213\005\377a\220\006\377a\215\005\377\\\213\003\377V\206\001\377~\251\061\377" "\216\301F\377l\242\"\377\241\313i\377\211\267A\377\215\274L\377\262\334\214" "\377h\215<\377h\214/\377h\240\007\377U\216\005\377W\216\004\377^\215\004\377Q\203" "\003\377e\234\004\377s\254\034\377\202\262E\377z\244\060\377i\227\011\377e\223\013" "\377d\225\013\377j\237\015\377c\222\017\377j\225\011\377\211\261\011\377{\245" "\012\377\177\250\037\377\234\300c\377|\251F\377\204\251Z\377{\241G\377g\226" "/\377R\206\015\377a\210\020\377dw\004\377\201\262\032\377\201\255\067\377x\247" "\061\377r\251.\377i\233\017\377z\245\002\377q\243-\377l\240\020\377`\224\027\377" "d\233\"\377d\234\030\377|\261-\377v\252(\377c\223\010\377[\205\005\377Nu\005\377" "x\244\005\377\200\260\022\377x\234\005\377\204\245\023\377z\245\031\377m\233\005\377" "q\233\005\377}\256&\377\204\260\005\377V\204\004\377z\247\015\377^\204\004\377g\222" "!\377\211\265a\377\200\254\011\377}\253\003\377v\241\002\377n\230\004\377c\212\002" "\377{\233\010\377\\\210\002\377r\243\014\377n\233\002\377q\232\002\377\201\250\005" "\377y\231\016\377u\237\011\377\217\254\017\377\202\256\025\377\236\312Q\377\206" "\261\061\377p\237\005\377{\245\013\377\234\274\016\377q\225\002\377\215\264\010\377" "t\237\021\377a\211\005\377\214\271\022\377z\254\016\377}\254\034\377\231\273\016" "\377\207\244\011\377\216\251\012\377\211\236\033\377\211\247\006\377Oc\001\377w" "\233\003\377\211\261\017\377\205\247\013\377r\227\006\377[\207\003\377\200\246A\377" "y\253\071\377d\221\034\377@c\002\377;a\002\377Iq\003\377W\211\005\377J\177\001\377g\231" "\003\377Lu\004\377W\216\003\377b\242\006\377[\230\010\377P\203\000\377u\230.\377U\202" "\005\377T\201\002\377Ir\003\377U\215\003\377I\177\003\377]\225\010\377Hi\003\377Vq\007\377" "Or\002\377]\212\004\377;X\001\377^\207\015\377c\217\020\377w\253\032\377\203\270\060" "\377\205\273\066\377\210\274\067\377r\244%\377Ms\015\377\177\241,\377a\213\023" "\377l\244\025\377[\217\003\377c\234\004\377a\227\005\377j\234\005\377v\251\013\377" "s\244\016\377x\247'\377y\245.\377q\242'\377o\237(\377g\226\036\377_\222\032" "\377]\217\016\377`\222\017\377h\234\033\377Z\215\010\377\201\257\020\377\201\242" "\040\377\177\253\037\377{\254%\377x\251\015\377~\246\013\377\206\254\031\377w" "\246\020\377m\243\027\377d\231\025\377b\232\027\377W\213\011\377v\246\003\377]\205" "\013\377e\225\013\377n\243\040\377z\253*\377X\211\020\377P~\004\377{\247=\377{" "\245*\377Wz\006\377Qv\011\377\200\256\003\377p\240\001\377\240\272+\377r\243\027" "\377\205\252\005\377s\240\004\377\214\264\004\377~\253\004\377^\216\020\377y\252\007" "\377R\201\003\377h\230\027\377t\242%\377\231\300S\377\243\303!\377\234\271\"" "\377\232\264$\377\207\240\021\377r\216\005\377\202\237\005\377h\226\003\377o\235" "\013\377\206\254\003\377j\227\003\377\214\261\005\377y\241\006\377r\236\007\377\221" "\262\014\377\205\255\016\377V\201\010\377}\247\015\377\216\261\013\377n\225\013" "\377\220\265\007\377\205\260\005\377\210\256\024\377z\243\004\377x\241\003\377v\243" "\010\377\215\267)\377\250\301.\377\252\302\033\377\222\257(\377\224\260\025" "\377~\233\006\377\220\256\007\377\203\241\003\377\207\254\002\377y\232\002\377\212" "\247\011\377\207\256\022\377", }; wlroots-0.17.1/examples/cat.h000066400000000000000000000004141454110342200160420ustar00rootroot00000000000000#ifndef _CAT_H #define _CAT_H struct gimp_texture { unsigned int width; unsigned int height; unsigned int bytes_per_pixel; /* 2:RGB16, 3:RGB, 4:RGBA */ unsigned char pixel_data[128 * 128 * 4 + 1]; }; extern const struct gimp_texture cat_tex; #endif wlroots-0.17.1/examples/embedded.c000066400000000000000000000155101454110342200170220ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200112L #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "xdg-shell-client-protocol.h" static struct wl_display *remote_display = NULL; static struct wl_compositor *compositor = NULL; static struct wl_subcompositor *subcompositor = NULL; static struct xdg_wm_base *wm_base = NULL; static struct wl_egl_window *egl_window = NULL; static struct wlr_egl_surface *egl_surface = NULL; static struct wl_surface *main_surface = NULL; static struct wl_callback *frame_callback = NULL; static struct wlr_scene *scene = NULL; static struct wlr_scene_output *scene_output = NULL; static struct wl_listener new_surface = {0}; static struct wl_listener output_frame = {0}; static EGLDisplay egl_display; static EGLConfig egl_config; static EGLContext egl_context; static int width = 500; static int height = 500; static void draw_main_surface(void); static void frame_handle_done(void *data, struct wl_callback *callback, uint32_t t) { wl_callback_destroy(callback); frame_callback = NULL; draw_main_surface(); } static const struct wl_callback_listener frame_listener = { .done = frame_handle_done, }; static void draw_main_surface(void) { frame_callback = wl_surface_frame(main_surface); wl_callback_add_listener(frame_callback, &frame_listener, NULL); eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context); eglSwapInterval(egl_display, 0); glViewport(0, 0, width, height); glClearColor(1, 0, 0, 1); glClear(GL_COLOR_BUFFER_BIT); eglSwapBuffers(egl_display, egl_surface); wl_display_flush(remote_display); } static void xdg_surface_handle_configure(void *data, struct xdg_surface *xdg_surface, uint32_t serial) { xdg_surface_ack_configure(xdg_surface, serial); if (frame_callback == NULL) { draw_main_surface(); } } static const struct xdg_surface_listener xdg_surface_listener = { .configure = xdg_surface_handle_configure, }; static void xdg_toplevel_handle_configure(void *data, struct xdg_toplevel *xdg_toplevel, int32_t w, int32_t h, struct wl_array *states) { if (w != 0 && h != 0) { width = w; height = h; } } static const struct xdg_toplevel_listener xdg_toplevel_listener = { .configure = xdg_toplevel_handle_configure, }; static void handle_global(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { if (strcmp(interface, wl_compositor_interface.name) == 0) { compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 4); } else if (strcmp(interface, wl_subcompositor_interface.name) == 0) { subcompositor = wl_registry_bind(registry, name, &wl_subcompositor_interface, 1); } else if (strcmp(interface, xdg_wm_base_interface.name) == 0) { wm_base = wl_registry_bind(registry, name, &xdg_wm_base_interface, 1); } } static void handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) { // Who cares? } static const struct wl_registry_listener registry_listener = { .global = handle_global, .global_remove = handle_global_remove, }; static void output_handle_frame(struct wl_listener *listener, void *data) { wlr_scene_output_commit(scene_output, NULL); struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); wlr_scene_output_send_frame_done(scene_output, &now); } static void handle_new_surface(struct wl_listener *listener, void *data) { struct wlr_surface *wlr_surface = data; wlr_scene_surface_create(&scene->tree, wlr_surface); } static void init_egl(struct wl_display *display) { egl_display = eglGetPlatformDisplay(EGL_PLATFORM_WAYLAND_KHR, display, NULL); eglInitialize(egl_display, NULL, NULL); EGLint matched = 0; const EGLint config_attribs[] = { EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_NONE, }; eglChooseConfig(egl_display, config_attribs, &egl_config, 1, &matched); const EGLint context_attribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE, }; egl_context = eglCreateContext(egl_display, egl_config, EGL_NO_CONTEXT, context_attribs); } int main(int argc, char *argv[]) { wlr_log_init(WLR_DEBUG, NULL); remote_display = wl_display_connect(NULL); struct wl_registry *registry = wl_display_get_registry(remote_display); wl_registry_add_listener(registry, ®istry_listener, NULL); wl_display_roundtrip(remote_display); init_egl(remote_display); struct wl_display *local_display = wl_display_create(); struct wlr_backend *backend = wlr_wl_backend_create(local_display, remote_display); struct wlr_renderer *renderer = wlr_renderer_autocreate(backend); wlr_renderer_init_wl_display(renderer, local_display); struct wlr_allocator *allocator = wlr_allocator_autocreate(backend, renderer); scene = wlr_scene_create(); struct wlr_compositor *wlr_compositor = wlr_compositor_create(local_display, 5, renderer); wlr_xdg_shell_create(local_display, 2); new_surface.notify = handle_new_surface; wl_signal_add(&wlr_compositor->events.new_surface, &new_surface); wlr_backend_start(backend); main_surface = wl_compositor_create_surface(compositor); struct xdg_surface *xdg_surface = xdg_wm_base_get_xdg_surface(wm_base, main_surface); struct xdg_toplevel *xdg_toplevel = xdg_surface_get_toplevel(xdg_surface); xdg_surface_add_listener(xdg_surface, &xdg_surface_listener, NULL); xdg_toplevel_add_listener(xdg_toplevel, &xdg_toplevel_listener, NULL); egl_window = wl_egl_window_create(main_surface, width, height); egl_surface = eglCreatePlatformWindowSurface(egl_display, egl_config, egl_window, NULL); struct wl_surface *child_surface = wl_compositor_create_surface(compositor); struct wl_subsurface *subsurface = wl_subcompositor_get_subsurface(subcompositor, child_surface, main_surface); wl_subsurface_set_position(subsurface, 20, 20); struct wlr_output *output = wlr_wl_output_create_from_surface(backend, child_surface); wlr_output_init_render(output, allocator, renderer); scene_output = wlr_scene_output_create(scene, output); output_frame.notify = output_handle_frame; wl_signal_add(&output->events.frame, &output_frame); struct wlr_output_state state; wlr_output_state_init(&state); wlr_output_state_set_enabled(&state, true); wlr_output_commit_state(output, &state); wlr_output_state_finish(&state); wl_surface_commit(main_surface); wl_display_flush(remote_display); const char *socket = wl_display_add_socket_auto(local_display); setenv("WAYLAND_DISPLAY", socket, true); wlr_log(WLR_INFO, "Running embedded Wayland compositor on WAYLAND_DISPLAY=%s", socket); wl_display_run(local_display); return 0; } wlroots-0.17.1/examples/fullscreen-shell.c000066400000000000000000000163271454110342200205470ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200112L #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /** * A minimal fullscreen-shell server. It only supports rendering. */ struct fullscreen_server { struct wl_display *wl_display; struct wlr_backend *backend; struct wlr_renderer *renderer; struct wlr_allocator *allocator; struct wlr_fullscreen_shell_v1 *fullscreen_shell; struct wl_listener present_surface; struct wlr_output_layout *output_layout; struct wl_list outputs; struct wl_listener new_output; }; struct fullscreen_output { struct wl_list link; struct fullscreen_server *server; struct wlr_output *wlr_output; struct wlr_surface *surface; struct wl_listener surface_destroy; struct wl_listener frame; }; struct render_data { struct wlr_output *output; struct wlr_render_pass *render_pass; struct timespec *when; }; static void render_surface(struct wlr_surface *surface, int sx, int sy, void *data) { struct render_data *rdata = data; struct wlr_output *output = rdata->output; struct wlr_texture *texture = wlr_surface_get_texture(surface); if (texture == NULL) { return; } struct wlr_box box = { .x = sx * output->scale, .y = sy * output->scale, .width = surface->current.width * output->scale, .height = surface->current.height * output->scale, }; enum wl_output_transform transform = wlr_output_transform_invert(surface->current.transform); transform = wlr_output_transform_compose(transform, output->transform); wlr_render_pass_add_texture(rdata->render_pass, &(struct wlr_render_texture_options){ .texture = texture, .dst_box = box, .transform = transform, }); wlr_surface_send_frame_done(surface, rdata->when); } static void output_handle_frame(struct wl_listener *listener, void *data) { struct fullscreen_output *output = wl_container_of(listener, output, frame); struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); int width, height; wlr_output_effective_resolution(output->wlr_output, &width, &height); struct wlr_output_state state; wlr_output_state_init(&state); struct wlr_render_pass *pass = wlr_output_begin_render_pass(output->wlr_output, &state, NULL, NULL); if (pass == NULL) { return; } wlr_render_pass_add_rect(pass, &(struct wlr_render_rect_options){ .color = { 0.3, 0.3, 0.3, 1.0 }, .box = { .width = width, .height = height }, }); if (output->surface != NULL) { struct render_data rdata = { .output = output->wlr_output, .render_pass = pass, .when = &now, }; wlr_surface_for_each_surface(output->surface, render_surface, &rdata); } wlr_render_pass_submit(pass); wlr_output_commit_state(output->wlr_output, &state); wlr_output_state_finish(&state); } static void output_set_surface(struct fullscreen_output *output, struct wlr_surface *surface); static void output_handle_surface_destroy(struct wl_listener *listener, void *data) { struct fullscreen_output *output = wl_container_of(listener, output, surface_destroy); output_set_surface(output, NULL); } static void output_set_surface(struct fullscreen_output *output, struct wlr_surface *surface) { if (output->surface == surface) { return; } if (output->surface != NULL) { wl_list_remove(&output->surface_destroy.link); output->surface = NULL; } if (surface != NULL) { output->surface_destroy.notify = output_handle_surface_destroy; wl_signal_add(&surface->events.destroy, &output->surface_destroy); output->surface = surface; } wlr_log(WLR_DEBUG, "Presenting surface %p on output %s", surface, output->wlr_output->name); } static void server_handle_new_output(struct wl_listener *listener, void *data) { struct fullscreen_server *server = wl_container_of(listener, server, new_output); struct wlr_output *wlr_output = data; wlr_output_init_render(wlr_output, server->allocator, server->renderer); struct fullscreen_output *output = calloc(1, sizeof(*output)); output->wlr_output = wlr_output; output->server = server; output->frame.notify = output_handle_frame; wl_signal_add(&wlr_output->events.frame, &output->frame); wl_list_insert(&server->outputs, &output->link); wlr_output_layout_add_auto(server->output_layout, wlr_output); wlr_output_create_global(wlr_output); struct wlr_output_state state; wlr_output_state_init(&state); wlr_output_state_set_enabled(&state, true); struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output); if (mode != NULL) { wlr_output_state_set_mode(&state, mode); } wlr_output_commit_state(wlr_output, &state); wlr_output_state_finish(&state); } static void server_handle_present_surface(struct wl_listener *listener, void *data) { struct fullscreen_server *server = wl_container_of(listener, server, present_surface); struct wlr_fullscreen_shell_v1_present_surface_event *event = data; struct fullscreen_output *output; wl_list_for_each(output, &server->outputs, link) { if (event->output == NULL || event->output == output->wlr_output) { output_set_surface(output, event->surface); } } } int main(int argc, char *argv[]) { wlr_log_init(WLR_DEBUG, NULL); char *startup_cmd = NULL; int c; while ((c = getopt(argc, argv, "s:")) != -1) { switch (c) { case 's': startup_cmd = optarg; break; default: printf("usage: %s [-s startup-command]\n", argv[0]); return EXIT_FAILURE; } } if (optind < argc) { printf("usage: %s [-s startup-command]\n", argv[0]); return EXIT_FAILURE; } struct fullscreen_server server = {0}; server.wl_display = wl_display_create(); server.backend = wlr_backend_autocreate(server.wl_display, NULL); server.renderer = wlr_renderer_autocreate(server.backend); wlr_renderer_init_wl_display(server.renderer, server.wl_display); server.allocator = wlr_allocator_autocreate(server.backend, server.renderer); wlr_compositor_create(server.wl_display, 5, server.renderer); server.output_layout = wlr_output_layout_create(); wl_list_init(&server.outputs); server.new_output.notify = server_handle_new_output; wl_signal_add(&server.backend->events.new_output, &server.new_output); server.fullscreen_shell = wlr_fullscreen_shell_v1_create(server.wl_display); server.present_surface.notify = server_handle_present_surface; wl_signal_add(&server.fullscreen_shell->events.present_surface, &server.present_surface); const char *socket = wl_display_add_socket_auto(server.wl_display); if (!socket) { wl_display_destroy(server.wl_display); return EXIT_FAILURE; } if (!wlr_backend_start(server.backend)) { wl_display_destroy(server.wl_display); return EXIT_FAILURE; } setenv("WAYLAND_DISPLAY", socket, true); if (startup_cmd != NULL) { if (fork() == 0) { execl("/bin/sh", "/bin/sh", "-c", startup_cmd, (void *)NULL); } } wlr_log(WLR_INFO, "Running Wayland compositor on WAYLAND_DISPLAY=%s", socket); wl_display_run(server.wl_display); wl_display_destroy_clients(server.wl_display); wl_display_destroy(server.wl_display); return 0; } wlroots-0.17.1/examples/meson.build000066400000000000000000000030721454110342200172670ustar00rootroot00000000000000cairo = dependency('cairo', required: false, disabler: true) # Only needed for drm_fourcc.h libdrm_header = dependency('libdrm').partial_dependency(compile_args: true, includes: true) wayland_client = dependency('wayland-client', required: false, disabler: true) wayland_egl = dependency('wayland-egl', required: false, disabler: true) egl = dependency('egl', version: '>= 1.5', required: false, disabler: true) glesv2 = dependency('glesv2', required: false, disabler: true) compositors = { 'simple': { 'src': 'simple.c', }, 'pointer': { 'src': 'pointer.c', }, 'touch': { 'src': ['touch.c', 'cat.c'], }, 'tablet': { 'src': 'tablet.c', }, 'rotation': { 'src': ['rotation.c', 'cat.c'], }, 'output-layout': { 'src': ['output-layout.c', 'cat.c'], }, 'fullscreen-shell': { 'src': 'fullscreen-shell.c', 'proto': ['fullscreen-shell-unstable-v1'], }, 'scene-graph': { 'src': 'scene-graph.c', 'proto': ['xdg-shell'], }, 'output-layers': { 'src': 'output-layers.c', 'proto': [ 'xdg-shell', ], }, 'cairo-buffer': { 'src': 'cairo-buffer.c', 'dep': cairo, }, 'embedded': { 'src': [ 'embedded.c', protocols_code['xdg-shell'], protocols_client_header['xdg-shell'], ], 'dep': [wayland_client, wayland_egl, egl, glesv2], }, } foreach name, info : compositors extra_src = [] foreach p : info.get('proto', []) extra_src += protocols_server_header[p] endforeach executable( name, [info.get('src'), extra_src], dependencies: [wlroots, libdrm_header, info.get('dep', [])], build_by_default: get_option('examples'), ) endforeach wlroots-0.17.1/examples/output-layers.c000066400000000000000000000246171454110342200201360ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200112L #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Simple compositor making use of the output layers API. The compositor will * attempt to display client surfaces with output layers. Input is * unimplemented. * * New surfaces are stacked on top of the existing ones as they appear. * Surfaces that don't make it into an output layer are rendered as usual. */ struct server { struct wl_display *wl_display; struct wlr_backend *backend; struct wlr_renderer *renderer; struct wlr_allocator *allocator; struct wlr_linux_dmabuf_v1 *linux_dmabuf_v1; struct wl_list outputs; struct wl_listener new_output; struct wl_listener new_surface; }; struct output_surface { struct wlr_surface *wlr_surface; struct wlr_output_layer *layer; struct server *server; struct wl_list link; int x, y; struct wlr_buffer *buffer; bool first_commit, layer_accepted, prev_layer_accepted; struct wl_listener destroy; struct wl_listener commit; struct wl_listener layer_feedback; }; struct output { struct wl_list link; struct server *server; struct wlr_output *wlr_output; struct wl_list surfaces; struct wl_listener frame; }; static void output_handle_frame(struct wl_listener *listener, void *data) { struct output *output = wl_container_of(listener, output, frame); struct wl_array layers_arr = {0}; struct output_surface *output_surface; wl_list_for_each(output_surface, &output->surfaces, link) { struct wlr_output_layer_state *layer_state = wl_array_add(&layers_arr, sizeof(*layer_state)); *layer_state = (struct wlr_output_layer_state){ .layer = output_surface->layer, .buffer = output_surface->buffer, .dst_box = { .x = output_surface->x, .y = output_surface->y, .width = output_surface->wlr_surface->current.width, .height = output_surface->wlr_surface->current.height, }, }; } struct wlr_output_state output_state; wlr_output_state_init(&output_state); wlr_output_state_set_layers(&output_state, layers_arr.data, layers_arr.size / sizeof(struct wlr_output_layer_state)); if (!wlr_output_test_state(output->wlr_output, &output_state)) { wlr_log(WLR_ERROR, "wlr_output_test() failed"); return; } int width, height; wlr_output_effective_resolution(output->wlr_output, &width, &height); struct wlr_render_pass *pass = wlr_output_begin_render_pass(output->wlr_output, &output_state, NULL, NULL); wlr_render_pass_add_rect(pass, &(struct wlr_render_rect_options){ .box = { .width = width, .height = height }, .color = { 0.3, 0.3, 0.3, 1 }, }); size_t i = 0; struct wlr_output_layer_state *layers = layers_arr.data; wl_list_for_each(output_surface, &output->surfaces, link) { struct wlr_surface *wlr_surface = output_surface->wlr_surface; output_surface->layer_accepted = layers[i].accepted; i++; if (wlr_surface->buffer == NULL || output_surface->layer_accepted) { continue; } struct wlr_texture *texture = wlr_surface_get_texture(wlr_surface); if (texture == NULL) { continue; } wlr_render_pass_add_texture(pass, &(struct wlr_render_texture_options){ .texture = texture, .dst_box = { .x = output_surface->x, .y = output_surface->y }, }); } wlr_render_pass_submit(pass); wlr_output_commit_state(output->wlr_output, &output_state); wlr_output_state_finish(&output_state); struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); wl_list_for_each(output_surface, &output->surfaces, link) { wlr_surface_send_frame_done(output_surface->wlr_surface, &now); if (output_surface->wlr_surface->buffer == NULL) { continue; } if ((output_surface->first_commit || !output_surface->prev_layer_accepted) && output_surface->layer_accepted) { wlr_log(WLR_INFO, "Scanning out wlr_surface %p on output '%s'", output_surface->wlr_surface, output->wlr_output->name); } if ((output_surface->first_commit || output_surface->prev_layer_accepted) && !output_surface->layer_accepted) { wlr_log(WLR_INFO, "Cannot scan out wlr_surface %p on output '%s'", output_surface->wlr_surface, output->wlr_output->name); } output_surface->prev_layer_accepted = output_surface->layer_accepted; output_surface->first_commit = false; } wl_array_release(&layers_arr); } static void server_handle_new_output(struct wl_listener *listener, void *data) { struct server *server = wl_container_of(listener, server, new_output); struct wlr_output *wlr_output = data; wlr_output_init_render(wlr_output, server->allocator, server->renderer); struct output *output = calloc(1, sizeof(*output)); output->wlr_output = wlr_output; output->server = server; wl_list_init(&output->surfaces); output->frame.notify = output_handle_frame; wl_signal_add(&wlr_output->events.frame, &output->frame); wl_list_insert(&server->outputs, &output->link); struct wlr_output_state state; wlr_output_state_init(&state); wlr_output_state_set_enabled(&state, true); struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output); if (mode != NULL) { wlr_output_state_set_mode(&state, mode); } wlr_output_commit_state(wlr_output, &state); wlr_output_state_finish(&state); wlr_output_create_global(wlr_output); } static void output_surface_handle_destroy(struct wl_listener *listener, void *data) { struct output_surface *output_surface = wl_container_of(listener, output_surface, destroy); wlr_buffer_unlock(output_surface->buffer); wl_list_remove(&output_surface->destroy.link); wl_list_remove(&output_surface->commit.link); wl_list_remove(&output_surface->layer_feedback.link); wl_list_remove(&output_surface->link); wlr_output_layer_destroy(output_surface->layer); free(output_surface); } static void output_surface_handle_commit(struct wl_listener *listener, void *data) { struct output_surface *output_surface = wl_container_of(listener, output_surface, commit); struct wlr_buffer *buffer = NULL; if (output_surface->wlr_surface->buffer != NULL) { buffer = wlr_buffer_lock(&output_surface->wlr_surface->buffer->base); } wlr_buffer_unlock(output_surface->buffer); output_surface->buffer = buffer; } static void output_surface_handle_layer_feedback(struct wl_listener *listener, void *data) { struct output_surface *output_surface = wl_container_of(listener, output_surface, layer_feedback); const struct wlr_output_layer_feedback_event *event = data; wlr_log(WLR_DEBUG, "Sending linux-dmabuf feedback to surface %p", output_surface->wlr_surface); struct wlr_linux_dmabuf_feedback_v1 feedback = {0}; const struct wlr_linux_dmabuf_feedback_v1_init_options options = { .main_renderer = output_surface->server->renderer, .output_layer_feedback_event = event, }; wlr_linux_dmabuf_feedback_v1_init_with_options(&feedback, &options); wlr_linux_dmabuf_v1_set_surface_feedback(output_surface->server->linux_dmabuf_v1, output_surface->wlr_surface, &feedback); wlr_linux_dmabuf_feedback_v1_finish(&feedback); } static void server_handle_new_surface(struct wl_listener *listener, void *data) { struct server *server = wl_container_of(listener, server, new_surface); struct wlr_surface *wlr_surface = data; struct output *output; wl_list_for_each(output, &server->outputs, link) { struct output_surface *output_surface = calloc(1, sizeof(*output_surface)); output_surface->wlr_surface = wlr_surface; output_surface->server = server; output_surface->destroy.notify = output_surface_handle_destroy; wl_signal_add(&wlr_surface->events.destroy, &output_surface->destroy); output_surface->commit.notify = output_surface_handle_commit; wl_signal_add(&wlr_surface->events.commit, &output_surface->commit); output_surface->layer = wlr_output_layer_create(output->wlr_output); output_surface->layer_feedback.notify = output_surface_handle_layer_feedback; wl_signal_add(&output_surface->layer->events.feedback, &output_surface->layer_feedback); int pos = 50 * wl_list_length(&output->surfaces); output_surface->x = output_surface->y = pos; output_surface->first_commit = true; wl_list_insert(output->surfaces.prev, &output_surface->link); } } int main(int argc, char *argv[]) { wlr_log_init(WLR_DEBUG, NULL); char *startup_cmd = NULL; int c; while ((c = getopt(argc, argv, "s:")) != -1) { switch (c) { case 's': startup_cmd = optarg; break; default: printf("usage: %s [-s startup-command]\n", argv[0]); return EXIT_FAILURE; } } if (optind < argc) { printf("usage: %s [-s startup-command]\n", argv[0]); return EXIT_FAILURE; } struct server server = {0}; server.wl_display = wl_display_create(); server.backend = wlr_backend_autocreate(server.wl_display, NULL); server.renderer = wlr_renderer_autocreate(server.backend); wlr_renderer_init_wl_shm(server.renderer, server.wl_display); if (wlr_renderer_get_dmabuf_texture_formats(server.renderer) != NULL) { wlr_drm_create(server.wl_display, server.renderer); server.linux_dmabuf_v1 = wlr_linux_dmabuf_v1_create_with_renderer( server.wl_display, 4, server.renderer); } server.allocator = wlr_allocator_autocreate(server.backend, server.renderer); struct wlr_compositor *compositor = wlr_compositor_create(server.wl_display, 5, server.renderer); wlr_xdg_shell_create(server.wl_display, 1); wl_list_init(&server.outputs); server.new_output.notify = server_handle_new_output; wl_signal_add(&server.backend->events.new_output, &server.new_output); server.new_surface.notify = server_handle_new_surface; wl_signal_add(&compositor->events.new_surface, &server.new_surface); const char *socket = wl_display_add_socket_auto(server.wl_display); if (!socket) { wl_display_destroy(server.wl_display); return EXIT_FAILURE; } if (!wlr_backend_start(server.backend)) { wl_display_destroy(server.wl_display); return EXIT_FAILURE; } setenv("WAYLAND_DISPLAY", socket, true); if (startup_cmd != NULL) { if (fork() == 0) { execlp("sh", "sh", "-c", startup_cmd, (char *)NULL); } } wlr_log(WLR_INFO, "Running Wayland compositor on WAYLAND_DISPLAY=%s", socket); wl_display_run(server.wl_display); wl_display_destroy_clients(server.wl_display); wl_display_destroy(server.wl_display); return EXIT_SUCCESS; } wlroots-0.17.1/examples/output-layout.c000066400000000000000000000231561454110342200201510ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200112L #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "cat.h" struct sample_state { struct wl_display *display; struct wl_listener new_output; struct wl_listener new_input; struct wlr_renderer *renderer; struct wlr_allocator *allocator; struct wlr_texture *cat_texture; struct wlr_output_layout *layout; float x_offs, y_offs; float x_vel, y_vel; struct timespec ts_last; }; struct sample_output { struct sample_state *sample; struct wlr_output *output; struct wl_listener frame; struct wl_listener destroy; }; struct sample_keyboard { struct sample_state *sample; struct wlr_keyboard *wlr_keyboard; struct wl_listener key; struct wl_listener destroy; }; static void animate_cat(struct sample_state *sample, struct wlr_output *output) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); long ms = (ts.tv_sec - sample->ts_last.tv_sec) * 1000 + (ts.tv_nsec - sample->ts_last.tv_nsec) / 1000000; // how many seconds have passed since the last time we animated float seconds = ms / 1000.0f; if (seconds > 0.1f) { // XXX when we switch vt, the rendering loop stops so try to detect // that and pause when it happens. seconds = 0.0f; } // check for collisions and bounce bool ur_collision = !wlr_output_layout_output_at(sample->layout, sample->x_offs + 128, sample->y_offs); bool ul_collision = !wlr_output_layout_output_at(sample->layout, sample->x_offs, sample->y_offs); bool ll_collision = !wlr_output_layout_output_at(sample->layout, sample->x_offs, sample->y_offs + 128); bool lr_collision = !wlr_output_layout_output_at(sample->layout, sample->x_offs + 128, sample->y_offs + 128); if (ur_collision && ul_collision && ll_collision && lr_collision) { // oops we went off the screen somehow struct wlr_output_layout_output *l_output = wlr_output_layout_get(sample->layout, output); sample->x_offs = l_output->x + 20; sample->y_offs = l_output->y + 20; } else if (ur_collision && ul_collision) { sample->y_vel = fabs(sample->y_vel); } else if (lr_collision && ll_collision) { sample->y_vel = -fabs(sample->y_vel); } else if (ll_collision && ul_collision) { sample->x_vel = fabs(sample->x_vel); } else if (ur_collision && lr_collision) { sample->x_vel = -fabs(sample->x_vel); } else { if (ur_collision || lr_collision) { sample->x_vel = -fabs(sample->x_vel); } if (ul_collision || ll_collision) { sample->x_vel = fabs(sample->x_vel); } if (ul_collision || ur_collision) { sample->y_vel = fabs(sample->y_vel); } if (ll_collision || lr_collision) { sample->y_vel = -fabs(sample->y_vel); } } sample->x_offs += sample->x_vel * seconds; sample->y_offs += sample->y_vel * seconds; sample->ts_last = ts; } static void output_frame_notify(struct wl_listener *listener, void *data) { struct sample_output *output = wl_container_of(listener, output, frame); struct sample_state *sample = output->sample; struct wlr_output *wlr_output = output->output; struct wlr_output_state output_state; wlr_output_state_init(&output_state); struct wlr_render_pass *pass = wlr_output_begin_render_pass(wlr_output, &output_state, NULL, NULL); wlr_render_pass_add_rect(pass, &(struct wlr_render_rect_options){ .box = { .width = wlr_output->width, .height = wlr_output->height }, .color = { 0.25, 0.25, 0.25, 1 }, }); animate_cat(sample, output->output); struct wlr_box box = { .x = sample->x_offs, .y = sample->y_offs, .width = 128, .height = 128, }; if (wlr_output_layout_intersects(sample->layout, output->output, &box)) { // transform global coordinates to local coordinates double local_x = sample->x_offs; double local_y = sample->y_offs; wlr_output_layout_output_coords(sample->layout, output->output, &local_x, &local_y); wlr_render_pass_add_texture(pass, &(struct wlr_render_texture_options){ .texture = sample->cat_texture, .dst_box = box, }); } wlr_render_pass_submit(pass); wlr_output_commit_state(wlr_output, &output_state); wlr_output_state_finish(&output_state); } static void update_velocities(struct sample_state *sample, float x_diff, float y_diff) { sample->x_vel += x_diff; sample->y_vel += y_diff; } static void output_remove_notify(struct wl_listener *listener, void *data) { struct sample_output *sample_output = wl_container_of(listener, sample_output, destroy); struct sample_state *sample = sample_output->sample; wlr_output_layout_remove(sample->layout, sample_output->output); wl_list_remove(&sample_output->frame.link); wl_list_remove(&sample_output->destroy.link); free(sample_output); } static void new_output_notify(struct wl_listener *listener, void *data) { struct wlr_output *output = data; struct sample_state *sample = wl_container_of(listener, sample, new_output); wlr_output_init_render(output, sample->allocator, sample->renderer); struct sample_output *sample_output = calloc(1, sizeof(*sample_output)); wlr_output_layout_add_auto(sample->layout, output); sample_output->output = output; sample_output->sample = sample; wl_signal_add(&output->events.frame, &sample_output->frame); sample_output->frame.notify = output_frame_notify; wl_signal_add(&output->events.destroy, &sample_output->destroy); sample_output->destroy.notify = output_remove_notify; struct wlr_output_state state; wlr_output_state_init(&state); wlr_output_state_set_enabled(&state, true); struct wlr_output_mode *mode = wlr_output_preferred_mode(output); if (mode != NULL) { wlr_output_state_set_mode(&state, mode); } wlr_output_commit_state(output, &state); wlr_output_state_finish(&state); } static void keyboard_key_notify(struct wl_listener *listener, void *data) { struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, key); struct sample_state *sample = keyboard->sample; struct wlr_keyboard_key_event *event = data; uint32_t keycode = event->keycode + 8; const xkb_keysym_t *syms; int nsyms = xkb_state_key_get_syms(keyboard->wlr_keyboard->xkb_state, keycode, &syms); for (int i = 0; i < nsyms; i++) { xkb_keysym_t sym = syms[i]; if (sym == XKB_KEY_Escape) { wl_display_terminate(sample->display); } // NOTE: It may be better to simply refer to our key state during each frame // and make this change in pixels/sec^2 // Also, key repeat int delta = 75; if (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { switch (sym) { case XKB_KEY_Left: update_velocities(sample, -delta, 0); break; case XKB_KEY_Right: update_velocities(sample, delta, 0); break; case XKB_KEY_Up: update_velocities(sample, 0, -delta); break; case XKB_KEY_Down: update_velocities(sample, 0, delta); break; } } } } static void keyboard_destroy_notify(struct wl_listener *listener, void *data) { struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, destroy); wl_list_remove(&keyboard->destroy.link); wl_list_remove(&keyboard->key.link); free(keyboard); } static void new_input_notify(struct wl_listener *listener, void *data) { struct wlr_input_device *device = data; struct sample_state *sample = wl_container_of(listener, sample, new_input); switch (device->type) { case WLR_INPUT_DEVICE_KEYBOARD:; struct sample_keyboard *keyboard = calloc(1, sizeof(*keyboard)); keyboard->wlr_keyboard = wlr_keyboard_from_input_device(device); keyboard->sample = sample; wl_signal_add(&device->events.destroy, &keyboard->destroy); keyboard->destroy.notify = keyboard_destroy_notify; wl_signal_add(&keyboard->wlr_keyboard->events.key, &keyboard->key); keyboard->key.notify = keyboard_key_notify; struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); if (!context) { wlr_log(WLR_ERROR, "Failed to create XKB context"); exit(1); } struct xkb_keymap *keymap = xkb_keymap_new_from_names(context, NULL, XKB_KEYMAP_COMPILE_NO_FLAGS); if (!keymap) { wlr_log(WLR_ERROR, "Failed to create XKB keymap"); exit(1); } wlr_keyboard_set_keymap(keyboard->wlr_keyboard, keymap); xkb_keymap_unref(keymap); xkb_context_unref(context); break; default: break; } } int main(int argc, char *argv[]) { wlr_log_init(WLR_DEBUG, NULL); struct wl_display *display = wl_display_create(); struct sample_state state = { .x_vel = 500, .y_vel = 500, .display = display, }; state.layout = wlr_output_layout_create(); clock_gettime(CLOCK_MONOTONIC, &state.ts_last); struct wlr_backend *wlr = wlr_backend_autocreate(display, NULL); if (!wlr) { exit(1); } wl_signal_add(&wlr->events.new_output, &state.new_output); state.new_output.notify = new_output_notify; wl_signal_add(&wlr->events.new_input, &state.new_input); state.new_input.notify = new_input_notify; state.renderer = wlr_renderer_autocreate(wlr); state.cat_texture = wlr_texture_from_pixels(state.renderer, DRM_FORMAT_ABGR8888, cat_tex.width * 4, cat_tex.width, cat_tex.height, cat_tex.pixel_data); state.allocator = wlr_allocator_autocreate(wlr, state.renderer); if (!wlr_backend_start(wlr)) { wlr_log(WLR_ERROR, "Failed to start backend"); wlr_backend_destroy(wlr); exit(1); } wl_display_run(display); wlr_texture_destroy(state.cat_texture); wl_display_destroy(state.display); wlr_output_layout_destroy(state.layout); } wlroots-0.17.1/examples/pointer.c000066400000000000000000000317561454110342200167630ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200112L #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct sample_state { struct wl_display *display; struct compositor_state *compositor; struct wlr_renderer *renderer; struct wlr_allocator *allocator; struct wlr_xcursor_manager *xcursor_manager; struct wlr_cursor *cursor; double cur_x, cur_y; float default_color[4]; float clear_color[4]; struct wlr_output_layout *layout; struct wl_list devices; struct timespec last_frame; struct wl_listener new_output; struct wl_listener new_input; struct wl_listener cursor_motion; struct wl_listener cursor_motion_absolute; struct wl_listener cursor_button; struct wl_listener cursor_axis; struct wl_listener touch_motion; struct wl_listener touch_up; struct wl_listener touch_down; struct wl_listener touch_cancel; struct wl_list touch_points; struct wl_listener tablet_tool_axis; struct wl_listener tablet_tool_proxmity; struct wl_listener tablet_tool_tip; struct wl_listener tablet_tool_button; }; struct touch_point { int32_t touch_id; double x, y; struct wl_list link; }; struct sample_output { struct sample_state *state; struct wlr_output *output; struct wl_listener frame; struct wl_listener destroy; }; struct sample_keyboard { struct sample_state *state; struct wlr_keyboard *wlr_keyboard; struct wl_listener key; struct wl_listener destroy; }; static void warp_to_touch(struct sample_state *state, struct wlr_input_device *dev) { if (wl_list_empty(&state->touch_points)) { return; } double x = 0, y = 0; size_t n = 0; struct touch_point *point; wl_list_for_each(point, &state->touch_points, link) { x += point->x; y += point->y; n++; } x /= n; y /= n; wlr_cursor_warp_absolute(state->cursor, dev, x, y); } static void output_frame_notify(struct wl_listener *listener, void *data) { struct sample_output *sample_output = wl_container_of(listener, sample_output, frame); struct sample_state *state = sample_output->state; struct wlr_output *wlr_output = sample_output->output; struct wlr_renderer *renderer = state->renderer; assert(renderer); struct wlr_output_state output_state; wlr_output_state_init(&output_state); struct wlr_render_pass *pass = wlr_output_begin_render_pass(wlr_output, &output_state, NULL, NULL); wlr_render_pass_add_rect(pass, &(struct wlr_render_rect_options){ .box = { .width = wlr_output->width, .height = wlr_output->height }, .color = { state->clear_color[0], state->clear_color[1], state->clear_color[2], state->clear_color[3], }, }); wlr_output_add_software_cursors_to_render_pass(wlr_output, pass, NULL); wlr_render_pass_submit(pass); wlr_output_commit_state(wlr_output, &output_state); wlr_output_state_finish(&output_state); } static void handle_cursor_motion(struct wl_listener *listener, void *data) { struct sample_state *sample = wl_container_of(listener, sample, cursor_motion); struct wlr_pointer_motion_event *event = data; wlr_cursor_move(sample->cursor, &event->pointer->base, event->delta_x, event->delta_y); } static void handle_cursor_motion_absolute(struct wl_listener *listener, void *data) { struct sample_state *sample = wl_container_of(listener, sample, cursor_motion_absolute); struct wlr_pointer_motion_absolute_event *event = data; sample->cur_x = event->x; sample->cur_y = event->y; wlr_cursor_warp_absolute(sample->cursor, &event->pointer->base, sample->cur_x, sample->cur_y); } static void handle_cursor_button(struct wl_listener *listener, void *data) { struct sample_state *sample = wl_container_of(listener, sample, cursor_button); struct wlr_pointer_button_event *event = data; float (*color)[4]; if (event->state == WLR_BUTTON_RELEASED) { color = &sample->default_color; memcpy(&sample->clear_color, color, sizeof(*color)); } else { float red[4] = { 0.25f, 0.25f, 0.25f, 1 }; red[event->button % 3] = 1; color = &red; memcpy(&sample->clear_color, color, sizeof(*color)); } } static void handle_cursor_axis(struct wl_listener *listener, void *data) { struct sample_state *sample = wl_container_of(listener, sample, cursor_axis); struct wlr_pointer_axis_event *event = data; for (size_t i = 0; i < 3; ++i) { sample->default_color[i] += event->delta > 0 ? -0.05f : 0.05f; if (sample->default_color[i] > 1.0f) { sample->default_color[i] = 1.0f; } if (sample->default_color[i] < 0.0f) { sample->default_color[i] = 0.0f; } } memcpy(&sample->clear_color, &sample->default_color, sizeof(sample->clear_color)); } static void handle_touch_up(struct wl_listener *listener, void *data) { struct sample_state *sample = wl_container_of(listener, sample, touch_up); struct wlr_touch_up_event *event = data; struct touch_point *point, *tmp; wl_list_for_each_safe(point, tmp, &sample->touch_points, link) { if (point->touch_id == event->touch_id) { wl_list_remove(&point->link); break; } } warp_to_touch(sample, &event->touch->base); } static void handle_touch_down(struct wl_listener *listener, void *data) { struct sample_state *sample = wl_container_of(listener, sample, touch_down); struct wlr_touch_down_event *event = data; struct touch_point *point = calloc(1, sizeof(*point)); point->touch_id = event->touch_id; point->x = event->x; point->y = event->y; wl_list_insert(&sample->touch_points, &point->link); warp_to_touch(sample, &event->touch->base); } static void handle_touch_motion(struct wl_listener *listener, void *data) { struct sample_state *sample = wl_container_of(listener, sample, touch_motion); struct wlr_touch_motion_event *event = data; struct touch_point *point; wl_list_for_each(point, &sample->touch_points, link) { if (point->touch_id == event->touch_id) { point->x = event->x; point->y = event->y; break; } } warp_to_touch(sample, &event->touch->base); } static void handle_touch_cancel(struct wl_listener *listener, void *data) { wlr_log(WLR_DEBUG, "TODO: touch cancel"); } static void handle_tablet_tool_axis(struct wl_listener *listener, void *data) { struct sample_state *sample = wl_container_of(listener, sample, tablet_tool_axis); struct wlr_tablet_tool_axis_event *event = data; if ((event->updated_axes & WLR_TABLET_TOOL_AXIS_X) && (event->updated_axes & WLR_TABLET_TOOL_AXIS_Y)) { wlr_cursor_warp_absolute(sample->cursor, &event->tablet->base, event->x, event->y); } } static void keyboard_key_notify(struct wl_listener *listener, void *data) { struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, key); struct sample_state *sample = keyboard->state; struct wlr_keyboard_key_event *event = data; uint32_t keycode = event->keycode + 8; const xkb_keysym_t *syms; int nsyms = xkb_state_key_get_syms(keyboard->wlr_keyboard->xkb_state, keycode, &syms); for (int i = 0; i < nsyms; i++) { xkb_keysym_t sym = syms[i]; if (sym == XKB_KEY_Escape) { wl_display_terminate(sample->display); } } } static void output_remove_notify(struct wl_listener *listener, void *data) { struct sample_output *sample_output = wl_container_of(listener, sample_output, destroy); struct sample_state *sample = sample_output->state; wlr_output_layout_remove(sample->layout, sample_output->output); wl_list_remove(&sample_output->frame.link); wl_list_remove(&sample_output->destroy.link); free(sample_output); } static void new_output_notify(struct wl_listener *listener, void *data) { struct wlr_output *output = data; struct sample_state *sample = wl_container_of(listener, sample, new_output); wlr_output_init_render(output, sample->allocator, sample->renderer); struct sample_output *sample_output = calloc(1, sizeof(*sample_output)); sample_output->output = output; sample_output->state = sample; wl_signal_add(&output->events.frame, &sample_output->frame); sample_output->frame.notify = output_frame_notify; wl_signal_add(&output->events.destroy, &sample_output->destroy); sample_output->destroy.notify = output_remove_notify; wlr_output_layout_add_auto(sample->layout, sample_output->output); struct wlr_output_state state; wlr_output_state_init(&state); wlr_output_state_set_enabled(&state, true); struct wlr_output_mode *mode = wlr_output_preferred_mode(output); if (mode != NULL) { wlr_output_state_set_mode(&state, mode); } wlr_output_commit_state(output, &state); wlr_output_state_finish(&state); } static void keyboard_destroy_notify(struct wl_listener *listener, void *data) { struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, destroy); wl_list_remove(&keyboard->destroy.link); wl_list_remove(&keyboard->key.link); free(keyboard); } static void new_input_notify(struct wl_listener *listener, void *data) { struct wlr_input_device *device = data; struct sample_state *state = wl_container_of(listener, state, new_input); switch (device->type) { case WLR_INPUT_DEVICE_POINTER: case WLR_INPUT_DEVICE_TOUCH: case WLR_INPUT_DEVICE_TABLET_TOOL: wlr_cursor_attach_input_device(state->cursor, device); break; case WLR_INPUT_DEVICE_KEYBOARD:; struct sample_keyboard *keyboard = calloc(1, sizeof(*keyboard)); keyboard->wlr_keyboard = wlr_keyboard_from_input_device(device); keyboard->state = state; wl_signal_add(&device->events.destroy, &keyboard->destroy); keyboard->destroy.notify = keyboard_destroy_notify; wl_signal_add(&keyboard->wlr_keyboard->events.key, &keyboard->key); keyboard->key.notify = keyboard_key_notify; struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); if (!context) { wlr_log(WLR_ERROR, "Failed to create XKB context"); exit(1); } struct xkb_keymap *keymap = xkb_keymap_new_from_names(context, NULL, XKB_KEYMAP_COMPILE_NO_FLAGS); if (!keymap) { wlr_log(WLR_ERROR, "Failed to create XKB keymap"); exit(1); } wlr_keyboard_set_keymap(keyboard->wlr_keyboard, keymap); xkb_keymap_unref(keymap); xkb_context_unref(context); break; default: break; } } int main(int argc, char *argv[]) { wlr_log_init(WLR_DEBUG, NULL); struct wl_display *display = wl_display_create(); struct sample_state state = { .default_color = { 0.25f, 0.25f, 0.25f, 1 }, .clear_color = { 0.25f, 0.25f, 0.25f, 1 }, .display = display }; struct wlr_backend *wlr = wlr_backend_autocreate(display, NULL); if (!wlr) { exit(1); } state.renderer = wlr_renderer_autocreate(wlr); state.allocator = wlr_allocator_autocreate(wlr, state.renderer); state.cursor = wlr_cursor_create(); state.layout = wlr_output_layout_create(); wlr_cursor_attach_output_layout(state.cursor, state.layout); //wlr_cursor_map_to_region(state.cursor, state.config->cursor.mapped_box); wl_list_init(&state.devices); wl_list_init(&state.touch_points); // pointer events wl_signal_add(&state.cursor->events.motion, &state.cursor_motion); state.cursor_motion.notify = handle_cursor_motion; wl_signal_add(&state.cursor->events.motion_absolute, &state.cursor_motion_absolute); state.cursor_motion_absolute.notify = handle_cursor_motion_absolute; wl_signal_add(&state.cursor->events.button, &state.cursor_button); state.cursor_button.notify = handle_cursor_button; wl_signal_add(&state.cursor->events.axis, &state.cursor_axis); state.cursor_axis.notify = handle_cursor_axis; // touch events wl_signal_add(&state.cursor->events.touch_up, &state.touch_up); state.touch_up.notify = handle_touch_up; wl_signal_add(&state.cursor->events.touch_down, &state.touch_down); state.touch_down.notify = handle_touch_down; wl_signal_add(&state.cursor->events.touch_motion, &state.touch_motion); state.touch_motion.notify = handle_touch_motion; wl_signal_add(&state.cursor->events.touch_cancel, &state.touch_cancel); state.touch_cancel.notify = handle_touch_cancel; wl_signal_add(&wlr->events.new_input, &state.new_input); state.new_input.notify = new_input_notify; wl_signal_add(&wlr->events.new_output, &state.new_output); state.new_output.notify = new_output_notify; // tool events wl_signal_add(&state.cursor->events.tablet_tool_axis, &state.tablet_tool_axis); state.tablet_tool_axis.notify = handle_tablet_tool_axis; state.xcursor_manager = wlr_xcursor_manager_create(NULL, 24); if (!state.xcursor_manager) { wlr_log(WLR_ERROR, "Failed to load default cursor"); return 1; } wlr_cursor_set_xcursor(state.cursor, state.xcursor_manager, "default"); clock_gettime(CLOCK_MONOTONIC, &state.last_frame); if (!wlr_backend_start(wlr)) { wlr_log(WLR_ERROR, "Failed to start backend"); wlr_backend_destroy(wlr); exit(1); } wl_display_run(display); wl_display_destroy(display); wlr_xcursor_manager_destroy(state.xcursor_manager); wlr_cursor_destroy(state.cursor); wlr_output_layout_destroy(state.layout); } wlroots-0.17.1/examples/rotation.c000066400000000000000000000220231454110342200171250ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200112L #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "cat.h" struct sample_state { struct wl_display *display; struct wl_listener new_output; struct wl_listener new_input; struct timespec last_frame; struct wlr_renderer *renderer; struct wlr_allocator *allocator; struct wlr_texture *cat_texture; struct wl_list outputs; enum wl_output_transform transform; }; struct sample_output { struct sample_state *sample; struct wlr_output *output; struct wl_listener frame; struct wl_listener destroy; float x_offs, y_offs; float x_vel, y_vel; struct wl_list link; }; struct sample_keyboard { struct sample_state *sample; struct wlr_keyboard *wlr_keyboard; struct wl_listener key; struct wl_listener destroy; }; static void output_frame_notify(struct wl_listener *listener, void *data) { struct sample_output *sample_output = wl_container_of(listener, sample_output, frame); struct sample_state *sample = sample_output->sample; struct wlr_output *wlr_output = sample_output->output; struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); int32_t width, height; wlr_output_effective_resolution(wlr_output, &width, &height); struct wlr_output_state output_state; wlr_output_state_init(&output_state); struct wlr_render_pass *pass = wlr_output_begin_render_pass(wlr_output, &output_state, NULL, NULL); wlr_render_pass_add_rect(pass, &(struct wlr_render_rect_options){ .box = { .width = wlr_output->width, .height = wlr_output->height }, .color = { 0.25, 0.25, 0.25, 1 }, }); for (int y = -128 + (int)sample_output->y_offs; y < height; y += 128) { for (int x = -128 + (int)sample_output->x_offs; x < width; x += 128) { wlr_render_pass_add_texture(pass, &(struct wlr_render_texture_options){ .texture = sample->cat_texture, .dst_box = { .x = x, .y = y }, .transform = wlr_output->transform, }); } } wlr_render_pass_submit(pass); wlr_output_commit_state(wlr_output, &output_state); wlr_output_state_finish(&output_state); long ms = (now.tv_sec - sample->last_frame.tv_sec) * 1000 + (now.tv_nsec - sample->last_frame.tv_nsec) / 1000000; float seconds = ms / 1000.0f; sample_output->x_offs += sample_output->x_vel * seconds; sample_output->y_offs += sample_output->y_vel * seconds; if (sample_output->x_offs > 128) { sample_output->x_offs = 0; } if (sample_output->y_offs > 128) { sample_output->y_offs = 0; } sample->last_frame = now; } static void update_velocities(struct sample_state *sample, float x_diff, float y_diff) { struct sample_output *sample_output; wl_list_for_each(sample_output, &sample->outputs, link) { sample_output->x_vel += x_diff; sample_output->y_vel += y_diff; } } static void output_remove_notify(struct wl_listener *listener, void *data) { struct sample_output *sample_output = wl_container_of(listener, sample_output, destroy); wl_list_remove(&sample_output->frame.link); wl_list_remove(&sample_output->destroy.link); free(sample_output); } static void new_output_notify(struct wl_listener *listener, void *data) { struct wlr_output *output = data; struct sample_state *sample = wl_container_of(listener, sample, new_output); wlr_output_init_render(output, sample->allocator, sample->renderer); struct sample_output *sample_output = calloc(1, sizeof(*sample_output)); sample_output->x_offs = sample_output->y_offs = 0; sample_output->x_vel = sample_output->y_vel = 128; sample_output->output = output; sample_output->sample = sample; wl_signal_add(&output->events.frame, &sample_output->frame); sample_output->frame.notify = output_frame_notify; wl_signal_add(&output->events.destroy, &sample_output->destroy); sample_output->destroy.notify = output_remove_notify; wl_list_insert(&sample->outputs, &sample_output->link); struct wlr_output_state state; wlr_output_state_init(&state); wlr_output_state_set_transform(&state, sample->transform); wlr_output_state_set_enabled(&state, true); struct wlr_output_mode *mode = wlr_output_preferred_mode(output); if (mode != NULL) { wlr_output_state_set_mode(&state, mode); } wlr_output_commit_state(output, &state); wlr_output_state_finish(&state); } static void keyboard_key_notify(struct wl_listener *listener, void *data) { struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, key); struct sample_state *sample = keyboard->sample; struct wlr_keyboard_key_event *event = data; uint32_t keycode = event->keycode + 8; const xkb_keysym_t *syms; int nsyms = xkb_state_key_get_syms(keyboard->wlr_keyboard->xkb_state, keycode, &syms); for (int i = 0; i < nsyms; i++) { xkb_keysym_t sym = syms[i]; if (sym == XKB_KEY_Escape) { wl_display_terminate(sample->display); } if (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { switch (sym) { case XKB_KEY_Left: update_velocities(sample, -16, 0); break; case XKB_KEY_Right: update_velocities(sample, 16, 0); break; case XKB_KEY_Up: update_velocities(sample, 0, -16); break; case XKB_KEY_Down: update_velocities(sample, 0, 16); break; } } } } static void keyboard_destroy_notify(struct wl_listener *listener, void *data) { struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, destroy); wl_list_remove(&keyboard->destroy.link); wl_list_remove(&keyboard->key.link); free(keyboard); } static void new_input_notify(struct wl_listener *listener, void *data) { struct wlr_input_device *device = data; struct sample_state *sample = wl_container_of(listener, sample, new_input); switch (device->type) { case WLR_INPUT_DEVICE_KEYBOARD:; struct sample_keyboard *keyboard = calloc(1, sizeof(*keyboard)); keyboard->wlr_keyboard = wlr_keyboard_from_input_device(device); keyboard->sample = sample; wl_signal_add(&device->events.destroy, &keyboard->destroy); keyboard->destroy.notify = keyboard_destroy_notify; wl_signal_add(&keyboard->wlr_keyboard->events.key, &keyboard->key); keyboard->key.notify = keyboard_key_notify; struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); if (!context) { wlr_log(WLR_ERROR, "Failed to create XKB context"); exit(1); } struct xkb_keymap *keymap = xkb_keymap_new_from_names(context, NULL, XKB_KEYMAP_COMPILE_NO_FLAGS); if (!keymap) { wlr_log(WLR_ERROR, "Failed to create XKB keymap"); exit(1); } wlr_keyboard_set_keymap(keyboard->wlr_keyboard, keymap); xkb_keymap_unref(keymap); xkb_context_unref(context); break; default: break; } } int main(int argc, char *argv[]) { int c; enum wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL; while ((c = getopt(argc, argv, "r:")) != -1) { switch (c) { case 'r': if (strcmp(optarg, "90") == 0) { transform = WL_OUTPUT_TRANSFORM_90; } else if (strcmp(optarg, "180") == 0) { transform = WL_OUTPUT_TRANSFORM_180; } else if (strcmp(optarg, "270") == 0) { transform = WL_OUTPUT_TRANSFORM_270; } else if (strcmp(optarg, "flipped") == 0) { transform = WL_OUTPUT_TRANSFORM_FLIPPED; } else if (strcmp(optarg, "flipped-90") == 0) { transform = WL_OUTPUT_TRANSFORM_FLIPPED_90; } else if (strcmp(optarg, "flipped-180") == 0) { transform = WL_OUTPUT_TRANSFORM_FLIPPED_180; } else if (strcmp(optarg, "flipped-270") == 0) { transform = WL_OUTPUT_TRANSFORM_FLIPPED_270; } else { wlr_log(WLR_ERROR, "got unknown transform value: %s", optarg); } break; default: return 1; } } wlr_log_init(WLR_DEBUG, NULL); struct wl_display *display = wl_display_create(); struct sample_state state = { .display = display, .transform = transform }; wl_list_init(&state.outputs); struct wlr_backend *wlr = wlr_backend_autocreate(display, NULL); if (!wlr) { exit(1); } wl_signal_add(&wlr->events.new_output, &state.new_output); state.new_output.notify = new_output_notify; wl_signal_add(&wlr->events.new_input, &state.new_input); state.new_input.notify = new_input_notify; clock_gettime(CLOCK_MONOTONIC, &state.last_frame); state.renderer = wlr_renderer_autocreate(wlr); if (!state.renderer) { wlr_log(WLR_ERROR, "Could not start compositor, OOM"); wlr_backend_destroy(wlr); exit(EXIT_FAILURE); } state.cat_texture = wlr_texture_from_pixels(state.renderer, DRM_FORMAT_ABGR8888, cat_tex.width * 4, cat_tex.width, cat_tex.height, cat_tex.pixel_data); if (!state.cat_texture) { wlr_log(WLR_ERROR, "Could not start compositor, OOM"); exit(EXIT_FAILURE); } state.allocator = wlr_allocator_autocreate(wlr, state.renderer); if (!wlr_backend_start(wlr)) { wlr_log(WLR_ERROR, "Failed to start backend"); wlr_backend_destroy(wlr); exit(1); } wl_display_run(display); wlr_texture_destroy(state.cat_texture); wl_display_destroy(display); } wlroots-0.17.1/examples/scene-graph.c000066400000000000000000000137141454110342200174710ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200112L #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Simple compositor making use of the scene-graph API. Input is unimplemented. * * New surfaces are stacked on top of the existing ones as they appear. */ static const int border_width = 3; struct server { struct wl_display *display; struct wlr_backend *backend; struct wlr_renderer *renderer; struct wlr_allocator *allocator; struct wlr_scene *scene; uint32_t surface_offset; struct wl_listener new_output; struct wl_listener new_surface; }; struct surface { struct wlr_surface *wlr; struct wlr_scene_surface *scene_surface; struct wlr_scene_rect *border; struct wl_list link; struct wl_listener commit; struct wl_listener destroy; }; struct output { struct wl_list link; struct server *server; struct wlr_output *wlr; struct wlr_scene_output *scene_output; struct wl_listener frame; }; static void output_handle_frame(struct wl_listener *listener, void *data) { struct output *output = wl_container_of(listener, output, frame); if (!wlr_scene_output_commit(output->scene_output, NULL)) { return; } struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); wlr_scene_output_send_frame_done(output->scene_output, &now); } static void server_handle_new_output(struct wl_listener *listener, void *data) { struct server *server = wl_container_of(listener, server, new_output); struct wlr_output *wlr_output = data; wlr_output_init_render(wlr_output, server->allocator, server->renderer); struct output *output = calloc(1, sizeof(*output)); output->wlr = wlr_output; output->server = server; output->frame.notify = output_handle_frame; wl_signal_add(&wlr_output->events.frame, &output->frame); output->scene_output = wlr_scene_output_create(server->scene, wlr_output); struct wlr_output_state state; wlr_output_state_init(&state); wlr_output_state_set_enabled(&state, true); struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output); if (mode != NULL) { wlr_output_state_set_mode(&state, mode); } wlr_output_commit_state(wlr_output, &state); wlr_output_state_finish(&state); wlr_output_create_global(wlr_output); } static void surface_handle_commit(struct wl_listener *listener, void *data) { struct surface *surface = wl_container_of(listener, surface, commit); wlr_scene_rect_set_size(surface->border, surface->wlr->current.width + 2 * border_width, surface->wlr->current.height + 2 * border_width); } static void surface_handle_destroy(struct wl_listener *listener, void *data) { struct surface *surface = wl_container_of(listener, surface, destroy); wlr_scene_node_destroy(&surface->scene_surface->buffer->node); wlr_scene_node_destroy(&surface->border->node); wl_list_remove(&surface->destroy.link); wl_list_remove(&surface->link); free(surface); } static void server_handle_new_surface(struct wl_listener *listener, void *data) { struct server *server = wl_container_of(listener, server, new_surface); struct wlr_surface *wlr_surface = data; int pos = server->surface_offset; server->surface_offset += 50; struct surface *surface = calloc(1, sizeof(*surface)); surface->wlr = wlr_surface; surface->commit.notify = surface_handle_commit; wl_signal_add(&wlr_surface->events.commit, &surface->commit); surface->destroy.notify = surface_handle_destroy; wl_signal_add(&wlr_surface->events.destroy, &surface->destroy); /* Border dimensions will be set in surface.commit handler */ surface->border = wlr_scene_rect_create(&server->scene->tree, 0, 0, (float[4]){ 0.5f, 0.5f, 0.5f, 1 }); wlr_scene_node_set_position(&surface->border->node, pos, pos); surface->scene_surface = wlr_scene_surface_create(&server->scene->tree, wlr_surface); wlr_scene_node_set_position(&surface->scene_surface->buffer->node, pos + border_width, pos + border_width); } int main(int argc, char *argv[]) { wlr_log_init(WLR_DEBUG, NULL); char *startup_cmd = NULL; int c; while ((c = getopt(argc, argv, "s:")) != -1) { switch (c) { case 's': startup_cmd = optarg; break; default: printf("usage: %s [-s startup-command]\n", argv[0]); return EXIT_FAILURE; } } if (optind < argc) { printf("usage: %s [-s startup-command]\n", argv[0]); return EXIT_FAILURE; } struct server server = {0}; server.surface_offset = 0; server.display = wl_display_create(); server.backend = wlr_backend_autocreate(server.display, NULL); server.scene = wlr_scene_create(); server.renderer = wlr_renderer_autocreate(server.backend); wlr_renderer_init_wl_display(server.renderer, server.display); server.allocator = wlr_allocator_autocreate(server.backend, server.renderer); struct wlr_compositor *compositor = wlr_compositor_create(server.display, 5, server.renderer); wlr_xdg_shell_create(server.display, 2); server.new_output.notify = server_handle_new_output; wl_signal_add(&server.backend->events.new_output, &server.new_output); server.new_surface.notify = server_handle_new_surface; wl_signal_add(&compositor->events.new_surface, &server.new_surface); const char *socket = wl_display_add_socket_auto(server.display); if (!socket) { wl_display_destroy(server.display); return EXIT_FAILURE; } if (!wlr_backend_start(server.backend)) { wl_display_destroy(server.display); return EXIT_FAILURE; } setenv("WAYLAND_DISPLAY", socket, true); if (startup_cmd != NULL) { if (fork() == 0) { execl("/bin/sh", "/bin/sh", "-c", startup_cmd, (void *)NULL); } } wlr_log(WLR_INFO, "Running Wayland compositor on WAYLAND_DISPLAY=%s", socket); wl_display_run(server.display); wl_display_destroy_clients(server.display); wl_display_destroy(server.display); return EXIT_SUCCESS; } wlroots-0.17.1/examples/simple.c000066400000000000000000000144301454110342200165620ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200112L #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct sample_state { struct wl_display *display; struct wl_listener new_output; struct wl_listener new_input; struct wlr_renderer *renderer; struct wlr_allocator *allocator; struct timespec last_frame; float color[4]; int dec; }; struct sample_output { struct sample_state *sample; struct wlr_output *output; struct wl_listener frame; struct wl_listener destroy; }; struct sample_keyboard { struct sample_state *sample; struct wlr_keyboard *wlr_keyboard; struct wl_listener key; struct wl_listener destroy; }; static void output_frame_notify(struct wl_listener *listener, void *data) { struct sample_output *sample_output = wl_container_of(listener, sample_output, frame); struct sample_state *sample = sample_output->sample; struct wlr_output *wlr_output = sample_output->output; struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); long ms = (now.tv_sec - sample->last_frame.tv_sec) * 1000 + (now.tv_nsec - sample->last_frame.tv_nsec) / 1000000; int inc = (sample->dec + 1) % 3; sample->color[inc] += ms / 2000.0f; sample->color[sample->dec] -= ms / 2000.0f; if (sample->color[sample->dec] < 0.0f) { sample->color[inc] = 1.0f; sample->color[sample->dec] = 0.0f; sample->dec = inc; } struct wlr_output_state state; wlr_output_state_init(&state); struct wlr_render_pass *pass = wlr_output_begin_render_pass(wlr_output, &state, NULL, NULL); wlr_render_pass_add_rect(pass, &(struct wlr_render_rect_options){ .box = { .width = wlr_output->width, .height = wlr_output->height }, .color = { .r = sample->color[0], .g = sample->color[1], .b = sample->color[2], .a = sample->color[3], }, }); wlr_render_pass_submit(pass); wlr_output_commit_state(wlr_output, &state); wlr_output_state_finish(&state); sample->last_frame = now; } static void output_remove_notify(struct wl_listener *listener, void *data) { struct sample_output *sample_output = wl_container_of(listener, sample_output, destroy); wlr_log(WLR_DEBUG, "Output removed"); wl_list_remove(&sample_output->frame.link); wl_list_remove(&sample_output->destroy.link); free(sample_output); } static void new_output_notify(struct wl_listener *listener, void *data) { struct wlr_output *output = data; struct sample_state *sample = wl_container_of(listener, sample, new_output); wlr_output_init_render(output, sample->allocator, sample->renderer); struct sample_output *sample_output = calloc(1, sizeof(*sample_output)); sample_output->output = output; sample_output->sample = sample; wl_signal_add(&output->events.frame, &sample_output->frame); sample_output->frame.notify = output_frame_notify; wl_signal_add(&output->events.destroy, &sample_output->destroy); sample_output->destroy.notify = output_remove_notify; struct wlr_output_state state; wlr_output_state_init(&state); wlr_output_state_set_enabled(&state, true); struct wlr_output_mode *mode = wlr_output_preferred_mode(output); if (mode != NULL) { wlr_output_state_set_mode(&state, mode); } wlr_output_commit_state(output, &state); wlr_output_state_finish(&state); } static void keyboard_key_notify(struct wl_listener *listener, void *data) { struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, key); struct sample_state *sample = keyboard->sample; struct wlr_keyboard_key_event *event = data; uint32_t keycode = event->keycode + 8; const xkb_keysym_t *syms; int nsyms = xkb_state_key_get_syms(keyboard->wlr_keyboard->xkb_state, keycode, &syms); for (int i = 0; i < nsyms; i++) { xkb_keysym_t sym = syms[i]; if (sym == XKB_KEY_Escape) { wl_display_terminate(sample->display); } } } static void keyboard_destroy_notify(struct wl_listener *listener, void *data) { struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, destroy); wl_list_remove(&keyboard->destroy.link); wl_list_remove(&keyboard->key.link); free(keyboard); } static void new_input_notify(struct wl_listener *listener, void *data) { struct wlr_input_device *device = data; struct sample_state *sample = wl_container_of(listener, sample, new_input); switch (device->type) { case WLR_INPUT_DEVICE_KEYBOARD:; struct sample_keyboard *keyboard = calloc(1, sizeof(*keyboard)); keyboard->wlr_keyboard = wlr_keyboard_from_input_device(device); keyboard->sample = sample; wl_signal_add(&device->events.destroy, &keyboard->destroy); keyboard->destroy.notify = keyboard_destroy_notify; wl_signal_add(&keyboard->wlr_keyboard->events.key, &keyboard->key); keyboard->key.notify = keyboard_key_notify; struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); if (!context) { wlr_log(WLR_ERROR, "Failed to create XKB context"); exit(1); } struct xkb_keymap *keymap = xkb_keymap_new_from_names(context, NULL, XKB_KEYMAP_COMPILE_NO_FLAGS); if (!keymap) { wlr_log(WLR_ERROR, "Failed to create XKB keymap"); exit(1); } wlr_keyboard_set_keymap(keyboard->wlr_keyboard, keymap); xkb_keymap_unref(keymap); xkb_context_unref(context); break; default: break; } } int main(void) { wlr_log_init(WLR_DEBUG, NULL); struct wl_display *display = wl_display_create(); struct sample_state state = { .color = { 1.0, 0.0, 0.0, 1.0 }, .dec = 0, .last_frame = { 0 }, .display = display }; struct wlr_backend *backend = wlr_backend_autocreate(display, NULL); if (!backend) { exit(1); } state.renderer = wlr_renderer_autocreate(backend); state.allocator = wlr_allocator_autocreate(backend, state.renderer); wl_signal_add(&backend->events.new_output, &state.new_output); state.new_output.notify = new_output_notify; wl_signal_add(&backend->events.new_input, &state.new_input); state.new_input.notify = new_input_notify; clock_gettime(CLOCK_MONOTONIC, &state.last_frame); if (!wlr_backend_start(backend)) { wlr_log(WLR_ERROR, "Failed to start backend"); wlr_backend_destroy(backend); exit(1); } wl_display_run(display); wl_display_destroy(display); } wlroots-0.17.1/examples/tablet.c000066400000000000000000000320661454110342200165510ustar00rootroot00000000000000#define _XOPEN_SOURCE 600 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct sample_state { struct wl_display *display; struct wlr_renderer *renderer; struct wlr_allocator *allocator; bool proximity, tap, button; double distance; double pressure; double x, y; double x_tilt, y_tilt; double width_mm, height_mm; double ring; struct wl_list link; float tool_color[4]; float pad_color[4]; struct timespec last_frame; struct wl_listener new_output; struct wl_listener new_input; struct wl_list tablet_tools; struct wl_list tablet_pads; }; struct tablet_tool_state { struct sample_state *sample; struct wlr_tablet *wlr_tablet; struct wl_listener destroy; struct wl_listener axis; struct wl_listener proximity; struct wl_listener tip; struct wl_listener button; struct wl_list link; void *data; }; struct tablet_pad_state { struct sample_state *sample; struct wlr_tablet_pad *wlr_tablet_pad; struct wl_listener destroy; struct wl_listener button; struct wl_listener ring; struct wl_list link; void *data; }; struct sample_output { struct sample_state *sample; struct wlr_output *output; struct wl_listener frame; struct wl_listener destroy; }; struct sample_keyboard { struct sample_state *sample; struct wlr_keyboard *wlr_keyboard; struct wl_listener key; struct wl_listener destroy; }; static void output_frame_notify(struct wl_listener *listener, void *data) { struct sample_output *sample_output = wl_container_of(listener, sample_output, frame); struct sample_state *sample = sample_output->sample; struct wlr_output *wlr_output = sample_output->output; struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); int32_t width, height; wlr_output_effective_resolution(wlr_output, &width, &height); struct wlr_output_state output_state; wlr_output_state_init(&output_state); struct wlr_render_pass *pass = wlr_output_begin_render_pass(wlr_output, &output_state, NULL, NULL); wlr_render_pass_add_rect(pass, &(struct wlr_render_rect_options){ .box = { .width = wlr_output->width, .height = wlr_output->height }, .color = { 0.25, 0.25, 0.25, 1 }, }); float distance = 0.8f * (1 - sample->distance); float tool_color[4] = { distance, distance, distance, 1 }; for (size_t i = 0; sample->button && i < 4; ++i) { tool_color[i] = sample->tool_color[i]; } float scale = 4; float pad_width = sample->width_mm * scale; float pad_height = sample->height_mm * scale; float left = width / 2.0f - pad_width / 2.0f; float top = height / 2.0f - pad_height / 2.0f; wlr_render_pass_add_rect(pass, &(struct wlr_render_rect_options){ .box = { .x = left, .y = top, .width = pad_width, .height = pad_height, }, .color = { sample->pad_color[0], sample->pad_color[1], sample->pad_color[2], sample->pad_color[3], }, }); if (sample->proximity) { struct wlr_box box = { .x = (sample->x * pad_width) - 8 * (sample->pressure + 1) + left, .y = (sample->y * pad_height) - 8 * (sample->pressure + 1) + top, .width = 16 * (sample->pressure + 1), .height = 16 * (sample->pressure + 1), }; // TODO: use sample->ring wlr_render_pass_add_rect(pass, &(struct wlr_render_rect_options){ .box = box, .color = { tool_color[0], tool_color[1], tool_color[2], tool_color[3], }, }); box.x += sample->x_tilt; box.y += sample->y_tilt; box.width /= 2; box.height /= 2; wlr_render_pass_add_rect(pass, &(struct wlr_render_rect_options){ .box = box, .color = { tool_color[0], tool_color[1], tool_color[2], tool_color[3], }, }); } wlr_render_pass_submit(pass); wlr_output_commit_state(wlr_output, &output_state); wlr_output_state_finish(&output_state); sample->last_frame = now; } static void tablet_tool_axis_notify(struct wl_listener *listener, void *data) { struct tablet_tool_state *tstate = wl_container_of(listener, tstate, axis); struct wlr_tablet_tool_axis_event *event = data; struct sample_state *sample = tstate->sample; if ((event->updated_axes & WLR_TABLET_TOOL_AXIS_X)) { sample->x = event->x; } if ((event->updated_axes & WLR_TABLET_TOOL_AXIS_Y)) { sample->y = event->y; } if ((event->updated_axes & WLR_TABLET_TOOL_AXIS_DISTANCE)) { sample->distance = event->distance; } if ((event->updated_axes & WLR_TABLET_TOOL_AXIS_PRESSURE)) { sample->pressure = event->pressure; } if ((event->updated_axes & WLR_TABLET_TOOL_AXIS_TILT_X)) { sample->x_tilt = event->tilt_x; } if ((event->updated_axes & WLR_TABLET_TOOL_AXIS_TILT_Y)) { sample->y_tilt = event->tilt_y; } } static void tablet_tool_proximity_notify(struct wl_listener *listener, void *data) { struct tablet_tool_state *tstate = wl_container_of(listener, tstate, proximity); struct wlr_tablet_tool_proximity_event *event = data; struct sample_state *sample = tstate->sample; sample->proximity = event->state == WLR_TABLET_TOOL_PROXIMITY_IN; } static void tablet_tool_button_notify(struct wl_listener *listener, void *data) { struct tablet_tool_state *tstate = wl_container_of(listener, tstate, button); struct wlr_tablet_tool_button_event *event = data; struct sample_state *sample = tstate->sample; if (event->state == WLR_BUTTON_RELEASED) { sample->button = false; } else { sample->button = true; for (size_t i = 0; i < 3; ++i) { if (event->button % 3 == i) { sample->tool_color[i] = 0; } else { sample->tool_color[i] = 1; } } } } static void tablet_pad_button_notify(struct wl_listener *listener, void *data) { struct tablet_pad_state *pstate = wl_container_of(listener, pstate, button); struct wlr_tablet_pad_button_event *event = data; struct sample_state *sample = pstate->sample; float default_color[4] = { 0.5, 0.5, 0.5, 1.0 }; if (event->state == WLR_BUTTON_RELEASED) { memcpy(sample->pad_color, default_color, sizeof(default_color)); } else { for (size_t i = 0; i < 3; ++i) { if (event->button % 3 == i) { sample->pad_color[i] = 0; } else { sample->pad_color[i] = 1; } } } } static void tablet_pad_ring_notify(struct wl_listener *listener, void *data) { struct tablet_pad_state *pstate = wl_container_of(listener, pstate, ring); struct wlr_tablet_pad_ring_event *event = data; struct sample_state *sample = pstate->sample; if (event->position != -1) { sample->ring = -(event->position * (M_PI / 180.0)); } } static void tablet_tool_destroy_notify(struct wl_listener *listener, void *data) { struct tablet_tool_state *tstate = wl_container_of(listener, tstate, destroy); wl_list_remove(&tstate->link); wl_list_remove(&tstate->destroy.link); wl_list_remove(&tstate->axis.link); wl_list_remove(&tstate->proximity.link); wl_list_remove(&tstate->button.link); free(tstate); } static void tablet_pad_destroy_notify(struct wl_listener *listener, void *data) { struct tablet_pad_state *pstate = wl_container_of(listener, pstate, destroy); wl_list_remove(&pstate->link); wl_list_remove(&pstate->destroy.link); wl_list_remove(&pstate->ring.link); wl_list_remove(&pstate->button.link); free(pstate); } static void output_remove_notify(struct wl_listener *listener, void *data) { struct sample_output *sample_output = wl_container_of(listener, sample_output, destroy); wl_list_remove(&sample_output->frame.link); wl_list_remove(&sample_output->destroy.link); free(sample_output); } static void new_output_notify(struct wl_listener *listener, void *data) { struct wlr_output *output = data; struct sample_state *sample = wl_container_of(listener, sample, new_output); wlr_output_init_render(output, sample->allocator, sample->renderer); struct sample_output *sample_output = calloc(1, sizeof(*sample_output)); sample_output->output = output; sample_output->sample = sample; wl_signal_add(&output->events.frame, &sample_output->frame); sample_output->frame.notify = output_frame_notify; wl_signal_add(&output->events.destroy, &sample_output->destroy); sample_output->destroy.notify = output_remove_notify; struct wlr_output_state state; wlr_output_state_init(&state); wlr_output_state_set_enabled(&state, true); struct wlr_output_mode *mode = wlr_output_preferred_mode(output); if (mode != NULL) { wlr_output_state_set_mode(&state, mode); } wlr_output_commit_state(output, &state); wlr_output_state_finish(&state); } static void keyboard_key_notify(struct wl_listener *listener, void *data) { struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, key); struct sample_state *sample = keyboard->sample; struct wlr_keyboard_key_event *event = data; uint32_t keycode = event->keycode + 8; const xkb_keysym_t *syms; int nsyms = xkb_state_key_get_syms(keyboard->wlr_keyboard->xkb_state, keycode, &syms); for (int i = 0; i < nsyms; i++) { xkb_keysym_t sym = syms[i]; if (sym == XKB_KEY_Escape) { wl_display_terminate(sample->display); } } } static void keyboard_destroy_notify(struct wl_listener *listener, void *data) { struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, destroy); wl_list_remove(&keyboard->destroy.link); wl_list_remove(&keyboard->key.link); free(keyboard); } static void new_input_notify(struct wl_listener *listener, void *data) { struct wlr_input_device *device = data; struct sample_state *sample = wl_container_of(listener, sample, new_input); switch (device->type) { case WLR_INPUT_DEVICE_KEYBOARD:; struct sample_keyboard *keyboard = calloc(1, sizeof(*keyboard)); keyboard->wlr_keyboard = wlr_keyboard_from_input_device(device); keyboard->sample = sample; wl_signal_add(&device->events.destroy, &keyboard->destroy); keyboard->destroy.notify = keyboard_destroy_notify; wl_signal_add(&keyboard->wlr_keyboard->events.key, &keyboard->key); keyboard->key.notify = keyboard_key_notify; struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); if (!context) { wlr_log(WLR_ERROR, "Failed to create XKB context"); exit(1); } struct xkb_keymap *keymap = xkb_keymap_new_from_names(context, NULL, XKB_KEYMAP_COMPILE_NO_FLAGS); if (!keymap) { wlr_log(WLR_ERROR, "Failed to create XKB keymap"); exit(1); } wlr_keyboard_set_keymap(keyboard->wlr_keyboard, keymap); xkb_keymap_unref(keymap); xkb_context_unref(context); break; case WLR_INPUT_DEVICE_TABLET_PAD:; struct tablet_pad_state *pstate = calloc(1, sizeof(*pstate)); pstate->wlr_tablet_pad = wlr_tablet_pad_from_input_device(device); pstate->sample = sample; pstate->destroy.notify = tablet_pad_destroy_notify; wl_signal_add(&device->events.destroy, &pstate->destroy); pstate->button.notify = tablet_pad_button_notify; wl_signal_add(&pstate->wlr_tablet_pad->events.button, &pstate->button); pstate->ring.notify = tablet_pad_ring_notify; wl_signal_add(&pstate->wlr_tablet_pad->events.ring, &pstate->ring); wl_list_insert(&sample->tablet_pads, &pstate->link); break; case WLR_INPUT_DEVICE_TABLET_TOOL:; struct wlr_tablet *tablet = wlr_tablet_from_input_device(device); sample->width_mm = tablet->width_mm == 0 ? 20 : tablet->width_mm; sample->height_mm = tablet->height_mm == 0 ? 10 : tablet->height_mm; struct tablet_tool_state *tstate = calloc(1, sizeof(*tstate)); tstate->wlr_tablet = tablet; tstate->sample = sample; tstate->destroy.notify = tablet_tool_destroy_notify; wl_signal_add(&device->events.destroy, &tstate->destroy); tstate->axis.notify = tablet_tool_axis_notify; wl_signal_add(&tablet->events.axis, &tstate->axis); tstate->proximity.notify = tablet_tool_proximity_notify; wl_signal_add(&tablet->events.proximity, &tstate->proximity); tstate->button.notify = tablet_tool_button_notify; wl_signal_add(&tablet->events.button, &tstate->button); wl_list_insert(&sample->tablet_tools, &tstate->link); break; default: break; } } int main(int argc, char *argv[]) { wlr_log_init(WLR_DEBUG, NULL); struct wl_display *display = wl_display_create(); struct sample_state state = { .display = display, .tool_color = { 1, 1, 1, 1 }, .pad_color = { 0.5, 0.5, 0.5, 1.0 } }; wl_list_init(&state.tablet_pads); wl_list_init(&state.tablet_tools); struct wlr_backend *wlr = wlr_backend_autocreate(display, NULL); if (!wlr) { exit(1); } wl_signal_add(&wlr->events.new_output, &state.new_output); state.new_output.notify = new_output_notify; wl_signal_add(&wlr->events.new_input, &state.new_input); state.new_input.notify = new_input_notify; clock_gettime(CLOCK_MONOTONIC, &state.last_frame); state.renderer = wlr_renderer_autocreate(wlr); if (!state.renderer) { wlr_log(WLR_ERROR, "Could not start compositor, OOM"); exit(EXIT_FAILURE); } state.allocator = wlr_allocator_autocreate(wlr, state.renderer); if (!wlr_backend_start(wlr)) { wlr_log(WLR_ERROR, "Failed to start backend"); wlr_backend_destroy(wlr); exit(1); } wl_display_run(display); wl_display_destroy(display); } wlroots-0.17.1/examples/touch.c000066400000000000000000000237621454110342200164230ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200112L #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "cat.h" struct sample_state { struct wl_display *display; struct wlr_renderer *renderer; struct wlr_allocator *allocator; struct wlr_texture *cat_texture; struct wl_list touch_points; struct timespec last_frame; struct wl_listener new_output; struct wl_listener new_input; struct wl_list touch; }; struct touch_point { int32_t touch_id; double x, y; struct wl_list link; }; struct touch_state { struct sample_state *sample; struct wlr_touch *wlr_touch; struct wl_listener destroy; struct wl_listener down; struct wl_listener up; struct wl_listener motion; struct wl_listener cancel; struct wl_list link; void *data; }; struct sample_output { struct sample_state *sample; struct wlr_output *output; struct wl_listener frame; struct wl_listener destroy; }; struct sample_keyboard { struct sample_state *sample; struct wlr_keyboard *wlr_keyboard; struct wl_listener key; struct wl_listener destroy; }; static void output_frame_notify(struct wl_listener *listener, void *data) { struct sample_output *sample_output = wl_container_of(listener, sample_output, frame); struct sample_state *sample = sample_output->sample; struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); struct wlr_output *wlr_output = sample_output->output; int32_t width, height; wlr_output_effective_resolution(wlr_output, &width, &height); struct wlr_output_state output_state; wlr_output_state_init(&output_state); struct wlr_render_pass *pass = wlr_output_begin_render_pass(wlr_output, &output_state, NULL, NULL); wlr_render_pass_add_rect(pass, &(struct wlr_render_rect_options){ .box = { .width = width, .height = height }, .color = { 0.25, 0.25, 0.25, 1 }, }); struct touch_point *p; wl_list_for_each(p, &sample->touch_points, link) { int x = (int)(p->x * width) - sample->cat_texture->width / 2; int y = (int)(p->y * height) - sample->cat_texture->height / 2; wlr_render_pass_add_texture(pass, &(struct wlr_render_texture_options){ .texture = sample->cat_texture, .dst_box = { .x = x, .y = y }, }); } wlr_render_pass_submit(pass); wlr_output_commit_state(wlr_output, &output_state); wlr_output_state_finish(&output_state); sample->last_frame = now; } static void touch_down_notify(struct wl_listener *listener, void *data) { struct wlr_touch_down_event *event = data; struct touch_state *tstate = wl_container_of(listener, tstate, down); struct sample_state *sample = tstate->sample; struct touch_point *point = calloc(1, sizeof(*point)); point->touch_id = event->touch_id; point->x = event->x; point->y = event->y; wl_list_insert(&sample->touch_points, &point->link); } static void touch_up_notify(struct wl_listener *listener, void *data ) { struct wlr_touch_up_event *event = data; struct touch_state *tstate = wl_container_of(listener, tstate, up); struct sample_state *sample = tstate->sample; struct touch_point *point, *tmp; wl_list_for_each_safe(point, tmp, &sample->touch_points, link) { if (point->touch_id == event->touch_id) { wl_list_remove(&point->link); break; } } } static void touch_motion_notify(struct wl_listener *listener, void *data) { struct wlr_touch_motion_event *event = data; struct touch_state *tstate = wl_container_of(listener, tstate, motion); struct sample_state *sample = tstate->sample; struct touch_point *point; wl_list_for_each(point, &sample->touch_points, link) { if (point->touch_id == event->touch_id) { point->x = event->x; point->y = event->y; break; } } } static void touch_cancel_notify(struct wl_listener *listener, void *data) { struct wlr_touch_cancel_event *event = data; struct touch_state *tstate = wl_container_of(listener, tstate, cancel); struct sample_state *sample = tstate->sample; struct touch_point *point, *tmp; wl_list_for_each_safe(point, tmp, &sample->touch_points, link) { if (point->touch_id == event->touch_id) { wl_list_remove(&point->link); break; } } } static void touch_destroy_notify(struct wl_listener *listener, void *data) { struct touch_state *tstate = wl_container_of(listener, tstate, destroy); wl_list_remove(&tstate->link); wl_list_remove(&tstate->destroy.link); wl_list_remove(&tstate->down.link); wl_list_remove(&tstate->up.link); wl_list_remove(&tstate->motion.link); wl_list_remove(&tstate->cancel.link); free(tstate); } static void output_remove_notify(struct wl_listener *listener, void *data) { struct sample_output *sample_output = wl_container_of(listener, sample_output, destroy); wl_list_remove(&sample_output->frame.link); wl_list_remove(&sample_output->destroy.link); free(sample_output); } static void new_output_notify(struct wl_listener *listener, void *data) { struct wlr_output *output = data; struct sample_state *sample = wl_container_of(listener, sample, new_output); wlr_output_init_render(output, sample->allocator, sample->renderer); struct sample_output *sample_output = calloc(1, sizeof(*sample_output)); sample_output->output = output; sample_output->sample = sample; wl_signal_add(&output->events.frame, &sample_output->frame); sample_output->frame.notify = output_frame_notify; wl_signal_add(&output->events.destroy, &sample_output->destroy); sample_output->destroy.notify = output_remove_notify; struct wlr_output_state state; wlr_output_state_init(&state); wlr_output_state_set_enabled(&state, true); struct wlr_output_mode *mode = wlr_output_preferred_mode(output); if (mode != NULL) { wlr_output_state_set_mode(&state, mode); } wlr_output_commit_state(output, &state); wlr_output_state_finish(&state); } static void keyboard_key_notify(struct wl_listener *listener, void *data) { struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, key); struct sample_state *sample = keyboard->sample; struct wlr_keyboard_key_event *event = data; uint32_t keycode = event->keycode + 8; const xkb_keysym_t *syms; int nsyms = xkb_state_key_get_syms(keyboard->wlr_keyboard->xkb_state, keycode, &syms); for (int i = 0; i < nsyms; i++) { xkb_keysym_t sym = syms[i]; if (sym == XKB_KEY_Escape) { wl_display_terminate(sample->display); } } } static void keyboard_destroy_notify(struct wl_listener *listener, void *data) { struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, destroy); wl_list_remove(&keyboard->destroy.link); wl_list_remove(&keyboard->key.link); free(keyboard); } static void new_input_notify(struct wl_listener *listener, void *data) { struct wlr_input_device *device = data; struct sample_state *sample = wl_container_of(listener, sample, new_input); switch (device->type) { case WLR_INPUT_DEVICE_KEYBOARD:; struct sample_keyboard *keyboard = calloc(1, sizeof(*keyboard)); keyboard->wlr_keyboard = wlr_keyboard_from_input_device(device); keyboard->sample = sample; wl_signal_add(&device->events.destroy, &keyboard->destroy); keyboard->destroy.notify = keyboard_destroy_notify; wl_signal_add(&keyboard->wlr_keyboard->events.key, &keyboard->key); keyboard->key.notify = keyboard_key_notify; struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); if (!context) { wlr_log(WLR_ERROR, "Failed to create XKB context"); exit(1); } struct xkb_keymap *keymap = xkb_keymap_new_from_names(context, NULL, XKB_KEYMAP_COMPILE_NO_FLAGS); if (!keymap) { wlr_log(WLR_ERROR, "Failed to create XKB keymap"); exit(1); } wlr_keyboard_set_keymap(keyboard->wlr_keyboard, keymap); xkb_keymap_unref(keymap); xkb_context_unref(context); break; case WLR_INPUT_DEVICE_TOUCH:; struct touch_state *tstate = calloc(1, sizeof(*tstate)); tstate->wlr_touch = wlr_touch_from_input_device(device); tstate->sample = sample; tstate->destroy.notify = touch_destroy_notify; wl_signal_add(&device->events.destroy, &tstate->destroy); tstate->down.notify = touch_down_notify; wl_signal_add(&tstate->wlr_touch->events.down, &tstate->down); tstate->motion.notify = touch_motion_notify; wl_signal_add(&tstate->wlr_touch->events.motion, &tstate->motion); tstate->up.notify = touch_up_notify; wl_signal_add(&tstate->wlr_touch->events.up, &tstate->up); tstate->cancel.notify = touch_cancel_notify; wl_signal_add(&tstate->wlr_touch->events.cancel, &tstate->cancel); wl_list_insert(&sample->touch, &tstate->link); break; default: break; } } int main(int argc, char *argv[]) { wlr_log_init(WLR_DEBUG, NULL); struct wl_display *display = wl_display_create(); struct sample_state state = { .display = display }; wl_list_init(&state.touch_points); wl_list_init(&state.touch); struct wlr_backend *wlr = wlr_backend_autocreate(display, NULL); if (!wlr) { exit(1); } wl_signal_add(&wlr->events.new_output, &state.new_output); state.new_output.notify = new_output_notify; wl_signal_add(&wlr->events.new_input, &state.new_input); state.new_input.notify = new_input_notify; clock_gettime(CLOCK_MONOTONIC, &state.last_frame); state.renderer = wlr_renderer_autocreate(wlr); if (!state.renderer) { wlr_log(WLR_ERROR, "Could not start compositor, OOM"); exit(EXIT_FAILURE); } state.cat_texture = wlr_texture_from_pixels(state.renderer, DRM_FORMAT_ARGB8888, cat_tex.width * 4, cat_tex.width, cat_tex.height, cat_tex.pixel_data); if (!state.cat_texture) { wlr_log(WLR_ERROR, "Could not start compositor, OOM"); exit(EXIT_FAILURE); } state.allocator = wlr_allocator_autocreate(wlr, state.renderer); if (!wlr_backend_start(wlr)) { wlr_log(WLR_ERROR, "Failed to start backend"); wlr_backend_destroy(wlr); exit(1); } wl_display_run(display); wlr_texture_destroy(state.cat_texture); wl_display_destroy(display); } wlroots-0.17.1/include/000077500000000000000000000000001454110342200147305ustar00rootroot00000000000000wlroots-0.17.1/include/backend/000077500000000000000000000000001454110342200163175ustar00rootroot00000000000000wlroots-0.17.1/include/backend/backend.h000066400000000000000000000004251454110342200200600ustar00rootroot00000000000000#ifndef BACKEND_WLR_BACKEND_H #define BACKEND_WLR_BACKEND_H #include /** * Get the supported buffer capabilities. * * This functions returns a bitfield of supported wlr_buffer_cap. */ uint32_t backend_get_buffer_caps(struct wlr_backend *backend); #endif wlroots-0.17.1/include/backend/drm/000077500000000000000000000000001454110342200171015ustar00rootroot00000000000000wlroots-0.17.1/include/backend/drm/drm.h000066400000000000000000000133431454110342200200400ustar00rootroot00000000000000#ifndef BACKEND_DRM_DRM_H #define BACKEND_DRM_DRM_H #include #include #include #include #include #include #include #include #include #include #include "backend/drm/iface.h" #include "backend/drm/properties.h" #include "backend/drm/renderer.h" struct wlr_drm_plane { uint32_t type; uint32_t id; /* Only initialized on multi-GPU setups */ struct wlr_drm_surface mgpu_surf; /* Buffer submitted to the kernel, will be presented on next vblank */ struct wlr_drm_fb *queued_fb; /* Buffer currently displayed on screen */ struct wlr_drm_fb *current_fb; struct wlr_drm_format_set formats; union wlr_drm_plane_props props; uint32_t initial_crtc_id; struct liftoff_plane *liftoff; struct liftoff_layer *liftoff_layer; }; struct wlr_drm_layer { struct wlr_output_layer *wlr; struct liftoff_layer *liftoff; struct wlr_addon addon; // wlr_output_layer.addons struct wl_list link; // wlr_drm_crtc.layers /* Buffer to be submitted to the kernel on the next page-flip */ struct wlr_drm_fb *pending_fb; /* Buffer submitted to the kernel, will be presented on next vblank */ struct wlr_drm_fb *queued_fb; /* Buffer currently displayed on screen */ struct wlr_drm_fb *current_fb; // One entry per wlr_drm_backend.planes bool *candidate_planes; }; struct wlr_drm_crtc { uint32_t id; struct wlr_drm_lease *lease; struct liftoff_output *liftoff; struct liftoff_layer *liftoff_composition_layer; struct wl_list layers; // wlr_drm_layer.link // Atomic modesetting only uint32_t mode_id; uint32_t gamma_lut; // Legacy only int legacy_gamma_size; struct wlr_drm_plane *primary; struct wlr_drm_plane *cursor; union wlr_drm_crtc_props props; }; struct wlr_drm_backend { struct wlr_backend backend; struct wlr_drm_backend *parent; const struct wlr_drm_interface *iface; bool addfb2_modifiers; int fd; char *name; struct wlr_device *dev; struct liftoff_device *liftoff; size_t num_crtcs; struct wlr_drm_crtc *crtcs; size_t num_planes; struct wlr_drm_plane *planes; struct wl_display *display; struct wl_event_source *drm_event; struct wl_listener display_destroy; struct wl_listener session_destroy; struct wl_listener session_active; struct wl_listener parent_destroy; struct wl_listener dev_change; struct wl_listener dev_remove; struct wl_list fbs; // wlr_drm_fb.link struct wl_list connectors; // wlr_drm_connector.link struct wl_list page_flips; // wlr_drm_page_flip.link /* Only initialized on multi-GPU setups */ struct wlr_drm_renderer mgpu_renderer; struct wlr_session *session; uint64_t cursor_width, cursor_height; struct wlr_drm_format_set mgpu_formats; bool supports_tearing_page_flips; }; struct wlr_drm_mode { struct wlr_output_mode wlr_mode; drmModeModeInfo drm_mode; }; struct wlr_drm_connector_state { const struct wlr_output_state *base; bool modeset; bool nonblock; bool active; drmModeModeInfo mode; struct wlr_drm_fb *primary_fb; }; /** * Per-page-flip tracking struct. * * We've asked for a state change in the kernel, and yet to receive a * notification for its completion. Currently, the kernel only has a queue * length of 1, and no way to modify your submissions after they're sent. * * However, we might have multiple in-flight page-flip events, for instance * when performing a non-blocking commit followed by a blocking commit. In * that case, conn will be set to NULL on the non-blocking commit to indicate * that it's been superseded. */ struct wlr_drm_page_flip { struct wl_list link; // wlr_drm_connector.page_flips struct wlr_drm_connector *conn; }; struct wlr_drm_connector { struct wlr_output output; // only valid if status != DISCONNECTED struct wlr_drm_backend *backend; char name[24]; drmModeConnection status; uint32_t id; uint64_t max_bpc_bounds[2]; struct wlr_drm_lease *lease; struct wlr_drm_crtc *crtc; uint32_t possible_crtcs; union wlr_drm_connector_props props; bool cursor_enabled; int cursor_x, cursor_y; int cursor_width, cursor_height; int cursor_hotspot_x, cursor_hotspot_y; /* Buffer to be submitted to the kernel on the next page-flip */ struct wlr_drm_fb *cursor_pending_fb; struct wl_list link; // wlr_drm_backend.connectors // Last committed page-flip struct wlr_drm_page_flip *pending_page_flip; }; struct wlr_drm_backend *get_drm_backend_from_backend( struct wlr_backend *wlr_backend); bool check_drm_features(struct wlr_drm_backend *drm); bool init_drm_resources(struct wlr_drm_backend *drm); void finish_drm_resources(struct wlr_drm_backend *drm); void scan_drm_connectors(struct wlr_drm_backend *state, struct wlr_device_hotplug_event *event); void scan_drm_leases(struct wlr_drm_backend *drm); int handle_drm_event(int fd, uint32_t mask, void *data); void destroy_drm_connector(struct wlr_drm_connector *conn); bool drm_connector_commit_state(struct wlr_drm_connector *conn, const struct wlr_output_state *state); bool drm_connector_is_cursor_visible(struct wlr_drm_connector *conn); bool drm_connector_supports_vrr(struct wlr_drm_connector *conn); size_t drm_crtc_get_gamma_lut_size(struct wlr_drm_backend *drm, struct wlr_drm_crtc *crtc); void drm_lease_destroy(struct wlr_drm_lease *lease); void drm_page_flip_destroy(struct wlr_drm_page_flip *page_flip); struct wlr_drm_fb *get_next_cursor_fb(struct wlr_drm_connector *conn); struct wlr_drm_layer *get_drm_layer(struct wlr_drm_backend *drm, struct wlr_output_layer *layer); #define wlr_drm_conn_log(conn, verb, fmt, ...) \ wlr_log(verb, "connector %s: " fmt, conn->name, ##__VA_ARGS__) #define wlr_drm_conn_log_errno(conn, verb, fmt, ...) \ wlr_log_errno(verb, "connector %s: " fmt, conn->name, ##__VA_ARGS__) #endif wlroots-0.17.1/include/backend/drm/iface.h000066400000000000000000000025471454110342200203310ustar00rootroot00000000000000#ifndef BACKEND_DRM_IFACE_H #define BACKEND_DRM_IFACE_H #include #include #include #include #include struct wlr_drm_backend; struct wlr_drm_connector; struct wlr_drm_crtc; struct wlr_drm_connector_state; struct wlr_drm_fb; struct wlr_drm_page_flip; // Used to provide atomic or legacy DRM functions struct wlr_drm_interface { bool (*init)(struct wlr_drm_backend *drm); void (*finish)(struct wlr_drm_backend *drm); // Commit all pending changes on a CRTC. bool (*crtc_commit)(struct wlr_drm_connector *conn, const struct wlr_drm_connector_state *state, struct wlr_drm_page_flip *page_flip, uint32_t flags, bool test_only); }; extern const struct wlr_drm_interface atomic_iface; extern const struct wlr_drm_interface legacy_iface; extern const struct wlr_drm_interface liftoff_iface; bool drm_legacy_crtc_set_gamma(struct wlr_drm_backend *drm, struct wlr_drm_crtc *crtc, size_t size, uint16_t *lut); bool create_mode_blob(struct wlr_drm_backend *drm, struct wlr_drm_connector *conn, const struct wlr_drm_connector_state *state, uint32_t *blob_id); bool create_gamma_lut_blob(struct wlr_drm_backend *drm, size_t size, const uint16_t *lut, uint32_t *blob_id); bool create_fb_damage_clips_blob(struct wlr_drm_backend *drm, int width, int height, const pixman_region32_t *damage, uint32_t *blob_id); #endif wlroots-0.17.1/include/backend/drm/monitor.h000066400000000000000000000011311454110342200207350ustar00rootroot00000000000000#ifndef BACKEND_DRM_MONITOR_H #define BACKEND_DRM_MONITOR_H #include /** * Helper to create new DRM sub-backends on GPU hotplug. */ struct wlr_drm_backend_monitor { struct wlr_backend *multi; struct wlr_backend *primary_drm; struct wlr_session *session; struct wl_listener multi_destroy; struct wl_listener primary_drm_destroy; struct wl_listener session_destroy; struct wl_listener session_add_drm_card; }; struct wlr_drm_backend_monitor *drm_backend_monitor_create( struct wlr_backend *multi, struct wlr_backend *primary_drm, struct wlr_session *session); #endif wlroots-0.17.1/include/backend/drm/properties.h000066400000000000000000000037761454110342200214630ustar00rootroot00000000000000#ifndef BACKEND_DRM_PROPERTIES_H #define BACKEND_DRM_PROPERTIES_H #include #include #include /* * These types contain the property ids for several DRM objects. * See https://01.org/linuxgraphics/gfx-docs/drm/gpu/drm-kms.html#kms-properties * for more details. */ union wlr_drm_connector_props { struct { uint32_t edid; uint32_t dpms; uint32_t link_status; // not guaranteed to exist uint32_t path; uint32_t vrr_capable; // not guaranteed to exist uint32_t subconnector; // not guaranteed to exist uint32_t non_desktop; uint32_t panel_orientation; // not guaranteed to exist uint32_t content_type; // not guaranteed to exist uint32_t max_bpc; // not guaranteed to exist // atomic-modesetting only uint32_t crtc_id; }; uint32_t props[4]; }; union wlr_drm_crtc_props { struct { // Neither of these are guaranteed to exist uint32_t vrr_enabled; uint32_t gamma_lut; uint32_t gamma_lut_size; // atomic-modesetting only uint32_t active; uint32_t mode_id; }; uint32_t props[6]; }; union wlr_drm_plane_props { struct { uint32_t type; uint32_t rotation; // Not guaranteed to exist uint32_t in_formats; // Not guaranteed to exist // atomic-modesetting only uint32_t src_x; uint32_t src_y; uint32_t src_w; uint32_t src_h; uint32_t crtc_x; uint32_t crtc_y; uint32_t crtc_w; uint32_t crtc_h; uint32_t fb_id; uint32_t crtc_id; uint32_t fb_damage_clips; }; uint32_t props[14]; }; bool get_drm_connector_props(int fd, uint32_t id, union wlr_drm_connector_props *out); bool get_drm_crtc_props(int fd, uint32_t id, union wlr_drm_crtc_props *out); bool get_drm_plane_props(int fd, uint32_t id, union wlr_drm_plane_props *out); bool get_drm_prop(int fd, uint32_t obj, uint32_t prop, uint64_t *ret); void *get_drm_prop_blob(int fd, uint32_t obj, uint32_t prop, size_t *ret_len); char *get_drm_prop_enum(int fd, uint32_t obj, uint32_t prop); bool introspect_drm_prop_range(int fd, uint32_t prop_id, uint64_t *min, uint64_t *max); #endif wlroots-0.17.1/include/backend/drm/renderer.h000066400000000000000000000031451454110342200210630ustar00rootroot00000000000000#ifndef BACKEND_DRM_RENDERER_H #define BACKEND_DRM_RENDERER_H #include #include #include #include #include struct wlr_drm_backend; struct wlr_drm_format; struct wlr_drm_plane; struct wlr_buffer; struct wlr_drm_renderer { struct wlr_drm_backend *backend; struct wlr_renderer *wlr_rend; struct wlr_allocator *allocator; }; struct wlr_drm_surface { struct wlr_drm_renderer *renderer; struct wlr_swapchain *swapchain; }; struct wlr_drm_fb { struct wlr_buffer *wlr_buf; struct wlr_addon addon; struct wlr_drm_backend *backend; struct wl_list link; // wlr_drm_backend.fbs uint32_t id; }; bool init_drm_renderer(struct wlr_drm_backend *drm, struct wlr_drm_renderer *renderer); void finish_drm_renderer(struct wlr_drm_renderer *renderer); bool init_drm_surface(struct wlr_drm_surface *surf, struct wlr_drm_renderer *renderer, int width, int height, const struct wlr_drm_format *drm_format); bool drm_fb_import(struct wlr_drm_fb **fb, struct wlr_drm_backend *drm, struct wlr_buffer *buf, const struct wlr_drm_format_set *formats); void drm_fb_destroy(struct wlr_drm_fb *fb); void drm_fb_clear(struct wlr_drm_fb **fb); void drm_fb_move(struct wlr_drm_fb **new, struct wlr_drm_fb **old); struct wlr_drm_fb *drm_fb_lock(struct wlr_drm_fb *fb); struct wlr_buffer *drm_surface_blit(struct wlr_drm_surface *surf, struct wlr_buffer *buffer); bool drm_plane_pick_render_format(struct wlr_drm_plane *plane, struct wlr_drm_format *fmt, struct wlr_drm_renderer *renderer); void drm_plane_finish_surface(struct wlr_drm_plane *plane); #endif wlroots-0.17.1/include/backend/drm/util.h000066400000000000000000000027111454110342200202300ustar00rootroot00000000000000#ifndef BACKEND_DRM_UTIL_H #define BACKEND_DRM_UTIL_H #include #include #include struct wlr_drm_connector; // Calculates a more accurate refresh rate (mHz) than what mode itself provides int32_t calculate_refresh_rate(const drmModeModeInfo *mode); enum wlr_output_mode_aspect_ratio get_picture_aspect_ratio(const drmModeModeInfo *mode); // Returns manufacturer based on pnp id const char *get_pnp_manufacturer(const char code[static 3]); // Populates the make/model/phys_{width,height} of output from the edid data void parse_edid(struct wlr_drm_connector *conn, size_t len, const uint8_t *data); const char *drm_connector_status_str(drmModeConnection status); void generate_cvt_mode(drmModeModeInfo *mode, int hdisplay, int vdisplay, float vrefresh); // Part of match_obj enum { UNMATCHED = (uint32_t)-1, SKIP = (uint32_t)-2, }; /* * Tries to match some DRM objects with some other DRM resource. * e.g. Match CRTCs with Encoders, CRTCs with Planes. * * objs contains a bit array which resources it can be matched with. * e.g. Bit 0 set means can be matched with res[0] * * res contains an index of which objs it is matched with or UNMATCHED. * * This solution is left in out. * Returns the total number of matched solutions. */ size_t match_obj(size_t num_objs, const uint32_t objs[static restrict num_objs], size_t num_res, const uint32_t res[static restrict num_res], uint32_t out[static restrict num_res]); #endif wlroots-0.17.1/include/backend/headless.h000066400000000000000000000012011454110342200202520ustar00rootroot00000000000000#ifndef BACKEND_HEADLESS_H #define BACKEND_HEADLESS_H #include #include #define HEADLESS_DEFAULT_REFRESH (60 * 1000) // 60 Hz struct wlr_headless_backend { struct wlr_backend backend; struct wl_display *display; struct wl_list outputs; struct wl_listener display_destroy; bool started; }; struct wlr_headless_output { struct wlr_output wlr_output; struct wlr_headless_backend *backend; struct wl_list link; struct wl_event_source *frame_timer; int frame_delay; // ms }; struct wlr_headless_backend *headless_backend_from_backend( struct wlr_backend *wlr_backend); #endif wlroots-0.17.1/include/backend/libinput.h000066400000000000000000000121471454110342200203230ustar00rootroot00000000000000#ifndef BACKEND_LIBINPUT_H #define BACKEND_LIBINPUT_H #include #include #include #include #include #include #include #include #include #include #include "config.h" struct wlr_libinput_backend { struct wlr_backend backend; struct wlr_session *session; struct wl_display *display; struct libinput *libinput_context; struct wl_event_source *input_event; struct wl_listener display_destroy; struct wl_listener session_destroy; struct wl_listener session_signal; struct wl_list devices; // wlr_libinput_device.link }; struct wlr_libinput_input_device { struct libinput_device *handle; struct wlr_keyboard keyboard; struct wlr_pointer pointer; struct wlr_switch switch_device; struct wlr_touch touch; struct wlr_tablet tablet; struct wl_list tablet_tools; // see backend/libinput/tablet_tool.c struct wlr_tablet_pad tablet_pad; struct wl_list link; }; uint32_t usec_to_msec(uint64_t usec); void handle_libinput_event(struct wlr_libinput_backend *state, struct libinput_event *event); void destroy_libinput_input_device(struct wlr_libinput_input_device *dev); extern const struct wlr_keyboard_impl libinput_keyboard_impl; extern const struct wlr_pointer_impl libinput_pointer_impl; extern const struct wlr_switch_impl libinput_switch_impl; extern const struct wlr_tablet_impl libinput_tablet_impl; extern const struct wlr_tablet_pad_impl libinput_tablet_pad_impl; extern const struct wlr_touch_impl libinput_touch_impl; void init_device_keyboard(struct wlr_libinput_input_device *dev); struct wlr_libinput_input_device *device_from_keyboard(struct wlr_keyboard *kb); void handle_keyboard_key(struct libinput_event *event, struct wlr_keyboard *kb); void init_device_pointer(struct wlr_libinput_input_device *dev); struct wlr_libinput_input_device *device_from_pointer(struct wlr_pointer *kb); void handle_pointer_motion(struct libinput_event *event, struct wlr_pointer *pointer); void handle_pointer_motion_abs(struct libinput_event *event, struct wlr_pointer *pointer); void handle_pointer_button(struct libinput_event *event, struct wlr_pointer *pointer); void handle_pointer_axis(struct libinput_event *event, struct wlr_pointer *pointer); #if HAVE_LIBINPUT_SCROLL_VALUE120 void handle_pointer_axis_value120(struct libinput_event *event, struct wlr_pointer *pointer, enum wlr_axis_source source); #endif void handle_pointer_swipe_begin(struct libinput_event *event, struct wlr_pointer *pointer); void handle_pointer_swipe_update(struct libinput_event *event, struct wlr_pointer *pointer); void handle_pointer_swipe_end(struct libinput_event *event, struct wlr_pointer *pointer); void handle_pointer_pinch_begin(struct libinput_event *event, struct wlr_pointer *pointer); void handle_pointer_pinch_update(struct libinput_event *event, struct wlr_pointer *pointer); void handle_pointer_pinch_end(struct libinput_event *event, struct wlr_pointer *pointer); void handle_pointer_hold_begin(struct libinput_event *event, struct wlr_pointer *pointer); void handle_pointer_hold_end(struct libinput_event *event, struct wlr_pointer *pointer); void init_device_switch(struct wlr_libinput_input_device *dev); struct wlr_libinput_input_device *device_from_switch( struct wlr_switch *switch_device); void handle_switch_toggle(struct libinput_event *event, struct wlr_switch *switch_device); void init_device_touch(struct wlr_libinput_input_device *dev); struct wlr_libinput_input_device *device_from_touch( struct wlr_touch *touch); void handle_touch_down(struct libinput_event *event, struct wlr_touch *touch); void handle_touch_up(struct libinput_event *event, struct wlr_touch *touch); void handle_touch_motion(struct libinput_event *event, struct wlr_touch *touch); void handle_touch_cancel(struct libinput_event *event, struct wlr_touch *touch); void handle_touch_frame(struct libinput_event *event, struct wlr_touch *touch); void init_device_tablet(struct wlr_libinput_input_device *dev); void finish_device_tablet(struct wlr_libinput_input_device *dev); struct wlr_libinput_input_device *device_from_tablet( struct wlr_tablet *tablet); void handle_tablet_tool_axis(struct libinput_event *event, struct wlr_tablet *tablet); void handle_tablet_tool_proximity(struct libinput_event *event, struct wlr_tablet *tablet); void handle_tablet_tool_tip(struct libinput_event *event, struct wlr_tablet *tablet); void handle_tablet_tool_button(struct libinput_event *event, struct wlr_tablet *tablet); void init_device_tablet_pad(struct wlr_libinput_input_device *dev); void finish_device_tablet_pad(struct wlr_libinput_input_device *dev); struct wlr_libinput_input_device *device_from_tablet_pad( struct wlr_tablet_pad *tablet_pad); void handle_tablet_pad_button(struct libinput_event *event, struct wlr_tablet_pad *tablet_pad); void handle_tablet_pad_ring(struct libinput_event *event, struct wlr_tablet_pad *tablet_pad); void handle_tablet_pad_strip(struct libinput_event *event, struct wlr_tablet_pad *tablet_pad); #endif wlroots-0.17.1/include/backend/multi.h000066400000000000000000000005531454110342200176250ustar00rootroot00000000000000#ifndef BACKEND_MULTI_H #define BACKEND_MULTI_H #include #include #include struct wlr_multi_backend { struct wlr_backend backend; struct wl_list backends; struct wl_listener display_destroy; struct { struct wl_signal backend_add; struct wl_signal backend_remove; } events; }; #endif wlroots-0.17.1/include/backend/session/000077500000000000000000000000001454110342200200025ustar00rootroot00000000000000wlroots-0.17.1/include/backend/session/session.h000066400000000000000000000011651454110342200216410ustar00rootroot00000000000000#ifndef BACKEND_SESSION_SESSION_H #define BACKEND_SESSION_SESSION_H #include struct wl_display; struct wlr_session; struct wlr_session *libseat_session_create(struct wl_display *disp); void libseat_session_destroy(struct wlr_session *base); int libseat_session_open_device(struct wlr_session *base, const char *path); void libseat_session_close_device(struct wlr_session *base, int fd); bool libseat_change_vt(struct wlr_session *base, unsigned vt); void session_init(struct wlr_session *session); struct wlr_device *session_open_if_kms(struct wlr_session *restrict session, const char *restrict path); #endif wlroots-0.17.1/include/backend/wayland.h000066400000000000000000000115631454110342200201350ustar00rootroot00000000000000#ifndef BACKEND_WAYLAND_H #define BACKEND_WAYLAND_H #include #include #include #include #include #include #include #include #include #include #include struct wlr_wl_backend { struct wlr_backend backend; /* local state */ bool started; struct wl_display *local_display; struct wl_list outputs; int drm_fd; struct wl_list buffers; // wlr_wl_buffer.link size_t requested_outputs; struct wl_listener local_display_destroy; char *activation_token; /* remote state */ struct wl_display *remote_display; bool own_remote_display; struct wl_event_source *remote_display_src; struct wl_registry *registry; struct wl_compositor *compositor; struct xdg_wm_base *xdg_wm_base; struct zxdg_decoration_manager_v1 *zxdg_decoration_manager_v1; struct zwp_pointer_gestures_v1 *zwp_pointer_gestures_v1; struct wp_presentation *presentation; struct wl_shm *shm; struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf_v1; struct zwp_relative_pointer_manager_v1 *zwp_relative_pointer_manager_v1; struct wl_list seats; // wlr_wl_seat.link struct zwp_tablet_manager_v2 *tablet_manager; struct wlr_drm_format_set shm_formats; struct wlr_drm_format_set linux_dmabuf_v1_formats; struct wl_drm *legacy_drm; struct xdg_activation_v1 *activation_v1; struct wl_subcompositor *subcompositor; struct wp_viewporter *viewporter; char *drm_render_name; }; struct wlr_wl_buffer { struct wlr_buffer *buffer; struct wl_buffer *wl_buffer; bool released; struct wl_list link; // wlr_wl_backend.buffers struct wl_listener buffer_destroy; }; struct wlr_wl_presentation_feedback { struct wlr_wl_output *output; struct wl_list link; struct wp_presentation_feedback *feedback; uint32_t commit_seq; }; struct wlr_wl_output_layer { struct wlr_addon addon; struct wl_surface *surface; struct wl_subsurface *subsurface; struct wp_viewport *viewport; bool mapped; }; struct wlr_wl_output { struct wlr_output wlr_output; struct wlr_wl_backend *backend; struct wl_list link; struct wl_surface *surface; bool own_surface; struct wl_callback *frame_callback; struct xdg_surface *xdg_surface; struct xdg_toplevel *xdg_toplevel; struct zxdg_toplevel_decoration_v1 *zxdg_toplevel_decoration_v1; struct wl_list presentation_feedbacks; bool configured; uint32_t enter_serial; struct { struct wlr_wl_pointer *pointer; struct wl_surface *surface; int32_t hotspot_x, hotspot_y; } cursor; }; struct wlr_wl_pointer { struct wlr_pointer wlr_pointer; struct wlr_wl_seat *seat; struct wlr_wl_output *output; enum wlr_axis_source axis_source; int32_t axis_discrete; uint32_t fingers; // trackpad gesture struct wl_listener output_destroy; struct wl_list link; }; struct wlr_wl_touch_points { int32_t ids[64]; size_t len; }; struct wlr_wl_seat { char *name; struct wl_seat *wl_seat; uint32_t global_name; struct wlr_wl_backend *backend; struct wl_keyboard *wl_keyboard; struct wlr_keyboard wlr_keyboard; struct wl_pointer *wl_pointer; struct wlr_wl_pointer *active_pointer; struct wl_list pointers; // wlr_wl_pointer.link struct zwp_pointer_gesture_swipe_v1 *gesture_swipe; struct zwp_pointer_gesture_pinch_v1 *gesture_pinch; struct zwp_pointer_gesture_hold_v1 *gesture_hold; struct zwp_relative_pointer_v1 *relative_pointer; struct wl_touch *wl_touch; struct wlr_touch wlr_touch; struct wlr_wl_touch_points touch_points; struct zwp_tablet_seat_v2 *zwp_tablet_seat_v2; struct zwp_tablet_v2 *zwp_tablet_v2; struct wlr_tablet wlr_tablet; struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2; struct wlr_tablet_tool wlr_tablet_tool; struct zwp_tablet_pad_v2 *zwp_tablet_pad_v2; struct wlr_tablet_pad wlr_tablet_pad; struct wl_list link; // wlr_wl_backend.seats }; struct wlr_wl_backend *get_wl_backend_from_backend(struct wlr_backend *backend); struct wlr_wl_output *get_wl_output_from_surface(struct wlr_wl_backend *wl, struct wl_surface *surface); void update_wl_output_cursor(struct wlr_wl_output *output); void init_seat_keyboard(struct wlr_wl_seat *seat); void init_seat_pointer(struct wlr_wl_seat *seat); void finish_seat_pointer(struct wlr_wl_seat *seat); void create_pointer(struct wlr_wl_seat *seat, struct wlr_wl_output *output); void init_seat_touch(struct wlr_wl_seat *seat); void init_seat_tablet(struct wlr_wl_seat *seat); void finish_seat_tablet(struct wlr_wl_seat *seat); bool create_wl_seat(struct wl_seat *wl_seat, struct wlr_wl_backend *wl, uint32_t global_name); void destroy_wl_seat(struct wlr_wl_seat *seat); void destroy_wl_buffer(struct wlr_wl_buffer *buffer); extern const struct wlr_pointer_impl wl_pointer_impl; extern const struct wlr_tablet_pad_impl wl_tablet_pad_impl; extern const struct wlr_tablet_impl wl_tablet_impl; #endif wlroots-0.17.1/include/backend/x11.h000066400000000000000000000064371454110342200171130ustar00rootroot00000000000000#ifndef BACKEND_X11_H #define BACKEND_X11_H #include #include #include #include #include #include #include #include #include #include #include #include #include "config.h" #if HAVE_XCB_ERRORS #include #endif #define XCB_EVENT_RESPONSE_TYPE_MASK 0x7f struct wlr_x11_backend; struct wlr_x11_output { struct wlr_output wlr_output; struct wlr_x11_backend *x11; struct wl_list link; // wlr_x11_backend.outputs xcb_window_t win; xcb_present_event_t present_event_id; int32_t win_width, win_height; struct wlr_pointer pointer; struct wlr_touch touch; struct wl_list touchpoints; // wlr_x11_touchpoint.link struct wl_list buffers; // wlr_x11_buffer.link pixman_region32_t exposed; uint64_t last_msc; struct { struct wlr_swapchain *swapchain; xcb_render_picture_t pic; } cursor; }; struct wlr_x11_touchpoint { uint32_t x11_id; int wayland_id; struct wl_list link; // wlr_x11_output.touch_points }; struct wlr_x11_backend { struct wlr_backend backend; struct wl_display *wl_display; bool started; xcb_connection_t *xcb; xcb_screen_t *screen; xcb_depth_t *depth; xcb_visualid_t visualid; xcb_colormap_t colormap; xcb_cursor_t transparent_cursor; xcb_render_pictformat_t argb32; bool have_shm; bool have_dri3; uint32_t dri3_major_version, dri3_minor_version; size_t requested_outputs; struct wl_list outputs; // wlr_x11_output.link struct wlr_keyboard keyboard; int drm_fd; struct wlr_drm_format_set dri3_formats; struct wlr_drm_format_set shm_formats; const struct wlr_x11_format *x11_format; struct wlr_drm_format_set primary_dri3_formats; struct wlr_drm_format_set primary_shm_formats; struct wl_event_source *event_source; struct { xcb_atom_t wm_protocols; xcb_atom_t wm_delete_window; xcb_atom_t net_wm_name; xcb_atom_t utf8_string; xcb_atom_t variable_refresh; } atoms; // The time we last received an event xcb_timestamp_t time; #if HAVE_XCB_ERRORS xcb_errors_context_t *errors_context; #endif uint8_t present_opcode; uint8_t xinput_opcode; struct wl_listener display_destroy; }; struct wlr_x11_buffer { struct wlr_x11_backend *x11; struct wlr_buffer *buffer; xcb_pixmap_t pixmap; struct wl_list link; // wlr_x11_output.buffers struct wl_listener buffer_destroy; size_t n_busy; }; struct wlr_x11_format { uint32_t drm; uint8_t depth, bpp; }; struct wlr_x11_backend *get_x11_backend_from_backend( struct wlr_backend *wlr_backend); struct wlr_x11_output *get_x11_output_from_window_id( struct wlr_x11_backend *x11, xcb_window_t window); extern const struct wlr_keyboard_impl x11_keyboard_impl; extern const struct wlr_pointer_impl x11_pointer_impl; extern const struct wlr_touch_impl x11_touch_impl; void handle_x11_xinput_event(struct wlr_x11_backend *x11, xcb_ge_generic_event_t *event); void update_x11_pointer_position(struct wlr_x11_output *output, xcb_timestamp_t time); void handle_x11_configure_notify(struct wlr_x11_output *output, xcb_configure_notify_event_t *event); void handle_x11_present_event(struct wlr_x11_backend *x11, xcb_ge_generic_event_t *event); #endif wlroots-0.17.1/include/interfaces/000077500000000000000000000000001454110342200170535ustar00rootroot00000000000000wlroots-0.17.1/include/interfaces/wlr_input_device.h000066400000000000000000000010621454110342200225650ustar00rootroot00000000000000#ifndef INTERFACES_INPUT_DEVICE_H #define INTERFACES_INPUT_DEVICE_H #include /** * Initializes a given wlr_input_device. Allocates memory for the name and sets * its vendor and product id to 0. * wlr_device must be non-NULL. */ void wlr_input_device_init(struct wlr_input_device *wlr_device, enum wlr_input_device_type type, const char *name); /** * Cleans up all the memory owned by a given wlr_input_device and signals * the destroy event. */ void wlr_input_device_finish(struct wlr_input_device *wlr_device); #endif wlroots-0.17.1/include/meson.build000066400000000000000000000017271454110342200171010ustar00rootroot00000000000000subdir('wlr') exclude_files = ['meson.build', 'config.h.in', 'version.h.in'] if not features.get('drm-backend') exclude_files += 'backend/drm.h' exclude_files += 'types/wlr_drm_lease_v1.h' endif if not features.get('libinput-backend') exclude_files += 'backend/libinput.h' endif if not features.get('x11-backend') exclude_files += 'backend/x11.h' endif if not features.get('xwayland') exclude_files += 'xwayland.h' endif if not features.get('gles2-renderer') exclude_files += ['render/egl.h', 'render/gles2.h'] endif if not features.get('vulkan-renderer') exclude_files += 'render/vulkan.h' endif if not features.get('session') exclude_files += 'backend/session.h' endif install_subdir('wlr', install_dir: get_option('includedir'), exclude_files: exclude_files, ) foreach name, have : internal_features internal_config.set10('HAVE_' + name.underscorify().to_upper(), have) endforeach wlr_files += configure_file( output: 'config.h', configuration: internal_config, ) wlroots-0.17.1/include/render/000077500000000000000000000000001454110342200162075ustar00rootroot00000000000000wlroots-0.17.1/include/render/allocator/000077500000000000000000000000001454110342200201675ustar00rootroot00000000000000wlroots-0.17.1/include/render/allocator/allocator.h000066400000000000000000000003701454110342200223200ustar00rootroot00000000000000#ifndef RENDER_ALLOCATOR_ALLOCATOR_H #define RENDER_ALLOCATOR_ALLOCATOR_H #include struct wlr_allocator *allocator_autocreate_with_drm_fd( struct wlr_backend *backend, struct wlr_renderer *renderer, int drm_fd); #endif wlroots-0.17.1/include/render/allocator/drm_dumb.h000066400000000000000000000014361454110342200221350ustar00rootroot00000000000000#ifndef RENDER_ALLOCATOR_DRM_DUMB_H #define RENDER_ALLOCATOR_DRM_DUMB_H #include #include #include "render/allocator/allocator.h" struct wlr_drm_dumb_buffer { struct wlr_buffer base; struct wl_list link; // wlr_drm_dumb_allocator.buffers int drm_fd; // -1 if the allocator has been destroyed struct wlr_dmabuf_attributes dmabuf; uint32_t format; uint32_t handle; uint32_t stride; uint32_t width, height; uint64_t size; void *data; }; struct wlr_drm_dumb_allocator { struct wlr_allocator base; struct wl_list buffers; // wlr_drm_dumb_buffer.link int drm_fd; }; /** * Creates a new drm dumb allocator from a DRM FD. * * Does not take ownership over the FD. */ struct wlr_allocator *wlr_drm_dumb_allocator_create(int fd); #endif wlroots-0.17.1/include/render/allocator/gbm.h000066400000000000000000000013021454110342200211010ustar00rootroot00000000000000#ifndef RENDER_ALLOCATOR_GBM_H #define RENDER_ALLOCATOR_GBM_H #include #include #include #include "render/allocator/allocator.h" struct wlr_gbm_buffer { struct wlr_buffer base; struct wl_list link; // wlr_gbm_allocator.buffers struct gbm_bo *gbm_bo; // NULL if the gbm_device has been destroyed struct wlr_dmabuf_attributes dmabuf; }; struct wlr_gbm_allocator { struct wlr_allocator base; int fd; struct gbm_device *gbm_device; struct wl_list buffers; // wlr_gbm_buffer.link }; /** * Creates a new GBM allocator from a DRM FD. * * Takes ownership over the FD. */ struct wlr_allocator *wlr_gbm_allocator_create(int drm_fd); #endif wlroots-0.17.1/include/render/allocator/shm.h000066400000000000000000000006451454110342200211340ustar00rootroot00000000000000#ifndef RENDER_ALLOCATOR_SHM_H #define RENDER_ALLOCATOR_SHM_H #include #include "render/allocator/allocator.h" struct wlr_shm_buffer { struct wlr_buffer base; struct wlr_shm_attributes shm; void *data; size_t size; }; struct wlr_shm_allocator { struct wlr_allocator base; }; /** * Creates a new shared memory allocator. */ struct wlr_allocator *wlr_shm_allocator_create(void); #endif wlroots-0.17.1/include/render/dmabuf.h000066400000000000000000000020241454110342200176140ustar00rootroot00000000000000#ifndef RENDER_DMABUF_H #define RENDER_DMABUF_H #include #include // Copied from to avoid #ifdef soup #define DMA_BUF_SYNC_READ (1 << 0) #define DMA_BUF_SYNC_WRITE (2 << 0) #define DMA_BUF_SYNC_RW (DMA_BUF_SYNC_READ | DMA_BUF_SYNC_WRITE) /** * Check whether DMA-BUF import/export from/to sync_file is available. * * If this function returns true, dmabuf_import_sync_file() is supported. */ bool dmabuf_check_sync_file_import_export(void); /** * Import a sync_file into a DMA-BUF with DMA_BUF_IOCTL_IMPORT_SYNC_FILE. * * This can be used to make explicit sync interoperate with implicit sync. */ bool dmabuf_import_sync_file(int dmabuf_fd, uint32_t flags, int sync_file_fd); /** * Export a sync_file from a DMA-BUF with DMA_BUF_IOCTL_EXPORT_SYNC_FILE. * * The sync_file FD is returned on success, -1 is returned on error. * * This can be used to make explicit sync interoperate with implicit sync. */ int dmabuf_export_sync_file(int dmabuf_fd, uint32_t flags); #endif wlroots-0.17.1/include/render/drm_format_set.h000066400000000000000000000017361454110342200213740ustar00rootroot00000000000000#ifndef RENDER_DRM_FORMAT_SET_H #define RENDER_DRM_FORMAT_SET_H #include void wlr_drm_format_init(struct wlr_drm_format *fmt, uint32_t format); bool wlr_drm_format_has(const struct wlr_drm_format *fmt, uint64_t modifier); bool wlr_drm_format_add(struct wlr_drm_format *fmt, uint64_t modifier); bool wlr_drm_format_copy(struct wlr_drm_format *dst, const struct wlr_drm_format *src); /** * Intersect modifiers for two DRM formats. The `dst` must be zeroed or initialized * with other state being replaced. * * Both arguments must have the same format field. If the formats aren't * compatible, NULL is returned. If either format doesn't support any modifier, * a format that doesn't support any modifier is returned. */ bool wlr_drm_format_intersect(struct wlr_drm_format *dst, const struct wlr_drm_format *a, const struct wlr_drm_format *b); bool wlr_drm_format_set_copy(struct wlr_drm_format_set *dst, const struct wlr_drm_format_set *src); #endif wlroots-0.17.1/include/render/egl.h000066400000000000000000000061621454110342200171340ustar00rootroot00000000000000#ifndef RENDER_EGL_H #define RENDER_EGL_H #include struct wlr_egl { EGLDisplay display; EGLContext context; EGLDeviceEXT device; // may be EGL_NO_DEVICE_EXT struct gbm_device *gbm_device; struct { // Display extensions bool KHR_image_base; bool EXT_image_dma_buf_import; bool EXT_image_dma_buf_import_modifiers; bool IMG_context_priority; bool EXT_create_context_robustness; // Device extensions bool EXT_device_drm; bool EXT_device_drm_render_node; // Client extensions bool EXT_device_query; bool KHR_platform_gbm; bool EXT_platform_device; bool KHR_display_reference; } exts; struct { PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT; PFNEGLCREATEIMAGEKHRPROC eglCreateImageKHR; PFNEGLDESTROYIMAGEKHRPROC eglDestroyImageKHR; PFNEGLQUERYDMABUFFORMATSEXTPROC eglQueryDmaBufFormatsEXT; PFNEGLQUERYDMABUFMODIFIERSEXTPROC eglQueryDmaBufModifiersEXT; PFNEGLDEBUGMESSAGECONTROLKHRPROC eglDebugMessageControlKHR; PFNEGLQUERYDISPLAYATTRIBEXTPROC eglQueryDisplayAttribEXT; PFNEGLQUERYDEVICESTRINGEXTPROC eglQueryDeviceStringEXT; PFNEGLQUERYDEVICESEXTPROC eglQueryDevicesEXT; } procs; bool has_modifiers; struct wlr_drm_format_set dmabuf_texture_formats; struct wlr_drm_format_set dmabuf_render_formats; }; struct wlr_egl_context { EGLDisplay display; EGLContext context; EGLSurface draw_surface; EGLSurface read_surface; }; /** * Initializes an EGL context for the given DRM FD. * * Will attempt to load all possibly required API functions. */ struct wlr_egl *wlr_egl_create_with_drm_fd(int drm_fd); /** * Frees all related EGL resources, makes the context not-current and * unbinds a bound wayland display. */ void wlr_egl_destroy(struct wlr_egl *egl); /** * Creates an EGL image from the given dmabuf attributes. Check usability * of the dmabuf with wlr_egl_check_import_dmabuf once first. */ EGLImageKHR wlr_egl_create_image_from_dmabuf(struct wlr_egl *egl, struct wlr_dmabuf_attributes *attributes, bool *external_only); /** * Get DMA-BUF formats suitable for sampling usage. */ const struct wlr_drm_format_set *wlr_egl_get_dmabuf_texture_formats( struct wlr_egl *egl); /** * Get DMA-BUF formats suitable for rendering usage. */ const struct wlr_drm_format_set *wlr_egl_get_dmabuf_render_formats( struct wlr_egl *egl); /** * Destroys an EGL image created with the given wlr_egl. */ bool wlr_egl_destroy_image(struct wlr_egl *egl, EGLImageKHR image); int wlr_egl_dup_drm_fd(struct wlr_egl *egl); /** * Save the current EGL context to the structure provided in the argument. * * This includes display, context, draw surface and read surface. */ void wlr_egl_save_context(struct wlr_egl_context *context); /** * Restore EGL context that was previously saved using wlr_egl_save_current(). */ bool wlr_egl_restore_context(struct wlr_egl_context *context); /** * Make the EGL context current. * * Callers are expected to clear the current context when they are done by * calling wlr_egl_unset_current(). */ bool wlr_egl_make_current(struct wlr_egl *egl); bool wlr_egl_unset_current(struct wlr_egl *egl); bool wlr_egl_is_current(struct wlr_egl *egl); #endif wlroots-0.17.1/include/render/gles2.h000066400000000000000000000114511454110342200173760ustar00rootroot00000000000000#ifndef RENDER_GLES2_H #define RENDER_GLES2_H #include #include #include #include #include #include #include #include #include #include #include #include #include // mesa ships old GL headers that don't include this type, so for distros that use headers from // mesa we need to def it ourselves until they update. // https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/23144 typedef void (GL_APIENTRYP PFNGLGETINTEGER64VEXTPROC) (GLenum pname, GLint64 *data); struct wlr_gles2_pixel_format { uint32_t drm_format; // optional field, if empty then internalformat = format GLint gl_internalformat; GLint gl_format, gl_type; bool has_alpha; }; struct wlr_gles2_tex_shader { GLuint program; GLint proj; GLint tex_proj; GLint tex; GLint alpha; GLint pos_attrib; }; struct wlr_gles2_renderer { struct wlr_renderer wlr_renderer; float projection[9]; struct wlr_egl *egl; int drm_fd; const char *exts_str; struct { bool EXT_read_format_bgra; bool KHR_debug; bool OES_egl_image_external; bool OES_egl_image; bool EXT_texture_type_2_10_10_10_REV; bool OES_texture_half_float_linear; bool EXT_texture_norm16; bool EXT_disjoint_timer_query; } exts; struct { PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES; PFNGLDEBUGMESSAGECALLBACKKHRPROC glDebugMessageCallbackKHR; PFNGLDEBUGMESSAGECONTROLKHRPROC glDebugMessageControlKHR; PFNGLPOPDEBUGGROUPKHRPROC glPopDebugGroupKHR; PFNGLPUSHDEBUGGROUPKHRPROC glPushDebugGroupKHR; PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC glEGLImageTargetRenderbufferStorageOES; PFNGLGETGRAPHICSRESETSTATUSKHRPROC glGetGraphicsResetStatusKHR; PFNGLGENQUERIESEXTPROC glGenQueriesEXT; PFNGLDELETEQUERIESEXTPROC glDeleteQueriesEXT; PFNGLQUERYCOUNTEREXTPROC glQueryCounterEXT; PFNGLGETQUERYOBJECTIVEXTPROC glGetQueryObjectivEXT; PFNGLGETQUERYOBJECTUI64VEXTPROC glGetQueryObjectui64vEXT; PFNGLGETINTEGER64VEXTPROC glGetInteger64vEXT; } procs; struct { struct { GLuint program; GLint proj; GLint color; GLint pos_attrib; } quad; struct wlr_gles2_tex_shader tex_rgba; struct wlr_gles2_tex_shader tex_rgbx; struct wlr_gles2_tex_shader tex_ext; } shaders; struct wl_list buffers; // wlr_gles2_buffer.link struct wl_list textures; // wlr_gles2_texture.link struct wlr_gles2_buffer *current_buffer; uint32_t viewport_width, viewport_height; }; struct wlr_gles2_render_timer { struct wlr_render_timer base; struct wlr_gles2_renderer *renderer; struct timespec cpu_start; struct timespec cpu_end; GLuint id; GLint64 gl_cpu_end; }; struct wlr_gles2_buffer { struct wlr_buffer *buffer; struct wlr_gles2_renderer *renderer; struct wl_list link; // wlr_gles2_renderer.buffers EGLImageKHR image; GLuint rbo; GLuint fbo; struct wlr_addon addon; }; struct wlr_gles2_texture { struct wlr_texture wlr_texture; struct wlr_gles2_renderer *renderer; struct wl_list link; // wlr_gles2_renderer.textures // Basically: // GL_TEXTURE_2D == mutable // GL_TEXTURE_EXTERNAL_OES == immutable GLenum target; GLuint tex; EGLImageKHR image; bool has_alpha; // Only affects target == GL_TEXTURE_2D uint32_t drm_format; // used to interpret upload data // If imported from a wlr_buffer struct wlr_buffer *buffer; struct wlr_addon buffer_addon; }; struct wlr_gles2_render_pass { struct wlr_render_pass base; struct wlr_gles2_buffer *buffer; float projection_matrix[9]; struct wlr_gles2_render_timer *timer; }; bool is_gles2_pixel_format_supported(const struct wlr_gles2_renderer *renderer, const struct wlr_gles2_pixel_format *format); const struct wlr_gles2_pixel_format *get_gles2_format_from_drm(uint32_t fmt); const struct wlr_gles2_pixel_format *get_gles2_format_from_gl( GLint gl_format, GLint gl_type, bool alpha); const uint32_t *get_gles2_shm_formats(const struct wlr_gles2_renderer *renderer, size_t *len); struct wlr_gles2_renderer *gles2_get_renderer( struct wlr_renderer *wlr_renderer); struct wlr_gles2_render_timer *gles2_get_render_timer( struct wlr_render_timer *timer); struct wlr_gles2_texture *gles2_get_texture( struct wlr_texture *wlr_texture); struct wlr_texture *gles2_texture_from_buffer(struct wlr_renderer *wlr_renderer, struct wlr_buffer *buffer); void gles2_texture_destroy(struct wlr_gles2_texture *texture); void push_gles2_debug_(struct wlr_gles2_renderer *renderer, const char *file, const char *func); #define push_gles2_debug(renderer) push_gles2_debug_(renderer, _WLR_FILENAME, __func__) void pop_gles2_debug(struct wlr_gles2_renderer *renderer); struct wlr_gles2_render_pass *begin_gles2_buffer_pass(struct wlr_gles2_buffer *buffer, struct wlr_gles2_render_timer *timer); #endif wlroots-0.17.1/include/render/pass.h000066400000000000000000000003141454110342200173240ustar00rootroot00000000000000#ifndef RENDER_PASS_H #define RENDER_PASS_H #include struct wlr_render_pass *begin_legacy_buffer_render_pass(struct wlr_renderer *renderer, struct wlr_buffer *buffer); #endif wlroots-0.17.1/include/render/pixel_format.h000066400000000000000000000036261454110342200210600ustar00rootroot00000000000000#ifndef RENDER_PIXEL_FORMAT_H #define RENDER_PIXEL_FORMAT_H #include /** * Information about a pixel format. * * A pixel format is identified via its DRM four character code (see ). * * Simple formats have a block size of 1×1 pixels and bytes_per_block contains * the number of bytes per pixel (including padding). * * Tiled formats (e.g. sub-sampled YCbCr) are described with a block size * greater than 1×1 pixels. A block is a rectangle of pixels which are stored * next to each other in a byte-aligned memory region. */ struct wlr_pixel_format_info { uint32_t drm_format; /* Equivalent of the format if it has an alpha channel, * DRM_FORMAT_INVALID (0) if NA */ uint32_t opaque_substitute; /* Bytes per block (including padding) */ uint32_t bytes_per_block; /* Size of a block in pixels (zero for 1×1) */ uint32_t block_width, block_height; /* True if the format has an alpha channel */ bool has_alpha; }; /** * Get pixel format information from a DRM FourCC. * * NULL is returned if the pixel format is unknown. */ const struct wlr_pixel_format_info *drm_get_pixel_format_info(uint32_t fmt); /** * Get the number of pixels per block for a pixel format. */ uint32_t pixel_format_info_pixels_per_block(const struct wlr_pixel_format_info *info); /** * Get the minimum stride for a given pixel format and width. */ int32_t pixel_format_info_min_stride(const struct wlr_pixel_format_info *info, int32_t width); /** * Check whether a stride is large enough for a given pixel format and width. */ bool pixel_format_info_check_stride(const struct wlr_pixel_format_info *info, int32_t stride, int32_t width); /** * Convert an enum wl_shm_format to a DRM FourCC. */ uint32_t convert_wl_shm_format_to_drm(enum wl_shm_format fmt); /** * Convert a DRM FourCC to an enum wl_shm_format. */ enum wl_shm_format convert_drm_format_to_wl_shm(uint32_t fmt); #endif wlroots-0.17.1/include/render/pixman.h000066400000000000000000000032611454110342200176560ustar00rootroot00000000000000#ifndef RENDER_PIXMAN_H #define RENDER_PIXMAN_H #include #include #include #include #include "render/pixel_format.h" struct wlr_pixman_pixel_format { uint32_t drm_format; pixman_format_code_t pixman_format; }; struct wlr_pixman_buffer; struct wlr_pixman_renderer { struct wlr_renderer wlr_renderer; struct wl_list buffers; // wlr_pixman_buffer.link struct wl_list textures; // wlr_pixman_texture.link struct wlr_pixman_buffer *current_buffer; int32_t width, height; struct wlr_drm_format_set drm_formats; }; struct wlr_pixman_buffer { struct wlr_buffer *buffer; struct wlr_pixman_renderer *renderer; pixman_image_t *image; struct wl_listener buffer_destroy; struct wl_list link; // wlr_pixman_renderer.buffers }; struct wlr_pixman_texture { struct wlr_texture wlr_texture; struct wlr_pixman_renderer *renderer; struct wl_list link; // wlr_pixman_renderer.textures pixman_image_t *image; pixman_format_code_t format; const struct wlr_pixel_format_info *format_info; void *data; // if created via texture_from_pixels struct wlr_buffer *buffer; // if created via texture_from_buffer }; struct wlr_pixman_render_pass { struct wlr_render_pass base; struct wlr_pixman_buffer *buffer; }; pixman_format_code_t get_pixman_format_from_drm(uint32_t fmt); uint32_t get_drm_format_from_pixman(pixman_format_code_t fmt); const uint32_t *get_pixman_drm_formats(size_t *len); bool begin_pixman_data_ptr_access(struct wlr_buffer *buffer, pixman_image_t **image_ptr, uint32_t flags); struct wlr_pixman_render_pass *begin_pixman_render_pass( struct wlr_pixman_buffer *buffer); #endif wlroots-0.17.1/include/render/vulkan.h000066400000000000000000000345341454110342200176710ustar00rootroot00000000000000#ifndef RENDER_VULKAN_H #define RENDER_VULKAN_H #include #include #include #include #include #include #include #include #include #include "util/rect_union.h" struct wlr_vk_descriptor_pool; struct wlr_vk_texture; struct wlr_vk_instance { VkInstance instance; VkDebugUtilsMessengerEXT messenger; struct { PFN_vkCreateDebugUtilsMessengerEXT createDebugUtilsMessengerEXT; PFN_vkDestroyDebugUtilsMessengerEXT destroyDebugUtilsMessengerEXT; } api; }; // Creates and initializes a vulkan instance. // The debug parameter determines if validation layers are enabled and a // debug messenger created. struct wlr_vk_instance *vulkan_instance_create(bool debug); void vulkan_instance_destroy(struct wlr_vk_instance *ini); // Logical vulkan device state. struct wlr_vk_device { struct wlr_vk_instance *instance; VkPhysicalDevice phdev; VkDevice dev; int drm_fd; bool implicit_sync_interop; bool sampler_ycbcr_conversion; // we only ever need one queue for rendering and transfer commands uint32_t queue_family; VkQueue queue; struct { PFN_vkGetMemoryFdPropertiesKHR vkGetMemoryFdPropertiesKHR; PFN_vkWaitSemaphoresKHR vkWaitSemaphoresKHR; PFN_vkGetSemaphoreCounterValueKHR vkGetSemaphoreCounterValueKHR; PFN_vkGetSemaphoreFdKHR vkGetSemaphoreFdKHR; PFN_vkImportSemaphoreFdKHR vkImportSemaphoreFdKHR; PFN_vkQueueSubmit2KHR vkQueueSubmit2KHR; } api; uint32_t format_prop_count; struct wlr_vk_format_props *format_props; struct wlr_drm_format_set dmabuf_render_formats; struct wlr_drm_format_set dmabuf_texture_formats; // supported formats for textures (contains only those formats // that support everything we need for textures) uint32_t shm_format_count; uint32_t *shm_formats; // to implement vulkan_get_shm_texture_formats }; // Tries to find the VkPhysicalDevice for the given drm fd. // Might find none and return VK_NULL_HANDLE. VkPhysicalDevice vulkan_find_drm_phdev(struct wlr_vk_instance *ini, int drm_fd); int vulkan_open_phdev_drm_fd(VkPhysicalDevice phdev); // Creates a device for the given instance and physical device. struct wlr_vk_device *vulkan_device_create(struct wlr_vk_instance *ini, VkPhysicalDevice phdev); void vulkan_device_destroy(struct wlr_vk_device *dev); // Tries to find any memory bit for the given vulkan device that // supports the given flags and is set in req_bits (e.g. if memory // type 2 is ok, (req_bits & (1 << 2)) must not be 0. // Set req_bits to 0xFFFFFFFF to allow all types. int vulkan_find_mem_type(struct wlr_vk_device *device, VkMemoryPropertyFlags flags, uint32_t req_bits); struct wlr_vk_format { uint32_t drm; VkFormat vk; bool is_srgb; bool is_ycbcr; }; extern const VkImageUsageFlags vulkan_render_usage, vulkan_shm_tex_usage, vulkan_dma_tex_usage; // Returns all known format mappings. // Might not be supported for gpu/usecase. const struct wlr_vk_format *vulkan_get_format_list(size_t *len); const struct wlr_vk_format *vulkan_get_format_from_drm(uint32_t drm_format); struct wlr_vk_format_modifier_props { VkDrmFormatModifierPropertiesEXT props; VkExtent2D max_extent; }; struct wlr_vk_format_props { struct wlr_vk_format format; struct { VkExtent2D max_extent; VkFormatFeatureFlags features; } shm; struct { uint32_t render_mod_count; struct wlr_vk_format_modifier_props *render_mods; uint32_t texture_mod_count; struct wlr_vk_format_modifier_props *texture_mods; } dmabuf; }; void vulkan_format_props_query(struct wlr_vk_device *dev, const struct wlr_vk_format *format); const struct wlr_vk_format_modifier_props *vulkan_format_props_find_modifier( struct wlr_vk_format_props *props, uint64_t mod, bool render); void vulkan_format_props_finish(struct wlr_vk_format_props *props); struct wlr_vk_pipeline_layout_key { const struct wlr_vk_format *ycbcr_format; enum wlr_scale_filter_mode filter_mode; }; struct wlr_vk_pipeline_layout { struct wlr_vk_pipeline_layout_key key; VkPipelineLayout vk; VkDescriptorSetLayout ds; VkSampler sampler; // for YCbCr pipelines only struct { VkSamplerYcbcrConversion conversion; VkFormat format; } ycbcr; struct wl_list link; // struct wlr_vk_renderer.pipeline_layouts }; // Constants used to pick the color transform for the texture drawing // fragment shader. Must match those in shaders/texture.frag enum wlr_vk_texture_transform { WLR_VK_TEXTURE_TRANSFORM_IDENTITY = 0, WLR_VK_TEXTURE_TRANSFORM_SRGB = 1, }; enum wlr_vk_shader_source { WLR_VK_SHADER_SOURCE_TEXTURE, WLR_VK_SHADER_SOURCE_SINGLE_COLOR, }; struct wlr_vk_pipeline_key { struct wlr_vk_pipeline_layout_key layout; enum wlr_vk_shader_source source; enum wlr_render_blend_mode blend_mode; // only used if source is texture enum wlr_vk_texture_transform texture_transform; }; struct wlr_vk_pipeline { struct wlr_vk_pipeline_key key; VkPipeline vk; const struct wlr_vk_pipeline_layout *layout; struct wlr_vk_render_format_setup *setup; struct wl_list link; // struct wlr_vk_render_format_setup }; // For each format we want to render, we need a separate renderpass // and therefore also separate pipelines. struct wlr_vk_render_format_setup { struct wl_list link; // wlr_vk_renderer.render_format_setups const struct wlr_vk_format *render_format; // used in renderpass VkRenderPass render_pass; VkPipeline output_pipe; struct wlr_vk_renderer *renderer; struct wl_list pipelines; // struct wlr_vk_pipeline.link }; // Renderer-internal represenation of an wlr_buffer imported for rendering. struct wlr_vk_render_buffer { struct wlr_buffer *wlr_buffer; struct wlr_addon addon; struct wlr_vk_renderer *renderer; struct wlr_vk_render_format_setup *render_setup; struct wl_list link; // wlr_vk_renderer.buffers VkImage image; VkImageView image_view; VkFramebuffer framebuffer; uint32_t mem_count; VkDeviceMemory memories[WLR_DMABUF_MAX_PLANES]; bool transitioned; VkImage blend_image; VkImageView blend_image_view; VkDeviceMemory blend_memory; VkDescriptorSet blend_descriptor_set; struct wlr_vk_descriptor_pool *blend_attachment_pool; bool blend_transitioned; }; struct wlr_vk_command_buffer { VkCommandBuffer vk; bool recording; uint64_t timeline_point; // Textures to destroy after the command buffer completes struct wl_list destroy_textures; // wlr_vk_texture.destroy_link // Staging shared buffers to release after the command buffer completes struct wl_list stage_buffers; // wlr_vk_shared_buffer.link // For DMA-BUF implicit sync interop, may be NULL VkSemaphore binary_semaphore; }; #define VULKAN_COMMAND_BUFFERS_CAP 64 // Vulkan wlr_renderer implementation on top of a wlr_vk_device. struct wlr_vk_renderer { struct wlr_renderer wlr_renderer; struct wlr_backend *backend; struct wlr_vk_device *dev; VkCommandPool command_pool; VkShaderModule vert_module; VkShaderModule tex_frag_module; VkShaderModule quad_frag_module; VkShaderModule output_module; struct wl_list pipeline_layouts; // struct wlr_vk_pipeline_layout.link // for blend->output subpass VkPipelineLayout output_pipe_layout; VkDescriptorSetLayout output_ds_layout; size_t last_output_pool_size; struct wl_list output_descriptor_pools; // wlr_vk_descriptor_pool.link VkSemaphore timeline_semaphore; uint64_t timeline_point; struct wlr_vk_render_buffer *current_render_buffer; struct wlr_vk_command_buffer *current_command_buffer; VkRect2D scissor; // needed for clearing VkPipeline bound_pipe; uint32_t render_width; uint32_t render_height; float projection[9]; size_t last_pool_size; struct wl_list descriptor_pools; // wlr_vk_descriptor_pool.link struct wl_list render_format_setups; // wlr_vk_render_format_setup.link struct wl_list textures; // wlr_vk_texture.link // Textures to return to foreign queue struct wl_list foreign_textures; // wlr_vk_texture.foreign_link struct wl_list render_buffers; // wlr_vk_render_buffer.link // Pool of command buffers struct wlr_vk_command_buffer command_buffers[VULKAN_COMMAND_BUFFERS_CAP]; struct { struct wlr_vk_command_buffer *cb; uint64_t last_timeline_point; struct wl_list buffers; // wlr_vk_shared_buffer.link } stage; struct { bool initialized; uint32_t drm_format; uint32_t width, height; VkImage dst_image; VkDeviceMemory dst_img_memory; } read_pixels_cache; }; // vertex shader push constant range data struct wlr_vk_vert_pcr_data { float mat4[4][4]; float uv_off[2]; float uv_size[2]; }; struct wlr_vk_texture_view { struct wl_list link; // struct wlr_vk_texture.views const struct wlr_vk_pipeline_layout *layout; VkDescriptorSet ds; VkImageView image_view; struct wlr_vk_descriptor_pool *ds_pool; }; struct wlr_vk_pipeline *setup_get_or_create_pipeline( struct wlr_vk_render_format_setup *setup, const struct wlr_vk_pipeline_key *key); struct wlr_vk_pipeline_layout *get_or_create_pipeline_layout( struct wlr_vk_renderer *renderer, const struct wlr_vk_pipeline_layout_key *key); struct wlr_vk_texture_view *vulkan_texture_get_or_create_view( struct wlr_vk_texture *texture, const struct wlr_vk_pipeline_layout *layout); // Creates a vulkan renderer for the given device. struct wlr_renderer *vulkan_renderer_create_for_device(struct wlr_vk_device *dev); // stage utility - for uploading/retrieving data // Gets an command buffer in recording state which is guaranteed to be // executed before the next frame. VkCommandBuffer vulkan_record_stage_cb(struct wlr_vk_renderer *renderer); // Submits the current stage command buffer and waits until it has // finished execution. bool vulkan_submit_stage_wait(struct wlr_vk_renderer *renderer); struct wlr_vk_render_pass { struct wlr_render_pass base; struct wlr_vk_renderer *renderer; struct wlr_vk_render_buffer *render_buffer; struct wlr_vk_command_buffer *command_buffer; struct rect_union updated_region; VkPipeline bound_pipeline; float projection[9]; bool failed; }; struct wlr_vk_render_pass *vulkan_begin_render_pass(struct wlr_vk_renderer *renderer, struct wlr_vk_render_buffer *buffer); // Suballocates a buffer span with the given size that can be mapped // and used as staging buffer. The allocation is implicitly released when the // stage cb has finished execution. The start of the span will be a multiple // of the given alignment. struct wlr_vk_buffer_span vulkan_get_stage_span( struct wlr_vk_renderer *renderer, VkDeviceSize size, VkDeviceSize alignment); // Tries to allocate a texture descriptor set. Will additionally // return the pool it was allocated from when successful (for freeing it later). struct wlr_vk_descriptor_pool *vulkan_alloc_texture_ds( struct wlr_vk_renderer *renderer, VkDescriptorSetLayout ds_layout, VkDescriptorSet *ds); // Tries to allocate a descriptor set for the blending image. Will // additionally return the pool it was allocated from when successful // (for freeing it later). struct wlr_vk_descriptor_pool *vulkan_alloc_blend_ds( struct wlr_vk_renderer *renderer, VkDescriptorSet *ds); // Frees the given descriptor set from the pool its pool. void vulkan_free_ds(struct wlr_vk_renderer *renderer, struct wlr_vk_descriptor_pool *pool, VkDescriptorSet ds); struct wlr_vk_format_props *vulkan_format_props_from_drm( struct wlr_vk_device *dev, uint32_t drm_format); struct wlr_vk_renderer *vulkan_get_renderer(struct wlr_renderer *r); struct wlr_vk_command_buffer *vulkan_acquire_command_buffer( struct wlr_vk_renderer *renderer); uint64_t vulkan_end_command_buffer(struct wlr_vk_command_buffer *cb, struct wlr_vk_renderer *renderer); void vulkan_reset_command_buffer(struct wlr_vk_command_buffer *cb); bool vulkan_wait_command_buffer(struct wlr_vk_command_buffer *cb, struct wlr_vk_renderer *renderer); bool vulkan_sync_render_buffer(struct wlr_vk_renderer *renderer, struct wlr_vk_render_buffer *render_buffer, struct wlr_vk_command_buffer *cb); bool vulkan_sync_foreign_texture(struct wlr_vk_texture *texture); // State (e.g. image texture) associated with a surface. struct wlr_vk_texture { struct wlr_texture wlr_texture; struct wlr_vk_renderer *renderer; uint32_t mem_count; VkDeviceMemory memories[WLR_DMABUF_MAX_PLANES]; VkImage image; const struct wlr_vk_format *format; enum wlr_vk_texture_transform transform; struct wlr_vk_command_buffer *last_used_cb; // to track when it can be destroyed bool dmabuf_imported; bool owned; // if dmabuf_imported: whether we have ownership of the image bool transitioned; // if dma_imported: whether we transitioned it away from preinit bool has_alpha; // whether the image is has alpha channel struct wl_list foreign_link; // wlr_vk_renderer.foreign_textures struct wl_list destroy_link; // wlr_vk_command_buffer.destroy_textures struct wl_list link; // wlr_vk_renderer.textures // If imported from a wlr_buffer struct wlr_buffer *buffer; struct wlr_addon buffer_addon; // For DMA-BUF implicit sync interop VkSemaphore foreign_semaphores[WLR_DMABUF_MAX_PLANES]; struct wl_list views; // struct wlr_vk_texture_ds.link }; struct wlr_vk_texture *vulkan_get_texture(struct wlr_texture *wlr_texture); VkImage vulkan_import_dmabuf(struct wlr_vk_renderer *renderer, const struct wlr_dmabuf_attributes *attribs, VkDeviceMemory mems[static WLR_DMABUF_MAX_PLANES], uint32_t *n_mems, bool for_render); struct wlr_texture *vulkan_texture_from_buffer( struct wlr_renderer *wlr_renderer, struct wlr_buffer *buffer); void vulkan_texture_destroy(struct wlr_vk_texture *texture); struct wlr_vk_descriptor_pool { VkDescriptorPool pool; uint32_t free; // number of textures that can be allocated struct wl_list link; // wlr_vk_renderer.descriptor_pools }; struct wlr_vk_allocation { VkDeviceSize start; VkDeviceSize size; }; // List of suballocated staging buffers. // Used to upload to/read from device local images. struct wlr_vk_shared_buffer { struct wl_list link; // wlr_vk_renderer.stage.buffers or wlr_vk_command_buffer.stage_buffers VkBuffer buffer; VkDeviceMemory memory; VkDeviceSize buf_size; struct wl_array allocs; // struct wlr_vk_allocation }; // Suballocated range on a buffer. struct wlr_vk_buffer_span { struct wlr_vk_shared_buffer *buffer; struct wlr_vk_allocation alloc; }; // util const char *vulkan_strerror(VkResult err); void vulkan_change_layout(VkCommandBuffer cb, VkImage img, VkImageLayout ol, VkPipelineStageFlags srcs, VkAccessFlags srca, VkImageLayout nl, VkPipelineStageFlags dsts, VkAccessFlags dsta); #define wlr_vk_error(fmt, res, ...) wlr_log(WLR_ERROR, fmt ": %s (%d)", \ vulkan_strerror(res), res, ##__VA_ARGS__) #endif // RENDER_VULKAN_H wlroots-0.17.1/include/render/wlr_renderer.h000066400000000000000000000017701454110342200210570ustar00rootroot00000000000000#ifndef RENDER_WLR_RENDERER_H #define RENDER_WLR_RENDERER_H #include /** * Automatically select and create a renderer suitable for the DRM FD. */ struct wlr_renderer *renderer_autocreate_with_drm_fd(int drm_fd); /** * Bind a buffer to the renderer. * * All subsequent rendering operations will operate on the supplied buffer. * After rendering operations are done, the caller must unbind a buffer by * calling renderer_bind_buffer with a NULL buffer. */ bool renderer_bind_buffer(struct wlr_renderer *r, struct wlr_buffer *buffer); /** * Get the supported render formats. Buffers allocated with a format from this * list may be attached via wlr_renderer_begin_with_buffer. */ const struct wlr_drm_format_set *wlr_renderer_get_render_formats( struct wlr_renderer *renderer); /** * Get the supported buffer capabilities. * * This functions returns a bitfield of supported wlr_buffer_cap. */ uint32_t renderer_get_render_buffer_caps(struct wlr_renderer *renderer); #endif wlroots-0.17.1/include/types/000077500000000000000000000000001454110342200160745ustar00rootroot00000000000000wlroots-0.17.1/include/types/wlr_buffer.h000066400000000000000000000043731454110342200204110ustar00rootroot00000000000000#ifndef TYPES_WLR_BUFFER #define TYPES_WLR_BUFFER #include /** * A read-only buffer that holds a data pointer. * * This is suitable for passing raw pixel data to a function that accepts a * wlr_buffer. */ struct wlr_readonly_data_buffer { struct wlr_buffer base; const void *data; uint32_t format; size_t stride; void *saved_data; }; /** * Wraps a read-only data pointer into a wlr_buffer. The data pointer may be * accessed until readonly_data_buffer_drop() is called. */ struct wlr_readonly_data_buffer *readonly_data_buffer_create(uint32_t format, size_t stride, uint32_t width, uint32_t height, const void *data); /** * Drops ownership of the buffer (see wlr_buffer_drop() for more details) and * perform a copy of the data pointer if a consumer still has the buffer locked. */ bool readonly_data_buffer_drop(struct wlr_readonly_data_buffer *buffer); struct wlr_dmabuf_buffer { struct wlr_buffer base; struct wlr_dmabuf_attributes dmabuf; bool saved; }; /** * Wraps a DMA-BUF into a wlr_buffer. The DMA-BUF may be accessed until * dmabuf_buffer_drop() is called. */ struct wlr_dmabuf_buffer *dmabuf_buffer_create( struct wlr_dmabuf_attributes *dmabuf); /** * Drops ownership of the buffer (see wlr_buffer_drop() for more details) and * takes a reference to the DMA-BUF (by dup'ing its file descriptors) if a * consumer still has the buffer locked. */ bool dmabuf_buffer_drop(struct wlr_dmabuf_buffer *buffer); /** * Check whether a buffer is fully opaque. * * When true is returned, the buffer is guaranteed to be fully opaque, but the * reverse is not true: false may be returned in cases where the buffer is fully * opaque. */ bool buffer_is_opaque(struct wlr_buffer *buffer); /** * Creates a struct wlr_client_buffer from a given struct wlr_buffer by creating * a texture from it, and copying its struct wl_resource. */ struct wlr_client_buffer *wlr_client_buffer_create(struct wlr_buffer *buffer, struct wlr_renderer *renderer); /** * Try to update the buffer's content. * * Fails if there's more than one reference to the buffer or if the texture * isn't mutable. */ bool wlr_client_buffer_apply_damage(struct wlr_client_buffer *client_buffer, struct wlr_buffer *next, const pixman_region32_t *damage); #endif wlroots-0.17.1/include/types/wlr_data_device.h000066400000000000000000000031511454110342200213610ustar00rootroot00000000000000#ifndef TYPES_WLR_DATA_DEVICE_H #define TYPES_WLR_DATA_DEVICE_H #include #include #define DATA_DEVICE_ALL_ACTIONS (WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY | \ WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE | \ WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK) struct wlr_client_data_source { struct wlr_data_source source; struct wlr_data_source_impl impl; struct wl_resource *resource; bool finalized; }; extern const struct wlr_surface_role drag_icon_surface_role; struct wlr_data_offer *data_offer_create(struct wl_resource *device_resource, struct wlr_data_source *source, enum wlr_data_offer_type type); void data_offer_update_action(struct wlr_data_offer *offer); void data_offer_destroy(struct wlr_data_offer *offer); struct wlr_client_data_source *client_data_source_create( struct wl_client *client, uint32_t version, uint32_t id, struct wl_list *resource_list); struct wlr_client_data_source *client_data_source_from_resource( struct wl_resource *resource); void data_source_notify_finish(struct wlr_data_source *source); struct wlr_seat_client *seat_client_from_data_device_resource( struct wl_resource *resource); /** * Creates a new wl_data_offer if there is a wl_data_source currently set as * the seat selection and sends it to the seat client, followed by the * wl_data_device.selection() event. If there is no current selection, the * wl_data_device.selection() event will carry a NULL wl_data_offer. If the * client does not have a wl_data_device for the seat nothing will be done. */ void seat_client_send_selection(struct wlr_seat_client *seat_client); #endif wlroots-0.17.1/include/types/wlr_keyboard.h000066400000000000000000000003771454110342200207400ustar00rootroot00000000000000#include void keyboard_key_update(struct wlr_keyboard *keyboard, struct wlr_keyboard_key_event *event); bool keyboard_modifier_update(struct wlr_keyboard *keyboard); void keyboard_led_update(struct wlr_keyboard *keyboard); wlroots-0.17.1/include/types/wlr_matrix.h000066400000000000000000000006301454110342200204340ustar00rootroot00000000000000#ifndef TYPES_WLR_MATRIX_H #define TYPES_WLR_MATRIX_H #include /** * Writes a 2D orthographic projection matrix to mat of (width, height) with a * specified wl_output_transform. * * Equivalent to glOrtho(0, width, 0, height, 1, -1) with the transform applied. */ void matrix_projection(float mat[static 9], int width, int height, enum wl_output_transform transform); #endif wlroots-0.17.1/include/types/wlr_output.h000066400000000000000000000017751454110342200205030ustar00rootroot00000000000000#ifndef TYPES_WLR_OUTPUT_H #define TYPES_WLR_OUTPUT_H #include #include void output_pending_resolution(struct wlr_output *output, const struct wlr_output_state *state, int *width, int *height); bool output_pending_enabled(struct wlr_output *output, const struct wlr_output_state *state); bool output_pick_format(struct wlr_output *output, const struct wlr_drm_format_set *display_formats, struct wlr_drm_format *format, uint32_t fmt); void output_clear_back_buffer(struct wlr_output *output); bool output_ensure_buffer(struct wlr_output *output, struct wlr_output_state *state, bool *new_back_buffer); bool output_cursor_set_texture(struct wlr_output_cursor *cursor, struct wlr_texture *texture, bool own_texture, const struct wlr_fbox *src_box, int dst_width, int dst_height, enum wl_output_transform transform, int32_t hotspot_x, int32_t hotspot_y); void output_defer_present(struct wlr_output *output, struct wlr_output_event_present event); #endif wlroots-0.17.1/include/types/wlr_region.h000066400000000000000000000004241454110342200204140ustar00rootroot00000000000000#ifndef TYPES_WLR_REGION_H #define TYPES_WLR_REGION_H #include struct wl_client; /* * Creates a new region resource with the provided new ID. */ struct wl_resource *region_create(struct wl_client *client, uint32_t version, uint32_t id); #endif wlroots-0.17.1/include/types/wlr_scene.h000066400000000000000000000003721454110342200202300ustar00rootroot00000000000000#ifndef TYPES_WLR_SCENE_H #define TYPES_WLR_SCENE_H #include struct wlr_scene *scene_node_get_root(struct wlr_scene_node *node); void scene_surface_set_clip(struct wlr_scene_surface *surface, struct wlr_box *clip); #endif wlroots-0.17.1/include/types/wlr_seat.h000066400000000000000000000025121454110342200200650ustar00rootroot00000000000000#ifndef TYPES_WLR_SEAT_H #define TYPES_WLR_SEAT_H #include #include extern const struct wlr_pointer_grab_interface default_pointer_grab_impl; extern const struct wlr_keyboard_grab_interface default_keyboard_grab_impl; extern const struct wlr_touch_grab_interface default_touch_grab_impl; void seat_client_create_pointer(struct wlr_seat_client *seat_client, uint32_t version, uint32_t id); void seat_client_create_inert_pointer(struct wl_client *client, uint32_t version, uint32_t id); void seat_client_destroy_pointer(struct wl_resource *resource); void seat_client_send_pointer_leave_raw(struct wlr_seat_client *seat_client, struct wlr_surface *surface); void seat_client_create_keyboard(struct wlr_seat_client *seat_client, uint32_t version, uint32_t id); void seat_client_create_inert_keyboard(struct wl_client *client, uint32_t version, uint32_t id); void seat_client_destroy_keyboard(struct wl_resource *resource); void seat_client_send_keyboard_leave_raw(struct wlr_seat_client *seat_client, struct wlr_surface *surface); void seat_client_create_touch(struct wlr_seat_client *seat_client, uint32_t version, uint32_t id); void seat_client_create_inert_touch(struct wl_client *client, uint32_t version, uint32_t id); void seat_client_destroy_touch(struct wl_resource *resource); #endif wlroots-0.17.1/include/types/wlr_subcompositor.h000066400000000000000000000004011454110342200220340ustar00rootroot00000000000000#ifndef TYPES_WLR_SUBCOMPOSITOR_H #define TYPES_WLR_SUBCOMPOSITOR_H #include void subsurface_consider_map(struct wlr_subsurface *subsurface); void subsurface_handle_parent_commit(struct wlr_subsurface *subsurface); #endif wlroots-0.17.1/include/types/wlr_tablet_v2.h000066400000000000000000000056211454110342200210170ustar00rootroot00000000000000#ifndef TYPES_WLR_TABLET_V2_H #define TYPES_WLR_TABLET_V2_H #include "tablet-unstable-v2-protocol.h" #include #include struct wlr_tablet_seat_v2 { struct wl_list link; // wlr_tablet_manager_v2.seats struct wlr_seat *wlr_seat; struct wlr_tablet_manager_v2 *manager; struct wl_list tablets; // wlr_tablet_v2_tablet.link struct wl_list tools; struct wl_list pads; struct wl_list clients; // wlr_tablet_seat_v2_client.link struct wl_listener seat_destroy; }; struct wlr_tablet_seat_client_v2 { struct wl_list seat_link; struct wl_list client_link; struct wl_client *wl_client; struct wl_resource *resource; struct wlr_tablet_manager_client_v2 *client; struct wlr_seat_client *seat_client; struct wl_listener seat_client_destroy; struct wl_list tools; // wlr_tablet_tool_client_v2.link struct wl_list tablets; // wlr_tablet_client_v2.link struct wl_list pads; // wlr_tablet_pad_client_v2.link }; struct wlr_tablet_client_v2 { struct wl_list seat_link; // wlr_tablet_seat_client_v2.tablet struct wl_list tablet_link; // wlr_tablet_v2_tablet.clients struct wl_client *client; struct wl_resource *resource; }; struct wlr_tablet_pad_client_v2 { struct wl_list seat_link; struct wl_list pad_link; struct wl_client *client; struct wl_resource *resource; struct wlr_tablet_v2_tablet_pad *pad; struct wlr_tablet_seat_client_v2 *seat; size_t button_count; size_t group_count; struct wl_resource **groups; size_t ring_count; struct wl_resource **rings; size_t strip_count; struct wl_resource **strips; }; struct wlr_tablet_tool_client_v2 { struct wl_list seat_link; struct wl_list tool_link; struct wl_client *client; struct wl_resource *resource; struct wlr_tablet_v2_tablet_tool *tool; struct wlr_tablet_seat_client_v2 *seat; struct wl_event_source *frame_source; }; struct wlr_tablet_client_v2 *tablet_client_from_resource(struct wl_resource *resource); void destroy_tablet_v2(struct wl_resource *resource); void add_tablet_client(struct wlr_tablet_seat_client_v2 *seat, struct wlr_tablet_v2_tablet *tablet); void destroy_tablet_pad_v2(struct wl_resource *resource); struct wlr_tablet_pad_client_v2 *tablet_pad_client_from_resource(struct wl_resource *resource); void add_tablet_pad_client(struct wlr_tablet_seat_client_v2 *seat, struct wlr_tablet_v2_tablet_pad *pad); void destroy_tablet_tool_v2(struct wl_resource *resource); struct wlr_tablet_tool_client_v2 *tablet_tool_client_from_resource(struct wl_resource *resource); void add_tablet_tool_client(struct wlr_tablet_seat_client_v2 *seat, struct wlr_tablet_v2_tablet_tool *tool); struct wlr_tablet_seat_client_v2 *tablet_seat_client_from_resource(struct wl_resource *resource); void tablet_seat_client_v2_destroy(struct wl_resource *resource); struct wlr_tablet_seat_v2 *get_or_create_tablet_seat( struct wlr_tablet_manager_v2 *manager, struct wlr_seat *wlr_seat); #endif /* TYPES_WLR_TABLET_V2_H */ wlroots-0.17.1/include/types/wlr_xdg_shell.h000066400000000000000000000031011454110342200210750ustar00rootroot00000000000000#ifndef TYPES_WLR_XDG_SHELL_H #define TYPES_WLR_XDG_SHELL_H #include #include #include "xdg-shell-protocol.h" void create_xdg_surface(struct wlr_xdg_client *client, struct wlr_surface *wlr_surface, uint32_t id); void destroy_xdg_surface(struct wlr_xdg_surface *surface); bool set_xdg_surface_role(struct wlr_xdg_surface *surface, enum wlr_xdg_surface_role role); void set_xdg_surface_role_object(struct wlr_xdg_surface *surface, struct wl_resource *role_resource); void create_xdg_positioner(struct wlr_xdg_client *client, uint32_t id); void create_xdg_popup(struct wlr_xdg_surface *surface, struct wlr_xdg_surface *parent, struct wlr_xdg_positioner *positioner, uint32_t id); void reset_xdg_popup(struct wlr_xdg_popup *popup); void destroy_xdg_popup(struct wlr_xdg_popup *popup); void handle_xdg_popup_committed(struct wlr_xdg_popup *popup); struct wlr_xdg_popup_configure *send_xdg_popup_configure( struct wlr_xdg_popup *popup); void handle_xdg_popup_ack_configure(struct wlr_xdg_popup *popup, struct wlr_xdg_popup_configure *configure); void create_xdg_toplevel(struct wlr_xdg_surface *surface, uint32_t id); void reset_xdg_toplevel(struct wlr_xdg_toplevel *toplevel); void destroy_xdg_toplevel(struct wlr_xdg_toplevel *toplevel); void handle_xdg_toplevel_committed(struct wlr_xdg_toplevel *toplevel); struct wlr_xdg_toplevel_configure *send_xdg_toplevel_configure( struct wlr_xdg_toplevel *toplevel); void handle_xdg_toplevel_ack_configure(struct wlr_xdg_toplevel *toplevel, struct wlr_xdg_toplevel_configure *configure); #endif wlroots-0.17.1/include/util/000077500000000000000000000000001454110342200157055ustar00rootroot00000000000000wlroots-0.17.1/include/util/array.h000066400000000000000000000006121454110342200171730ustar00rootroot00000000000000#ifndef UTIL_ARRAY_H #define UTIL_ARRAY_H #include #include #include /** * Remove a chunk of memory of the specified size at the specified offset. */ void array_remove_at(struct wl_array *arr, size_t offset, size_t size); /** * Grow or shrink the array to fit the specifized size. */ bool array_realloc(struct wl_array *arr, size_t size); #endif wlroots-0.17.1/include/util/env.h000066400000000000000000000007701454110342200166520ustar00rootroot00000000000000#ifndef UTIL_ENV_H #define UTIL_ENV_H #include #include /** * Parse a bool from an environment variable. * * On success, the parsed value is returned. On error, false is returned. */ bool env_parse_bool(const char *option); /** * Pick a choice from an environment variable. * * On success, the choice index is returned. On error, zero is returned. * * switches is a NULL-terminated array. */ size_t env_parse_switch(const char *option, const char **switches); #endif wlroots-0.17.1/include/util/global.h000066400000000000000000000005271454110342200173220ustar00rootroot00000000000000#ifndef UTIL_GLOBAL_H #define UTIL_GLOBAL_H #include /** * Destroy a transient global. * * Globals that are created and destroyed on the fly need special handling to * prevent race conditions with wl_registry. Use this function to destroy them. */ void wlr_global_destroy_safe(struct wl_global *global); #endif wlroots-0.17.1/include/util/rect_union.h000066400000000000000000000047321454110342200202310ustar00rootroot00000000000000#ifndef UTIL_RECT_UNION_H #define UTIL_RECT_UNION_H #include #include #include #include /** * `struct rect_union` is a data structure to efficiently accumulate a number * of rectangles and then, when needed, compute a disjoint cover of their union. * (That is: produce a list of disjoint rectangles which covers every point * that was contained in one of the added rectangles.) * * Add rectangles to the union with `rect_union_add()`; to compute the disjoint * union, run `rect_union_evaluate()`, which will place the result in `.region`. * If there were any allocation failures, `.region` will instead contain the * bounding box for the entire list of rectangles. * * Example usage: * * struct rect_union r; * rect_union_init(&r); * for (j in ...) { * rect_union_add(&r, box[j]); * } * const pixman_region32_t *reg = rect_union_evaluate(&r); * int nboxes; * pixman_box32_t *boxes = pixman_region32_rectangles(reg, nboxes); * for (int i = 0; i < nboxes; i++) { * do_stuff(boxes[i]); * } * rect_union_destroy(&r); * */ struct rect_union { pixman_box32_t bounding_box; // Always up-to-date bounding box pixman_region32_t region; // Updated only on _evaluate() struct wl_array unsorted; // pixman_box32_t bool alloc_failure; // If this is true, fall back to computing a bounding box }; /** * Initialize *r, disregarding any previous contents. */ void rect_union_init(struct rect_union *r); /** * Free heap data associated with *r; should only be called after rect_union_init. * Leaves *r in an invalid state. */ void rect_union_finish(struct rect_union *r); /** * Add a rectangle to the union. If `box` is empty or invalid (x2 > x1 || y2 > y1), * do nothing. * * Amortized time: O(1) */ void rect_union_add(struct rect_union *r, pixman_box32_t box); /** * Compute an exact cover of the rectangles added so far, and return * a pointer to a pixman_region32_t giving that cover. The pointer will * remain valid until the next time *r is modified. If there was an allocation * failure, this function may return a single-rectangle bounding box instead. * * This may be called multiple times and interleaved with rect_union_add(). * * Worst case time: O(t^2), where t is the number of rectangles in the list. * Best case time: O(t), if rectangles are disjoint and have y-x band structure */ const pixman_region32_t *rect_union_evaluate(struct rect_union *r); #endif wlroots-0.17.1/include/util/set.h000066400000000000000000000012071454110342200166510ustar00rootroot00000000000000#ifndef UTIL_SET_H #define UTIL_SET_H #include #include #include /** * Add target to values. * * Target is added to the end of the set. * * Returns the index of target, or -1 if the set is full or target already * exists. */ ssize_t set_add(uint32_t values[], size_t *len, size_t cap, uint32_t target); /** * Remove target from values. * * When target is removed, the last element of the set is moved to where * target was. * * Returns the previous index of target, or -1 if target wasn't in values. */ ssize_t set_remove(uint32_t values[], size_t *len, size_t cap, uint32_t target); #endif wlroots-0.17.1/include/util/shm.h000066400000000000000000000003321454110342200166430ustar00rootroot00000000000000#ifndef UTIL_SHM_H #define UTIL_SHM_H #include #include int create_shm_file(void); int allocate_shm_file(size_t size); bool allocate_shm_file_pair(size_t size, int *rw_fd, int *ro_fd); #endif wlroots-0.17.1/include/util/time.h000066400000000000000000000012251454110342200170140ustar00rootroot00000000000000#ifndef UTIL_TIME_H #define UTIL_TIME_H #include #include /** * Get the current time, in milliseconds. */ int64_t get_current_time_msec(void); /** * Convert a timespec to milliseconds. */ int64_t timespec_to_msec(const struct timespec *a); /** * Convert a timespec to nanoseconds. */ int64_t timespec_to_nsec(const struct timespec *a); /** * Convert nanoseconds to a timespec. */ void timespec_from_nsec(struct timespec *r, int64_t nsec); /** * Subtracts timespec `b` from timespec `a`, and stores the difference in `r`. */ void timespec_sub(struct timespec *r, const struct timespec *a, const struct timespec *b); #endif wlroots-0.17.1/include/util/token.h000066400000000000000000000004161454110342200171770ustar00rootroot00000000000000#ifndef UTIL_TOKEN_H #define UTIL_TOKEN_H #include /** * Number of bytes used by a token, including the terminating zero byte. */ #define TOKEN_SIZE 33 /** * Generate a random token string. */ bool generate_token(char out[static TOKEN_SIZE]); #endif wlroots-0.17.1/include/wlr/000077500000000000000000000000001454110342200155345ustar00rootroot00000000000000wlroots-0.17.1/include/wlr/backend.h000066400000000000000000000036161454110342200173020ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_BACKEND_H #define WLR_BACKEND_H #include struct wlr_session; struct wlr_backend_impl; /** * A backend provides a set of input and output devices. */ struct wlr_backend { const struct wlr_backend_impl *impl; struct { /** Raised when destroyed */ struct wl_signal destroy; /** Raised when new inputs are added, passed the struct wlr_input_device */ struct wl_signal new_input; /** Raised when new outputs are added, passed the struct wlr_output */ struct wl_signal new_output; } events; }; /** * Automatically initializes the most suitable backend given the environment. * Will always return a multi-backend. The backend is created but not started. * Returns NULL on failure. * * If session_ptr is not NULL, it's populated with the session which has been * created with the backend, if any. */ struct wlr_backend *wlr_backend_autocreate(struct wl_display *display, struct wlr_session **session_ptr); /** * Start the backend. This may signal new_input or new_output immediately, but * may also wait until the display's event loop begins. Returns false on * failure. */ bool wlr_backend_start(struct wlr_backend *backend); /** * Destroy the backend and clean up all of its resources. Normally called * automatically when the struct wl_display is destroyed. */ void wlr_backend_destroy(struct wlr_backend *backend); /** * Returns the DRM node file descriptor used by the backend's underlying * platform. Can be used by consumers for additional rendering operations. * The consumer must not close the file descriptor since the backend continues * to have ownership of it. */ int wlr_backend_get_drm_fd(struct wlr_backend *backend); #endif wlroots-0.17.1/include/wlr/backend/000077500000000000000000000000001454110342200171235ustar00rootroot00000000000000wlroots-0.17.1/include/wlr/backend/drm.h000066400000000000000000000051621454110342200200620ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_BACKEND_DRM_H #define WLR_BACKEND_DRM_H #include #include #include #include struct wlr_drm_backend; typedef struct _drmModeModeInfo drmModeModeInfo; struct wlr_drm_lease { int fd; uint32_t lessee_id; struct wlr_drm_backend *backend; struct { struct wl_signal destroy; } events; void *data; }; /** * Creates a DRM backend using the specified GPU file descriptor (typically from * a device node in /dev/dri). * * To slave this to another DRM backend, pass it as the parent (which _must_ be * a DRM backend, other kinds of backends raise SIGABRT). */ struct wlr_backend *wlr_drm_backend_create(struct wl_display *display, struct wlr_session *session, struct wlr_device *dev, struct wlr_backend *parent); bool wlr_backend_is_drm(struct wlr_backend *backend); bool wlr_output_is_drm(struct wlr_output *output); /** * Get the KMS connector object ID. */ uint32_t wlr_drm_connector_get_id(struct wlr_output *output); /** * Tries to open non-master DRM FD. The compositor must not call drmSetMaster() * on the returned FD. * * Returns a valid opened DRM FD, or -1 on error. */ int wlr_drm_backend_get_non_master_fd(struct wlr_backend *backend); /** * Leases the given outputs to the caller. The outputs must be from the * associated DRM backend. * * Returns NULL on error. */ struct wlr_drm_lease *wlr_drm_create_lease(struct wlr_output **outputs, size_t n_outputs, int *lease_fd); /** * Terminates and destroys a given lease. * * The outputs will be owned again by the backend. */ void wlr_drm_lease_terminate(struct wlr_drm_lease *lease); /** * Add mode to the list of available modes. */ struct wlr_output_mode *wlr_drm_connector_add_mode(struct wlr_output *output, const drmModeModeInfo *mode); /** * Get the raw DRM mode information from a struct wlr_output_mode. * * The mode passed in must belong to a DRM output. */ const drmModeModeInfo *wlr_drm_mode_get_info(struct wlr_output_mode *mode); /** * Get the connector's panel orientation. * * On some devices the panel is mounted in the casing in such a way that the * top side of the panel does not match with the top side of the device. This * function returns the output transform which needs to be applied to compensate * for this. */ enum wl_output_transform wlr_drm_connector_get_panel_orientation( struct wlr_output *output); #endif wlroots-0.17.1/include/wlr/backend/headless.h000066400000000000000000000015521454110342200210670ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_BACKEND_HEADLESS_H #define WLR_BACKEND_HEADLESS_H #include #include /** * Creates a headless backend. A headless backend has no outputs or inputs by * default. */ struct wlr_backend *wlr_headless_backend_create(struct wl_display *display); /** * Create a new headless output. * * The buffers presented on the output won't be displayed to the user. */ struct wlr_output *wlr_headless_add_output(struct wlr_backend *backend, unsigned int width, unsigned int height); bool wlr_backend_is_headless(struct wlr_backend *backend); bool wlr_output_is_headless(struct wlr_output *output); #endif wlroots-0.17.1/include/wlr/backend/interface.h000066400000000000000000000016401454110342200212350ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_BACKEND_INTERFACE_H #define WLR_BACKEND_INTERFACE_H #include #include struct wlr_backend_impl { bool (*start)(struct wlr_backend *backend); void (*destroy)(struct wlr_backend *backend); int (*get_drm_fd)(struct wlr_backend *backend); uint32_t (*get_buffer_caps)(struct wlr_backend *backend); }; /** * Initializes common state on a struct wlr_backend and sets the implementation * to the provided struct wlr_backend_impl reference. */ void wlr_backend_init(struct wlr_backend *backend, const struct wlr_backend_impl *impl); /** * Emit the destroy event and clean up common backend state. */ void wlr_backend_finish(struct wlr_backend *backend); #endif wlroots-0.17.1/include/wlr/backend/libinput.h000066400000000000000000000015171454110342200211260ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_BACKEND_LIBINPUT_H #define WLR_BACKEND_LIBINPUT_H #include #include #include #include struct wlr_input_device; struct wlr_backend *wlr_libinput_backend_create(struct wl_display *display, struct wlr_session *session); /** * Gets the underlying struct libinput_device handle for the given input device. */ struct libinput_device *wlr_libinput_get_device_handle( struct wlr_input_device *dev); bool wlr_backend_is_libinput(struct wlr_backend *backend); bool wlr_input_device_is_libinput(struct wlr_input_device *device); #endif wlroots-0.17.1/include/wlr/backend/multi.h000066400000000000000000000020471454110342200204310ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_BACKEND_MULTI_H #define WLR_BACKEND_MULTI_H #include /** * Creates a multi-backend. Multi-backends wrap an arbitrary number of backends * and aggregate their new_output/new_input signals. */ struct wlr_backend *wlr_multi_backend_create(struct wl_display *display); /** * Adds the given backend to the multi backend. This should be done before the * new backend is started. */ bool wlr_multi_backend_add(struct wlr_backend *multi, struct wlr_backend *backend); void wlr_multi_backend_remove(struct wlr_backend *multi, struct wlr_backend *backend); bool wlr_backend_is_multi(struct wlr_backend *backend); bool wlr_multi_is_empty(struct wlr_backend *backend); void wlr_multi_for_each_backend(struct wlr_backend *backend, void (*callback)(struct wlr_backend *backend, void *data), void *data); #endif wlroots-0.17.1/include/wlr/backend/session.h000066400000000000000000000052201454110342200207560ustar00rootroot00000000000000#ifndef WLR_BACKEND_SESSION_H #define WLR_BACKEND_SESSION_H #include #include #include struct libseat; struct wlr_device { int fd; int device_id; dev_t dev; struct wl_list link; struct { struct wl_signal change; // struct wlr_device_change_event struct wl_signal remove; } events; }; struct wlr_session { /* * Signal for when the session becomes active/inactive. * It's called when we swap virtual terminal. */ bool active; /* * 0 if virtual terminals are not supported * i.e. seat != "seat0" */ unsigned vtnr; char seat[256]; struct udev *udev; struct udev_monitor *mon; struct wl_event_source *udev_event; struct libseat *seat_handle; struct wl_event_source *libseat_event; struct wl_list devices; struct wl_display *display; struct wl_listener display_destroy; struct { struct wl_signal active; struct wl_signal add_drm_card; // struct wlr_session_add_event struct wl_signal destroy; } events; }; struct wlr_session_add_event { const char *path; }; enum wlr_device_change_type { WLR_DEVICE_HOTPLUG = 1, WLR_DEVICE_LEASE, }; struct wlr_device_hotplug_event { uint32_t connector_id; uint32_t prop_id; }; struct wlr_device_change_event { enum wlr_device_change_type type; union { struct wlr_device_hotplug_event hotplug; }; }; /* * Opens a session, taking control of the current virtual terminal. * This should not be called if another program is already in control * of the terminal (Xorg, another Wayland compositor, etc.). * * Returns NULL on error. */ struct wlr_session *wlr_session_create(struct wl_display *disp); /* * Closes a previously opened session and restores the virtual terminal. * You should call wlr_session_close_file() on each files you opened * with wlr_session_open_file() before you call this. */ void wlr_session_destroy(struct wlr_session *session); /* * Opens the file at path. * * This can only be used to open DRM or evdev (input) devices. Files opened via * this function must be closed by calling wlr_session_close_file(). * * When the session becomes inactive: * * - DRM files lose their DRM master status * - evdev files become invalid and should be closed */ struct wlr_device *wlr_session_open_file(struct wlr_session *session, const char *path); /* * Closes a file previously opened with wlr_session_open_file(). */ void wlr_session_close_file(struct wlr_session *session, struct wlr_device *device); /* * Changes the virtual terminal. */ bool wlr_session_change_vt(struct wlr_session *session, unsigned vt); ssize_t wlr_session_find_gpus(struct wlr_session *session, size_t ret_len, struct wlr_device **ret); #endif wlroots-0.17.1/include/wlr/backend/wayland.h000066400000000000000000000040731454110342200207370ustar00rootroot00000000000000#ifndef WLR_BACKEND_WAYLAND_H #define WLR_BACKEND_WAYLAND_H #include #include #include #include #include struct wlr_input_device; /** * Creates a new Wayland backend. This backend will be created with no outputs; * you must use wlr_wl_output_create() to add them. * * The remote_display argument is an existing libwayland-client struct wl_display * to use. Leave it NULL to create a new connection to the compositor. */ struct wlr_backend *wlr_wl_backend_create(struct wl_display *display, struct wl_display *remote_display); /** * Returns the remote struct wl_display used by the Wayland backend. */ struct wl_display *wlr_wl_backend_get_remote_display(struct wlr_backend *backend); /** * Adds a new output to this backend. * * This creates a new xdg_toplevel in the parent Wayland compositor. * * You may remove outputs by destroying them. * * Note that if called before initializing the backend, this will return NULL * and your outputs will be created during initialization (and given to you via * the new_output signal). */ struct wlr_output *wlr_wl_output_create(struct wlr_backend *backend); /** * Create a new output from an existing struct wl_surface. */ struct wlr_output *wlr_wl_output_create_from_surface(struct wlr_backend *backend, struct wl_surface *surface); /** * Check whether the provided backend is a Wayland backend. */ bool wlr_backend_is_wl(struct wlr_backend *backend); /** * Check whether the provided input device is a Wayland input device. */ bool wlr_input_device_is_wl(struct wlr_input_device *device); /** * Check whether the provided output device is a Wayland output device. */ bool wlr_output_is_wl(struct wlr_output *output); /** * Sets the title of a struct wlr_output which is a Wayland toplevel. */ void wlr_wl_output_set_title(struct wlr_output *output, const char *title); /** * Returns the remote struct wl_surface used by the Wayland output. */ struct wl_surface *wlr_wl_output_get_surface(struct wlr_output *output); #endif wlroots-0.17.1/include/wlr/backend/x11.h000066400000000000000000000026251454110342200177120ustar00rootroot00000000000000#ifndef WLR_BACKEND_X11_H #define WLR_BACKEND_X11_H #include #include #include #include struct wlr_input_device; /** * Creates a new X11 backend. This backend will be created with no outputs; * you must use wlr_x11_output_create() to add them. * * The `x11_display` argument is the name of the X Display socket. Set * to NULL for the default behaviour of XOpenDisplay(). */ struct wlr_backend *wlr_x11_backend_create(struct wl_display *display, const char *x11_display); /** * Adds a new output to this backend. You may remove outputs by destroying them. * Note that if called before initializing the backend, this will return NULL * and your outputs will be created during initialization (and given to you via * the new_output signal). */ struct wlr_output *wlr_x11_output_create(struct wlr_backend *backend); /** * Check whether this backend is an X11 backend. */ bool wlr_backend_is_x11(struct wlr_backend *backend); /** * Check whether this input device is an X11 input device. */ bool wlr_input_device_is_x11(struct wlr_input_device *device); /** * Check whether this output device is an X11 output device. */ bool wlr_output_is_x11(struct wlr_output *output); /** * Sets the title of a struct wlr_output which is an X11 window. */ void wlr_x11_output_set_title(struct wlr_output *output, const char *title); #endif wlroots-0.17.1/include/wlr/config.h.in000066400000000000000000000005061454110342200175600ustar00rootroot00000000000000#ifndef WLR_CONFIG_H #define WLR_CONFIG_H #mesondefine WLR_HAS_DRM_BACKEND #mesondefine WLR_HAS_LIBINPUT_BACKEND #mesondefine WLR_HAS_X11_BACKEND #mesondefine WLR_HAS_GLES2_RENDERER #mesondefine WLR_HAS_VULKAN_RENDERER #mesondefine WLR_HAS_GBM_ALLOCATOR #mesondefine WLR_HAS_XWAYLAND #mesondefine WLR_HAS_SESSION #endif wlroots-0.17.1/include/wlr/interfaces/000077500000000000000000000000001454110342200176575ustar00rootroot00000000000000wlroots-0.17.1/include/wlr/interfaces/wlr_buffer.h000066400000000000000000000030331454110342200221640ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_INTERFACES_WLR_BUFFER_H #define WLR_INTERFACES_WLR_BUFFER_H #include struct wlr_buffer_impl { void (*destroy)(struct wlr_buffer *buffer); bool (*get_dmabuf)(struct wlr_buffer *buffer, struct wlr_dmabuf_attributes *attribs); bool (*get_shm)(struct wlr_buffer *buffer, struct wlr_shm_attributes *attribs); bool (*begin_data_ptr_access)(struct wlr_buffer *buffer, uint32_t flags, void **data, uint32_t *format, size_t *stride); void (*end_data_ptr_access)(struct wlr_buffer *buffer); }; struct wlr_buffer_resource_interface { const char *name; bool (*is_instance)(struct wl_resource *resource); struct wlr_buffer *(*from_resource)(struct wl_resource *resource); }; /** * Initialize a buffer. This function should be called by producers. The * initialized buffer is referenced: once the producer is done with the buffer * they should call wlr_buffer_drop(). */ void wlr_buffer_init(struct wlr_buffer *buffer, const struct wlr_buffer_impl *impl, int width, int height); /** * Allows the registration of a struct wl_resource implementation. * * The matching function will be called for the struct wl_resource when creating * a struct wlr_buffer from a struct wl_resource. */ void wlr_buffer_register_resource_interface( const struct wlr_buffer_resource_interface *iface); #endif wlroots-0.17.1/include/wlr/interfaces/wlr_keyboard.h000066400000000000000000000017321454110342200225170ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_INTERFACES_WLR_KEYBOARD_H #define WLR_INTERFACES_WLR_KEYBOARD_H #include #include struct wlr_keyboard_impl { const char *name; void (*led_update)(struct wlr_keyboard *keyboard, uint32_t leds); }; void wlr_keyboard_init(struct wlr_keyboard *keyboard, const struct wlr_keyboard_impl *impl, const char *name); /** * Cleans up all of the resources owned by the struct wlr_keyboard. */ void wlr_keyboard_finish(struct wlr_keyboard *keyboard); void wlr_keyboard_notify_key(struct wlr_keyboard *keyboard, struct wlr_keyboard_key_event *event); void wlr_keyboard_notify_modifiers(struct wlr_keyboard *keyboard, uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group); #endif wlroots-0.17.1/include/wlr/interfaces/wlr_output.h000066400000000000000000000077451454110342200222710ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_INTERFACES_WLR_OUTPUT_H #define WLR_INTERFACES_WLR_OUTPUT_H #include #include #include /** * Output state fields that don't require backend support. Backends can ignore * them without breaking the API contract. */ #define WLR_OUTPUT_STATE_BACKEND_OPTIONAL \ (WLR_OUTPUT_STATE_DAMAGE | \ WLR_OUTPUT_STATE_SCALE | \ WLR_OUTPUT_STATE_TRANSFORM | \ WLR_OUTPUT_STATE_RENDER_FORMAT | \ WLR_OUTPUT_STATE_SUBPIXEL | \ WLR_OUTPUT_STATE_LAYERS) /** * A backend implementation of struct wlr_output. * * The commit function is mandatory. Other functions are optional. */ struct wlr_output_impl { /** * Set the output cursor plane image. * * If buffer is NULL, the cursor should be hidden. * * The hotspot indicates the offset that needs to be applied to the * top-left corner of the image to match the cursor position. In other * words, the image should be displayed at (x - hotspot_x, y - hotspot_y). * The hotspot is given in the buffer's coordinate space. */ bool (*set_cursor)(struct wlr_output *output, struct wlr_buffer *buffer, int hotspot_x, int hotspot_y); /** * Set the output cursor plane position. * * The position is relative to the cursor hotspot, see set_cursor. */ bool (*move_cursor)(struct wlr_output *output, int x, int y); /** * Cleanup backend-specific resources tied to the output. */ void (*destroy)(struct wlr_output *output); /** * Check that the supplied output state is a valid configuration. * * If this function returns true, commit can only fail due to a runtime * error. */ bool (*test)(struct wlr_output *output, const struct wlr_output_state *state); /** * Commit the supplied output state. * * If a buffer has been attached, a frame event is scheduled. */ bool (*commit)(struct wlr_output *output, const struct wlr_output_state *state); /** * Get the maximum number of gamma LUT elements for each channel. * * Zero can be returned if the output doesn't support gamma LUTs. */ size_t (*get_gamma_size)(struct wlr_output *output); /** * Get the list of formats suitable for the cursor, assuming a buffer with * the specified capabilities. * * If unimplemented, the cursor buffer has no format constraint. If NULL is * returned, no format is suitable. */ const struct wlr_drm_format_set *(*get_cursor_formats)( struct wlr_output *output, uint32_t buffer_caps); /** * Get the size suitable for the cursor buffer. Attempts to use a different * size for the cursor may fail. */ void (*get_cursor_size)(struct wlr_output *output, int *width, int *height); /** * Get the list of DRM formats suitable for the primary buffer, * assuming a buffer with the specified capabilities. * * If unimplemented, the primary buffer has no format constraint. If NULL * is returned, no format is suitable. */ const struct wlr_drm_format_set *(*get_primary_formats)( struct wlr_output *output, uint32_t buffer_caps); }; /** * Initialize a new output. */ void wlr_output_init(struct wlr_output *output, struct wlr_backend *backend, const struct wlr_output_impl *impl, struct wl_display *display, const struct wlr_output_state *state); /** * Notify compositors that they need to submit a new frame in order to apply * output changes. */ void wlr_output_update_needs_frame(struct wlr_output *output); /** * Send a frame event. * * See wlr_output.events.frame. */ void wlr_output_send_frame(struct wlr_output *output); /** * Send a present event. * * See wlr_output.events.present. */ void wlr_output_send_present(struct wlr_output *output, struct wlr_output_event_present *event); /** * Request the compositor to apply new state. */ void wlr_output_send_request_state(struct wlr_output *output, const struct wlr_output_state *state); #endif wlroots-0.17.1/include/wlr/interfaces/wlr_pointer.h000066400000000000000000000010511454110342200223710ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_INTERFACES_WLR_POINTER_H #define WLR_INTERFACES_WLR_POINTER_H #include struct wlr_pointer_impl { const char *name; }; void wlr_pointer_init(struct wlr_pointer *pointer, const struct wlr_pointer_impl *impl, const char *name); void wlr_pointer_finish(struct wlr_pointer *pointer); #endif wlroots-0.17.1/include/wlr/interfaces/wlr_switch.h000066400000000000000000000010531454110342200222140ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_INTERFACES_WLR_SWITCH_H #define WLR_INTERFACES_WLR_SWITCH_H #include struct wlr_switch_impl { const char *name; }; void wlr_switch_init(struct wlr_switch *switch_device, const struct wlr_switch_impl *impl, const char *name); void wlr_switch_finish(struct wlr_switch *switch_device); #endif wlroots-0.17.1/include/wlr/interfaces/wlr_tablet_pad.h000066400000000000000000000014221454110342200230120ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_INTERFACES_WLR_TABLET_PAD_H #define WLR_INTERFACES_WLR_TABLET_PAD_H #include struct wlr_tablet_pad_impl { const char *name; }; void wlr_tablet_pad_init(struct wlr_tablet_pad *pad, const struct wlr_tablet_pad_impl *impl, const char *name); /** * Cleans up the resources owned by a struct wlr_tablet_pad. * * This function will not clean the memory allocated by * struct wlr_tablet_pad_group, it's the responsibility of the caller to clean * it. */ void wlr_tablet_pad_finish(struct wlr_tablet_pad *pad); #endif wlroots-0.17.1/include/wlr/interfaces/wlr_tablet_tool.h000066400000000000000000000010541454110342200232240ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_INTERFACES_WLR_TABLET_TOOL_H #define WLR_INTERFACES_WLR_TABLET_TOOL_H #include struct wlr_tablet_impl { const char *name; }; void wlr_tablet_init(struct wlr_tablet *tablet, const struct wlr_tablet_impl *impl, const char *name); void wlr_tablet_finish(struct wlr_tablet *tablet); #endif wlroots-0.17.1/include/wlr/interfaces/wlr_touch.h000066400000000000000000000010221454110342200220310ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_INTERFACES_WLR_TOUCH_H #define WLR_INTERFACES_WLR_TOUCH_H #include struct wlr_touch_impl { const char *name; }; void wlr_touch_init(struct wlr_touch *touch, const struct wlr_touch_impl *impl, const char *name); void wlr_touch_finish(struct wlr_touch *touch); #endif wlroots-0.17.1/include/wlr/meson.build000066400000000000000000000013641454110342200177020ustar00rootroot00000000000000version_base = meson.project_version().split('-')[0] version_array = version_base.split('.') version_data = configuration_data() version_data.set_quoted('WLR_VERSION_STR', meson.project_version()) version_data.set('WLR_VERSION_MAJOR', version_array[0]) version_data.set('WLR_VERSION_MINOR', version_array[1]) version_data.set('WLR_VERSION_MICRO', version_array[2]) conf_data = configuration_data() foreach name, have : features conf_data.set10('WLR_HAS_' + name.underscorify().to_upper(), have) endforeach conf_h = configure_file( input: 'config.h.in', output: 'config.h', configuration: conf_data, ) ver_h = configure_file( input: 'version.h.in', output: 'version.h', configuration: version_data, ) install_headers(conf_h, ver_h, subdir: 'wlr') wlroots-0.17.1/include/wlr/render/000077500000000000000000000000001454110342200170135ustar00rootroot00000000000000wlroots-0.17.1/include/wlr/render/allocator.h000066400000000000000000000040361454110342200211470ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_ALLOCATOR_H #define WLR_ALLOCATOR_H #include struct wlr_allocator; struct wlr_backend; struct wlr_drm_format; struct wlr_renderer; struct wlr_allocator_interface { struct wlr_buffer *(*create_buffer)(struct wlr_allocator *alloc, int width, int height, const struct wlr_drm_format *format); void (*destroy)(struct wlr_allocator *alloc); }; void wlr_allocator_init(struct wlr_allocator *alloc, const struct wlr_allocator_interface *impl, uint32_t buffer_caps); struct wlr_allocator { const struct wlr_allocator_interface *impl; // Capabilities of the buffers created with this allocator uint32_t buffer_caps; struct { struct wl_signal destroy; } events; }; /** * Creates the adequate struct wlr_allocator given a backend and a renderer. */ struct wlr_allocator *wlr_allocator_autocreate(struct wlr_backend *backend, struct wlr_renderer *renderer); /** * Destroy the allocator. */ void wlr_allocator_destroy(struct wlr_allocator *alloc); /** * Allocate a new buffer. * * When the caller is done with it, they must unreference it by calling * wlr_buffer_drop(). * * The `format` passed in indicates the format to use and the list of * acceptable modifiers. The order in which modifiers are listed is not * significant. * * When running with legacy drivers which don't support explicit modifiers, the * allocator must recognize two modifiers: INVALID (for implicit tiling and/or * compression) and LINEAR. * * The allocator must return a buffer using one of the modifiers listed. In * particular, allocators must not return a buffer with an implicit modifier * unless the user has allowed it by passing INVALID in the modifier list. */ struct wlr_buffer *wlr_allocator_create_buffer(struct wlr_allocator *alloc, int width, int height, const struct wlr_drm_format *format); #endif wlroots-0.17.1/include/wlr/render/dmabuf.h000066400000000000000000000033711454110342200204260ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_RENDER_DMABUF_H #define WLR_RENDER_DMABUF_H #include #include #define WLR_DMABUF_MAX_PLANES 4 /** * A Linux DMA-BUF pixel buffer. * * If the buffer was allocated with explicit modifiers enabled, the `modifier` * field must not be INVALID. * * If the buffer was allocated with explicit modifiers disabled (either because * the driver doesn't support it, or because the user didn't specify a valid * modifier list), the `modifier` field can have two values: INVALID means that * an implicit vendor-defined modifier is in use, LINEAR means that the buffer * is linear. The `modifier` field must not have any other value. * * When importing a DMA-BUF, users must not ignore the modifier unless it's * INVALID or LINEAR. In particular, users must not import a DMA-BUF to a * legacy API which doesn't support specifying an explicit modifier unless the * modifier is set to INVALID or LINEAR. */ struct wlr_dmabuf_attributes { int32_t width, height; uint32_t format; // FourCC code, see DRM_FORMAT_* in uint64_t modifier; // see DRM_FORMAT_MOD_* in int n_planes; uint32_t offset[WLR_DMABUF_MAX_PLANES]; uint32_t stride[WLR_DMABUF_MAX_PLANES]; int fd[WLR_DMABUF_MAX_PLANES]; }; /** * Closes all file descriptors in the DMA-BUF attributes. */ void wlr_dmabuf_attributes_finish(struct wlr_dmabuf_attributes *attribs); /** * Clones the DMA-BUF attributes. */ bool wlr_dmabuf_attributes_copy(struct wlr_dmabuf_attributes *dst, const struct wlr_dmabuf_attributes *src); #endif wlroots-0.17.1/include/wlr/render/drm_format_set.h000066400000000000000000000062361454110342200222000ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_RENDER_DRM_FORMAT_SET_H #define WLR_RENDER_DRM_FORMAT_SET_H #include #include #include /** A single DRM format, with a set of modifiers attached. */ struct wlr_drm_format { // The actual DRM format, from `drm_fourcc.h` uint32_t format; // The number of modifiers size_t len; // The capacity of the array; do not use. size_t capacity; // The actual modifiers uint64_t *modifiers; }; /** * Free all resources allocated to this DRM format. */ void wlr_drm_format_finish(struct wlr_drm_format *format); /** * A set of DRM formats and modifiers. * * This is used to describe the supported format + modifier combinations. For * instance, backends will report the set they can display, and renderers will * report the set they can render to. For a more general overview of formats * and modifiers, see: * https://www.kernel.org/doc/html/next/userspace-api/dma-buf-alloc-exchange.html#formats-and-modifiers * * For compatibility with legacy drivers which don't support explicit * modifiers, the special modifier DRM_FORMAT_MOD_INVALID is used to indicate * that implicit modifiers are supported. Legacy drivers can also support the * DRM_FORMAT_MOD_LINEAR modifier, which forces the buffer to have a linear * layout. * * Users must not assume that implicit modifiers are supported unless INVALID * is listed in the modifier list. */ struct wlr_drm_format_set { // The number of formats size_t len; // The capacity of the array; private to wlroots size_t capacity; // A pointer to an array of `struct wlr_drm_format *` of length `len`. struct wlr_drm_format *formats; }; /** * Free all of the DRM formats in the set, making the set empty. */ void wlr_drm_format_set_finish(struct wlr_drm_format_set *set); /** * Return a pointer to a member of this struct wlr_drm_format_set of format * `format`, or NULL if none exists. */ const struct wlr_drm_format *wlr_drm_format_set_get( const struct wlr_drm_format_set *set, uint32_t format); bool wlr_drm_format_set_has(const struct wlr_drm_format_set *set, uint32_t format, uint64_t modifier); bool wlr_drm_format_set_add(struct wlr_drm_format_set *set, uint32_t format, uint64_t modifier); /** * Intersect two DRM format sets `a` and `b`, storing in the destination set * `dst` the format + modifier pairs which are in both source sets. The `dst` * must either be zeroed or initialized with other state to be replaced. * * Returns false on failure or when the intersection is empty. */ bool wlr_drm_format_set_intersect(struct wlr_drm_format_set *dst, const struct wlr_drm_format_set *a, const struct wlr_drm_format_set *b); /** * Unions DRM format set `a` and `b`, storing in the destination set * `dst`. The `dst` must either be zeroed or initialized with other state * to be replaced. * * Returns false on failure. */ bool wlr_drm_format_set_union(struct wlr_drm_format_set *dst, const struct wlr_drm_format_set *a, const struct wlr_drm_format_set *b); #endif wlroots-0.17.1/include/wlr/render/egl.h000066400000000000000000000024421454110342200177350ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_RENDER_EGL_H #define WLR_RENDER_EGL_H #ifndef EGL_NO_X11 #define EGL_NO_X11 #endif #ifndef EGL_NO_PLATFORM_SPECIFIC_TYPES #define EGL_NO_PLATFORM_SPECIFIC_TYPES #endif #include #include #include #include #include #include #include #include struct wlr_egl; /** * Create a struct wlr_egl with an existing EGL display and context. * * This is typically used by compositors which want to customize EGL * initialization. */ struct wlr_egl *wlr_egl_create_with_context(EGLDisplay display, EGLContext context); /** * Get the EGL display used by the struct wlr_egl. * * This is typically used by compositors which need to perform custom OpenGL * operations. */ EGLDisplay wlr_egl_get_display(struct wlr_egl *egl); /** * Get the EGL context used by the struct wlr_egl. * * This is typically used by compositors which need to perform custom OpenGL * operations. */ EGLContext wlr_egl_get_context(struct wlr_egl *egl); #endif wlroots-0.17.1/include/wlr/render/gles2.h000066400000000000000000000023141454110342200202000ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_RENDER_GLES2_H #define WLR_RENDER_GLES2_H #include #include struct wlr_egl; struct wlr_renderer *wlr_gles2_renderer_create_with_drm_fd(int drm_fd); struct wlr_renderer *wlr_gles2_renderer_create(struct wlr_egl *egl); struct wlr_egl *wlr_gles2_renderer_get_egl(struct wlr_renderer *renderer); bool wlr_gles2_renderer_check_ext(struct wlr_renderer *renderer, const char *ext); /** * Returns the OpenGL FBO of current buffer. */ GLuint wlr_gles2_renderer_get_current_fbo(struct wlr_renderer *wlr_renderer); struct wlr_gles2_texture_attribs { GLenum target; /* either GL_TEXTURE_2D or GL_TEXTURE_EXTERNAL_OES */ GLuint tex; bool has_alpha; }; bool wlr_renderer_is_gles2(struct wlr_renderer *wlr_renderer); bool wlr_render_timer_is_gles2(struct wlr_render_timer *timer); bool wlr_texture_is_gles2(struct wlr_texture *texture); void wlr_gles2_texture_get_attribs(struct wlr_texture *texture, struct wlr_gles2_texture_attribs *attribs); #endif wlroots-0.17.1/include/wlr/render/interface.h000066400000000000000000000075161454110342200211350ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_RENDER_INTERFACE_H #define WLR_RENDER_INTERFACE_H #include #include #include #include #include #include struct wlr_box; struct wlr_fbox; struct wlr_renderer_impl { bool (*bind_buffer)(struct wlr_renderer *renderer, struct wlr_buffer *buffer); bool (*begin)(struct wlr_renderer *renderer, uint32_t width, uint32_t height); void (*end)(struct wlr_renderer *renderer); void (*clear)(struct wlr_renderer *renderer, const float color[static 4]); void (*scissor)(struct wlr_renderer *renderer, struct wlr_box *box); bool (*render_subtexture_with_matrix)(struct wlr_renderer *renderer, struct wlr_texture *texture, const struct wlr_fbox *box, const float matrix[static 9], float alpha); void (*render_quad_with_matrix)(struct wlr_renderer *renderer, const float color[static 4], const float matrix[static 9]); const uint32_t *(*get_shm_texture_formats)( struct wlr_renderer *renderer, size_t *len); const struct wlr_drm_format_set *(*get_dmabuf_texture_formats)( struct wlr_renderer *renderer); const struct wlr_drm_format_set *(*get_render_formats)( struct wlr_renderer *renderer); uint32_t (*preferred_read_format)(struct wlr_renderer *renderer); bool (*read_pixels)(struct wlr_renderer *renderer, uint32_t fmt, uint32_t stride, uint32_t width, uint32_t height, uint32_t src_x, uint32_t src_y, uint32_t dst_x, uint32_t dst_y, void *data); void (*destroy)(struct wlr_renderer *renderer); int (*get_drm_fd)(struct wlr_renderer *renderer); uint32_t (*get_render_buffer_caps)(struct wlr_renderer *renderer); struct wlr_texture *(*texture_from_buffer)(struct wlr_renderer *renderer, struct wlr_buffer *buffer); struct wlr_render_pass *(*begin_buffer_pass)(struct wlr_renderer *renderer, struct wlr_buffer *buffer, const struct wlr_buffer_pass_options *options); struct wlr_render_timer *(*render_timer_create)(struct wlr_renderer *renderer); }; void wlr_renderer_init(struct wlr_renderer *renderer, const struct wlr_renderer_impl *impl); struct wlr_texture_impl { bool (*update_from_buffer)(struct wlr_texture *texture, struct wlr_buffer *buffer, const pixman_region32_t *damage); void (*destroy)(struct wlr_texture *texture); }; void wlr_texture_init(struct wlr_texture *texture, struct wlr_renderer *rendener, const struct wlr_texture_impl *impl, uint32_t width, uint32_t height); struct wlr_render_pass { const struct wlr_render_pass_impl *impl; }; void wlr_render_pass_init(struct wlr_render_pass *pass, const struct wlr_render_pass_impl *impl); struct wlr_render_pass_impl { bool (*submit)(struct wlr_render_pass *pass); void (*add_texture)(struct wlr_render_pass *pass, const struct wlr_render_texture_options *options); /* Implementers are also guaranteed that options->box is nonempty */ void (*add_rect)(struct wlr_render_pass *pass, const struct wlr_render_rect_options *options); }; struct wlr_render_timer { const struct wlr_render_timer_impl *impl; }; struct wlr_render_timer_impl { int (*get_duration_ns)(struct wlr_render_timer *timer); void (*destroy)(struct wlr_render_timer *timer); }; void wlr_render_texture_options_get_src_box(const struct wlr_render_texture_options *options, struct wlr_fbox *box); void wlr_render_texture_options_get_dst_box(const struct wlr_render_texture_options *options, struct wlr_box *box); float wlr_render_texture_options_get_alpha(const struct wlr_render_texture_options *options); void wlr_render_rect_options_get_box(const struct wlr_render_rect_options *options, const struct wlr_buffer *buffer, struct wlr_box *box); #endif wlroots-0.17.1/include/wlr/render/pass.h000066400000000000000000000060161454110342200201350ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_RENDER_PASS_H #define WLR_RENDER_PASS_H #include #include #include #include struct wlr_renderer; struct wlr_buffer; /** * A render pass accumulates drawing operations until submitted to the GPU. */ struct wlr_render_pass; /** * An object that can be queried after a render to get the duration of the render. */ struct wlr_render_timer; struct wlr_buffer_pass_options { /* Timer to measure the duration of the render pass */ struct wlr_render_timer *timer; }; /** * Begin a new render pass with the supplied destination buffer. * * Callers must call wlr_render_pass_submit() once they are done with the * render pass. */ struct wlr_render_pass *wlr_renderer_begin_buffer_pass(struct wlr_renderer *renderer, struct wlr_buffer *buffer, const struct wlr_buffer_pass_options *options); /** * Submit the render pass. * * The render pass cannot be used after this function is called. */ bool wlr_render_pass_submit(struct wlr_render_pass *render_pass); /** * Blend modes. */ enum wlr_render_blend_mode { /* Pre-multiplied alpha (default) */ WLR_RENDER_BLEND_MODE_PREMULTIPLIED, /* Blending is disabled */ WLR_RENDER_BLEND_MODE_NONE, }; /** * Filter modes. */ enum wlr_scale_filter_mode { /* bilinear texture filtering (default) */ WLR_SCALE_FILTER_BILINEAR, /* nearest texture filtering */ WLR_SCALE_FILTER_NEAREST, }; struct wlr_render_texture_options { /* Source texture */ struct wlr_texture *texture; /* Source coordinates, leave empty to render the whole texture */ struct wlr_fbox src_box; /* Destination coordinates, width/height default to the texture size */ struct wlr_box dst_box; /* Opacity between 0 (transparent) and 1 (opaque), leave NULL for opaque */ const float *alpha; /* Clip region, leave NULL to disable clipping */ const pixman_region32_t *clip; /* Transform applied to the source texture */ enum wl_output_transform transform; /* Filtering */ enum wlr_scale_filter_mode filter_mode; /* Blend mode */ enum wlr_render_blend_mode blend_mode; }; /** * Render a texture. */ void wlr_render_pass_add_texture(struct wlr_render_pass *render_pass, const struct wlr_render_texture_options *options); /** * A color value. * * Each channel has values between 0 and 1 inclusive. The R, G, B * channels need to be pre-multiplied by A. */ struct wlr_render_color { float r, g, b, a; }; struct wlr_render_rect_options { /* Rectangle coordinates */ struct wlr_box box; /* Source color */ struct wlr_render_color color; /* Clip region, leave NULL to disable clipping */ const pixman_region32_t *clip; /* Blend mode */ enum wlr_render_blend_mode blend_mode; }; /** * Render a rectangle. */ void wlr_render_pass_add_rect(struct wlr_render_pass *render_pass, const struct wlr_render_rect_options *options); #endif wlroots-0.17.1/include/wlr/render/pixman.h000066400000000000000000000013451454110342200204630ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_RENDER_PIXMAN_H #define WLR_RENDER_PIXMAN_H #include #include struct wlr_renderer *wlr_pixman_renderer_create(void); /** * Returns the image of current buffer. */ pixman_image_t *wlr_pixman_renderer_get_current_image( struct wlr_renderer *wlr_renderer); bool wlr_renderer_is_pixman(struct wlr_renderer *wlr_renderer); bool wlr_texture_is_pixman(struct wlr_texture *texture); pixman_image_t *wlr_pixman_texture_get_image(struct wlr_texture *wlr_texture); #endif wlroots-0.17.1/include/wlr/render/swapchain.h000066400000000000000000000024551454110342200211470ustar00rootroot00000000000000#ifndef WLR_RENDER_SWAPCHAIN_H #define WLR_RENDER_SWAPCHAIN_H #include #include #include #define WLR_SWAPCHAIN_CAP 4 struct wlr_swapchain_slot { struct wlr_buffer *buffer; bool acquired; // waiting for release int age; struct wl_listener release; }; struct wlr_swapchain { struct wlr_allocator *allocator; // NULL if destroyed int width, height; struct wlr_drm_format format; struct wlr_swapchain_slot slots[WLR_SWAPCHAIN_CAP]; struct wl_listener allocator_destroy; }; struct wlr_swapchain *wlr_swapchain_create( struct wlr_allocator *alloc, int width, int height, const struct wlr_drm_format *format); void wlr_swapchain_destroy(struct wlr_swapchain *swapchain); /** * Acquire a buffer from the swap chain. * * The returned buffer is locked. When the caller is done with it, they must * unlock it by calling wlr_buffer_unlock. */ struct wlr_buffer *wlr_swapchain_acquire(struct wlr_swapchain *swapchain, int *age); /** * Mark the buffer as submitted for presentation. This needs to be called by * swap chain users on frame boundaries. * * If the buffer hasn't been created via the swap chain, the call is ignored. */ void wlr_swapchain_set_buffer_submitted(struct wlr_swapchain *swapchain, struct wlr_buffer *buffer); #endif wlroots-0.17.1/include/wlr/render/vulkan.h000066400000000000000000000023021454110342200204610ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_RENDER_VULKAN_H #define WLR_RENDER_VULKAN_H #include #include struct wlr_vk_image_attribs { VkImage image; VkImageLayout layout; VkFormat format; }; struct wlr_renderer *wlr_vk_renderer_create_with_drm_fd(int drm_fd); VkInstance wlr_vk_renderer_get_instance(struct wlr_renderer *renderer); VkPhysicalDevice wlr_vk_renderer_get_physical_device(struct wlr_renderer *renderer); VkDevice wlr_vk_renderer_get_device(struct wlr_renderer *renderer); uint32_t wlr_vk_renderer_get_queue_family(struct wlr_renderer *renderer); void wlr_vk_renderer_get_current_image_attribs(struct wlr_renderer *renderer, struct wlr_vk_image_attribs *attribs); bool wlr_renderer_is_vk(struct wlr_renderer *wlr_renderer); bool wlr_texture_is_vk(struct wlr_texture *texture); void wlr_vk_texture_get_image_attribs(struct wlr_texture *texture, struct wlr_vk_image_attribs *attribs); bool wlr_vk_texture_has_alpha(struct wlr_texture *texture); #endif wlroots-0.17.1/include/wlr/render/wlr_renderer.h000066400000000000000000000121601454110342200216560ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_RENDER_WLR_RENDERER_H #define WLR_RENDER_WLR_RENDERER_H #include #include #include #include #include struct wlr_backend; struct wlr_renderer_impl; struct wlr_drm_format_set; struct wlr_buffer; struct wlr_box; struct wlr_fbox; /** * A renderer for basic 2D operations. */ struct wlr_renderer { struct { struct wl_signal destroy; /** * Emitted when the GPU is lost, e.g. on GPU reset. * * Compositors should destroy the renderer and re-create it. */ struct wl_signal lost; } events; // private state const struct wlr_renderer_impl *impl; bool rendering; bool rendering_with_buffer; }; /** * Automatically create a new renderer. * * Selects an appropriate renderer type to use depending on the backend, * platform, environment, etc. */ struct wlr_renderer *wlr_renderer_autocreate(struct wlr_backend *backend); /** * Start a render pass with the provided viewport. * * This should be called after wlr_output_attach_render(). Compositors must call * wlr_renderer_end() when they are done. * * Returns false on failure, in which case compositors shouldn't try rendering. */ bool wlr_renderer_begin(struct wlr_renderer *r, uint32_t width, uint32_t height); /** * Start a render pass on the provided struct wlr_buffer. * * Compositors must call wlr_renderer_end() when they are done. */ bool wlr_renderer_begin_with_buffer(struct wlr_renderer *r, struct wlr_buffer *buffer); /** * End a render pass. */ void wlr_renderer_end(struct wlr_renderer *r); /** * Clear the viewport with the provided color. */ void wlr_renderer_clear(struct wlr_renderer *r, const float color[static 4]); /** * Defines a scissor box. Only pixels that lie within the scissor box can be * modified by drawing functions. Providing a NULL `box` disables the scissor * box. */ void wlr_renderer_scissor(struct wlr_renderer *r, struct wlr_box *box); /** * Renders the requested texture. */ bool wlr_render_texture(struct wlr_renderer *r, struct wlr_texture *texture, const float projection[static 9], int x, int y, float alpha); /** * Renders the requested texture using the provided matrix. */ bool wlr_render_texture_with_matrix(struct wlr_renderer *r, struct wlr_texture *texture, const float matrix[static 9], float alpha); /** * Renders the requested texture using the provided matrix, after cropping it * to the provided rectangle. */ bool wlr_render_subtexture_with_matrix(struct wlr_renderer *r, struct wlr_texture *texture, const struct wlr_fbox *box, const float matrix[static 9], float alpha); /** * Renders a solid rectangle in the specified color. */ void wlr_render_rect(struct wlr_renderer *r, const struct wlr_box *box, const float color[static 4], const float projection[static 9]); /** * Renders a solid quadrangle in the specified color with the specified matrix. */ void wlr_render_quad_with_matrix(struct wlr_renderer *r, const float color[static 4], const float matrix[static 9]); /** * Get the shared-memory formats supporting import usage. Buffers allocated * with a format from this list may be imported via wlr_texture_from_pixels(). */ const uint32_t *wlr_renderer_get_shm_texture_formats( struct wlr_renderer *r, size_t *len); /** * Get the DMA-BUF formats supporting sampling usage. Buffers allocated with * a format from this list may be imported via wlr_texture_from_dmabuf(). */ const struct wlr_drm_format_set *wlr_renderer_get_dmabuf_texture_formats( struct wlr_renderer *renderer); /** * Reads out of pixels of the currently bound surface into data. `stride` is in * bytes. */ bool wlr_renderer_read_pixels(struct wlr_renderer *r, uint32_t fmt, uint32_t stride, uint32_t width, uint32_t height, uint32_t src_x, uint32_t src_y, uint32_t dst_x, uint32_t dst_y, void *data); /** * Initializes wl_shm, linux-dmabuf and other buffer factory protocols. * * Returns false on failure. */ bool wlr_renderer_init_wl_display(struct wlr_renderer *r, struct wl_display *wl_display); /** * Initializes wl_shm on the provided struct wl_display. */ bool wlr_renderer_init_wl_shm(struct wlr_renderer *r, struct wl_display *wl_display); /** * Obtains the FD of the DRM device used for rendering, or -1 if unavailable. * * The caller doesn't have ownership of the FD, it must not close it. */ int wlr_renderer_get_drm_fd(struct wlr_renderer *r); /** * Destroys the renderer. * * Textures must be destroyed separately. */ void wlr_renderer_destroy(struct wlr_renderer *renderer); /** * Allocate and initialise a new render timer. */ struct wlr_render_timer *wlr_render_timer_create(struct wlr_renderer *renderer); /** * Get the render duration in nanoseconds from the timer. * * Returns -1 if the duration is unavailable. */ int wlr_render_timer_get_duration_ns(struct wlr_render_timer *timer); /** * Destroy the render timer. */ void wlr_render_timer_destroy(struct wlr_render_timer *timer); #endif wlroots-0.17.1/include/wlr/render/wlr_texture.h000066400000000000000000000034761454110342200215620ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_RENDER_WLR_TEXTURE_H #define WLR_RENDER_WLR_TEXTURE_H #include #include #include #include struct wlr_buffer; struct wlr_renderer; struct wlr_texture_impl; struct wlr_texture { const struct wlr_texture_impl *impl; uint32_t width, height; struct wlr_renderer *renderer; }; /** * Create a new texture from raw pixel data. `stride` is in bytes. The returned * texture is mutable. */ struct wlr_texture *wlr_texture_from_pixels(struct wlr_renderer *renderer, uint32_t fmt, uint32_t stride, uint32_t width, uint32_t height, const void *data); /** * Create a new texture from a DMA-BUF. The returned texture is immutable. */ struct wlr_texture *wlr_texture_from_dmabuf(struct wlr_renderer *renderer, struct wlr_dmabuf_attributes *attribs); /** * Update a texture with a struct wlr_buffer's contents. * * The update might be rejected (in case the texture is immutable, the buffer * has an unsupported type/format, etc), so callers must be prepared to fall * back to re-creating the texture from scratch via wlr_texture_from_buffer(). * * The damage can be used by the renderer as an optimization: only the supplied * region needs to be updated. */ bool wlr_texture_update_from_buffer(struct wlr_texture *texture, struct wlr_buffer *buffer, const pixman_region32_t *damage); /** * Destroys the texture. */ void wlr_texture_destroy(struct wlr_texture *texture); /** * Create a new texture from a buffer. */ struct wlr_texture *wlr_texture_from_buffer(struct wlr_renderer *renderer, struct wlr_buffer *buffer); #endif wlroots-0.17.1/include/wlr/types/000077500000000000000000000000001454110342200167005ustar00rootroot00000000000000wlroots-0.17.1/include/wlr/types/wlr_buffer.h000066400000000000000000000107451454110342200212150ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_TYPES_WLR_BUFFER_H #define WLR_TYPES_WLR_BUFFER_H #include #include #include #include struct wlr_buffer; struct wlr_renderer; struct wlr_shm_attributes { int fd; uint32_t format; int width, height, stride; off_t offset; }; /** * Buffer capabilities. * * These bits indicate the features supported by a struct wlr_buffer. There is * one bit per function in struct wlr_buffer_impl. */ enum wlr_buffer_cap { WLR_BUFFER_CAP_DATA_PTR = 1 << 0, WLR_BUFFER_CAP_DMABUF = 1 << 1, WLR_BUFFER_CAP_SHM = 1 << 2, }; /** * A buffer containing pixel data. * * A buffer has a single producer (the party who created the buffer) and * multiple consumers (parties reading the buffer). When all consumers are done * with the buffer, it gets released and can be re-used by the producer. When * the producer and all consumers are done with the buffer, it gets destroyed. */ struct wlr_buffer { const struct wlr_buffer_impl *impl; int width, height; bool dropped; size_t n_locks; bool accessing_data_ptr; struct { struct wl_signal destroy; struct wl_signal release; } events; struct wlr_addon_set addons; }; /** * Unreference the buffer. This function should be called by producers when * they are done with the buffer. */ void wlr_buffer_drop(struct wlr_buffer *buffer); /** * Lock the buffer. This function should be called by consumers to make * sure the buffer can be safely read from. Once the consumer is done with the * buffer, they should call wlr_buffer_unlock(). */ struct wlr_buffer *wlr_buffer_lock(struct wlr_buffer *buffer); /** * Unlock the buffer. This function should be called by consumers once they are * done with the buffer. */ void wlr_buffer_unlock(struct wlr_buffer *buffer); /** * Reads the DMA-BUF attributes of the buffer. If this buffer isn't a DMA-BUF, * returns false. * * The returned DMA-BUF attributes are valid for the lifetime of the * struct wlr_buffer. The caller isn't responsible for cleaning up the DMA-BUF * attributes. */ bool wlr_buffer_get_dmabuf(struct wlr_buffer *buffer, struct wlr_dmabuf_attributes *attribs); /** * Read shared memory attributes of the buffer. If this buffer isn't shared * memory, returns false. * * The returned shared memory attributes are valid for the lifetime of the * struct wlr_buffer. The caller isn't responsible for cleaning up the shared * memory attributes. */ bool wlr_buffer_get_shm(struct wlr_buffer *buffer, struct wlr_shm_attributes *attribs); /** * Transforms a struct wl_resource into a struct wlr_buffer and locks it. Once * the caller is done with the buffer, they must call wlr_buffer_unlock(). * * The provided struct wl_resource must be a wl_buffer. */ struct wlr_buffer *wlr_buffer_try_from_resource(struct wl_resource *resource); /** * Buffer data pointer access flags. */ enum wlr_buffer_data_ptr_access_flag { /** * The buffer contents can be read back. */ WLR_BUFFER_DATA_PTR_ACCESS_READ = 1 << 0, /** * The buffer contents can be written to. */ WLR_BUFFER_DATA_PTR_ACCESS_WRITE = 1 << 1, }; /** * Get a pointer to a region of memory referring to the buffer's underlying * storage. The format and stride can be used to interpret the memory region * contents. * * The returned pointer should be pointing to a valid memory region for the * operations specified in the flags. The returned pointer is only valid up to * the next wlr_buffer_end_data_ptr_access() call. */ bool wlr_buffer_begin_data_ptr_access(struct wlr_buffer *buffer, uint32_t flags, void **data, uint32_t *format, size_t *stride); void wlr_buffer_end_data_ptr_access(struct wlr_buffer *buffer); /** * A client buffer. */ struct wlr_client_buffer { struct wlr_buffer base; /** * The buffer's texture, if any. A buffer will not have a texture if the * client destroys the buffer before it has been released. */ struct wlr_texture *texture; /** * The buffer this client buffer was created from. NULL if destroyed. */ struct wlr_buffer *source; // private state struct wl_listener source_destroy; size_t n_ignore_locks; }; /** * Get a client buffer from a generic buffer. If the buffer isn't a client * buffer, returns NULL. */ struct wlr_client_buffer *wlr_client_buffer_get(struct wlr_buffer *buffer); #endif wlroots-0.17.1/include/wlr/types/wlr_compositor.h000066400000000000000000000324341454110342200221410ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_TYPES_WLR_COMPOSITOR_H #define WLR_TYPES_WLR_COMPOSITOR_H #include #include #include #include #include #include #include #include enum wlr_surface_state_field { WLR_SURFACE_STATE_BUFFER = 1 << 0, WLR_SURFACE_STATE_SURFACE_DAMAGE = 1 << 1, WLR_SURFACE_STATE_BUFFER_DAMAGE = 1 << 2, WLR_SURFACE_STATE_OPAQUE_REGION = 1 << 3, WLR_SURFACE_STATE_INPUT_REGION = 1 << 4, WLR_SURFACE_STATE_TRANSFORM = 1 << 5, WLR_SURFACE_STATE_SCALE = 1 << 6, WLR_SURFACE_STATE_FRAME_CALLBACK_LIST = 1 << 7, WLR_SURFACE_STATE_VIEWPORT = 1 << 8, WLR_SURFACE_STATE_OFFSET = 1 << 9, }; struct wlr_surface_state { uint32_t committed; // enum wlr_surface_state_field // Sequence number of the surface state. Incremented on each commit, may // overflow. uint32_t seq; struct wlr_buffer *buffer; int32_t dx, dy; // relative to previous position pixman_region32_t surface_damage, buffer_damage; // clipped to bounds pixman_region32_t opaque, input; enum wl_output_transform transform; int32_t scale; struct wl_list frame_callback_list; // wl_resource int width, height; // in surface-local coordinates int buffer_width, buffer_height; struct wl_list subsurfaces_below; struct wl_list subsurfaces_above; /** * The viewport is applied after the surface transform and scale. * * If has_src is true, the surface content is cropped to the provided * rectangle. If has_dst is true, the surface content is scaled to the * provided rectangle. */ struct { bool has_src, has_dst; // In coordinates after scale/transform are applied, but before the // destination rectangle is applied struct wlr_fbox src; int dst_width, dst_height; // in surface-local coordinates } viewport; // Number of locks that prevent this surface state from being committed. size_t cached_state_locks; struct wl_list cached_state_link; // wlr_surface.cached }; struct wlr_surface_role { const char *name; /** * If true, the role isn't represented by any object. * For example, this applies to cursor surfaces. */ bool no_object; /** * Called when a new surface state is committed. May be NULL. * * If the role is represented by an object, this is only called if * such object exists. */ void (*commit)(struct wlr_surface *surface); /** * Called when the surface is unmapped. May be NULL. * * If the role is represented by an object, this is only called if * such object exists. */ void (*unmap)(struct wlr_surface *surface); /** * Called when the object representing the role is destroyed. May be NULL. */ void (*destroy)(struct wlr_surface *surface); }; struct wlr_surface_output { struct wlr_surface *surface; struct wlr_output *output; struct wl_list link; // wlr_surface.current_outputs struct wl_listener bind; struct wl_listener destroy; }; struct wlr_surface { struct wl_resource *resource; struct wlr_renderer *renderer; // may be NULL /** * The surface's buffer, if any. A surface has an attached buffer when it * commits with a non-null buffer in its pending state. A surface will not * have a buffer if it has never committed one, has committed a null buffer, * or something went wrong with uploading the buffer. */ struct wlr_client_buffer *buffer; /** * The last commit's buffer damage, in buffer-local coordinates. This * contains both the damage accumulated by the client via * `wlr_surface_state.surface_damage` and `wlr_surface_state.buffer_damage`. * If the buffer has been resized, the whole buffer is damaged. * * This region needs to be scaled and transformed into output coordinates, * just like the buffer's texture. In addition, if the buffer has shrunk the * old size needs to be damaged and if the buffer has moved the old and new * positions need to be damaged. */ pixman_region32_t buffer_damage; /** * The last commit's damage caused by surface and its subsurfaces' * movement, in surface-local coordinates. */ pixman_region32_t external_damage; /** * The current opaque region, in surface-local coordinates. It is clipped to * the surface bounds. If the surface's buffer is using a fully opaque * format, this is set to the whole surface. */ pixman_region32_t opaque_region; /** * The current input region, in surface-local coordinates. It is clipped to * the surface bounds. * * If the protocol states that the input region is ignored, this is empty. */ pixman_region32_t input_region; /** * `current` contains the current, committed surface state. `pending` * accumulates state changes from the client between commits and shouldn't * be accessed by the compositor directly. */ struct wlr_surface_state current, pending; struct wl_list cached; // wlr_surface_state.cached_link /** * Whether the surface is ready to be displayed. */ bool mapped; /** * The lifetime-bound role of the surface. NULL if the role was never set. */ const struct wlr_surface_role *role; /** * The role object representing the role. NULL if the role isn't * represented by any object or the object was destroyed. */ struct wl_resource *role_resource; struct { struct wl_signal client_commit; struct wl_signal precommit; // const struct wlr_surface_state * struct wl_signal commit; /** * The `map` event signals that the surface has a non-null buffer * committed and is ready to be displayed. */ struct wl_signal map; /** * The `unmap` event signals that the surface shouldn't be displayed * anymore. This can happen when a null buffer is committed, * the associated role object is destroyed, or when the role-specific * conditions for the surface to be mapped no longer apply. */ struct wl_signal unmap; struct wl_signal new_subsurface; struct wl_signal destroy; } events; struct wl_list current_outputs; // wlr_surface_output.link struct wlr_addon_set addons; void *data; // private state struct wl_listener renderer_destroy; struct wl_listener role_resource_destroy; struct { int32_t scale; enum wl_output_transform transform; int width, height; int buffer_width, buffer_height; } previous; bool unmap_commit; bool opaque; bool has_buffer; int32_t preferred_buffer_scale; bool preferred_buffer_transform_sent; enum wl_output_transform preferred_buffer_transform; }; struct wlr_renderer; struct wlr_compositor { struct wl_global *global; struct wlr_renderer *renderer; // may be NULL struct wl_listener display_destroy; struct { struct wl_signal new_surface; struct wl_signal destroy; } events; }; typedef void (*wlr_surface_iterator_func_t)(struct wlr_surface *surface, int sx, int sy, void *data); /** * Set the lifetime role for this surface. * * If the surface already has a different role and/or has a role object set, * the function fails and sends an error to the client. * * Returns true on success, false otherwise. */ bool wlr_surface_set_role(struct wlr_surface *surface, const struct wlr_surface_role *role, struct wl_resource *error_resource, uint32_t error_code); /** * Set the role object for this surface. The surface must have a role and * no already set role object. * * When the resource is destroyed, the surface is unmapped, * wlr_surface_role.destroy is called and the role object is unset. */ void wlr_surface_set_role_object(struct wlr_surface *surface, struct wl_resource *role_resource); /** * Map the surface. If the surface is already mapped, this is no-op. * * This function must only be used by surface role implementations. */ void wlr_surface_map(struct wlr_surface *surface); /** * Unmap the surface. If the surface is already unmapped, this is no-op. * * This function must only be used by surface role implementations. */ void wlr_surface_unmap(struct wlr_surface *surface); /** * Whether or not this surface currently has an attached buffer. A surface has * an attached buffer when it commits with a non-null buffer in its pending * state. A surface will not have a buffer if it has never committed one, has * committed a null buffer, or something went wrong with uploading the buffer. */ bool wlr_surface_has_buffer(struct wlr_surface *surface); /** * Get the texture of the buffer currently attached to this surface. Returns * NULL if no buffer is currently attached or if something went wrong with * uploading the buffer. */ struct wlr_texture *wlr_surface_get_texture(struct wlr_surface *surface); /** * Get the root of the subsurface tree for this surface. Can return NULL if * a surface in the tree has been destroyed. */ struct wlr_surface *wlr_surface_get_root_surface(struct wlr_surface *surface); /** * Check if the surface accepts input events at the given surface-local * coordinates. Does not check the surface's subsurfaces. */ bool wlr_surface_point_accepts_input(struct wlr_surface *surface, double sx, double sy); /** * Find a surface in this surface's tree that accepts input events and has all * parents mapped (except this surface, which can be unmapped) at the given * surface-local coordinates. Returns the surface and coordinates in the leaf * surface coordinate system or NULL if no surface is found at that location. */ struct wlr_surface *wlr_surface_surface_at(struct wlr_surface *surface, double sx, double sy, double *sub_x, double *sub_y); /** * Notify the client that the surface has entered an output. * * This is a no-op if the surface has already entered the output. */ void wlr_surface_send_enter(struct wlr_surface *surface, struct wlr_output *output); /** * Notify the client that the surface has left an output. * * This is a no-op if the surface has already left the output. */ void wlr_surface_send_leave(struct wlr_surface *surface, struct wlr_output *output); /** * Complete the queued frame callbacks for this surface. * * This will send an event to the client indicating that now is a good time to * draw its next frame. */ void wlr_surface_send_frame_done(struct wlr_surface *surface, const struct timespec *when); /** * Get the bounding box that contains the surface and all subsurfaces in * surface coordinates. * X and y may be negative, if there are subsurfaces with negative position. */ void wlr_surface_get_extends(struct wlr_surface *surface, struct wlr_box *box); /** * Get the struct wlr_surface corresponding to a wl_surface resource. * * This asserts that the resource is a valid wl_surface resource created by * wlroots and will never return NULL. */ struct wlr_surface *wlr_surface_from_resource(struct wl_resource *resource); /** * Call `iterator` on each mapped surface in the surface tree (whether or not * this surface is mapped), with the surface's position relative to the root * surface. The function is called from root to leaves (in rendering order). */ void wlr_surface_for_each_surface(struct wlr_surface *surface, wlr_surface_iterator_func_t iterator, void *user_data); /** * Get the effective surface damage in surface-local coordinate space. Besides * buffer damage, this includes damage induced by resizing and moving the * surface and its subsurfaces. The resulting damage is not expected to be * bounded by the surface itself. */ void wlr_surface_get_effective_damage(struct wlr_surface *surface, pixman_region32_t *damage); /** * Get the source rectangle describing the region of the buffer that needs to * be sampled to render this surface's current state. The box is in * buffer-local coordinates. * * If the viewport's source rectangle is unset, the position is zero and the * size is the buffer's. */ void wlr_surface_get_buffer_source_box(struct wlr_surface *surface, struct wlr_fbox *box); /** * Acquire a lock for the pending surface state. * * The state won't be committed before the caller releases the lock. Instead, * the state becomes cached. The caller needs to use wlr_surface_unlock_cached() * to release the lock. * * Returns a surface commit sequence number for the cached state. */ uint32_t wlr_surface_lock_pending(struct wlr_surface *surface); /** * Release a lock for a cached state. * * Callers should not assume that the cached state will immediately be * committed. Another caller may still have an active lock. */ void wlr_surface_unlock_cached(struct wlr_surface *surface, uint32_t seq); /** * Set the preferred buffer scale for the surface. * * This sends an event to the client indicating the preferred scale to use for * buffers attached to this surface. */ void wlr_surface_set_preferred_buffer_scale(struct wlr_surface *surface, int32_t scale); /** * Set the preferred buffer transform for the surface. * * This sends an event to the client indicating the preferred transform to use * for buffers attached to this surface. */ void wlr_surface_set_preferred_buffer_transform(struct wlr_surface *surface, enum wl_output_transform transform); /** * Create the wl_compositor global, which can be used by clients to create * surfaces and regions. * * If a renderer is supplied, the compositor will create struct wlr_texture * objects from client buffers on surface commit. */ struct wlr_compositor *wlr_compositor_create(struct wl_display *display, uint32_t version, struct wlr_renderer *renderer); #endif wlroots-0.17.1/include/wlr/types/wlr_content_type_v1.h000066400000000000000000000015231454110342200230570ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_TYPES_WLR_CONTENT_TYPE_V1_H #define WLR_TYPES_WLR_CONTENT_TYPE_V1_H #include #include "content-type-v1-protocol.h" struct wlr_surface; struct wlr_content_type_manager_v1 { struct wl_global *global; struct { struct wl_signal destroy; } events; void *data; // private state struct wl_listener display_destroy; }; struct wlr_content_type_manager_v1 *wlr_content_type_manager_v1_create( struct wl_display *display, uint32_t version); enum wp_content_type_v1_type wlr_surface_get_content_type_v1( struct wlr_content_type_manager_v1 *manager, struct wlr_surface *surface); #endif wlroots-0.17.1/include/wlr/types/wlr_cursor.h000066400000000000000000000164621454110342200212630ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_TYPES_WLR_CURSOR_H #define WLR_TYPES_WLR_CURSOR_H #include #include #include struct wlr_input_device; struct wlr_xcursor_manager; /** * wlr_cursor implements the behavior of the "cursor", that is, the image on the * screen typically moved about with a mouse or so. It provides tracking for * this in global coordinates, and integrates with struct wlr_output, * struct wlr_output_layout, and struct wlr_input_device. You can use it to * abstract multiple input devices over a single cursor, constrain cursor * movement to the usable area of a struct wlr_output_layout and communicate * position updates to the hardware cursor, constrain specific input devices to * specific outputs or regions of the screen, and so on. */ struct wlr_box; struct wlr_cursor_state; struct wlr_cursor { struct wlr_cursor_state *state; double x, y; /** * The interpretation of these signals is the responsibility of the * compositor, but some helpers are provided for your benefit. If you * receive a relative motion event, for example, you may want to call * wlr_cursor_move(). If you receive an absolute event, call * wlr_cursor_warp_absolute(). If you pass an input device into these * functions, it will apply the region/output constraints associated with * that device to the resulting cursor motion. If an output layout is * attached, these functions will constrain the resulting cursor motion to * within the usable space of the output layout. * * Re-broadcasting these signals to, for example, a struct wlr_seat, is also * your responsibility. */ struct { struct wl_signal motion; struct wl_signal motion_absolute; struct wl_signal button; struct wl_signal axis; struct wl_signal frame; struct wl_signal swipe_begin; struct wl_signal swipe_update; struct wl_signal swipe_end; struct wl_signal pinch_begin; struct wl_signal pinch_update; struct wl_signal pinch_end; struct wl_signal hold_begin; struct wl_signal hold_end; struct wl_signal touch_up; struct wl_signal touch_down; struct wl_signal touch_motion; struct wl_signal touch_cancel; struct wl_signal touch_frame; struct wl_signal tablet_tool_axis; struct wl_signal tablet_tool_proximity; struct wl_signal tablet_tool_tip; struct wl_signal tablet_tool_button; } events; void *data; }; struct wlr_cursor *wlr_cursor_create(void); void wlr_cursor_destroy(struct wlr_cursor *cur); /** * Warp the cursor to the given x and y in layout coordinates. If x and y are * out of the layout boundaries or constraints, no warp will happen. * * `dev` may be passed to respect device mapping constraints. If `dev` is NULL, * device mapping constraints will be ignored. * * Returns true when the cursor warp was successful. */ bool wlr_cursor_warp(struct wlr_cursor *cur, struct wlr_input_device *dev, double lx, double ly); /** * Convert absolute 0..1 coordinates to layout coordinates. * * `dev` may be passed to respect device mapping constraints. If `dev` is NULL, * device mapping constraints will be ignored. */ void wlr_cursor_absolute_to_layout_coords(struct wlr_cursor *cur, struct wlr_input_device *dev, double x, double y, double *lx, double *ly); /** * Warp the cursor to the given x and y coordinates. If the given point is out * of the layout boundaries or constraints, the closest point will be used. * If one coordinate is NAN, it will be ignored. * * `dev` may be passed to respect device mapping constraints. If `dev` is NULL, * device mapping constraints will be ignored. */ void wlr_cursor_warp_closest(struct wlr_cursor *cur, struct wlr_input_device *dev, double x, double y); /** * Warp the cursor to the given x and y in absolute 0..1 coordinates. If the * given point is out of the layout boundaries or constraints, the closest point * will be used. If one coordinate is NAN, it will be ignored. * * `dev` may be passed to respect device mapping constraints. If `dev` is NULL, * device mapping constraints will be ignored. */ void wlr_cursor_warp_absolute(struct wlr_cursor *cur, struct wlr_input_device *dev, double x, double y); /** * Move the cursor in the direction of the given x and y layout coordinates. If * one coordinate is NAN, it will be ignored. * * `dev` may be passed to respect device mapping constraints. If `dev` is NULL, * device mapping constraints will be ignored. */ void wlr_cursor_move(struct wlr_cursor *cur, struct wlr_input_device *dev, double delta_x, double delta_y); /** * Set the cursor buffer. * * The buffer is used on all outputs and is scaled accordingly. The hotspot is * expressed in logical coordinates. A NULL buffer hides the cursor. */ void wlr_cursor_set_buffer(struct wlr_cursor *cur, struct wlr_buffer *buffer, int32_t hotspot_x, int32_t hotspot_y, float scale); /** * Hide the cursor image. */ void wlr_cursor_unset_image(struct wlr_cursor *cur); /** * Set the cursor image from an XCursor theme. * * The image will be loaded from the struct wlr_xcursor_manager. */ void wlr_cursor_set_xcursor(struct wlr_cursor *cur, struct wlr_xcursor_manager *manager, const char *name); /** * Set the cursor surface. The surface can be committed to update the cursor * image. The surface position is subtracted from the hotspot. A NULL surface * commit hides the cursor. */ void wlr_cursor_set_surface(struct wlr_cursor *cur, struct wlr_surface *surface, int32_t hotspot_x, int32_t hotspot_y); /** * Attaches this input device to this cursor. The input device must be one of: * * - WLR_INPUT_DEVICE_POINTER * - WLR_INPUT_DEVICE_TOUCH * - WLR_INPUT_DEVICE_TABLET_TOOL */ void wlr_cursor_attach_input_device(struct wlr_cursor *cur, struct wlr_input_device *dev); void wlr_cursor_detach_input_device(struct wlr_cursor *cur, struct wlr_input_device *dev); /** * Uses the given layout to establish the boundaries and movement semantics of * this cursor. Cursors without an output layout allow infinite movement in any * direction and do not support absolute input events. */ void wlr_cursor_attach_output_layout(struct wlr_cursor *cur, struct wlr_output_layout *l); /** * Attaches this cursor to the given output, which must be among the outputs in * the current output_layout for this cursor. This call is invalid for a cursor * without an associated output layout. */ void wlr_cursor_map_to_output(struct wlr_cursor *cur, struct wlr_output *output); /** * Maps all input from a specific input device to a given output. The input * device must be attached to this cursor and the output must be among the * outputs in the attached output layout. */ void wlr_cursor_map_input_to_output(struct wlr_cursor *cur, struct wlr_input_device *dev, struct wlr_output *output); /** * Maps this cursor to an arbitrary region on the associated * struct wlr_output_layout. */ void wlr_cursor_map_to_region(struct wlr_cursor *cur, const struct wlr_box *box); /** * Maps inputs from this input device to an arbitrary region on the associated * struct wlr_output_layout. */ void wlr_cursor_map_input_to_region(struct wlr_cursor *cur, struct wlr_input_device *dev, const struct wlr_box *box); #endif wlroots-0.17.1/include/wlr/types/wlr_cursor_shape_v1.h000066400000000000000000000030211454110342200230340ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_TYPES_WLR_CURSOR_SHAPE_V1_H #define WLR_TYPES_WLR_CURSOR_SHAPE_V1_H #include #include "cursor-shape-v1-protocol.h" /** * Manager for the cursor-shape-v1 protocol. * * Compositors should listen to the request_set_shape event and handle it in * the same way as wlr_seat.events.request_set_cursor. */ struct wlr_cursor_shape_manager_v1 { struct wl_global *global; struct { struct wl_signal request_set_shape; // struct wlr_cursor_shape_manager_v1_request_set_shape_event struct wl_signal destroy; } events; void *data; // private state struct wl_listener display_destroy; }; enum wlr_cursor_shape_manager_v1_device_type { WLR_CURSOR_SHAPE_MANAGER_V1_DEVICE_TYPE_POINTER, WLR_CURSOR_SHAPE_MANAGER_V1_DEVICE_TYPE_TABLET_TOOL, }; struct wlr_cursor_shape_manager_v1_request_set_shape_event { struct wlr_seat_client *seat_client; enum wlr_cursor_shape_manager_v1_device_type device_type; uint32_t serial; enum wp_cursor_shape_device_v1_shape shape; }; struct wlr_cursor_shape_manager_v1 *wlr_cursor_shape_manager_v1_create( struct wl_display *display, uint32_t version); /** * Get the name of a cursor shape. * * The name can be used to load a cursor from an XCursor theme. */ const char *wlr_cursor_shape_v1_name(enum wp_cursor_shape_device_v1_shape shape); #endif wlroots-0.17.1/include/wlr/types/wlr_damage_ring.h000066400000000000000000000043211454110342200221720ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_TYPES_WLR_DAMAGE_RING_H #define WLR_TYPES_WLR_DAMAGE_RING_H #include #include #include #include /* For triple buffering, a history of two frames is required. */ #define WLR_DAMAGE_RING_PREVIOUS_LEN 2 struct wlr_box; struct wlr_damage_ring { int32_t width, height; // Difference between the current buffer and the previous one pixman_region32_t current; // private state pixman_region32_t previous[WLR_DAMAGE_RING_PREVIOUS_LEN]; size_t previous_idx; }; void wlr_damage_ring_init(struct wlr_damage_ring *ring); void wlr_damage_ring_finish(struct wlr_damage_ring *ring); /** * Set ring bounds and damage the ring fully. * * Next time damage will be added, it will be cropped to the ring bounds. * If at least one of the dimensions is 0, bounds are removed. * * By default, a damage ring doesn't have bounds. */ void wlr_damage_ring_set_bounds(struct wlr_damage_ring *ring, int32_t width, int32_t height); /** * Add a region to the current damage. * * Returns true if the region intersects the ring bounds, false otherwise. */ bool wlr_damage_ring_add(struct wlr_damage_ring *ring, const pixman_region32_t *damage); /** * Add a box to the current damage. * * Returns true if the box intersects the ring bounds, false otherwise. */ bool wlr_damage_ring_add_box(struct wlr_damage_ring *ring, const struct wlr_box *box); /** * Damage the ring fully. */ void wlr_damage_ring_add_whole(struct wlr_damage_ring *ring); /** * Rotate the damage ring. This needs to be called after using the accumulated * damage, e.g. after rendering to an output's back buffer. */ void wlr_damage_ring_rotate(struct wlr_damage_ring *ring); /** * Get accumulated damage, which is the difference between the current buffer * and the buffer with age of buffer_age; in context of rendering, this is * the region that needs to be redrawn. */ void wlr_damage_ring_get_buffer_damage(struct wlr_damage_ring *ring, int buffer_age, pixman_region32_t *damage); #endif wlroots-0.17.1/include/wlr/types/wlr_data_control_v1.h000066400000000000000000000024651454110342200230230ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_TYPES_WLR_DATA_CONTROL_V1_H #define WLR_TYPES_WLR_DATA_CONTROL_V1_H #include #include struct wlr_data_control_manager_v1 { struct wl_global *global; struct wl_list devices; // wlr_data_control_device_v1.link struct { struct wl_signal destroy; struct wl_signal new_device; // wlr_data_control_device_v1 } events; struct wl_listener display_destroy; }; struct wlr_data_control_device_v1 { struct wl_resource *resource; struct wlr_data_control_manager_v1 *manager; struct wl_list link; // wlr_data_control_manager_v1.devices struct wlr_seat *seat; struct wl_resource *selection_offer_resource; // current selection offer struct wl_resource *primary_selection_offer_resource; // current primary selection offer struct wl_listener seat_destroy; struct wl_listener seat_set_selection; struct wl_listener seat_set_primary_selection; }; struct wlr_data_control_manager_v1 *wlr_data_control_manager_v1_create( struct wl_display *display); void wlr_data_control_device_v1_destroy( struct wlr_data_control_device_v1 *device); #endif wlroots-0.17.1/include/wlr/types/wlr_data_device.h000066400000000000000000000153311454110342200221700ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_TYPES_WLR_DATA_DEVICE_H #define WLR_TYPES_WLR_DATA_DEVICE_H #include #include struct wlr_data_device_manager { struct wl_global *global; struct wl_list data_sources; struct wl_listener display_destroy; struct { struct wl_signal destroy; } events; void *data; }; enum wlr_data_offer_type { WLR_DATA_OFFER_SELECTION, WLR_DATA_OFFER_DRAG, }; struct wlr_data_offer { struct wl_resource *resource; struct wlr_data_source *source; enum wlr_data_offer_type type; struct wl_list link; // wlr_seat.{selection_offers,drag_offers} uint32_t actions; enum wl_data_device_manager_dnd_action preferred_action; bool in_ask; struct wl_listener source_destroy; }; /** * A data source implementation. Only the `send` function is mandatory. Refer to * the matching `wlr_data_source_*` functions documentation to know what they do. */ struct wlr_data_source_impl { void (*send)(struct wlr_data_source *source, const char *mime_type, int32_t fd); void (*accept)(struct wlr_data_source *source, uint32_t serial, const char *mime_type); void (*destroy)(struct wlr_data_source *source); void (*dnd_drop)(struct wlr_data_source *source); void (*dnd_finish)(struct wlr_data_source *source); void (*dnd_action)(struct wlr_data_source *source, enum wl_data_device_manager_dnd_action action); }; struct wlr_data_source { const struct wlr_data_source_impl *impl; // source metadata struct wl_array mime_types; int32_t actions; // source status bool accepted; // drag'n'drop status enum wl_data_device_manager_dnd_action current_dnd_action; uint32_t compositor_action; struct { struct wl_signal destroy; } events; }; struct wlr_drag; struct wlr_drag_icon { struct wlr_drag *drag; struct wlr_surface *surface; struct { struct wl_signal destroy; } events; struct wl_listener surface_destroy; void *data; }; enum wlr_drag_grab_type { WLR_DRAG_GRAB_KEYBOARD, WLR_DRAG_GRAB_KEYBOARD_POINTER, WLR_DRAG_GRAB_KEYBOARD_TOUCH, }; struct wlr_drag { enum wlr_drag_grab_type grab_type; struct wlr_seat_keyboard_grab keyboard_grab; struct wlr_seat_pointer_grab pointer_grab; struct wlr_seat_touch_grab touch_grab; struct wlr_seat *seat; struct wlr_seat_client *seat_client; struct wlr_seat_client *focus_client; struct wlr_drag_icon *icon; // can be NULL struct wlr_surface *focus; // can be NULL struct wlr_data_source *source; // can be NULL bool started, dropped, cancelling; int32_t grab_touch_id, touch_id; // if WLR_DRAG_GRAB_TOUCH struct { struct wl_signal focus; struct wl_signal motion; // struct wlr_drag_motion_event struct wl_signal drop; // struct wlr_drag_drop_event struct wl_signal destroy; } events; struct wl_listener source_destroy; struct wl_listener seat_client_destroy; struct wl_listener icon_destroy; void *data; }; struct wlr_drag_motion_event { struct wlr_drag *drag; uint32_t time; double sx, sy; }; struct wlr_drag_drop_event { struct wlr_drag *drag; uint32_t time; }; /** * Create a wl_data_device_manager global for this display. */ struct wlr_data_device_manager *wlr_data_device_manager_create( struct wl_display *display); /** * Requests a selection to be set for the seat. If the request comes from * a client, then set `client` to be the matching seat client so that this * function can verify that the serial provided was once sent to the client * on this seat. */ void wlr_seat_request_set_selection(struct wlr_seat *seat, struct wlr_seat_client *client, struct wlr_data_source *source, uint32_t serial); /** * Sets the current selection for the seat. NULL can be provided to clear it. * This removes the previous one if there was any. In case the selection doesn't * come from a client, wl_display_next_serial() can be used to generate a * serial. */ void wlr_seat_set_selection(struct wlr_seat *seat, struct wlr_data_source *source, uint32_t serial); /** * Creates a new drag. To request to start the drag, call * wlr_seat_request_start_drag(). */ struct wlr_drag *wlr_drag_create(struct wlr_seat_client *seat_client, struct wlr_data_source *source, struct wlr_surface *icon_surface); /** * Requests a drag to be started on the seat. */ void wlr_seat_request_start_drag(struct wlr_seat *seat, struct wlr_drag *drag, struct wlr_surface *origin, uint32_t serial); /** * Starts a drag on the seat. This starts an implicit keyboard grab, but doesn't * start a pointer or a touch grab. */ void wlr_seat_start_drag(struct wlr_seat *seat, struct wlr_drag *drag, uint32_t serial); /** * Starts a pointer drag on the seat. This starts implicit keyboard and pointer * grabs. */ void wlr_seat_start_pointer_drag(struct wlr_seat *seat, struct wlr_drag *drag, uint32_t serial); /** * Starts a touch drag on the seat. This starts implicit keyboard and touch * grabs. */ void wlr_seat_start_touch_drag(struct wlr_seat *seat, struct wlr_drag *drag, uint32_t serial, struct wlr_touch_point *point); /** * Initializes the data source with the provided implementation. */ void wlr_data_source_init(struct wlr_data_source *source, const struct wlr_data_source_impl *impl); /** * Sends the data as the specified MIME type over the passed file descriptor, * then close it. */ void wlr_data_source_send(struct wlr_data_source *source, const char *mime_type, int32_t fd); /** * Notifies the data source that a target accepts one of the offered MIME types. * If a target doesn't accept any of the offered types, `mime_type` is NULL. */ void wlr_data_source_accept(struct wlr_data_source *source, uint32_t serial, const char *mime_type); /** * Notifies the data source it is no longer valid and should be destroyed. That * destroys immediately the data source. */ void wlr_data_source_destroy(struct wlr_data_source *source); /** * Notifies the data source that the drop operation was performed. This does not * indicate acceptance. * * The data source may still be used in the future and should not be destroyed * here. */ void wlr_data_source_dnd_drop(struct wlr_data_source *source); /** * Notifies the data source that the drag-and-drop operation concluded. That * potentially destroys immediately the data source. */ void wlr_data_source_dnd_finish(struct wlr_data_source *source); /** * Notifies the data source that a target accepts the drag with the specified * action. * * This shouldn't be called after wlr_data_source_dnd_drop() unless the * drag-and-drop operation ended in an "ask" action. */ void wlr_data_source_dnd_action(struct wlr_data_source *source, enum wl_data_device_manager_dnd_action action); #endif wlroots-0.17.1/include/wlr/types/wlr_drm.h000066400000000000000000000022711454110342200205210ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_TYPES_WLR_DRM_H #define WLR_TYPES_WLR_DRM_H #include #include #include struct wlr_renderer; struct wlr_drm_buffer { struct wlr_buffer base; struct wl_resource *resource; // can be NULL if the client destroyed it struct wlr_dmabuf_attributes dmabuf; struct wl_listener release; }; /** * A stub implementation of Mesa's wl_drm protocol. * * It only implements the minimum necessary for modern clients to behave * properly. In particular, flink handles are left unimplemented. */ struct wlr_drm { struct wl_global *global; struct { struct wl_signal destroy; } events; // private state char *node_name; struct wlr_drm_format_set formats; struct wl_listener display_destroy; }; struct wlr_drm_buffer *wlr_drm_buffer_try_from_resource( struct wl_resource *resource); struct wlr_drm *wlr_drm_create(struct wl_display *display, struct wlr_renderer *renderer); #endif wlroots-0.17.1/include/wlr/types/wlr_drm_lease_v1.h000066400000000000000000000077731454110342200223140ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_TYPES_WLR_DRM_LEASE_V1_H #define WLR_TYPES_WLR_DRM_LEASE_V1_H #include struct wlr_backend; struct wlr_output; struct wlr_drm_lease_v1_manager { struct wl_list devices; // wlr_drm_lease_device_v1.link struct wl_display *display; struct wl_listener display_destroy; struct { /** * Upon receiving this signal, call * wlr_drm_lease_device_v1_grant_lease_request() to grant a lease of the * requested DRM resources, or * wlr_drm_lease_device_v1_reject_lease_request() to reject the request. */ struct wl_signal request; } events; }; struct wlr_drm_lease_device_v1 { struct wl_list resources; struct wl_global *global; struct wlr_drm_lease_v1_manager *manager; struct wlr_backend *backend; struct wl_list connectors; // wlr_drm_lease_connector_v1.link struct wl_list leases; // wlr_drm_lease_v1.link struct wl_list requests; // wlr_drm_lease_request_v1.link struct wl_list link; // wlr_drm_lease_v1_manager.devices struct wl_listener backend_destroy; void *data; }; struct wlr_drm_lease_v1; struct wlr_drm_lease_connector_v1 { struct wl_list resources; // wl_resource_get_link() struct wlr_output *output; struct wlr_drm_lease_device_v1 *device; /** NULL if no client is currently leasing this connector */ struct wlr_drm_lease_v1 *active_lease; struct wl_listener destroy; struct wl_list link; // wlr_drm_lease_device_v1.connectors }; struct wlr_drm_lease_request_v1 { struct wl_resource *resource; struct wlr_drm_lease_device_v1 *device; struct wlr_drm_lease_connector_v1 **connectors; size_t n_connectors; struct wl_resource *lease_resource; bool invalid; struct wl_list link; // wlr_drm_lease_device_v1.requests }; struct wlr_drm_lease_v1 { struct wl_resource *resource; struct wlr_drm_lease *drm_lease; struct wlr_drm_lease_device_v1 *device; struct wlr_drm_lease_connector_v1 **connectors; size_t n_connectors; struct wl_list link; // wlr_drm_lease_device_v1.leases struct wl_listener destroy; void *data; }; /** * Creates a DRM lease manager. A DRM lease device will be created for each * DRM backend supplied in case of a struct wlr_multi_backend. * * Returns NULL if no DRM backend is supplied. */ struct wlr_drm_lease_v1_manager *wlr_drm_lease_v1_manager_create( struct wl_display *display, struct wlr_backend *backend); /** * Offers a wlr_output for lease. * * Returns false if the output can't be offered to lease. */ bool wlr_drm_lease_v1_manager_offer_output( struct wlr_drm_lease_v1_manager *manager, struct wlr_output *output); /** * Withdraws a previously offered output for lease. If the output is leased to * a client, a finished event will be send and the lease will be terminated. */ void wlr_drm_lease_v1_manager_withdraw_output( struct wlr_drm_lease_v1_manager *manager, struct wlr_output *output); /** * Grants a client's lease request. The lease device will then provision the * DRM lease and transfer the file descriptor to the client. After calling this, * each struct wlr_output leased is destroyed, and will be re-issued through * wlr_backend.events.new_outputs when the lease is revoked. * * This will return NULL without leasing any resources if the lease is invalid; * this can happen for example if two clients request the same resources and an * attempt to grant both leases is made. */ struct wlr_drm_lease_v1 *wlr_drm_lease_request_v1_grant( struct wlr_drm_lease_request_v1 *request); /** * Rejects a client's lease request. The output will still be available to * lease until withdrawn by the compositor. */ void wlr_drm_lease_request_v1_reject(struct wlr_drm_lease_request_v1 *request); /** * Revokes a client's lease request. The output will still be available to * lease until withdrawn by the compositor. */ void wlr_drm_lease_v1_revoke(struct wlr_drm_lease_v1 *lease); #endif wlroots-0.17.1/include/wlr/types/wlr_export_dmabuf_v1.h000066400000000000000000000017751454110342200232140ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_TYPES_WLR_EXPORT_DMABUF_V1_H #define WLR_TYPES_WLR_EXPORT_DMABUF_V1_H #include #include #include struct wlr_export_dmabuf_manager_v1 { struct wl_global *global; struct wl_list frames; // wlr_export_dmabuf_frame_v1.link struct wl_listener display_destroy; struct { struct wl_signal destroy; } events; }; struct wlr_export_dmabuf_frame_v1 { struct wl_resource *resource; struct wlr_export_dmabuf_manager_v1 *manager; struct wl_list link; // wlr_export_dmabuf_manager_v1.frames struct wlr_output *output; bool cursor_locked; struct wl_listener output_commit; struct wl_listener output_destroy; }; struct wlr_export_dmabuf_manager_v1 *wlr_export_dmabuf_manager_v1_create( struct wl_display *display); #endif wlroots-0.17.1/include/wlr/types/wlr_foreign_toplevel_management_v1.h000066400000000000000000000120241454110342200261010ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_TYPES_WLR_FOREIGN_TOPLEVEL_MANAGEMENT_V1_H #define WLR_TYPES_WLR_FOREIGN_TOPLEVEL_MANAGEMENT_V1_H #include #include struct wlr_foreign_toplevel_manager_v1 { struct wl_event_loop *event_loop; struct wl_global *global; struct wl_list resources; // wl_resource_get_link() struct wl_list toplevels; // wlr_foreign_toplevel_handle_v1.link struct wl_listener display_destroy; struct { struct wl_signal destroy; } events; void *data; }; enum wlr_foreign_toplevel_handle_v1_state { WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MAXIMIZED = (1 << 0), WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MINIMIZED = (1 << 1), WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED = (1 << 2), WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN = (1 << 3), }; struct wlr_foreign_toplevel_handle_v1_output { struct wl_list link; // wlr_foreign_toplevel_handle_v1.outputs struct wlr_output *output; struct wlr_foreign_toplevel_handle_v1 *toplevel; // private state struct wl_listener output_bind; struct wl_listener output_destroy; }; struct wlr_foreign_toplevel_handle_v1 { struct wlr_foreign_toplevel_manager_v1 *manager; struct wl_list resources; struct wl_list link; struct wl_event_source *idle_source; char *title; char *app_id; struct wlr_foreign_toplevel_handle_v1 *parent; struct wl_list outputs; // wlr_foreign_toplevel_v1_output.link uint32_t state; // enum wlr_foreign_toplevel_v1_state struct { // struct wlr_foreign_toplevel_handle_v1_maximized_event struct wl_signal request_maximize; // struct wlr_foreign_toplevel_handle_v1_minimized_event struct wl_signal request_minimize; // struct wlr_foreign_toplevel_handle_v1_activated_event struct wl_signal request_activate; // struct wlr_foreign_toplevel_handle_v1_fullscreen_event struct wl_signal request_fullscreen; struct wl_signal request_close; // struct wlr_foreign_toplevel_handle_v1_set_rectangle_event struct wl_signal set_rectangle; struct wl_signal destroy; } events; void *data; }; struct wlr_foreign_toplevel_handle_v1_maximized_event { struct wlr_foreign_toplevel_handle_v1 *toplevel; bool maximized; }; struct wlr_foreign_toplevel_handle_v1_minimized_event { struct wlr_foreign_toplevel_handle_v1 *toplevel; bool minimized; }; struct wlr_foreign_toplevel_handle_v1_activated_event { struct wlr_foreign_toplevel_handle_v1 *toplevel; struct wlr_seat *seat; }; struct wlr_foreign_toplevel_handle_v1_fullscreen_event { struct wlr_foreign_toplevel_handle_v1 *toplevel; bool fullscreen; struct wlr_output *output; }; struct wlr_foreign_toplevel_handle_v1_set_rectangle_event { struct wlr_foreign_toplevel_handle_v1 *toplevel; struct wlr_surface *surface; int32_t x, y, width, height; }; struct wlr_foreign_toplevel_manager_v1 *wlr_foreign_toplevel_manager_v1_create( struct wl_display *display); struct wlr_foreign_toplevel_handle_v1 *wlr_foreign_toplevel_handle_v1_create( struct wlr_foreign_toplevel_manager_v1 *manager); /** * Destroy the given toplevel handle, sending the closed event to any * client. Also, if the destroyed toplevel is set as a parent of any * other valid toplevel, clients still holding a handle to both are * sent a parent signal with NULL parent. If this is not desired, the * caller should ensure that any child toplevels are destroyed before * the parent. */ void wlr_foreign_toplevel_handle_v1_destroy( struct wlr_foreign_toplevel_handle_v1 *toplevel); void wlr_foreign_toplevel_handle_v1_set_title( struct wlr_foreign_toplevel_handle_v1 *toplevel, const char *title); void wlr_foreign_toplevel_handle_v1_set_app_id( struct wlr_foreign_toplevel_handle_v1 *toplevel, const char *app_id); void wlr_foreign_toplevel_handle_v1_output_enter( struct wlr_foreign_toplevel_handle_v1 *toplevel, struct wlr_output *output); void wlr_foreign_toplevel_handle_v1_output_leave( struct wlr_foreign_toplevel_handle_v1 *toplevel, struct wlr_output *output); void wlr_foreign_toplevel_handle_v1_set_maximized( struct wlr_foreign_toplevel_handle_v1 *toplevel, bool maximized); void wlr_foreign_toplevel_handle_v1_set_minimized( struct wlr_foreign_toplevel_handle_v1 *toplevel, bool minimized); void wlr_foreign_toplevel_handle_v1_set_activated( struct wlr_foreign_toplevel_handle_v1 *toplevel, bool activated); void wlr_foreign_toplevel_handle_v1_set_fullscreen( struct wlr_foreign_toplevel_handle_v1* toplevel, bool fullscreen); /** * Set the parent of a toplevel. If the parent changed from its previous * value, also sends a parent event to all clients that hold handles to * both toplevel and parent (no message is sent to clients that have * previously destroyed their parent handle). NULL is allowed as the * parent, meaning no parent exists. */ void wlr_foreign_toplevel_handle_v1_set_parent( struct wlr_foreign_toplevel_handle_v1 *toplevel, struct wlr_foreign_toplevel_handle_v1 *parent); #endif wlroots-0.17.1/include/wlr/types/wlr_fractional_scale_v1.h000066400000000000000000000014041454110342200236330ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_TYPES_WLR_FRACTIONAL_SCALE_V1_H #define WLR_TYPES_WLR_FRACTIONAL_SCALE_V1_H #include struct wlr_surface; struct wlr_fractional_scale_manager_v1 { struct wl_global *global; struct { struct wl_signal destroy; } events; // private state struct wl_listener display_destroy; }; void wlr_fractional_scale_v1_notify_scale( struct wlr_surface *surface, double scale); struct wlr_fractional_scale_manager_v1 *wlr_fractional_scale_manager_v1_create( struct wl_display *display, uint32_t version); #endif wlroots-0.17.1/include/wlr/types/wlr_fullscreen_shell_v1.h000066400000000000000000000017311454110342200236760ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_TYPES_WLR_FULLSCREEN_SHELL_V1_H #define WLR_TYPES_WLR_FULLSCREEN_SHELL_V1_H #include #include "fullscreen-shell-unstable-v1-protocol.h" struct wlr_fullscreen_shell_v1 { struct wl_global *global; struct { struct wl_signal destroy; // struct wlr_fullscreen_shell_v1_present_surface_event struct wl_signal present_surface; } events; struct wl_listener display_destroy; void *data; }; struct wlr_fullscreen_shell_v1_present_surface_event { struct wl_client *client; struct wlr_surface *surface; // can be NULL enum zwp_fullscreen_shell_v1_present_method method; struct wlr_output *output; // can be NULL }; struct wlr_fullscreen_shell_v1 *wlr_fullscreen_shell_v1_create( struct wl_display *display); #endif wlroots-0.17.1/include/wlr/types/wlr_gamma_control_v1.h000066400000000000000000000024771454110342200231770ustar00rootroot00000000000000#ifndef WLR_TYPES_WLR_GAMMA_CONTROL_V1_H #define WLR_TYPES_WLR_GAMMA_CONTROL_V1_H #include struct wlr_output; struct wlr_output_state; struct wlr_gamma_control_manager_v1 { struct wl_global *global; struct wl_list controls; // wlr_gamma_control_v1.link struct wl_listener display_destroy; struct { struct wl_signal destroy; struct wl_signal set_gamma; // struct wlr_gamma_control_manager_v1_set_gamma_event } events; void *data; }; struct wlr_gamma_control_manager_v1_set_gamma_event { struct wlr_output *output; struct wlr_gamma_control_v1 *control; // may be NULL }; struct wlr_gamma_control_v1 { struct wl_resource *resource; struct wlr_output *output; struct wlr_gamma_control_manager_v1 *manager; struct wl_list link; uint16_t *table; size_t ramp_size; struct wl_listener output_destroy_listener; void *data; }; struct wlr_gamma_control_manager_v1 *wlr_gamma_control_manager_v1_create( struct wl_display *display); struct wlr_gamma_control_v1 *wlr_gamma_control_manager_v1_get_control( struct wlr_gamma_control_manager_v1 *manager, struct wlr_output *output); bool wlr_gamma_control_v1_apply(struct wlr_gamma_control_v1 *gamma_control, struct wlr_output_state *output_state); void wlr_gamma_control_v1_send_failed_and_destroy(struct wlr_gamma_control_v1 *gamma_control); #endif wlroots-0.17.1/include/wlr/types/wlr_idle_inhibit_v1.h000066400000000000000000000026741454110342200227770ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_TYPES_WLR_IDLE_INHIBIT_V1_H #define WLR_TYPES_WLR_IDLE_INHIBIT_V1_H #include /* This interface permits clients to inhibit the idle behavior such as * screenblanking, locking, and screensaving. * * This allows clients to ensure they stay visible instead of being hidden by * power-saving. * * Inhibitors are created for surfaces. They should only be in effect, while * this surface is visible. * The effect could also be limited to outputs it is displayed on (e.g. * dimm/dpms off outputs, except the one a video is displayed on). */ struct wlr_idle_inhibit_manager_v1 { struct wl_list inhibitors; // wlr_idle_inhibit_inhibitor_v1.link struct wl_global *global; struct wl_listener display_destroy; struct { struct wl_signal new_inhibitor; // struct wlr_idle_inhibitor_v1 struct wl_signal destroy; } events; void *data; }; struct wlr_idle_inhibitor_v1 { struct wlr_surface *surface; struct wl_resource *resource; struct wl_listener surface_destroy; struct wl_list link; // wlr_idle_inhibit_manager_v1.inhibitors struct { struct wl_signal destroy; } events; void *data; }; struct wlr_idle_inhibit_manager_v1 *wlr_idle_inhibit_v1_create(struct wl_display *display); #endif wlroots-0.17.1/include/wlr/types/wlr_idle_notify_v1.h000066400000000000000000000021331454110342200226470ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_TYPES_WLR_IDLE_NOTIFY_H #define WLR_TYPES_WLR_IDLE_NOTIFY_H #include struct wlr_seat; /** * An idle notifier, implementing the ext-idle-notify-v1 protocol. */ struct wlr_idle_notifier_v1; /** * Create the ext_idle_notifier_v1 global. */ struct wlr_idle_notifier_v1 *wlr_idle_notifier_v1_create(struct wl_display *display); /** * Inhibit idle. * * Compositors should call this function when the idle state is disabled, e.g. * because a visible client is using the idle-inhibit protocol. */ void wlr_idle_notifier_v1_set_inhibited(struct wlr_idle_notifier_v1 *notifier, bool inhibited); /** * Notify for user activity on a seat. * * Compositors should call this function whenever an input event is triggered * on a seat. */ void wlr_idle_notifier_v1_notify_activity(struct wlr_idle_notifier_v1 *notifier, struct wlr_seat *seat); #endif wlroots-0.17.1/include/wlr/types/wlr_input_device.h000066400000000000000000000014151454110342200224140ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_TYPES_WLR_INPUT_DEVICE_H #define WLR_TYPES_WLR_INPUT_DEVICE_H #include enum wlr_button_state { WLR_BUTTON_RELEASED, WLR_BUTTON_PRESSED, }; enum wlr_input_device_type { WLR_INPUT_DEVICE_KEYBOARD, WLR_INPUT_DEVICE_POINTER, WLR_INPUT_DEVICE_TOUCH, WLR_INPUT_DEVICE_TABLET_TOOL, WLR_INPUT_DEVICE_TABLET_PAD, WLR_INPUT_DEVICE_SWITCH, }; struct wlr_input_device { enum wlr_input_device_type type; unsigned int vendor, product; char *name; struct { struct wl_signal destroy; } events; void *data; }; #endif wlroots-0.17.1/include/wlr/types/wlr_input_inhibitor.h000066400000000000000000000016671454110342200231550ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_TYPES_INPUT_INHIBITOR_H #define WLR_TYPES_INPUT_INHIBITOR_H #include /* * NOTE: Following the protocol deprecation, wlr/types/wlr_input_inhibitor.h is * deprecated and will be removed in the next release. */ struct wlr_input_inhibit_manager { struct wl_global *global; struct wl_client *active_client; struct wl_resource *active_inhibitor; struct wl_listener display_destroy; struct { struct wl_signal activate; // struct wlr_input_inhibit_manager struct wl_signal deactivate; // struct wlr_input_inhibit_manager struct wl_signal destroy; } events; void *data; }; struct wlr_input_inhibit_manager *wlr_input_inhibit_manager_create( struct wl_display *display); #endif wlroots-0.17.1/include/wlr/types/wlr_input_method_v2.h000066400000000000000000000104261454110342200230460ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_TYPES_WLR_INPUT_METHOD_V2_H #define WLR_TYPES_WLR_INPUT_METHOD_V2_H #include #include #include #include #include struct wlr_input_method_v2_preedit_string { char *text; int32_t cursor_begin; int32_t cursor_end; }; struct wlr_input_method_v2_delete_surrounding_text { uint32_t before_length; uint32_t after_length; }; struct wlr_input_method_v2_state { struct wlr_input_method_v2_preedit_string preedit; char *commit_text; struct wlr_input_method_v2_delete_surrounding_text delete; }; struct wlr_input_method_v2 { struct wl_resource *resource; struct wlr_seat *seat; struct wlr_seat_client *seat_client; struct wlr_input_method_v2_state pending; struct wlr_input_method_v2_state current; bool active; // pending compositor-side state bool client_active; // state known to the client uint32_t current_serial; // received in last commit call struct wl_list popup_surfaces; struct wlr_input_method_keyboard_grab_v2 *keyboard_grab; struct wl_list link; struct wl_listener seat_client_destroy; struct { struct wl_signal commit; // struct wlr_input_method_v2 struct wl_signal new_popup_surface; // struct wlr_input_popup_surface_v2 struct wl_signal grab_keyboard; // struct wlr_input_method_keyboard_grab_v2 struct wl_signal destroy; // struct wlr_input_method_v2 } events; }; struct wlr_input_popup_surface_v2 { struct wl_resource *resource; struct wlr_input_method_v2 *input_method; struct wl_list link; struct wlr_surface *surface; struct { struct wl_signal destroy; } events; void *data; }; struct wlr_input_method_keyboard_grab_v2 { struct wl_resource *resource; struct wlr_input_method_v2 *input_method; struct wlr_keyboard *keyboard; struct wl_listener keyboard_keymap; struct wl_listener keyboard_repeat_info; struct wl_listener keyboard_destroy; struct { struct wl_signal destroy; // struct wlr_input_method_keyboard_grab_v2 } events; }; struct wlr_input_method_manager_v2 { struct wl_global *global; struct wl_list input_methods; // struct wlr_input_method_v2.link struct wl_listener display_destroy; struct { struct wl_signal input_method; // struct wlr_input_method_v2 struct wl_signal destroy; // struct wlr_input_method_manager_v2 } events; }; struct wlr_input_method_manager_v2 *wlr_input_method_manager_v2_create( struct wl_display *display); void wlr_input_method_v2_send_activate( struct wlr_input_method_v2 *input_method); void wlr_input_method_v2_send_deactivate( struct wlr_input_method_v2 *input_method); void wlr_input_method_v2_send_surrounding_text( struct wlr_input_method_v2 *input_method, const char *text, uint32_t cursor, uint32_t anchor); void wlr_input_method_v2_send_content_type( struct wlr_input_method_v2 *input_method, uint32_t hint, uint32_t purpose); void wlr_input_method_v2_send_text_change_cause( struct wlr_input_method_v2 *input_method, uint32_t cause); void wlr_input_method_v2_send_done(struct wlr_input_method_v2 *input_method); void wlr_input_method_v2_send_unavailable( struct wlr_input_method_v2 *input_method); /** * Get a struct wlr_input_popup_surface_v2 from a struct wlr_surface. * * Returns NULL if the surface has a different role or if the input popup * surface has been destroyed. */ struct wlr_input_popup_surface_v2 *wlr_input_popup_surface_v2_try_from_wlr_surface( struct wlr_surface *surface); void wlr_input_popup_surface_v2_send_text_input_rectangle( struct wlr_input_popup_surface_v2 *popup_surface, struct wlr_box *sbox); void wlr_input_method_keyboard_grab_v2_send_key( struct wlr_input_method_keyboard_grab_v2 *keyboard_grab, uint32_t time, uint32_t key, uint32_t state); void wlr_input_method_keyboard_grab_v2_send_modifiers( struct wlr_input_method_keyboard_grab_v2 *keyboard_grab, struct wlr_keyboard_modifiers *modifiers); void wlr_input_method_keyboard_grab_v2_set_keyboard( struct wlr_input_method_keyboard_grab_v2 *keyboard_grab, struct wlr_keyboard *keyboard); void wlr_input_method_keyboard_grab_v2_destroy( struct wlr_input_method_keyboard_grab_v2 *keyboard_grab); #endif wlroots-0.17.1/include/wlr/types/wlr_keyboard.h000066400000000000000000000066751454110342200215530ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_TYPES_WLR_KEYBOARD_H #define WLR_TYPES_WLR_KEYBOARD_H #include #include #include #include #include #include #define WLR_LED_COUNT 3 enum wlr_keyboard_led { WLR_LED_NUM_LOCK = 1 << 0, WLR_LED_CAPS_LOCK = 1 << 1, WLR_LED_SCROLL_LOCK = 1 << 2, }; #define WLR_MODIFIER_COUNT 8 enum wlr_keyboard_modifier { WLR_MODIFIER_SHIFT = 1 << 0, WLR_MODIFIER_CAPS = 1 << 1, WLR_MODIFIER_CTRL = 1 << 2, WLR_MODIFIER_ALT = 1 << 3, WLR_MODIFIER_MOD2 = 1 << 4, WLR_MODIFIER_MOD3 = 1 << 5, WLR_MODIFIER_LOGO = 1 << 6, WLR_MODIFIER_MOD5 = 1 << 7, }; #define WLR_KEYBOARD_KEYS_CAP 32 struct wlr_keyboard_impl; struct wlr_keyboard_modifiers { xkb_mod_mask_t depressed; xkb_mod_mask_t latched; xkb_mod_mask_t locked; xkb_mod_mask_t group; }; struct wlr_keyboard { struct wlr_input_device base; const struct wlr_keyboard_impl *impl; struct wlr_keyboard_group *group; char *keymap_string; size_t keymap_size; int keymap_fd; struct xkb_keymap *keymap; struct xkb_state *xkb_state; xkb_led_index_t led_indexes[WLR_LED_COUNT]; xkb_mod_index_t mod_indexes[WLR_MODIFIER_COUNT]; uint32_t leds; uint32_t keycodes[WLR_KEYBOARD_KEYS_CAP]; size_t num_keycodes; struct wlr_keyboard_modifiers modifiers; struct { int32_t rate; int32_t delay; } repeat_info; struct { /** * The `key` event signals with a struct wlr_keyboard_key_event that a * key has been pressed or released on the keyboard. This event is * emitted before the xkb state of the keyboard has been updated * (including modifiers). */ struct wl_signal key; /** * The `modifiers` event signals that the modifier state of the * struct wlr_keyboard has been updated. At this time, you can read the * modifier state of the struct wlr_keyboard and handle the updated * state by sending it to clients. */ struct wl_signal modifiers; struct wl_signal keymap; struct wl_signal repeat_info; } events; void *data; }; struct wlr_keyboard_key_event { uint32_t time_msec; uint32_t keycode; bool update_state; // if backend doesn't update modifiers on its own enum wl_keyboard_key_state state; }; /** * Get a struct wlr_keyboard from a struct wlr_input_device. * * Asserts that the input device is a keyboard. */ struct wlr_keyboard *wlr_keyboard_from_input_device( struct wlr_input_device *input_device); bool wlr_keyboard_set_keymap(struct wlr_keyboard *kb, struct xkb_keymap *keymap); bool wlr_keyboard_keymaps_match(struct xkb_keymap *km1, struct xkb_keymap *km2); /** * Set the keyboard repeat info. * * rate is in key repeats/second and delay is in milliseconds. */ void wlr_keyboard_set_repeat_info(struct wlr_keyboard *kb, int32_t rate_hz, int32_t delay_ms); /** * Update the LEDs on the device, if any. * * leds is a bitmask of enum wlr_keyboard_led. * * If the device doesn't have the provided LEDs, this function is a no-op. */ void wlr_keyboard_led_update(struct wlr_keyboard *keyboard, uint32_t leds); /** * Get the set of currently depressed or latched modifiers. * * A bitmask of enum wlr_keyboard_modifier is returned. */ uint32_t wlr_keyboard_get_modifiers(struct wlr_keyboard *keyboard); #endif wlroots-0.17.1/include/wlr/types/wlr_keyboard_group.h000066400000000000000000000037141454110342200227560ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_TYPES_WLR_KEYBOARD_GROUP_H #define WLR_TYPES_WLR_KEYBOARD_GROUP_H #include #include struct wlr_keyboard_group { struct wlr_keyboard keyboard; struct wl_list devices; // keyboard_group_device.link struct wl_list keys; // keyboard_group_key.link struct { /** * Sent when a keyboard has entered the group with keys currently * pressed that are not pressed by any other keyboard in the group. The * data for this signal will be a struct wl_array containing the key * codes. This should be used to update the compositor's internal state. * Bindings should not be triggered based off of these key codes and * they should also not notify any surfaces of the key press. */ struct wl_signal enter; /** * Sent when a keyboard has left the group with keys currently pressed * that are not pressed by any other keyboard in the group. The data for * this signal will be a struct wl_array containing the key codes. This * should be used to update the compositor's internal state. Bindings * should not be triggered based off of these key codes. Additionally, * surfaces should only be notified if they received a corresponding key * press for the key code. */ struct wl_signal leave; } events; void *data; }; struct wlr_keyboard_group *wlr_keyboard_group_create(void); struct wlr_keyboard_group *wlr_keyboard_group_from_wlr_keyboard( struct wlr_keyboard *keyboard); bool wlr_keyboard_group_add_keyboard(struct wlr_keyboard_group *group, struct wlr_keyboard *keyboard); void wlr_keyboard_group_remove_keyboard(struct wlr_keyboard_group *group, struct wlr_keyboard *keyboard); void wlr_keyboard_group_destroy(struct wlr_keyboard_group *group); #endif wlroots-0.17.1/include/wlr/types/wlr_keyboard_shortcuts_inhibit_v1.h000066400000000000000000000051731454110342200257750ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_TYPES_WLR_KEYBOARD_SHORTCUTS_INHIBIT_V1_H #define WLR_TYPES_WLR_KEYBOARD_SHORTCUTS_INHIBIT_V1_H #include #include /* This interface permits clients to inhibit keyboard shortcut processing by * the compositor. * * This allows clients to pass them on to e.g. remote desktops or virtual * machine guests. * * Inhibitors are created for surfaces and seats. They should only be in effect * while this surface has focus. */ struct wlr_keyboard_shortcuts_inhibit_manager_v1 { // wlr_keyboard_shortcuts_inhibitor_v1.link struct wl_list inhibitors; struct wl_global *global; struct wl_listener display_destroy; struct { struct wl_signal new_inhibitor; // struct wlr_keyboard_shortcuts_inhibitor_v1 struct wl_signal destroy; } events; void *data; }; struct wlr_keyboard_shortcuts_inhibitor_v1 { struct wlr_surface *surface; struct wlr_seat *seat; bool active; struct wl_resource *resource; struct wl_listener surface_destroy; struct wl_listener seat_destroy; // wlr_keyboard_shortcuts_inhibit_manager_v1.inhibitors struct wl_list link; struct { struct wl_signal destroy; } events; void *data; }; /* * A compositor creating a manager will handle the new_inhibitor event and call * wlr_keyboard_shortcuts_inhibitor_v1_activate() if it decides to honour the * inhibitor. This will send the active event to the client, confirming * activation of the inhibitor. From then on the compositor should respect the * inhibitor until it calls wlr_keyboard_shortcuts_inhibitor_v1_deactivate() to * suspend the inhibitor with an inactive event to the client or receives the * destroy signal from wlroots, telling it that the inhibitor has been * destroyed. * * Not sending the active event to the client is the only way under the * protocol to let the client know that the compositor will not be honouring an * inhibitor. It's the client's job to somehow deal with not receiving the * event, i.e. not assume that shortcuts are inhibited and maybe destroy the * pending and request a new inhibitor after a timeout. */ struct wlr_keyboard_shortcuts_inhibit_manager_v1 * wlr_keyboard_shortcuts_inhibit_v1_create(struct wl_display *display); void wlr_keyboard_shortcuts_inhibitor_v1_activate( struct wlr_keyboard_shortcuts_inhibitor_v1 *inhibitor); void wlr_keyboard_shortcuts_inhibitor_v1_deactivate( struct wlr_keyboard_shortcuts_inhibitor_v1 *inhibitor); #endif wlroots-0.17.1/include/wlr/types/wlr_layer_shell_v1.h000066400000000000000000000137361454110342200226600ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_TYPES_WLR_LAYER_SHELL_V1_H #define WLR_TYPES_WLR_LAYER_SHELL_V1_H #include #include #include #include #include "wlr-layer-shell-unstable-v1-protocol.h" /** * wlr_layer_shell_v1 allows clients to arrange themselves in "layers" on the * desktop in accordance with the wlr-layer-shell protocol. When a client is * added, the new_surface signal will be raised and passed a reference to our * struct wlr_layer_surface_v1. At this time, the client will have configured the * surface as it desires, including information like desired anchors and * margins. The compositor should use this information to decide how to arrange * the layer on-screen, then determine the dimensions of the layer and call * wlr_layer_surface_v1_configure(). The client will then attach a buffer and * commit the surface, at which point the wlr_layer_surface_v1 map signal is * raised and the compositor should begin rendering the surface. */ struct wlr_layer_shell_v1 { struct wl_global *global; struct wl_listener display_destroy; struct { // Note: the output may be NULL. In this case, it is your // responsibility to assign an output before returning. struct wl_signal new_surface; // struct wlr_layer_surface_v1 struct wl_signal destroy; } events; void *data; }; enum wlr_layer_surface_v1_state_field { WLR_LAYER_SURFACE_V1_STATE_DESIRED_SIZE = 1 << 0, WLR_LAYER_SURFACE_V1_STATE_ANCHOR = 1 << 1, WLR_LAYER_SURFACE_V1_STATE_EXCLUSIVE_ZONE = 1 << 2, WLR_LAYER_SURFACE_V1_STATE_MARGIN = 1 << 3, WLR_LAYER_SURFACE_V1_STATE_KEYBOARD_INTERACTIVITY = 1 << 4, WLR_LAYER_SURFACE_V1_STATE_LAYER = 1 << 5, }; struct wlr_layer_surface_v1_state { uint32_t committed; // enum wlr_layer_surface_v1_state_field uint32_t anchor; int32_t exclusive_zone; struct { int32_t top, right, bottom, left; } margin; enum zwlr_layer_surface_v1_keyboard_interactivity keyboard_interactive; uint32_t desired_width, desired_height; enum zwlr_layer_shell_v1_layer layer; uint32_t configure_serial; uint32_t actual_width, actual_height; }; struct wlr_layer_surface_v1_configure { struct wl_list link; // wlr_layer_surface_v1.configure_list uint32_t serial; uint32_t width, height; }; struct wlr_layer_surface_v1 { struct wlr_surface *surface; struct wlr_output *output; struct wl_resource *resource; struct wlr_layer_shell_v1 *shell; struct wl_list popups; // wlr_xdg_popup.link char *namespace; bool added, configured; struct wl_list configure_list; struct wlr_layer_surface_v1_state current, pending; // Whether the surface is ready to receive configure events bool initialized; // Whether the latest commit is an initial commit bool initial_commit; struct { /** * The destroy signal indicates that the struct wlr_layer_surface is * about to be freed. It is guaranteed that the unmap signal is raised * before the destroy signal if the layer surface is destroyed while * mapped. */ struct wl_signal destroy; /** * The new_popup signal is raised when a new popup is created. The data * parameter passed to the listener is a pointer to the new * struct wlr_xdg_popup. */ struct wl_signal new_popup; } events; void *data; }; struct wlr_layer_shell_v1 *wlr_layer_shell_v1_create(struct wl_display *display, uint32_t version); /** * Notifies the layer surface to configure itself with this width/height. The * layer_surface will signal its map event when the surface is ready to assume * this size. Returns the associated configure serial. */ uint32_t wlr_layer_surface_v1_configure(struct wlr_layer_surface_v1 *surface, uint32_t width, uint32_t height); /** * Notify the client that the surface has been closed and destroy the * struct wlr_layer_surface_v1, rendering the resource inert. */ void wlr_layer_surface_v1_destroy(struct wlr_layer_surface_v1 *surface); /** * Get a struct wlr_layer_surface from a struct wlr_surface. * * Returns NULL if the surface doesn't have the layer surface role or if * the layer surface has been destroyed. */ struct wlr_layer_surface_v1 *wlr_layer_surface_v1_try_from_wlr_surface( struct wlr_surface *surface); /** * Calls the iterator function for each mapped sub-surface and popup of this * surface (whether or not this surface is mapped). */ void wlr_layer_surface_v1_for_each_surface(struct wlr_layer_surface_v1 *surface, wlr_surface_iterator_func_t iterator, void *user_data); /** * Call `iterator` on each popup's surface and popup's subsurface in the * layer surface's tree, with the surfaces's position relative to the root * layer surface. The function is called from root to leaves (in rendering * order). */ void wlr_layer_surface_v1_for_each_popup_surface( struct wlr_layer_surface_v1 *surface, wlr_surface_iterator_func_t iterator, void *user_data); /** * Find a surface within this layer-surface tree at the given surface-local * coordinates. Returns the surface and coordinates in the leaf surface * coordinate system or NULL if no surface is found at that location. */ struct wlr_surface *wlr_layer_surface_v1_surface_at( struct wlr_layer_surface_v1 *surface, double sx, double sy, double *sub_x, double *sub_y); /** * Find a surface within this layer-surface's popup tree at the given * surface-local coordinates. Returns the surface and coordinates in the leaf * surface coordinate system or NULL if no surface is found at that location. */ struct wlr_surface *wlr_layer_surface_v1_popup_surface_at( struct wlr_layer_surface_v1 *surface, double sx, double sy, double *sub_x, double *sub_y); /** * Get the corresponding struct wlr_layer_surface_v1 from a resource. * * Aborts if the resource doesn't have the correct type. */ struct wlr_layer_surface_v1 *wlr_layer_surface_v1_from_resource( struct wl_resource *resource); #endif wlroots-0.17.1/include/wlr/types/wlr_linux_dmabuf_v1.h000066400000000000000000000073331454110342200230260ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_TYPES_WLR_LINUX_DMABUF_H #define WLR_TYPES_WLR_LINUX_DMABUF_H #include #include #include #include #include #include struct wlr_surface; struct wlr_dmabuf_v1_buffer { struct wlr_buffer base; struct wl_resource *resource; // can be NULL if the client destroyed it struct wlr_dmabuf_attributes attributes; // private state struct wl_listener release; }; /** * Returns the struct wlr_dmabuf_buffer if the given resource was created * via the linux-dmabuf buffer protocol or NULL otherwise. */ struct wlr_dmabuf_v1_buffer *wlr_dmabuf_v1_buffer_try_from_buffer_resource( struct wl_resource *buffer_resource); struct wlr_linux_dmabuf_feedback_v1 { dev_t main_device; struct wl_array tranches; // struct wlr_linux_dmabuf_feedback_v1_tranche }; struct wlr_linux_dmabuf_feedback_v1_tranche { dev_t target_device; uint32_t flags; // bitfield of enum zwp_linux_dmabuf_feedback_v1_tranche_flags struct wlr_drm_format_set formats; }; /* the protocol interface */ struct wlr_linux_dmabuf_v1 { struct wl_global *global; struct { struct wl_signal destroy; } events; // private state struct wlr_linux_dmabuf_feedback_v1_compiled *default_feedback; struct wlr_drm_format_set default_formats; // for legacy clients struct wl_list surfaces; // wlr_linux_dmabuf_v1_surface.link int main_device_fd; // to sanity check FDs sent by clients, -1 if unavailable struct wl_listener display_destroy; }; /** * Create the linux-dmabuf-unstable-v1 global. * * Compositors using struct wlr_renderer should use * wlr_linux_dmabuf_v1_create_with_renderer() instead. */ struct wlr_linux_dmabuf_v1 *wlr_linux_dmabuf_v1_create(struct wl_display *display, uint32_t version, const struct wlr_linux_dmabuf_feedback_v1 *default_feedback); /** * Create the linux-dmabuf-unstable-v1 global. * * The default DMA-BUF feedback is initialized from the struct wlr_renderer. */ struct wlr_linux_dmabuf_v1 *wlr_linux_dmabuf_v1_create_with_renderer(struct wl_display *display, uint32_t version, struct wlr_renderer *renderer); /** * Set a surface's DMA-BUF feedback. * * Passing a NULL feedback resets it to the default feedback. */ bool wlr_linux_dmabuf_v1_set_surface_feedback( struct wlr_linux_dmabuf_v1 *linux_dmabuf, struct wlr_surface *surface, const struct wlr_linux_dmabuf_feedback_v1 *feedback); /** * Append a tranche at the end of the DMA-BUF feedback list. * * Tranches must be added with decreasing priority. */ struct wlr_linux_dmabuf_feedback_v1_tranche *wlr_linux_dmabuf_feedback_add_tranche( struct wlr_linux_dmabuf_feedback_v1 *feedback); /** * Release resources allocated by a DMA-BUF feedback object. */ void wlr_linux_dmabuf_feedback_v1_finish(struct wlr_linux_dmabuf_feedback_v1 *feedback); struct wlr_linux_dmabuf_feedback_v1_init_options { // Main renderer used by the compositor struct wlr_renderer *main_renderer; // Output on which direct scan-out is possible on the primary plane, or NULL struct wlr_output *scanout_primary_output; // Output layer feedback event, or NULL const struct wlr_output_layer_feedback_event *output_layer_feedback_event; }; /** * Initialize a DMA-BUF feedback object with the provided options. * * The caller is responsible for calling wlr_linux_dmabuf_feedback_v1_finish() after use. */ bool wlr_linux_dmabuf_feedback_v1_init_with_options(struct wlr_linux_dmabuf_feedback_v1 *feedback, const struct wlr_linux_dmabuf_feedback_v1_init_options *options); #endif wlroots-0.17.1/include/wlr/types/wlr_matrix.h000066400000000000000000000035611454110342200212460ustar00rootroot00000000000000/* * This is a stable interface of wlroots. Future changes will be limited to: * * - New functions * - New struct members * - New enum members * * Note that wlroots does not make an ABI compatibility promise - in the future, * the layout and size of structs used by wlroots may change, requiring code * depending on this header to be recompiled (but not edited). * * Breaking changes are announced in the release notes and follow a 1-year * deprecation schedule. */ #ifndef WLR_TYPES_WLR_MATRIX_H #define WLR_TYPES_WLR_MATRIX_H #include struct wlr_box; /** Writes the identity matrix into mat */ void wlr_matrix_identity(float mat[static 9]); /** mat ← a × b */ void wlr_matrix_multiply(float mat[static 9], const float a[static 9], const float b[static 9]); void wlr_matrix_transpose(float mat[static 9], const float a[static 9]); /** Writes a 2D translation matrix to mat of magnitude (x, y) */ void wlr_matrix_translate(float mat[static 9], float x, float y); /** Writes a 2D scale matrix to mat of magnitude (x, y) */ void wlr_matrix_scale(float mat[static 9], float x, float y); /** Writes a 2D rotation matrix to mat at an angle of rad radians */ void wlr_matrix_rotate(float mat[static 9], float rad); /** Writes a transformation matrix which applies the specified * wl_output_transform to mat */ void wlr_matrix_transform(float mat[static 9], enum wl_output_transform transform); /** Shortcut for the various matrix operations involved in projecting the * specified wlr_box onto a given orthographic projection with a given * rotation. The result is written to mat, which can be applied to each * coordinate of the box to get a new coordinate from [-1,1]. */ void wlr_matrix_project_box(float mat[static 9], const struct wlr_box *box, enum wl_output_transform transform, float rotation, const float projection[static 9]); #endif wlroots-0.17.1/include/wlr/types/wlr_output.h000066400000000000000000000641751454110342200213120ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_TYPES_WLR_OUTPUT_H #define WLR_TYPES_WLR_OUTPUT_H #include #include #include #include #include #include #include #include enum wlr_output_mode_aspect_ratio { WLR_OUTPUT_MODE_ASPECT_RATIO_NONE, WLR_OUTPUT_MODE_ASPECT_RATIO_4_3, WLR_OUTPUT_MODE_ASPECT_RATIO_16_9, WLR_OUTPUT_MODE_ASPECT_RATIO_64_27, WLR_OUTPUT_MODE_ASPECT_RATIO_256_135, }; struct wlr_output_mode { int32_t width, height; int32_t refresh; // mHz bool preferred; enum wlr_output_mode_aspect_ratio picture_aspect_ratio; struct wl_list link; }; struct wlr_output_cursor { struct wlr_output *output; double x, y; bool enabled; bool visible; uint32_t width, height; struct wlr_fbox src_box; enum wl_output_transform transform; int32_t hotspot_x, hotspot_y; struct wlr_texture *texture; bool own_texture; struct wl_list link; }; enum wlr_output_adaptive_sync_status { WLR_OUTPUT_ADAPTIVE_SYNC_DISABLED, WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED, }; enum wlr_output_state_field { WLR_OUTPUT_STATE_BUFFER = 1 << 0, WLR_OUTPUT_STATE_DAMAGE = 1 << 1, WLR_OUTPUT_STATE_MODE = 1 << 2, WLR_OUTPUT_STATE_ENABLED = 1 << 3, WLR_OUTPUT_STATE_SCALE = 1 << 4, WLR_OUTPUT_STATE_TRANSFORM = 1 << 5, WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED = 1 << 6, WLR_OUTPUT_STATE_GAMMA_LUT = 1 << 7, WLR_OUTPUT_STATE_RENDER_FORMAT = 1 << 8, WLR_OUTPUT_STATE_SUBPIXEL = 1 << 9, WLR_OUTPUT_STATE_LAYERS = 1 << 10, }; enum wlr_output_state_mode_type { WLR_OUTPUT_STATE_MODE_FIXED, WLR_OUTPUT_STATE_MODE_CUSTOM, }; /** * Holds the double-buffered output state. */ struct wlr_output_state { uint32_t committed; // enum wlr_output_state_field // Set to true to allow output reconfiguration to occur which may result // in temporary output disruptions and content misrepresentations. bool allow_reconfiguration; pixman_region32_t damage; // output-buffer-local coordinates bool enabled; float scale; enum wl_output_transform transform; bool adaptive_sync_enabled; uint32_t render_format; enum wl_output_subpixel subpixel; struct wlr_buffer *buffer; /* Request a tearing page-flip. When enabled, this may cause the output to * display a part of the previous buffer and a part of the current buffer at * the same time. The backend may reject the commit if a tearing page-flip * cannot be performed, in which case the caller should fall back to a * regular page-flip at the next wlr_output.frame event. */ bool tearing_page_flip; enum wlr_output_state_mode_type mode_type; struct wlr_output_mode *mode; struct { int32_t width, height; int32_t refresh; // mHz, may be zero } custom_mode; uint16_t *gamma_lut; size_t gamma_lut_size; struct wlr_output_layer_state *layers; size_t layers_len; }; struct wlr_output_impl; struct wlr_render_pass; /** * A compositor output region. This typically corresponds to a monitor that * displays part of the compositor space. * * The `frame` event will be emitted when it is a good time for the compositor * to submit a new frame. * * To render a new frame, compositors should call wlr_output_attach_render(), * render and call wlr_output_commit(). No rendering should happen outside a * `frame` event handler or before wlr_output_attach_render(). */ struct wlr_output { const struct wlr_output_impl *impl; struct wlr_backend *backend; struct wl_display *display; struct wl_global *global; struct wl_list resources; char *name; char *description; // may be NULL char *make, *model, *serial; // may be NULL int32_t phys_width, phys_height; // mm // Note: some backends may have zero modes struct wl_list modes; // wlr_output_mode.link struct wlr_output_mode *current_mode; int32_t width, height; int32_t refresh; // mHz, may be zero bool enabled; float scale; enum wl_output_subpixel subpixel; enum wl_output_transform transform; enum wlr_output_adaptive_sync_status adaptive_sync_status; uint32_t render_format; bool needs_frame; // damage for cursors and fullscreen surface, in output-local coordinates bool frame_pending; float transform_matrix[9]; // true for example with VR headsets bool non_desktop; struct wlr_output_state pending; // Commit sequence number. Incremented on each commit, may overflow. uint32_t commit_seq; struct { // Request to render a frame struct wl_signal frame; // Emitted when software cursors or backend-specific logic damage the // output struct wl_signal damage; // struct wlr_output_event_damage // Emitted when a new frame needs to be committed (because of // backend-specific logic) struct wl_signal needs_frame; // Emitted right before commit struct wl_signal precommit; // struct wlr_output_event_precommit // Emitted right after commit struct wl_signal commit; // struct wlr_output_event_commit // Emitted right after a commit has been presented to the user for // enabled outputs struct wl_signal present; // struct wlr_output_event_present // Emitted after a client bound the wl_output global struct wl_signal bind; // struct wlr_output_event_bind struct wl_signal description; struct wl_signal request_state; // struct wlr_output_state struct wl_signal destroy; } events; struct wl_event_source *idle_frame; struct wl_event_source *idle_done; int attach_render_locks; // number of locks forcing rendering struct wl_list cursors; // wlr_output_cursor.link struct wlr_output_cursor *hardware_cursor; struct wlr_swapchain *cursor_swapchain; struct wlr_buffer *cursor_front_buffer; int software_cursor_locks; // number of locks forcing software cursors struct wl_list layers; // wlr_output_layer.link struct wlr_allocator *allocator; struct wlr_renderer *renderer; struct wlr_swapchain *swapchain; struct wlr_buffer *back_buffer; struct wl_listener display_destroy; struct wlr_addon_set addons; void *data; }; struct wlr_output_event_damage { struct wlr_output *output; const pixman_region32_t *damage; // output-buffer-local coordinates }; struct wlr_output_event_precommit { struct wlr_output *output; struct timespec *when; const struct wlr_output_state *state; }; struct wlr_output_event_commit { struct wlr_output *output; struct timespec *when; const struct wlr_output_state *state; }; enum wlr_output_present_flag { // The presentation was synchronized to the "vertical retrace" by the // display hardware such that tearing does not happen. WLR_OUTPUT_PRESENT_VSYNC = 0x1, // The display hardware provided measurements that the hardware driver // converted into a presentation timestamp. WLR_OUTPUT_PRESENT_HW_CLOCK = 0x2, // The display hardware signalled that it started using the new image // content. WLR_OUTPUT_PRESENT_HW_COMPLETION = 0x4, // The presentation of this update was done zero-copy. WLR_OUTPUT_PRESENT_ZERO_COPY = 0x8, }; struct wlr_output_event_present { struct wlr_output *output; // Frame submission for which this presentation event is for (see // wlr_output.commit_seq). uint32_t commit_seq; // Whether the frame was presented at all. bool presented; // Time when the content update turned into light the first time. struct timespec *when; // Vertical retrace counter. Zero if unavailable. unsigned seq; // Prediction of how many nanoseconds after `when` the very next output // refresh may occur. Zero if unknown. int refresh; // nsec uint32_t flags; // enum wlr_output_present_flag }; struct wlr_output_event_bind { struct wlr_output *output; struct wl_resource *resource; }; struct wlr_output_event_request_state { struct wlr_output *output; const struct wlr_output_state *state; }; struct wlr_surface; /** * Enables or disables the output. A disabled output is turned off and doesn't * emit `frame` events. * * Whether an output is enabled is double-buffered state, see * wlr_output_commit(). */ void wlr_output_enable(struct wlr_output *output, bool enable); void wlr_output_create_global(struct wlr_output *output); void wlr_output_destroy_global(struct wlr_output *output); /** * Initialize the output's rendering subsystem with the provided allocator and * renderer. After initialization, this function may invoked again to reinitialize * the allocator and renderer to different values. * * Call this function prior to any call to wlr_output_attach_render(), * wlr_output_commit() or wlr_output_cursor_create(). * * The buffer capabilities of the provided must match the capabilities of the * output's backend. Returns false otherwise. */ bool wlr_output_init_render(struct wlr_output *output, struct wlr_allocator *allocator, struct wlr_renderer *renderer); /** * Returns the preferred mode for this output. If the output doesn't support * modes, returns NULL. */ struct wlr_output_mode *wlr_output_preferred_mode(struct wlr_output *output); /** * Sets the output mode. The output needs to be enabled. * * Mode is double-buffered state, see wlr_output_commit(). */ void wlr_output_set_mode(struct wlr_output *output, struct wlr_output_mode *mode); /** * Sets a custom mode on the output. * * When the output advertises fixed modes, custom modes are not guaranteed to * work correctly, they may result in visual artifacts. If a suitable fixed mode * is available, compositors should prefer it and use wlr_output_set_mode() * instead of custom modes. * * Setting `refresh` to zero lets the backend pick a preferred value. The * output needs to be enabled. * * Custom mode is double-buffered state, see wlr_output_commit(). */ void wlr_output_set_custom_mode(struct wlr_output *output, int32_t width, int32_t height, int32_t refresh); /** * Sets a transform for the output. * * Transform is double-buffered state, see wlr_output_commit(). */ void wlr_output_set_transform(struct wlr_output *output, enum wl_output_transform transform); /** * Enables or disables adaptive sync (ie. variable refresh rate) on this * output. On some backends, this is just a hint and may be ignored. * Compositors can inspect `wlr_output.adaptive_sync_status` to query the * effective status. Backends that don't support adaptive sync will reject * the output commit. * * When enabled, compositors can submit frames a little bit later than the * deadline without dropping a frame. * * Adaptive sync is double-buffered state, see wlr_output_commit(). */ void wlr_output_enable_adaptive_sync(struct wlr_output *output, bool enabled); /** * Set the output buffer render format. Default value: DRM_FORMAT_XRGB8888 * * While high bit depth render formats are necessary for a monitor to receive * useful high bit data, they do not guarantee it; a DRM_FORMAT_XBGR2101010 * buffer will only lead to sending 10-bpc image data to the monitor if * hardware and software permit this. * * This only affects the format of the output buffer used when rendering, * as with wlr_output_attach_render(). It has no impact on the cursor buffer * format, or on the formats supported for direct scan-out (see also * wlr_output_attach_buffer()). * * This format is double-buffered state, see wlr_output_commit(). */ void wlr_output_set_render_format(struct wlr_output *output, uint32_t format); /** * Sets a scale for the output. * * Scale is double-buffered state, see wlr_output_commit(). */ void wlr_output_set_scale(struct wlr_output *output, float scale); void wlr_output_set_subpixel(struct wlr_output *output, enum wl_output_subpixel subpixel); /** * Set the output name. * * Output names are subject to the following rules: * * - Each output name must be unique. * - The name cannot change after the output has been advertised to clients. * * For more details, see the protocol documentation for wl_output.name. */ void wlr_output_set_name(struct wlr_output *output, const char *name); void wlr_output_set_description(struct wlr_output *output, const char *desc); /** * Schedule a done event. * * This is intended to be used by wl_output add-on interfaces. */ void wlr_output_schedule_done(struct wlr_output *output); void wlr_output_destroy(struct wlr_output *output); /** * Computes the transformed output resolution. */ void wlr_output_transformed_resolution(struct wlr_output *output, int *width, int *height); /** * Computes the transformed and scaled output resolution. */ void wlr_output_effective_resolution(struct wlr_output *output, int *width, int *height); /** * Attach the renderer's buffer to the output. Compositors must call this * function before rendering. After they are done rendering, they should call * wlr_output_commit() to submit the new frame. The output needs to be * enabled. * * If non-NULL, `buffer_age` is set to the drawing buffer age in number of * frames or -1 if unknown. This is useful for damage tracking. * * If the compositor decides not to render after calling this function, it * must call wlr_output_rollback(). */ bool wlr_output_attach_render(struct wlr_output *output, int *buffer_age); /** * Attach a buffer to the output. Compositors should call wlr_output_commit() * to submit the new frame. The output needs to be enabled. * * Not all backends support direct scan-out on all buffers. Compositors can * check whether a buffer is supported by calling wlr_output_test(). */ void wlr_output_attach_buffer(struct wlr_output *output, struct wlr_buffer *buffer); /** * Get the preferred format for reading pixels. * This function might change the current rendering context. */ uint32_t wlr_output_preferred_read_format(struct wlr_output *output); /** * Set the damage region for the frame to be submitted. This is the region of * the screen that has changed since the last frame. * * Compositors implementing damage tracking should call this function with the * damaged region in output-buffer-local coordinates. * * This region is not to be confused with the renderer's buffer damage, ie. the * region compositors need to repaint. Compositors usually need to repaint more * than what changed since last frame since multiple render buffers are used. */ void wlr_output_set_damage(struct wlr_output *output, const pixman_region32_t *damage); /** * Set the output layers state. * * See struct wlr_output_layer for more details on output layers. * * This state is double-buffered, see wlr_output_commit(). The layers array * must remain valid until the wlr_output_test() or wlr_output_commit() call. */ void wlr_output_set_layers(struct wlr_output *output, struct wlr_output_layer_state *layers, size_t layers_len); /** * Test whether the pending output state would be accepted by the backend. If * this function returns true, wlr_output_commit() can only fail due to a * runtime error. * * This function doesn't mutate the pending state. */ bool wlr_output_test(struct wlr_output *output); /** * Commit the pending output state. If wlr_output_attach_render() has been * called, the pending frame will be submitted for display and a `frame` event * will be scheduled. * * On failure, the pending changes are rolled back. */ bool wlr_output_commit(struct wlr_output *output); /** * Discard the pending output state. */ void wlr_output_rollback(struct wlr_output *output); /** * Test whether this output state would be accepted by the backend. If this * function returns true, wlr_output_commit_state() will only fail due to a * runtime error. This function does not change the current state of the * output. */ bool wlr_output_test_state(struct wlr_output *output, const struct wlr_output_state *state); /** * Attempts to apply the state to this output. This function may fail for any * reason and return false. If failed, none of the state would have been applied, * this function is atomic. If the commit succeeded, true is returned. * * Note: wlr_output_state_finish() would typically be called after the state * has been committed. */ bool wlr_output_commit_state(struct wlr_output *output, const struct wlr_output_state *state); /** * Manually schedules a `frame` event. If a `frame` event is already pending, * it is a no-op. */ void wlr_output_schedule_frame(struct wlr_output *output); /** * Returns the maximum length of each gamma ramp, or 0 if unsupported. */ size_t wlr_output_get_gamma_size(struct wlr_output *output); /** * Sets the gamma table for this output. `r`, `g` and `b` are gamma ramps for * red, green and blue. `size` is the length of the ramps and must not exceed * the value returned by wlr_output_get_gamma_size(). * * Providing zero-sized ramps resets the gamma table. * * The gamma table is double-buffered state, see wlr_output_commit(). */ void wlr_output_set_gamma(struct wlr_output *output, size_t size, const uint16_t *r, const uint16_t *g, const uint16_t *b); /** * Returns the wlr_output matching the provided wl_output resource. If the * resource isn't a wl_output, it aborts. If the resource is inert (because the * wlr_output has been destroyed), NULL is returned. */ struct wlr_output *wlr_output_from_resource(struct wl_resource *resource); /** * Locks the output to only use rendering instead of direct scan-out. This is * useful if direct scan-out needs to be temporarily disabled (e.g. during * screen capture). There must be as many unlocks as there have been locks to * restore the original state. There should never be an unlock before a lock. */ void wlr_output_lock_attach_render(struct wlr_output *output, bool lock); /** * Locks the output to only use software cursors instead of hardware cursors. * This is useful if hardware cursors need to be temporarily disabled (e.g. * during screen capture). There must be as many unlocks as there have been * locks to restore the original state. There should never be an unlock before * a lock. */ void wlr_output_lock_software_cursors(struct wlr_output *output, bool lock); /** * Renders software cursors. This is a utility function that can be called when * compositors render. */ void wlr_output_render_software_cursors(struct wlr_output *output, const pixman_region32_t *damage); /** * Render software cursors. * * This is a utility function that can be called when compositors render. */ void wlr_output_add_software_cursors_to_render_pass(struct wlr_output *output, struct wlr_render_pass *render_pass, const pixman_region32_t *damage); /** * Get the set of DRM formats suitable for the primary buffer, assuming a * buffer with the specified capabilities. * * NULL is returned if the backend doesn't have any format constraint, ie. all * formats are supported. An empty set is returned if the backend doesn't * support any format. */ const struct wlr_drm_format_set *wlr_output_get_primary_formats( struct wlr_output *output, uint32_t buffer_caps); /** * Check whether direct scan-out is allowed on the output. * * Direct scan-out is typically disallowed when there are software cursors or * during screen capture. */ bool wlr_output_is_direct_scanout_allowed(struct wlr_output *output); struct wlr_output_cursor *wlr_output_cursor_create(struct wlr_output *output); bool wlr_output_cursor_set_buffer(struct wlr_output_cursor *cursor, struct wlr_buffer *buffer, int32_t hotspot_x, int32_t hotspot_y); bool wlr_output_cursor_move(struct wlr_output_cursor *cursor, double x, double y); void wlr_output_cursor_destroy(struct wlr_output_cursor *cursor); /** * Initialize an output state. */ void wlr_output_state_init(struct wlr_output_state *state); /** * Releases all resources associated with an output state. */ void wlr_output_state_finish(struct wlr_output_state *state); /** * Enables or disables an output. A disabled output is turned off and doesn't * emit `frame` events. * * This state will be applied once wlr_output_commit_state() is called. */ void wlr_output_state_set_enabled(struct wlr_output_state *state, bool enabled); /** * Sets the output mode of an output. An output mode will specify the resolution * and refresh rate, among other things. * * This state will be applied once wlr_output_commit_state() is called. */ void wlr_output_state_set_mode(struct wlr_output_state *state, struct wlr_output_mode *mode); /** * Sets a custom output mode for an output. See wlr_output_state_set_mode() * for details. * * When the output advertises fixed modes, custom modes are not guaranteed to * work correctly, they may result in visual artifacts. If a suitable fixed mode * is available, compositors should prefer it and use wlr_output_state_set_mode() * instead of custom modes. * * Setting `refresh` to zero lets the backend pick a preferred value. The * output needs to be enabled. * * This state will be applied once wlr_output_commit_state() is called. */ void wlr_output_state_set_custom_mode(struct wlr_output_state *state, int32_t width, int32_t height, int32_t refresh); /** * Sets the scale of an output. The scale is used to increase the size of UI * elements to aid users who use high DPI outputs. * * This state will be applied once wlr_output_commit_state() is called. */ void wlr_output_state_set_scale(struct wlr_output_state *state, float scale); /** * Sets the transform of an output. The transform is used to rotate or flip * the contents of the screen. * * This state will be applied once wlr_output_commit_state() is called. */ void wlr_output_state_set_transform(struct wlr_output_state *state, enum wl_output_transform transform); /** * Enables or disable adaptive sync for an output (ie. variable refresh rate). * Compositors can inspect `wlr_output.adaptive_sync_status` to query the * effective status. Backends that don't support adaptive sync will reject the * output commit. * * This state will be applied once wlr_output_commit_state() is called. */ void wlr_output_state_set_adaptive_sync_enabled(struct wlr_output_state *state, bool enabled); /** * Sets the render format for an output. * * The default value is DRM_FORMAT_XRGB8888. * * While high bit depth render formats are necessary for a monitor to receive * useful high bit data, they do not guarantee it; a DRM_FORMAT_XBGR2101010 * buffer will only lead to sending 10-bpc image data to the monitor if * hardware and software permit this. * * This only affects the format of the output buffer used when rendering using * the output's own swapchain as with wlr_output_begin_render_pass(). It has no * impact on the cursor buffer format, or on the formats supported for direct * scan-out (see also wlr_output_state_set_buffer()). * * This state will be applied once wlr_output_commit_state() is called. */ void wlr_output_state_set_render_format(struct wlr_output_state *state, uint32_t format); /** * Sets the subpixel layout hint for an output. Note that this is only a hint * and may be ignored. The default value depends on the backend. * * This state will be applied once wlr_output_commit_state() is called. */ void wlr_output_state_set_subpixel(struct wlr_output_state *state, enum wl_output_subpixel subpixel); /** * Sets the buffer for an output. The buffer contains the contents of the * screen. If the compositor wishes to present a new frame, they must commit * with a buffer. * * This state will be applied once wlr_output_commit_state() is called. */ void wlr_output_state_set_buffer(struct wlr_output_state *state, struct wlr_buffer *buffer); /** * Sets the gamma table for an output. `r`, `g` and `b` are gamma ramps for * red, green and blue. `size` is the length of the ramps and must not exceed * the value returned by wlr_output_get_gamma_size(). * * Providing zero-sized ramps resets the gamma table. * * This state will be applied once wlr_output_commit_state() is called. */ bool wlr_output_state_set_gamma_lut(struct wlr_output_state *state, size_t ramp_size, const uint16_t *r, const uint16_t *g, const uint16_t *b); /** * Sets the damage region for an output. This is used as a hint to the backend * and can be used to reduce power consumption or increase performance on some * devices. * * This should be called in along with wlr_output_state_set_buffer(). * This state will be applied once wlr_output_commit_state() is called. */ void wlr_output_state_set_damage(struct wlr_output_state *state, const pixman_region32_t *damage); /** * Set the output layers for an output. Output layers are used to try and take * advantage of backend features that may reduce the amount of things that * need to be composited. * * The array must be kept valid by the caller until wlr_output_state_finish() * and all copies of the state have been finished as well. * This state will be applied once wlr_output_commit_state() is called. */ void wlr_output_state_set_layers(struct wlr_output_state *state, struct wlr_output_layer_state *layers, size_t layers_len); /** * Copies the output state from src to dst. It is safe to then * wlr_output_state_finish() src and have dst still be valid. * * Note: The lifetime of the output layers inside the state are not managed. It * is the responsibility of the constructor of the output layers to make sure * they remain valid for the output state and all copies made. */ bool wlr_output_state_copy(struct wlr_output_state *dst, const struct wlr_output_state *src); /** * Re-configure the swapchain as required for the output's primary buffer. * * If a NULL swapchain is passed in, a new swapchain is allocated. If the * swapchain is already suitable for the output's primary buffer, this function * is a no-op. * * The state describes the output changes the swapchain's buffers will be * committed with. A NULL state indicates no change. */ bool wlr_output_configure_primary_swapchain(struct wlr_output *output, const struct wlr_output_state *state, struct wlr_swapchain **swapchain); /** * Begin a render pass on this output. * * Same as wlr_output_attach_render(), but returns a struct wlr_render_pass. */ struct wlr_render_pass *wlr_output_begin_render_pass(struct wlr_output *output, struct wlr_output_state *state, int *buffer_age, struct wlr_render_timer *timer); /** * Returns the transform that, when composed with `tr`, gives * `WL_OUTPUT_TRANSFORM_NORMAL`. */ enum wl_output_transform wlr_output_transform_invert( enum wl_output_transform tr); /** * Returns a transform that, when applied, has the same effect as applying * sequentially `tr_a` and `tr_b`. */ enum wl_output_transform wlr_output_transform_compose( enum wl_output_transform tr_a, enum wl_output_transform tr_b); #endif wlroots-0.17.1/include/wlr/types/wlr_output_layer.h000066400000000000000000000062151454110342200224750ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_TYPES_WLR_OUTPUT_LAYER_H #define WLR_TYPES_WLR_OUTPUT_LAYER_H #include #include #include #include /** * An output layer. * * Output layers are displayed between the output primary buffer (see * wlr_output_attach_buffer() and wlr_output_attach_render()) and the cursor * buffer. They can offload some rendering work to the backend. * * To configure output layers, callers should call wlr_output_layer_create() to * create layers, attach struct wlr_output_layer_state onto * struct wlr_output_state via wlr_output_set_layers() to describe their new * state, and commit the output via wlr_output_commit(). * * Backends may have arbitrary limitations when it comes to displaying output * layers. Backends indicate whether or not a layer can be displayed via * wlr_output_layer_state.accepted after wlr_output_test() or * wlr_output_commit() is called. Compositors using the output layers API * directly are expected to setup layers, call wlr_output_test(), paint the * layers that the backend rejected with the renderer, then call * wlr_output_commit(). * * Callers are responsible for disabling output layers when they need the full * output contents to be composited onto a single buffer, e.g. during screen * capture. * * Callers must always include the state for all layers on output test/commit. */ struct wlr_output_layer { struct wl_list link; // wlr_output.layers struct wlr_addon_set addons; struct { struct wl_signal feedback; // struct wlr_output_layer_feedback_event } events; void *data; // private state struct wlr_fbox src_box; struct wlr_box dst_box; }; /** * State for an output layer. */ struct wlr_output_layer_state { struct wlr_output_layer *layer; // Buffer to display, or NULL to disable the layer struct wlr_buffer *buffer; // Source box, leave empty to use the whole buffer struct wlr_fbox src_box; // Destination box in output-buffer-local coordinates struct wlr_box dst_box; // Damaged region since last commit in buffer-local coordinates. Leave NULL // to damage the whole buffer. const pixman_region32_t *damage; // Populated by the backend after wlr_output_test() and wlr_output_commit(), // indicates whether the backend has acknowledged and will take care of // displaying the layer bool accepted; }; /** * Feedback for an output layer. * * After an output commit, if the backend is not able to display a layer, it * can send feedback events. These events can be used to re-allocate the * layer's buffers so that they have a higher chance to get displayed. */ struct wlr_output_layer_feedback_event { dev_t target_device; const struct wlr_drm_format_set *formats; }; /** * Create a new output layer. */ struct wlr_output_layer *wlr_output_layer_create(struct wlr_output *output); /** * Destroy an output layer. */ void wlr_output_layer_destroy(struct wlr_output_layer *layer); #endif wlroots-0.17.1/include/wlr/types/wlr_output_layout.h000066400000000000000000000116461454110342200227020ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_TYPES_WLR_OUTPUT_LAYOUT_H #define WLR_TYPES_WLR_OUTPUT_LAYOUT_H #include #include #include #include struct wlr_box; /** * Helper to arrange outputs in a 2D coordinate space. The output effective * resolution is used, see wlr_output_effective_resolution(). * * Outputs added to the output layout are automatically exposed to clients (see * wlr_output_create_global()). They are no longer exposed when removed from the * layout. */ struct wlr_output_layout { struct wl_list outputs; struct { struct wl_signal add; // struct wlr_output_layout_output struct wl_signal change; struct wl_signal destroy; } events; void *data; }; struct wlr_output_layout_output { struct wlr_output_layout *layout; struct wlr_output *output; int x, y; struct wl_list link; bool auto_configured; struct { struct wl_signal destroy; } events; // private state struct wlr_addon addon; struct wl_listener commit; }; struct wlr_output_layout *wlr_output_layout_create(void); void wlr_output_layout_destroy(struct wlr_output_layout *layout); /** * Get the output layout for the specified output. Returns NULL if no output * matches. */ struct wlr_output_layout_output *wlr_output_layout_get( struct wlr_output_layout *layout, struct wlr_output *reference); /** * Get the output at the specified layout coordinates. Returns NULL if no * output matches the coordinates. */ struct wlr_output *wlr_output_layout_output_at( struct wlr_output_layout *layout, double lx, double ly); /** * Add the output to the layout at the specified coordinates. If the output is * already a part of the output layout, it will become manually configured and * will be moved to the specified coordinates. * * Returns true on success, false on a memory allocation error. */ struct wlr_output_layout_output *wlr_output_layout_add(struct wlr_output_layout *layout, struct wlr_output *output, int lx, int ly); /** * Add the output to the layout as automatically configured. This will place * the output in a sensible location in the layout. The coordinates of * the output in the layout will be adjusted dynamically when the layout * changes. If the output is already a part of the layout, it will become * automatically configured. * * Returns true on success, false on a memory allocation error. */ struct wlr_output_layout_output *wlr_output_layout_add_auto(struct wlr_output_layout *layout, struct wlr_output *output); /** * Remove the output from the layout. If the output is already not a part of * the layout, this function is a no-op. */ void wlr_output_layout_remove(struct wlr_output_layout *layout, struct wlr_output *output); /** * Given x and y in layout coordinates, adjusts them to local output * coordinates relative to the given reference output. */ void wlr_output_layout_output_coords(struct wlr_output_layout *layout, struct wlr_output *reference, double *lx, double *ly); bool wlr_output_layout_contains_point(struct wlr_output_layout *layout, struct wlr_output *reference, int lx, int ly); bool wlr_output_layout_intersects(struct wlr_output_layout *layout, struct wlr_output *reference, const struct wlr_box *target_lbox); /** * Get the closest point on this layout from the given point from the reference * output. If reference is NULL, gets the closest point from the entire layout. * If the layout is empty, the result is the given point itself. */ void wlr_output_layout_closest_point(struct wlr_output_layout *layout, struct wlr_output *reference, double lx, double ly, double *dest_lx, double *dest_ly); /** * Get the box of the layout for the given reference output in layout * coordinates. If `reference` is NULL, the box will be for the extents of the * entire layout. If the output isn't in the layout, the box will be empty. */ void wlr_output_layout_get_box(struct wlr_output_layout *layout, struct wlr_output *reference, struct wlr_box *dest_box); /** * Get the output closest to the center of the layout extents. */ struct wlr_output *wlr_output_layout_get_center_output( struct wlr_output_layout *layout); enum wlr_direction { WLR_DIRECTION_UP = 1 << 0, WLR_DIRECTION_DOWN = 1 << 1, WLR_DIRECTION_LEFT = 1 << 2, WLR_DIRECTION_RIGHT = 1 << 3, }; /** * Get the closest adjacent output to the reference output from the reference * point in the given direction. */ struct wlr_output *wlr_output_layout_adjacent_output( struct wlr_output_layout *layout, enum wlr_direction direction, struct wlr_output *reference, double ref_lx, double ref_ly); struct wlr_output *wlr_output_layout_farthest_output( struct wlr_output_layout *layout, enum wlr_direction direction, struct wlr_output *reference, double ref_lx, double ref_ly); #endif wlroots-0.17.1/include/wlr/types/wlr_output_management_v1.h000066400000000000000000000115071454110342200241030ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_TYPES_WLR_OUTPUT_MANAGEMENT_V1_H #define WLR_TYPES_WLR_OUTPUT_MANAGEMENT_V1_H #include #include #include struct wlr_output_manager_v1 { struct wl_display *display; struct wl_global *global; struct wl_list resources; // wl_resource_get_link() struct wl_list heads; // wlr_output_head_v1.link uint32_t serial; bool current_configuration_dirty; struct { /** * The `apply` and `test` events are emitted when a client requests a * configuration to be applied or tested. The compositor should send * feedback with `wlr_output_configuration_v1_send_succeeded` xor * `wlr_output_configuration_v1_send_failed`. * * The compositor gains ownership over the configuration (passed as the * event data). That is, the compositor is responsible for destroying * the configuration. */ struct wl_signal apply; // struct wlr_output_configuration_v1 struct wl_signal test; // struct wlr_output_configuration_v1 struct wl_signal destroy; } events; struct wl_listener display_destroy; void *data; }; struct wlr_output_head_v1_state { struct wlr_output *output; bool enabled; struct wlr_output_mode *mode; struct { int32_t width, height; int32_t refresh; } custom_mode; int32_t x, y; enum wl_output_transform transform; float scale; bool adaptive_sync_enabled; }; struct wlr_output_head_v1 { struct wlr_output_head_v1_state state; struct wlr_output_manager_v1 *manager; struct wl_list link; // wlr_output_manager_v1.heads struct wl_list resources; // wl_resource_get_link() struct wl_list mode_resources; // wl_resource_get_link() struct wl_listener output_destroy; }; struct wlr_output_configuration_v1 { struct wl_list heads; // wlr_output_configuration_head_v1.link // client state struct wlr_output_manager_v1 *manager; uint32_t serial; bool finalized; // client has requested to apply the config bool finished; // feedback has been sent by the compositor struct wl_resource *resource; // can be NULL if destroyed early }; struct wlr_output_configuration_head_v1 { struct wlr_output_head_v1_state state; struct wlr_output_configuration_v1 *config; struct wl_list link; // wlr_output_configuration_v1.heads // client state struct wl_resource *resource; // can be NULL if finalized or disabled struct wl_listener output_destroy; }; /** * Create a new output manager. The compositor is responsible for calling * wlr_output_manager_v1_set_configuration() whenever the current output * configuration changes. */ struct wlr_output_manager_v1 *wlr_output_manager_v1_create( struct wl_display *display); /** * Updates the output manager's current configuration. This will broadcast any * changes to all clients. * * This function takes ownership over `config`. That is, the compositor must not * access the configuration anymore. */ void wlr_output_manager_v1_set_configuration( struct wlr_output_manager_v1 *manager, struct wlr_output_configuration_v1 *config); /** * Create a new, empty output configuration. Compositors should add current head * status with wlr_output_configuration_head_v1_create(). They can then call * wlr_output_manager_v1_set_configuration(). */ struct wlr_output_configuration_v1 *wlr_output_configuration_v1_create(void); void wlr_output_configuration_v1_destroy( struct wlr_output_configuration_v1 *config); /** * If the configuration comes from a client request, this sends positive * feedback to the client (configuration has been applied). */ void wlr_output_configuration_v1_send_succeeded( struct wlr_output_configuration_v1 *config); /** * If the configuration comes from a client request, this sends negative * feedback to the client (configuration has not been applied). */ void wlr_output_configuration_v1_send_failed( struct wlr_output_configuration_v1 *config); /** * Create a new configuration head for the given output. This adds the head to * the provided output configuration. * * The configuration head will be pre-filled with data from `output`. The * compositor should adjust this data according to its current internal state. */ struct wlr_output_configuration_head_v1 * wlr_output_configuration_head_v1_create( struct wlr_output_configuration_v1 *config, struct wlr_output *output); /** * Apply the head state on the supplied struct wlr_output_state. * * Compositors can then pass the resulting struct wlr_output_state to * wlr_output_commit_state() or wlr_output_test_state(). * * The position needs to be applied manually by the caller. */ void wlr_output_head_v1_state_apply( const struct wlr_output_head_v1_state *head_state, struct wlr_output_state *output_state); #endif wlroots-0.17.1/include/wlr/types/wlr_output_power_management_v1.h000066400000000000000000000020131454110342200253070ustar00rootroot00000000000000#ifndef WLR_TYPES_WLR_OUTPUT_POWER_MANAGEMENT_V1_H #define WLR_TYPES_WLR_OUTPUT_POWER_MANAGEMENT_V1_H #include #include "wlr-output-power-management-unstable-v1-protocol.h" struct wlr_output_power_manager_v1 { struct wl_global *global; struct wl_list output_powers; // wlr_output_power_v1.link struct wl_listener display_destroy; struct { struct wl_signal set_mode; // struct wlr_output_power_v1_set_mode_event struct wl_signal destroy; } events; void *data; }; struct wlr_output_power_v1 { struct wl_resource *resource; struct wlr_output *output; struct wlr_output_power_manager_v1 *manager; struct wl_list link; // wlr_output_power_manager_v1.output_powers struct wl_listener output_destroy_listener; struct wl_listener output_commit_listener; void *data; }; struct wlr_output_power_v1_set_mode_event { struct wlr_output *output; enum zwlr_output_power_v1_mode mode; }; struct wlr_output_power_manager_v1 *wlr_output_power_manager_v1_create( struct wl_display *display); #endif wlroots-0.17.1/include/wlr/types/wlr_pointer.h000066400000000000000000000073601454110342200214230ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_TYPES_WLR_POINTER_H #define WLR_TYPES_WLR_POINTER_H #include #include #include #include struct wlr_pointer_impl; struct wlr_pointer { struct wlr_input_device base; const struct wlr_pointer_impl *impl; char *output_name; struct { struct wl_signal motion; // struct wlr_pointer_motion_event struct wl_signal motion_absolute; // struct wlr_pointer_motion_absolute_event struct wl_signal button; // struct wlr_pointer_button_event struct wl_signal axis; // struct wlr_pointer_axis_event struct wl_signal frame; struct wl_signal swipe_begin; // struct wlr_pointer_swipe_begin_event struct wl_signal swipe_update; // struct wlr_pointer_swipe_update_event struct wl_signal swipe_end; // struct wlr_pointer_swipe_end_event struct wl_signal pinch_begin; // struct wlr_pointer_pinch_begin_event struct wl_signal pinch_update; // struct wlr_pointer_pinch_update_event struct wl_signal pinch_end; // struct wlr_pointer_pinch_end_event struct wl_signal hold_begin; // struct wlr_pointer_hold_begin_event struct wl_signal hold_end; // struct wlr_pointer_hold_end_event } events; void *data; }; struct wlr_pointer_motion_event { struct wlr_pointer *pointer; uint32_t time_msec; double delta_x, delta_y; double unaccel_dx, unaccel_dy; }; struct wlr_pointer_motion_absolute_event { struct wlr_pointer *pointer; uint32_t time_msec; // From 0..1 double x, y; }; struct wlr_pointer_button_event { struct wlr_pointer *pointer; uint32_t time_msec; uint32_t button; enum wlr_button_state state; }; enum wlr_axis_source { WLR_AXIS_SOURCE_WHEEL, WLR_AXIS_SOURCE_FINGER, WLR_AXIS_SOURCE_CONTINUOUS, WLR_AXIS_SOURCE_WHEEL_TILT, }; enum wlr_axis_orientation { WLR_AXIS_ORIENTATION_VERTICAL, WLR_AXIS_ORIENTATION_HORIZONTAL, }; #define WLR_POINTER_AXIS_DISCRETE_STEP 120 struct wlr_pointer_axis_event { struct wlr_pointer *pointer; uint32_t time_msec; enum wlr_axis_source source; enum wlr_axis_orientation orientation; double delta; int32_t delta_discrete; }; struct wlr_pointer_swipe_begin_event { struct wlr_pointer *pointer; uint32_t time_msec; uint32_t fingers; }; struct wlr_pointer_swipe_update_event { struct wlr_pointer *pointer; uint32_t time_msec; uint32_t fingers; // Relative coordinates of the logical center of the gesture // compared to the previous event. double dx, dy; }; struct wlr_pointer_swipe_end_event { struct wlr_pointer *pointer; uint32_t time_msec; bool cancelled; }; struct wlr_pointer_pinch_begin_event { struct wlr_pointer *pointer; uint32_t time_msec; uint32_t fingers; }; struct wlr_pointer_pinch_update_event { struct wlr_pointer *pointer; uint32_t time_msec; uint32_t fingers; // Relative coordinates of the logical center of the gesture // compared to the previous event. double dx, dy; // Absolute scale compared to the begin event double scale; // Relative angle in degrees clockwise compared to the previous event. double rotation; }; struct wlr_pointer_pinch_end_event { struct wlr_pointer *pointer; uint32_t time_msec; bool cancelled; }; struct wlr_pointer_hold_begin_event { struct wlr_pointer *pointer; uint32_t time_msec; uint32_t fingers; }; struct wlr_pointer_hold_end_event { struct wlr_pointer *pointer; uint32_t time_msec; bool cancelled; }; /** * Get a struct wlr_pointer from a struct wlr_input_device. * * Asserts that the input device is a pointer. */ struct wlr_pointer *wlr_pointer_from_input_device( struct wlr_input_device *input_device); #endif wlroots-0.17.1/include/wlr/types/wlr_pointer_constraints_v1.h000066400000000000000000000051231454110342200244530ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_TYPES_WLR_POINTER_CONSTRAINTS_V1_H #define WLR_TYPES_WLR_POINTER_CONSTRAINTS_V1_H #include #include #include #include #include "pointer-constraints-unstable-v1-protocol.h" struct wlr_seat; enum wlr_pointer_constraint_v1_type { WLR_POINTER_CONSTRAINT_V1_LOCKED, WLR_POINTER_CONSTRAINT_V1_CONFINED, }; enum wlr_pointer_constraint_v1_state_field { WLR_POINTER_CONSTRAINT_V1_STATE_REGION = 1 << 0, WLR_POINTER_CONSTRAINT_V1_STATE_CURSOR_HINT = 1 << 1, }; struct wlr_pointer_constraint_v1_state { uint32_t committed; // enum wlr_pointer_constraint_v1_state_field pixman_region32_t region; // only valid for locked_pointer struct { double x, y; } cursor_hint; }; struct wlr_pointer_constraint_v1 { struct wlr_pointer_constraints_v1 *pointer_constraints; struct wl_resource *resource; struct wlr_surface *surface; struct wlr_seat *seat; enum zwp_pointer_constraints_v1_lifetime lifetime; enum wlr_pointer_constraint_v1_type type; pixman_region32_t region; struct wlr_pointer_constraint_v1_state current, pending; struct wl_listener surface_commit; struct wl_listener surface_destroy; struct wl_listener seat_destroy; struct wl_list link; // wlr_pointer_constraints_v1.constraints struct { /** * Called when a pointer constraint's region is updated, * post-surface-commit. */ struct wl_signal set_region; struct wl_signal destroy; } events; void *data; }; struct wlr_pointer_constraints_v1 { struct wl_global *global; struct wl_list constraints; // wlr_pointer_constraint_v1.link struct { /** * Called when a new pointer constraint is created. * * The data pointer is a struct wlr_pointer_constraint_v1. */ struct wl_signal new_constraint; } events; struct wl_listener display_destroy; void *data; }; struct wlr_pointer_constraints_v1 *wlr_pointer_constraints_v1_create( struct wl_display *display); struct wlr_pointer_constraint_v1 * wlr_pointer_constraints_v1_constraint_for_surface( struct wlr_pointer_constraints_v1 *pointer_constraints, struct wlr_surface *surface, struct wlr_seat *seat); void wlr_pointer_constraint_v1_send_activated( struct wlr_pointer_constraint_v1 *constraint); /** * Deactivate the constraint. May destroy the constraint. */ void wlr_pointer_constraint_v1_send_deactivated( struct wlr_pointer_constraint_v1 *constraint); #endif wlroots-0.17.1/include/wlr/types/wlr_pointer_gestures_v1.h000066400000000000000000000040521454110342200237450ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_TYPES_WLR_POINTER_GESTURES_V1_H #define WLR_TYPES_WLR_POINTER_GESTURES_V1_H #include #include struct wlr_surface; struct wlr_pointer_gestures_v1 { struct wl_global *global; struct wl_list swipes; // wl_resource_get_link() struct wl_list pinches; // wl_resource_get_link() struct wl_list holds; // wl_resource_get_link() struct wl_listener display_destroy; struct { struct wl_signal destroy; } events; void *data; }; struct wlr_pointer_gestures_v1 *wlr_pointer_gestures_v1_create( struct wl_display *display); void wlr_pointer_gestures_v1_send_swipe_begin( struct wlr_pointer_gestures_v1 *gestures, struct wlr_seat *seat, uint32_t time_msec, uint32_t fingers); void wlr_pointer_gestures_v1_send_swipe_update( struct wlr_pointer_gestures_v1 *gestures, struct wlr_seat *seat, uint32_t time_msec, double dx, double dy); void wlr_pointer_gestures_v1_send_swipe_end( struct wlr_pointer_gestures_v1 *gestures, struct wlr_seat *seat, uint32_t time_msec, bool cancelled); void wlr_pointer_gestures_v1_send_pinch_begin( struct wlr_pointer_gestures_v1 *gestures, struct wlr_seat *seat, uint32_t time_msec, uint32_t fingers); void wlr_pointer_gestures_v1_send_pinch_update( struct wlr_pointer_gestures_v1 *gestures, struct wlr_seat *seat, uint32_t time_msec, double dx, double dy, double scale, double rotation); void wlr_pointer_gestures_v1_send_pinch_end( struct wlr_pointer_gestures_v1 *gestures, struct wlr_seat *seat, uint32_t time_msec, bool cancelled); void wlr_pointer_gestures_v1_send_hold_begin( struct wlr_pointer_gestures_v1 *gestures, struct wlr_seat *seat, uint32_t time_msec, uint32_t fingers); void wlr_pointer_gestures_v1_send_hold_end( struct wlr_pointer_gestures_v1 *gestures, struct wlr_seat *seat, uint32_t time_msec, bool cancelled); #endif wlroots-0.17.1/include/wlr/types/wlr_presentation_time.h000066400000000000000000000065371454110342200235010ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_TYPES_WLR_PRESENTATION_TIME_H #define WLR_TYPES_WLR_PRESENTATION_TIME_H #include #include #include #include struct wlr_surface; struct wlr_output; struct wlr_output_event_present; struct wlr_presentation { struct wl_global *global; struct { struct wl_signal destroy; } events; struct wl_listener display_destroy; }; struct wlr_presentation_feedback { struct wl_list resources; // wl_resource_get_link() // Only when the wlr_presentation_surface_textured_on_output() or // wlr_presentation_surface_scanned_out_on_output() helper has been called. struct wlr_output *output; bool output_committed; uint32_t output_commit_seq; bool zero_copy; struct wl_listener output_commit; struct wl_listener output_present; struct wl_listener output_destroy; }; struct wlr_presentation_event { struct wlr_output *output; uint64_t tv_sec; uint32_t tv_nsec; uint32_t refresh; uint64_t seq; uint32_t flags; // enum wp_presentation_feedback_kind }; struct wlr_backend; struct wlr_presentation *wlr_presentation_create(struct wl_display *display, struct wlr_backend *backend); /** * Mark the current surface's buffer as sampled. * * The compositor must call this function when it uses the surface's current * contents (e.g. when rendering the surface's current texture, when * referencing its current buffer, or when directly scanning out its current * buffer). A wlr_presentation_feedback is returned. The compositor should call * wlr_presentation_feedback_send_presented() if this content has been displayed, * then wlr_presentation_feedback_destroy(). * * NULL is returned if the client hasn't requested presentation feedback for * this surface. */ struct wlr_presentation_feedback *wlr_presentation_surface_sampled( struct wlr_presentation *presentation, struct wlr_surface *surface); void wlr_presentation_feedback_send_presented( struct wlr_presentation_feedback *feedback, const struct wlr_presentation_event *event); void wlr_presentation_feedback_destroy( struct wlr_presentation_feedback *feedback); /** * Fill a wlr_presentation_event from a struct wlr_output_event_present. */ void wlr_presentation_event_from_output(struct wlr_presentation_event *event, const struct wlr_output_event_present *output_event); /** * Mark the current surface's buffer as textured on the given output. * * Instead of calling wlr_presentation_surface_sampled() and managing the * struct wlr_presentation_feedback itself, the compositor can call this function * before a wlr_output_commit() call to indicate that the surface's current * contents have been copied to a buffer which will be displayed on the output. */ void wlr_presentation_surface_textured_on_output( struct wlr_presentation *presentation, struct wlr_surface *surface, struct wlr_output *output); /** * Mark the current surface's buffer as scanned out on the given output. * * Same as wlr_presentation_surface_textured_on_output(), but indicates direct * scan-out. */ void wlr_presentation_surface_scanned_out_on_output( struct wlr_presentation *presentation, struct wlr_surface *surface, struct wlr_output *output); #endif wlroots-0.17.1/include/wlr/types/wlr_primary_selection.h000066400000000000000000000037711454110342200234750ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_TYPES_WLR_PRIMARY_SELECTION_H #define WLR_TYPES_WLR_PRIMARY_SELECTION_H #include #include struct wlr_primary_selection_source; /** * A data source implementation. Only the `send` function is mandatory. */ struct wlr_primary_selection_source_impl { void (*send)(struct wlr_primary_selection_source *source, const char *mime_type, int fd); void (*destroy)(struct wlr_primary_selection_source *source); }; /** * A source is the sending side of a selection. */ struct wlr_primary_selection_source { const struct wlr_primary_selection_source_impl *impl; // source metadata struct wl_array mime_types; struct { struct wl_signal destroy; } events; void *data; }; void wlr_primary_selection_source_init( struct wlr_primary_selection_source *source, const struct wlr_primary_selection_source_impl *impl); void wlr_primary_selection_source_destroy( struct wlr_primary_selection_source *source); void wlr_primary_selection_source_send( struct wlr_primary_selection_source *source, const char *mime_type, int fd); /** * Request setting the primary selection. If `client` is not null, then the * serial will be checked against the set of serials sent to the client on that * seat. */ void wlr_seat_request_set_primary_selection(struct wlr_seat *seat, struct wlr_seat_client *client, struct wlr_primary_selection_source *source, uint32_t serial); /** * Sets the current primary selection for the seat. NULL can be provided to * clear it. This removes the previous one if there was any. In case the * selection doesn't come from a client, wl_display_next_serial() can be used to * generate a serial. */ void wlr_seat_set_primary_selection(struct wlr_seat *seat, struct wlr_primary_selection_source *source, uint32_t serial); #endif wlroots-0.17.1/include/wlr/types/wlr_primary_selection_v1.h000066400000000000000000000024041454110342200240730ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_TYPES_WLR_PRIMARY_SELECTION_V1_H #define WLR_TYPES_WLR_PRIMARY_SELECTION_V1_H #include #include struct wlr_primary_selection_v1_device_manager { struct wl_global *global; struct wl_list devices; // wlr_primary_selection_v1_device.link struct wl_listener display_destroy; struct { struct wl_signal destroy; } events; void *data; }; /** * A device is a per-seat object used to set and get the current selection. */ struct wlr_primary_selection_v1_device { struct wlr_primary_selection_v1_device_manager *manager; struct wlr_seat *seat; struct wl_list link; // wlr_primary_selection_v1_device_manager.devices struct wl_list resources; // wl_resource_get_link() struct wl_list offers; // wl_resource_get_link() struct wl_listener seat_destroy; struct wl_listener seat_focus_change; struct wl_listener seat_set_primary_selection; void *data; }; struct wlr_primary_selection_v1_device_manager * wlr_primary_selection_v1_device_manager_create(struct wl_display *display); #endif wlroots-0.17.1/include/wlr/types/wlr_region.h000066400000000000000000000014611454110342200212220ustar00rootroot00000000000000/* * This is a stable interface of wlroots. Future changes will be limited to: * * - New functions * - New struct members * - New enum members * * Note that wlroots does not make an ABI compatibility promise - in the future, * the layout and size of structs used by wlroots may change, requiring code * depending on this header to be recompiled (but not edited). * * Breaking changes are announced in the release notes and follow a 1-year * deprecation schedule. */ #ifndef WLR_TYPES_WLR_REGION_H #define WLR_TYPES_WLR_REGION_H #include struct wl_resource; /** * Obtain a Pixman region from a wl_region resource. * * To allow clients to create wl_region objects, call wlr_compositor_create(). */ const pixman_region32_t *wlr_region_from_resource(struct wl_resource *resource); #endif wlroots-0.17.1/include/wlr/types/wlr_relative_pointer_v1.h000066400000000000000000000042351454110342200237220ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_TYPES_WLR_RELATIVE_POINTER_V1_H #define WLR_TYPES_WLR_RELATIVE_POINTER_V1_H #include /** * This protocol specifies a set of interfaces used for making clients able to * receive relative pointer events not obstructed by barriers (such as the * monitor edge or pointer constraints). */ /** * A global interface used for getting the relative pointer object for a given * pointer. */ struct wlr_relative_pointer_manager_v1 { struct wl_global *global; struct wl_list relative_pointers; // wlr_relative_pointer_v1.link struct { struct wl_signal destroy; struct wl_signal new_relative_pointer; // struct wlr_relative_pointer_v1 } events; struct wl_listener display_destroy_listener; void *data; }; /** * A wp_relative_pointer object is an extension to the wl_pointer interface * used for emitting relative pointer events. It shares the same focus as * wl_pointer objects of the same seat and will only emit events when it has * focus. */ struct wlr_relative_pointer_v1 { struct wl_resource *resource; struct wl_resource *pointer_resource; struct wlr_seat *seat; struct wl_list link; // wlr_relative_pointer_manager_v1.relative_pointers struct { struct wl_signal destroy; } events; struct wl_listener seat_destroy; struct wl_listener pointer_destroy; void *data; }; struct wlr_relative_pointer_manager_v1 *wlr_relative_pointer_manager_v1_create( struct wl_display *display); /** * Send a relative motion event to the seat. Time is given in microseconds * (unlike wl_pointer which uses milliseconds). */ void wlr_relative_pointer_manager_v1_send_relative_motion( struct wlr_relative_pointer_manager_v1 *manager, struct wlr_seat *seat, uint64_t time_usec, double dx, double dy, double dx_unaccel, double dy_unaccel); /** * Get a relative pointer from its resource. Returns NULL if inert. */ struct wlr_relative_pointer_v1 *wlr_relative_pointer_v1_from_resource( struct wl_resource *resource); #endif wlroots-0.17.1/include/wlr/types/wlr_scene.h000066400000000000000000000431741454110342200210430ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_TYPES_WLR_SCENE_H #define WLR_TYPES_WLR_SCENE_H /** * The scene-graph API provides a declarative way to display surfaces. The * compositor creates a scene, adds surfaces, then renders the scene on * outputs. * * The scene-graph API only supports basic 2D composition operations (like the * KMS API or the Wayland protocol does). For anything more complicated, * compositors need to implement custom rendering logic. */ #include #include #include #include #include #include #include #include struct wlr_output; struct wlr_output_layout; struct wlr_output_layout_output; struct wlr_xdg_surface; struct wlr_layer_surface_v1; struct wlr_drag_icon; struct wlr_surface; struct wlr_scene_node; struct wlr_scene_buffer; struct wlr_scene_output_layout; struct wlr_presentation; struct wlr_linux_dmabuf_v1; struct wlr_output_state; typedef bool (*wlr_scene_buffer_point_accepts_input_func_t)( struct wlr_scene_buffer *buffer, double *sx, double *sy); typedef void (*wlr_scene_buffer_iterator_func_t)( struct wlr_scene_buffer *buffer, int sx, int sy, void *user_data); enum wlr_scene_node_type { WLR_SCENE_NODE_TREE, WLR_SCENE_NODE_RECT, WLR_SCENE_NODE_BUFFER, }; /** A node is an object in the scene. */ struct wlr_scene_node { enum wlr_scene_node_type type; struct wlr_scene_tree *parent; struct wl_list link; // wlr_scene_tree.children bool enabled; int x, y; // relative to parent struct { struct wl_signal destroy; } events; void *data; struct wlr_addon_set addons; // private state pixman_region32_t visible; }; enum wlr_scene_debug_damage_option { WLR_SCENE_DEBUG_DAMAGE_NONE, WLR_SCENE_DEBUG_DAMAGE_RERENDER, WLR_SCENE_DEBUG_DAMAGE_HIGHLIGHT }; /** A sub-tree in the scene-graph. */ struct wlr_scene_tree { struct wlr_scene_node node; struct wl_list children; // wlr_scene_node.link }; /** The root scene-graph node. */ struct wlr_scene { struct wlr_scene_tree tree; struct wl_list outputs; // wlr_scene_output.link // May be NULL struct wlr_presentation *presentation; struct wlr_linux_dmabuf_v1 *linux_dmabuf_v1; // private state struct wl_listener presentation_destroy; struct wl_listener linux_dmabuf_v1_destroy; enum wlr_scene_debug_damage_option debug_damage_option; bool direct_scanout; bool calculate_visibility; }; /** A scene-graph node displaying a single surface. */ struct wlr_scene_surface { struct wlr_scene_buffer *buffer; struct wlr_surface *surface; // private state struct wlr_box clip; struct wlr_addon addon; struct wl_listener outputs_update; struct wl_listener output_enter; struct wl_listener output_leave; struct wl_listener output_sample; struct wl_listener frame_done; struct wl_listener surface_destroy; struct wl_listener surface_commit; }; /** A scene-graph node displaying a solid-colored rectangle */ struct wlr_scene_rect { struct wlr_scene_node node; int width, height; float color[4]; }; struct wlr_scene_outputs_update_event { struct wlr_scene_output **active; size_t size; }; struct wlr_scene_output_sample_event { struct wlr_scene_output *output; bool direct_scanout; }; /** A scene-graph node displaying a buffer */ struct wlr_scene_buffer { struct wlr_scene_node node; // May be NULL struct wlr_buffer *buffer; struct { struct wl_signal outputs_update; // struct wlr_scene_outputs_update_event struct wl_signal output_enter; // struct wlr_scene_output struct wl_signal output_leave; // struct wlr_scene_output struct wl_signal output_sample; // struct wlr_scene_output_sample_event struct wl_signal frame_done; // struct timespec } events; // May be NULL wlr_scene_buffer_point_accepts_input_func_t point_accepts_input; /** * The output that the largest area of this buffer is displayed on. * This may be NULL if the buffer is not currently displayed on any * outputs. This is the output that should be used for frame callbacks, * presentation feedback, etc. */ struct wlr_scene_output *primary_output; float opacity; enum wlr_scale_filter_mode filter_mode; struct wlr_fbox src_box; int dst_width, dst_height; enum wl_output_transform transform; pixman_region32_t opaque_region; // private state uint64_t active_outputs; struct wlr_texture *texture; struct wlr_linux_dmabuf_feedback_v1_init_options prev_feedback_options; }; /** A viewport for an output in the scene-graph */ struct wlr_scene_output { struct wlr_output *output; struct wl_list link; // wlr_scene.outputs struct wlr_scene *scene; struct wlr_addon addon; struct wlr_damage_ring damage_ring; int x, y; struct { struct wl_signal destroy; } events; // private state uint8_t index; bool prev_scanout; struct wl_listener output_commit; struct wl_listener output_damage; struct wl_listener output_needs_frame; struct wl_list damage_highlight_regions; struct wl_array render_list; }; struct wlr_scene_timer { int64_t pre_render_duration; struct wlr_render_timer *render_timer; }; /** A layer shell scene helper */ struct wlr_scene_layer_surface_v1 { struct wlr_scene_tree *tree; struct wlr_layer_surface_v1 *layer_surface; // private state struct wl_listener tree_destroy; struct wl_listener layer_surface_destroy; struct wl_listener layer_surface_map; struct wl_listener layer_surface_unmap; }; /** * Immediately destroy the scene-graph node. */ void wlr_scene_node_destroy(struct wlr_scene_node *node); /** * Enable or disable this node. If a node is disabled, all of its children are * implicitly disabled as well. */ void wlr_scene_node_set_enabled(struct wlr_scene_node *node, bool enabled); /** * Set the position of the node relative to its parent. */ void wlr_scene_node_set_position(struct wlr_scene_node *node, int x, int y); /** * Move the node right above the specified sibling. * Asserts that node and sibling are distinct and share the same parent. */ void wlr_scene_node_place_above(struct wlr_scene_node *node, struct wlr_scene_node *sibling); /** * Move the node right below the specified sibling. * Asserts that node and sibling are distinct and share the same parent. */ void wlr_scene_node_place_below(struct wlr_scene_node *node, struct wlr_scene_node *sibling); /** * Move the node above all of its sibling nodes. */ void wlr_scene_node_raise_to_top(struct wlr_scene_node *node); /** * Move the node below all of its sibling nodes. */ void wlr_scene_node_lower_to_bottom(struct wlr_scene_node *node); /** * Move the node to another location in the tree. */ void wlr_scene_node_reparent(struct wlr_scene_node *node, struct wlr_scene_tree *new_parent); /** * Get the node's layout-local coordinates. * * True is returned if the node and all of its ancestors are enabled. */ bool wlr_scene_node_coords(struct wlr_scene_node *node, int *lx, int *ly); /** * Call `iterator` on each buffer in the scene-graph, with the buffer's * position in layout coordinates. The function is called from root to leaves * (in rendering order). */ void wlr_scene_node_for_each_buffer(struct wlr_scene_node *node, wlr_scene_buffer_iterator_func_t iterator, void *user_data); /** * Find the topmost node in this scene-graph that contains the point at the * given layout-local coordinates. (For surface nodes, this means accepting * input events at that point.) Returns the node and coordinates relative to the * returned node, or NULL if no node is found at that location. */ struct wlr_scene_node *wlr_scene_node_at(struct wlr_scene_node *node, double lx, double ly, double *nx, double *ny); /** * Create a new scene-graph. */ struct wlr_scene *wlr_scene_create(void); /** * Handle presentation feedback for all surfaces in the scene, assuming that * scene outputs and the scene rendering functions are used. * * Asserts that a struct wlr_presentation hasn't already been set for the scene. */ void wlr_scene_set_presentation(struct wlr_scene *scene, struct wlr_presentation *presentation); /** * Handles linux_dmabuf_v1 feedback for all surfaces in the scene. * * Asserts that a struct wlr_linux_dmabuf_v1 hasn't already been set for the scene. */ void wlr_scene_set_linux_dmabuf_v1(struct wlr_scene *scene, struct wlr_linux_dmabuf_v1 *linux_dmabuf_v1); /** * Add a node displaying nothing but its children. */ struct wlr_scene_tree *wlr_scene_tree_create(struct wlr_scene_tree *parent); /** * Add a node displaying a single surface to the scene-graph. * * The child sub-surfaces are ignored. * * wlr_surface_send_enter() and wlr_surface_send_leave() will be called * automatically based on the position of the surface and outputs in * the scene. */ struct wlr_scene_surface *wlr_scene_surface_create(struct wlr_scene_tree *parent, struct wlr_surface *surface); /** * If this node represents a wlr_scene_buffer, that buffer will be returned. It * is not legal to feed a node that does not represent a wlr_scene_buffer. */ struct wlr_scene_buffer *wlr_scene_buffer_from_node(struct wlr_scene_node *node); /** * If this node represents a wlr_scene_tree, that tree will be returned. It * is not legal to feed a node that does not represent a wlr_scene_tree. */ struct wlr_scene_tree *wlr_scene_tree_from_node(struct wlr_scene_node *node); /** * If this node represents a wlr_scene_rect, that rect will be returned. It * is not legal to feed a node that does not represent a wlr_scene_rect. */ struct wlr_scene_rect *wlr_scene_rect_from_node(struct wlr_scene_node *node); /** * If this buffer is backed by a surface, then the struct wlr_scene_surface is * returned. If not, NULL will be returned. */ struct wlr_scene_surface *wlr_scene_surface_try_from_buffer( struct wlr_scene_buffer *scene_buffer); /** * Add a node displaying a solid-colored rectangle to the scene-graph. */ struct wlr_scene_rect *wlr_scene_rect_create(struct wlr_scene_tree *parent, int width, int height, const float color[static 4]); /** * Change the width and height of an existing rectangle node. */ void wlr_scene_rect_set_size(struct wlr_scene_rect *rect, int width, int height); /** * Change the color of an existing rectangle node. */ void wlr_scene_rect_set_color(struct wlr_scene_rect *rect, const float color[static 4]); /** * Add a node displaying a buffer to the scene-graph. * * If the buffer is NULL, this node will not be displayed. */ struct wlr_scene_buffer *wlr_scene_buffer_create(struct wlr_scene_tree *parent, struct wlr_buffer *buffer); /** * Sets the buffer's backing buffer. * * If the buffer is NULL, the buffer node will not be displayed. */ void wlr_scene_buffer_set_buffer(struct wlr_scene_buffer *scene_buffer, struct wlr_buffer *buffer); /** * Sets the buffer's backing buffer with a custom damage region. * * The damage region is in buffer-local coordinates. If the region is NULL, * the whole buffer node will be damaged. */ void wlr_scene_buffer_set_buffer_with_damage(struct wlr_scene_buffer *scene_buffer, struct wlr_buffer *buffer, const pixman_region32_t *region); /** * Sets the buffer's opaque region. This is an optimization hint used to * determine if buffers which reside under this one need to be rendered or not. */ void wlr_scene_buffer_set_opaque_region(struct wlr_scene_buffer *scene_buffer, const pixman_region32_t *region); /** * Set the source rectangle describing the region of the buffer which will be * sampled to render this node. This allows cropping the buffer. * * If NULL, the whole buffer is sampled. By default, the source box is NULL. */ void wlr_scene_buffer_set_source_box(struct wlr_scene_buffer *scene_buffer, const struct wlr_fbox *box); /** * Set the destination size describing the region of the scene-graph the buffer * will be painted onto. This allows scaling the buffer. * * If zero, the destination size will be the buffer size. By default, the * destination size is zero. */ void wlr_scene_buffer_set_dest_size(struct wlr_scene_buffer *scene_buffer, int width, int height); /** * Set a transform which will be applied to the buffer. */ void wlr_scene_buffer_set_transform(struct wlr_scene_buffer *scene_buffer, enum wl_output_transform transform); /** * Sets the opacity of this buffer */ void wlr_scene_buffer_set_opacity(struct wlr_scene_buffer *scene_buffer, float opacity); /** * Sets the filter mode to use when scaling the buffer */ void wlr_scene_buffer_set_filter_mode(struct wlr_scene_buffer *scene_buffer, enum wlr_scale_filter_mode filter_mode); /** * Calls the buffer's frame_done signal. */ void wlr_scene_buffer_send_frame_done(struct wlr_scene_buffer *scene_buffer, struct timespec *now); /** * Add a viewport for the specified output to the scene-graph. * * An output can only be added once to the scene-graph. */ struct wlr_scene_output *wlr_scene_output_create(struct wlr_scene *scene, struct wlr_output *output); /** * Destroy a scene-graph output. */ void wlr_scene_output_destroy(struct wlr_scene_output *scene_output); /** * Set the output's position in the scene-graph. */ void wlr_scene_output_set_position(struct wlr_scene_output *scene_output, int lx, int ly); struct wlr_scene_output_state_options { struct wlr_scene_timer *timer; }; /** * Render and commit an output. */ bool wlr_scene_output_commit(struct wlr_scene_output *scene_output, const struct wlr_scene_output_state_options *options); /** * Render and populate given output state. */ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, struct wlr_output_state *state, const struct wlr_scene_output_state_options *options); /** * Retrieve the duration in nanoseconds between the last wlr_scene_output_commit() call and the end * of its operations, including those on the GPU that may have finished after the call returned. * * Returns -1 if the duration is unavailable. */ int64_t wlr_scene_timer_get_duration_ns(struct wlr_scene_timer *timer); void wlr_scene_timer_finish(struct wlr_scene_timer *timer); /** * Call wlr_surface_send_frame_done() on all surfaces in the scene rendered by * wlr_scene_output_commit() for which wlr_scene_surface.primary_output * matches the given scene_output. */ void wlr_scene_output_send_frame_done(struct wlr_scene_output *scene_output, struct timespec *now); /** * Call `iterator` on each buffer in the scene-graph visible on the output, * with the buffer's position in layout coordinates. The function is called * from root to leaves (in rendering order). */ void wlr_scene_output_for_each_buffer(struct wlr_scene_output *scene_output, wlr_scene_buffer_iterator_func_t iterator, void *user_data); /** * Get a scene-graph output from a struct wlr_output. * * If the output hasn't been added to the scene-graph, returns NULL. */ struct wlr_scene_output *wlr_scene_get_scene_output(struct wlr_scene *scene, struct wlr_output *output); /** * Attach an output layout to a scene. * * The resulting scene output layout allows to synchronize the positions of scene * outputs with the positions of corresponding layout outputs. * * It is automatically destroyed when the scene or the output layout is destroyed. */ struct wlr_scene_output_layout *wlr_scene_attach_output_layout(struct wlr_scene *scene, struct wlr_output_layout *output_layout); /** * Add an output to the scene output layout. * * When the layout output is repositioned, the scene output will be repositioned * accordingly. */ void wlr_scene_output_layout_add_output(struct wlr_scene_output_layout *sol, struct wlr_output_layout_output *lo, struct wlr_scene_output *so); /** * Add a node displaying a surface and all of its sub-surfaces to the * scene-graph. */ struct wlr_scene_tree *wlr_scene_subsurface_tree_create( struct wlr_scene_tree *parent, struct wlr_surface *surface); /** * Sets a cropping region for any subsurface trees that are children of this * scene node. The clip coordinate space will be that of the root surface of * the subsurface tree. * * A NULL or empty clip will disable clipping */ void wlr_scene_subsurface_tree_set_clip(struct wlr_scene_node *node, struct wlr_box *clip); /** * Add a node displaying an xdg_surface and all of its sub-surfaces to the * scene-graph. * * The origin of the returned scene-graph node will match the top-left corner * of the xdg_surface window geometry. */ struct wlr_scene_tree *wlr_scene_xdg_surface_create( struct wlr_scene_tree *parent, struct wlr_xdg_surface *xdg_surface); /** * Add a node displaying a layer_surface_v1 and all of its sub-surfaces to the * scene-graph. * * The origin of the returned scene-graph node will match the top-left corner * of the layer surface. */ struct wlr_scene_layer_surface_v1 *wlr_scene_layer_surface_v1_create( struct wlr_scene_tree *parent, struct wlr_layer_surface_v1 *layer_surface); /** * Configure a layer_surface_v1, position its scene node in accordance to its * current state, and update the remaining usable area. * * full_area represents the entire area that may be used by the layer surface * if its exclusive_zone is -1, and is usually the output dimensions. * usable_area represents what remains of full_area that can be used if * exclusive_zone is >= 0. usable_area is updated if the surface has a positive * exclusive_zone, so that it can be used for the next layer surface. */ void wlr_scene_layer_surface_v1_configure( struct wlr_scene_layer_surface_v1 *scene_layer_surface, const struct wlr_box *full_area, struct wlr_box *usable_area); /** * Add a node displaying a drag icon and all its sub-surfaces to the * scene-graph. */ struct wlr_scene_tree *wlr_scene_drag_icon_create( struct wlr_scene_tree *parent, struct wlr_drag_icon *drag_icon); #endif wlroots-0.17.1/include/wlr/types/wlr_screencopy_v1.h000066400000000000000000000025621454110342200225220ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_TYPES_WLR_SCREENCOPY_V1_H #define WLR_TYPES_WLR_SCREENCOPY_V1_H #include #include #include #include struct wlr_screencopy_manager_v1 { struct wl_global *global; struct wl_list frames; // wlr_screencopy_frame_v1.link struct wl_listener display_destroy; struct { struct wl_signal destroy; } events; void *data; }; struct wlr_screencopy_v1_client { int ref; struct wlr_screencopy_manager_v1 *manager; struct wl_list damages; }; struct wlr_screencopy_frame_v1 { struct wl_resource *resource; struct wlr_screencopy_v1_client *client; struct wl_list link; // wlr_screencopy_manager_v1.frames uint32_t shm_format, dmabuf_format; // DRM format codes struct wlr_box box; int shm_stride; bool overlay_cursor, cursor_locked; bool with_damage; enum wlr_buffer_cap buffer_cap; struct wlr_buffer *buffer; struct wlr_output *output; struct wl_listener output_commit; struct wl_listener output_destroy; struct wl_listener output_enable; void *data; }; struct wlr_screencopy_manager_v1 *wlr_screencopy_manager_v1_create( struct wl_display *display); #endif wlroots-0.17.1/include/wlr/types/wlr_seat.h000066400000000000000000000605221454110342200206760ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_TYPES_WLR_SEAT_H #define WLR_TYPES_WLR_SEAT_H #include #include #include #include #include struct wlr_surface; #define WLR_SERIAL_RINGSET_SIZE 128 struct wlr_serial_range { uint32_t min_incl; uint32_t max_incl; }; struct wlr_serial_ringset { struct wlr_serial_range data[WLR_SERIAL_RINGSET_SIZE]; int end; int count; }; /** * Contains state for a single client's bound wl_seat resource and can be used * to issue input events to that client. The lifetime of these objects is * managed by struct wlr_seat; some may be NULL. */ struct wlr_seat_client { struct wl_client *client; struct wlr_seat *seat; struct wl_list link; // lists of wl_resource struct wl_list resources; struct wl_list pointers; struct wl_list keyboards; struct wl_list touches; struct wl_list data_devices; struct { struct wl_signal destroy; } events; // set of serials which were sent to the client on this seat // for use by wlr_seat_client_{next_serial,validate_event_serial} struct wlr_serial_ringset serials; bool needs_touch_frame; // When the client doesn't support high-resolution scroll, accumulate deltas // until we can notify a discrete event. // Some mice have a free spinning wheel, making possible to lock the wheel // when the accumulator value is not 0. To avoid synchronization issues // between the mouse wheel and the accumulators, store the last delta and // when the scroll direction changes, reset the accumulator. // Indexed by wlr_axis_orientation. struct { int32_t acc_discrete[2]; int32_t last_discrete[2]; double acc_axis[2]; } value120; }; struct wlr_touch_point { int32_t touch_id; struct wlr_surface *surface; // may be NULL if destroyed struct wlr_seat_client *client; struct wlr_surface *focus_surface; struct wlr_seat_client *focus_client; double sx, sy; struct wl_listener surface_destroy; struct wl_listener focus_surface_destroy; struct wl_listener client_destroy; struct { struct wl_signal destroy; } events; struct wl_list link; }; struct wlr_seat_pointer_grab; struct wlr_pointer_grab_interface { void (*enter)(struct wlr_seat_pointer_grab *grab, struct wlr_surface *surface, double sx, double sy); void (*clear_focus)(struct wlr_seat_pointer_grab *grab); void (*motion)(struct wlr_seat_pointer_grab *grab, uint32_t time_msec, double sx, double sy); uint32_t (*button)(struct wlr_seat_pointer_grab *grab, uint32_t time_msec, uint32_t button, enum wlr_button_state state); void (*axis)(struct wlr_seat_pointer_grab *grab, uint32_t time_msec, enum wlr_axis_orientation orientation, double value, int32_t value_discrete, enum wlr_axis_source source); void (*frame)(struct wlr_seat_pointer_grab *grab); void (*cancel)(struct wlr_seat_pointer_grab *grab); }; struct wlr_seat_keyboard_grab; struct wlr_keyboard_grab_interface { void (*enter)(struct wlr_seat_keyboard_grab *grab, struct wlr_surface *surface, const uint32_t keycodes[], size_t num_keycodes, const struct wlr_keyboard_modifiers *modifiers); void (*clear_focus)(struct wlr_seat_keyboard_grab *grab); void (*key)(struct wlr_seat_keyboard_grab *grab, uint32_t time_msec, uint32_t key, uint32_t state); void (*modifiers)(struct wlr_seat_keyboard_grab *grab, const struct wlr_keyboard_modifiers *modifiers); void (*cancel)(struct wlr_seat_keyboard_grab *grab); }; struct wlr_seat_touch_grab; struct wlr_touch_grab_interface { uint32_t (*down)(struct wlr_seat_touch_grab *grab, uint32_t time_msec, struct wlr_touch_point *point); void (*up)(struct wlr_seat_touch_grab *grab, uint32_t time_msec, struct wlr_touch_point *point); void (*motion)(struct wlr_seat_touch_grab *grab, uint32_t time_msec, struct wlr_touch_point *point); void (*enter)(struct wlr_seat_touch_grab *grab, uint32_t time_msec, struct wlr_touch_point *point); void (*frame)(struct wlr_seat_touch_grab *grab); // Cancel grab void (*cancel)(struct wlr_seat_touch_grab *grab); // Send wl_touch.cancel void (*wl_cancel)(struct wlr_seat_touch_grab *grab, struct wlr_surface *surface); }; /** * Passed to wlr_seat_touch_start_grab() to start a grab of the touch device. * The grabber is responsible for handling touch events for the seat. */ struct wlr_seat_touch_grab { const struct wlr_touch_grab_interface *interface; struct wlr_seat *seat; void *data; }; /** * Passed to wlr_seat_keyboard_start_grab() to start a grab of the keyboard. * The grabber is responsible for handling keyboard events for the seat. */ struct wlr_seat_keyboard_grab { const struct wlr_keyboard_grab_interface *interface; struct wlr_seat *seat; void *data; }; /** * Passed to wlr_seat_pointer_start_grab() to start a grab of the pointer. The * grabber is responsible for handling pointer events for the seat. */ struct wlr_seat_pointer_grab { const struct wlr_pointer_grab_interface *interface; struct wlr_seat *seat; void *data; }; #define WLR_POINTER_BUTTONS_CAP 16 struct wlr_seat_pointer_state { struct wlr_seat *seat; struct wlr_seat_client *focused_client; struct wlr_surface *focused_surface; double sx, sy; struct wlr_seat_pointer_grab *grab; struct wlr_seat_pointer_grab *default_grab; bool sent_axis_source; enum wlr_axis_source cached_axis_source; uint32_t buttons[WLR_POINTER_BUTTONS_CAP]; size_t button_count; uint32_t grab_button; uint32_t grab_serial; uint32_t grab_time; struct wl_listener surface_destroy; struct { struct wl_signal focus_change; // struct wlr_seat_pointer_focus_change_event } events; }; struct wlr_seat_keyboard_state { struct wlr_seat *seat; struct wlr_keyboard *keyboard; struct wlr_seat_client *focused_client; struct wlr_surface *focused_surface; struct wl_listener keyboard_destroy; struct wl_listener keyboard_keymap; struct wl_listener keyboard_repeat_info; struct wl_listener surface_destroy; struct wlr_seat_keyboard_grab *grab; struct wlr_seat_keyboard_grab *default_grab; struct { struct wl_signal focus_change; // struct wlr_seat_keyboard_focus_change_event } events; }; struct wlr_seat_touch_state { struct wlr_seat *seat; struct wl_list touch_points; // wlr_touch_point.link uint32_t grab_serial; uint32_t grab_id; struct wlr_seat_touch_grab *grab; struct wlr_seat_touch_grab *default_grab; }; struct wlr_primary_selection_source; struct wlr_seat { struct wl_global *global; struct wl_display *display; struct wl_list clients; char *name; uint32_t capabilities; uint32_t accumulated_capabilities; struct timespec last_event; struct wlr_data_source *selection_source; uint32_t selection_serial; struct wl_list selection_offers; // wlr_data_offer.link struct wlr_primary_selection_source *primary_selection_source; uint32_t primary_selection_serial; // `drag` goes away before `drag_source`, when the implicit grab ends struct wlr_drag *drag; struct wlr_data_source *drag_source; uint32_t drag_serial; struct wl_list drag_offers; // wlr_data_offer.link struct wlr_seat_pointer_state pointer_state; struct wlr_seat_keyboard_state keyboard_state; struct wlr_seat_touch_state touch_state; struct wl_listener display_destroy; struct wl_listener selection_source_destroy; struct wl_listener primary_selection_source_destroy; struct wl_listener drag_source_destroy; struct { struct wl_signal pointer_grab_begin; struct wl_signal pointer_grab_end; struct wl_signal keyboard_grab_begin; struct wl_signal keyboard_grab_end; struct wl_signal touch_grab_begin; struct wl_signal touch_grab_end; // struct wlr_seat_pointer_request_set_cursor_event struct wl_signal request_set_cursor; // Called when an application _wants_ to set the selection (user copies some data). // Compositors should listen to this event and call wlr_seat_set_selection() // if they want to accept the client's request. struct wl_signal request_set_selection; // struct wlr_seat_request_set_selection_event // Called after the data source is set for the selection. struct wl_signal set_selection; // Called when an application _wants_ to set the primary selection (user selects some data). // Compositors should listen to this event and call wlr_seat_set_primary_selection() // if they want to accept the client's request. struct wl_signal request_set_primary_selection; // struct wlr_seat_request_set_primary_selection_event // Called after the primary selection source object is set. struct wl_signal set_primary_selection; // struct wlr_seat_request_start_drag_event struct wl_signal request_start_drag; struct wl_signal start_drag; // struct wlr_drag struct wl_signal destroy; } events; void *data; }; struct wlr_seat_pointer_request_set_cursor_event { struct wlr_seat_client *seat_client; struct wlr_surface *surface; uint32_t serial; int32_t hotspot_x, hotspot_y; }; struct wlr_seat_request_set_selection_event { struct wlr_data_source *source; uint32_t serial; }; struct wlr_seat_request_set_primary_selection_event { struct wlr_primary_selection_source *source; uint32_t serial; }; struct wlr_seat_request_start_drag_event { struct wlr_drag *drag; struct wlr_surface *origin; uint32_t serial; }; struct wlr_seat_pointer_focus_change_event { struct wlr_seat *seat; struct wlr_surface *old_surface, *new_surface; double sx, sy; }; struct wlr_seat_keyboard_focus_change_event { struct wlr_seat *seat; struct wlr_surface *old_surface, *new_surface; }; /** * Allocates a new struct wlr_seat and adds a wl_seat global to the display. */ struct wlr_seat *wlr_seat_create(struct wl_display *display, const char *name); /** * Destroys a seat, removes its wl_seat global and clears focus for all * devices belonging to the seat. */ void wlr_seat_destroy(struct wlr_seat *wlr_seat); /** * Gets a struct wlr_seat_client for the specified client, or returns NULL if no * client is bound for that client. */ struct wlr_seat_client *wlr_seat_client_for_wl_client(struct wlr_seat *wlr_seat, struct wl_client *wl_client); /** * Updates the capabilities available on this seat. * Will automatically send them to all clients. */ void wlr_seat_set_capabilities(struct wlr_seat *wlr_seat, uint32_t capabilities); /** * Updates the name of this seat. * Will automatically send it to all clients. */ void wlr_seat_set_name(struct wlr_seat *wlr_seat, const char *name); /** * Whether or not the surface has pointer focus */ bool wlr_seat_pointer_surface_has_focus(struct wlr_seat *wlr_seat, struct wlr_surface *surface); /** * Send a pointer enter event to the given surface and consider it to be the * focused surface for the pointer. This will send a leave event to the last * surface that was entered. Coordinates for the enter event are surface-local. * This function does not respect pointer grabs: you probably want * wlr_seat_pointer_notify_enter() instead. */ void wlr_seat_pointer_enter(struct wlr_seat *wlr_seat, struct wlr_surface *surface, double sx, double sy); /** * Clear the focused surface for the pointer and leave all entered surfaces. * This function does not respect pointer grabs: you probably want * wlr_seat_pointer_notify_clear_focus() instead. */ void wlr_seat_pointer_clear_focus(struct wlr_seat *wlr_seat); /** * Send a motion event to the surface with pointer focus. Coordinates for the * motion event are surface-local. This function does not respect pointer grabs: * you probably want wlr_seat_pointer_notify_motion() instead. */ void wlr_seat_pointer_send_motion(struct wlr_seat *wlr_seat, uint32_t time_msec, double sx, double sy); /** * Send a button event to the surface with pointer focus. Coordinates for the * button event are surface-local. Returns the serial. This function does not * respect pointer grabs: you probably want wlr_seat_pointer_notify_button() * instead. */ uint32_t wlr_seat_pointer_send_button(struct wlr_seat *wlr_seat, uint32_t time_msec, uint32_t button, enum wlr_button_state state); /** * Send an axis event to the surface with pointer focus. This function does not * respect pointer grabs: you probably want wlr_seat_pointer_notify_axis() * instead. */ void wlr_seat_pointer_send_axis(struct wlr_seat *wlr_seat, uint32_t time_msec, enum wlr_axis_orientation orientation, double value, int32_t value_discrete, enum wlr_axis_source source); /** * Send a frame event to the surface with pointer focus. This function does not * respect pointer grabs: you probably want wlr_seat_pointer_notify_frame() * instead. */ void wlr_seat_pointer_send_frame(struct wlr_seat *wlr_seat); /** * Notify the seat of a pointer enter event to the given surface and request it * to be the focused surface for the pointer. Pass surface-local coordinates * where the enter occurred. This will send a leave event to the currently- * focused surface. Defers to any grab of the pointer. */ void wlr_seat_pointer_notify_enter(struct wlr_seat *wlr_seat, struct wlr_surface *surface, double sx, double sy); /** * Notify the seat of a pointer leave event to the currently-focused surface. * Defers to any grab of the pointer. */ void wlr_seat_pointer_notify_clear_focus(struct wlr_seat *wlr_seat); /** * Warp the pointer of this seat to the given surface-local coordinates, without * generating motion events. */ void wlr_seat_pointer_warp(struct wlr_seat *wlr_seat, double sx, double sy); /** * Notify the seat of motion over the given surface. Pass surface-local * coordinates where the pointer motion occurred. Defers to any grab of the * pointer. */ void wlr_seat_pointer_notify_motion(struct wlr_seat *wlr_seat, uint32_t time_msec, double sx, double sy); /** * Notify the seat that a button has been pressed. Returns the serial of the * button press or zero if no button press was sent. Defers to any grab of the * pointer. */ uint32_t wlr_seat_pointer_notify_button(struct wlr_seat *wlr_seat, uint32_t time_msec, uint32_t button, enum wlr_button_state state); /** * Notify the seat of an axis event. Defers to any grab of the pointer. */ void wlr_seat_pointer_notify_axis(struct wlr_seat *wlr_seat, uint32_t time_msec, enum wlr_axis_orientation orientation, double value, int32_t value_discrete, enum wlr_axis_source source); /** * Notify the seat of a frame event. Frame events are sent to end a group of * events that logically belong together. Motion, button and axis events should * all be followed by a frame event. Defers to any grab of the pointer. */ void wlr_seat_pointer_notify_frame(struct wlr_seat *wlr_seat); /** * Start a grab of the pointer of this seat. The grabber is responsible for * handling all pointer events until the grab ends. */ void wlr_seat_pointer_start_grab(struct wlr_seat *wlr_seat, struct wlr_seat_pointer_grab *grab); /** * End the grab of the pointer of this seat. This reverts the grab back to the * default grab for the pointer. */ void wlr_seat_pointer_end_grab(struct wlr_seat *wlr_seat); /** * Whether or not the pointer has a grab other than the default grab. */ bool wlr_seat_pointer_has_grab(struct wlr_seat *seat); /** * Set this keyboard as the active keyboard for the seat. */ void wlr_seat_set_keyboard(struct wlr_seat *seat, struct wlr_keyboard *keyboard); /** * Get the active keyboard for the seat. */ struct wlr_keyboard *wlr_seat_get_keyboard(struct wlr_seat *seat); /** * Send the keyboard key to focused keyboard resources. This function does not * respect keyboard grabs: you probably want wlr_seat_keyboard_notify_key() * instead. */ void wlr_seat_keyboard_send_key(struct wlr_seat *seat, uint32_t time_msec, uint32_t key, uint32_t state); /** * Send the modifier state to focused keyboard resources. This function does * not respect keyboard grabs: you probably want * wlr_seat_keyboard_notify_modifiers() instead. */ void wlr_seat_keyboard_send_modifiers(struct wlr_seat *seat, const struct wlr_keyboard_modifiers *modifiers); /** * Send a keyboard enter event to the given surface and consider it to be the * focused surface for the keyboard. This will send a leave event to the last * surface that was entered. This function does not respect keyboard grabs: you * probably want wlr_seat_keyboard_notify_enter() instead. */ void wlr_seat_keyboard_enter(struct wlr_seat *seat, struct wlr_surface *surface, const uint32_t keycodes[], size_t num_keycodes, const struct wlr_keyboard_modifiers *modifiers); /** * Clear the focused surface for the keyboard and leave all entered surfaces. * This function does not respect keyboard grabs: you probably want * wlr_seat_keyboard_notify_clear_focus() instead. */ void wlr_seat_keyboard_clear_focus(struct wlr_seat *wlr_seat); /** * Notify the seat that a key has been pressed on the keyboard. Defers to any * keyboard grabs. */ void wlr_seat_keyboard_notify_key(struct wlr_seat *seat, uint32_t time_msec, uint32_t key, uint32_t state); /** * Notify the seat that the modifiers for the keyboard have changed. Defers to * any keyboard grabs. */ void wlr_seat_keyboard_notify_modifiers(struct wlr_seat *seat, const struct wlr_keyboard_modifiers *modifiers); /** * Notify the seat that the keyboard focus has changed and request it to be the * focused surface for this keyboard. Defers to any current grab of the seat's * keyboard. */ void wlr_seat_keyboard_notify_enter(struct wlr_seat *seat, struct wlr_surface *surface, const uint32_t keycodes[], size_t num_keycodes, const struct wlr_keyboard_modifiers *modifiers); /** * Notify the seat of a keyboard leave event to the currently-focused surface. * Defers to any keyboard grabs. */ void wlr_seat_keyboard_notify_clear_focus(struct wlr_seat *wlr_seat); /** * Start a grab of the keyboard of this seat. The grabber is responsible for * handling all keyboard events until the grab ends. */ void wlr_seat_keyboard_start_grab(struct wlr_seat *wlr_seat, struct wlr_seat_keyboard_grab *grab); /** * End the grab of the keyboard of this seat. This reverts the grab back to the * default grab for the keyboard. */ void wlr_seat_keyboard_end_grab(struct wlr_seat *wlr_seat); /** * Whether or not the keyboard has a grab other than the default grab */ bool wlr_seat_keyboard_has_grab(struct wlr_seat *seat); /** * Get the active touch point with the given `touch_id`. If the touch point does * not exist or is no longer active, returns NULL. */ struct wlr_touch_point *wlr_seat_touch_get_point(struct wlr_seat *seat, int32_t touch_id); /** * Notify the seat that the touch point given by `touch_id` has entered a new * surface. The surface is required. To clear focus, use * wlr_seat_touch_point_clear_focus(). */ void wlr_seat_touch_point_focus(struct wlr_seat *seat, struct wlr_surface *surface, uint32_t time_msec, int32_t touch_id, double sx, double sy); /** * Clear the focused surface for the touch point given by `touch_id`. */ void wlr_seat_touch_point_clear_focus(struct wlr_seat *seat, uint32_t time_msec, int32_t touch_id); /** * Send a touch down event to the client of the given surface. All future touch * events for this point will go to this surface. If the touch down is valid, * this will add a new touch point with the given `touch_id`. The touch down may * not be valid if the surface seat client does not accept touch input. * Coordinates are surface-local. This function does not respect touch grabs: * you probably want wlr_seat_touch_notify_down() instead. */ uint32_t wlr_seat_touch_send_down(struct wlr_seat *seat, struct wlr_surface *surface, uint32_t time_msec, int32_t touch_id, double sx, double sy); /** * Send a touch up event for the touch point given by the `touch_id`. The event * will go to the client for the surface given in the corresponding touch down * event. This will remove the touch point. This function does not respect touch * grabs: you probably want wlr_seat_touch_notify_up() instead. */ void wlr_seat_touch_send_up(struct wlr_seat *seat, uint32_t time_msec, int32_t touch_id); /** * Send a touch motion event for the touch point given by the `touch_id`. The * event will go to the client for the surface given in the corresponding touch * down event. This function does not respect touch grabs: you probably want * wlr_seat_touch_notify_motion() instead. */ void wlr_seat_touch_send_motion(struct wlr_seat *seat, uint32_t time_msec, int32_t touch_id, double sx, double sy); /** * Notify the seat that this is a global gesture and the client should cancel * processing it. The event will go to the client for the surface given. * This function does not respect touch grabs: you probably want * wlr_seat_touch_notify_cancel() instead. */ void wlr_seat_touch_send_cancel(struct wlr_seat *seat, struct wlr_surface *surface); void wlr_seat_touch_send_frame(struct wlr_seat *seat); /** * Notify the seat of a touch down on the given surface. Defers to any grab of * the touch device. */ uint32_t wlr_seat_touch_notify_down(struct wlr_seat *seat, struct wlr_surface *surface, uint32_t time_msec, int32_t touch_id, double sx, double sy); /** * Notify the seat that the touch point given by `touch_id` is up. Defers to any * grab of the touch device. */ void wlr_seat_touch_notify_up(struct wlr_seat *seat, uint32_t time_msec, int32_t touch_id); /** * Notify the seat that the touch point given by `touch_id` has moved. Defers to * any grab of the touch device. The seat should be notified of touch motion * even if the surface is not the owner of the touch point for processing by * grabs. */ void wlr_seat_touch_notify_motion(struct wlr_seat *seat, uint32_t time_msec, int32_t touch_id, double sx, double sy); /** * Notify the seat that this is a global gesture and the client should * cancel processing it. Defers to any grab of the touch device. */ void wlr_seat_touch_notify_cancel(struct wlr_seat *seat, struct wlr_surface *surface); void wlr_seat_touch_notify_frame(struct wlr_seat *seat); /** * How many touch points are currently down for the seat. */ int wlr_seat_touch_num_points(struct wlr_seat *seat); /** * Start a grab of the touch device of this seat. The grabber is responsible for * handling all touch events until the grab ends. */ void wlr_seat_touch_start_grab(struct wlr_seat *wlr_seat, struct wlr_seat_touch_grab *grab); /** * End the grab of the touch device of this seat. This reverts the grab back to * the default grab for the touch device. */ void wlr_seat_touch_end_grab(struct wlr_seat *wlr_seat); /** * Whether or not the seat has a touch grab other than the default grab. */ bool wlr_seat_touch_has_grab(struct wlr_seat *seat); /** * Check whether this serial is valid to start a pointer grab action. */ bool wlr_seat_validate_pointer_grab_serial(struct wlr_seat *seat, struct wlr_surface *origin, uint32_t serial); /** * Check whether this serial is valid to start a touch grab action. If it's the * case and point_ptr is non-NULL, `*point_ptr` is set to the touch point matching * the serial. */ bool wlr_seat_validate_touch_grab_serial(struct wlr_seat *seat, struct wlr_surface *origin, uint32_t serial, struct wlr_touch_point **point_ptr); /** * Return a new serial (from wl_display_serial_next()) for the client, and * update the seat client's set of valid serials. Use this for all input * events; otherwise wlr_seat_client_validate_event_serial() may fail when * handed a correctly functioning client's request serials. */ uint32_t wlr_seat_client_next_serial(struct wlr_seat_client *client); /** * Return true if the serial number could have been produced by * wlr_seat_client_next_serial() and is "older" (by less than UINT32_MAX/2) than * the current display serial value. * * This function should have no false negatives, and the only false positive * responses allowed are for elements that are still "older" than the current * display serial value and also older than all serial values remaining in * the seat client's serial ring buffer, if that buffer is also full. */ bool wlr_seat_client_validate_event_serial(struct wlr_seat_client *client, uint32_t serial); /** * Get a seat client from a seat resource. Returns NULL if inert. */ struct wlr_seat_client *wlr_seat_client_from_resource( struct wl_resource *resource); /** * Get a seat client from a pointer resource. Returns NULL if inert. */ struct wlr_seat_client *wlr_seat_client_from_pointer_resource( struct wl_resource *resource); /** * Check whether a surface has bound to touch events. */ bool wlr_surface_accepts_touch(struct wlr_seat *wlr_seat, struct wlr_surface *surface); #endif wlroots-0.17.1/include/wlr/types/wlr_security_context_v1.h000066400000000000000000000030501454110342200237540ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_TYPES_WLR_SECURITY_CONTEXT_V1_H #define WLR_TYPES_WLR_SECURITY_CONTEXT_V1_H #include /** * An implementation of the security context protocol. * * Compositors can create this manager, setup a filter for Wayland globals via * wl_display_set_global_filter(), and inside the filter query the security * context state via wlr_security_context_manager_v1_lookup_client(). */ struct wlr_security_context_manager_v1 { struct wl_global *global; struct { struct wl_signal destroy; struct wl_signal commit; // struct wlr_security_context_v1_commit_event } events; void *data; // private state struct wl_list contexts; // wlr_security_context_v1.link struct wl_listener display_destroy; }; struct wlr_security_context_v1_state { char *sandbox_engine; // may be NULL char *app_id; // may be NULL char *instance_id; // may be NULL }; struct wlr_security_context_v1_commit_event { const struct wlr_security_context_v1_state *state; // Client which created the security context struct wl_client *parent_client; }; struct wlr_security_context_manager_v1 *wlr_security_context_manager_v1_create( struct wl_display *display); const struct wlr_security_context_v1_state *wlr_security_context_manager_v1_lookup_client( struct wlr_security_context_manager_v1 *manager, struct wl_client *client); #endif wlroots-0.17.1/include/wlr/types/wlr_server_decoration.h000066400000000000000000000041621454110342200234550ustar00rootroot00000000000000/* * This protocol is obsolete and will be removed in a future version. The * recommended replacement is xdg-decoration. */ /* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_TYPES_WLR_SERVER_DECORATION_H #define WLR_TYPES_WLR_SERVER_DECORATION_H #include /** * Possible values to use in request_mode and the event mode. Same as * org_kde_kwin_server_decoration_manager_mode. */ enum wlr_server_decoration_manager_mode { /** * Undecorated: The surface is not decorated at all, neither server nor * client-side. An example is a popup surface which should not be * decorated. */ WLR_SERVER_DECORATION_MANAGER_MODE_NONE = 0, /** * Client-side decoration: The decoration is part of the surface and the * client. */ WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT = 1, /** * Server-side decoration: The server embeds the surface into a decoration * frame. */ WLR_SERVER_DECORATION_MANAGER_MODE_SERVER = 2, }; /** * A decoration negotiation interface which implements the KDE protocol. */ struct wlr_server_decoration_manager { struct wl_global *global; struct wl_list resources; // wl_resource_get_link() struct wl_list decorations; // wlr_server_decoration.link uint32_t default_mode; // enum wlr_server_decoration_manager_mode struct wl_listener display_destroy; struct { struct wl_signal new_decoration; struct wl_signal destroy; } events; void *data; }; struct wlr_server_decoration { struct wl_resource *resource; struct wlr_surface *surface; struct wl_list link; uint32_t mode; // enum wlr_server_decoration_manager_mode struct { struct wl_signal destroy; struct wl_signal mode; } events; struct wl_listener surface_destroy_listener; void *data; }; struct wlr_server_decoration_manager *wlr_server_decoration_manager_create( struct wl_display *display); void wlr_server_decoration_manager_set_default_mode( struct wlr_server_decoration_manager *manager, uint32_t default_mode); #endif wlroots-0.17.1/include/wlr/types/wlr_session_lock_v1.h000066400000000000000000000045471454110342200230500ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_TYPES_WLR_SESSION_LOCK_H #define WLR_TYPES_WLR_SESSION_LOCK_H #include #include #include struct wlr_session_lock_manager_v1 { struct wl_global *global; struct { struct wl_signal new_lock; // struct wlr_session_lock_v1 struct wl_signal destroy; } events; void *data; // private state struct wl_listener display_destroy; }; struct wlr_session_lock_v1 { struct wl_resource *resource; struct wl_list surfaces; // struct wlr_session_lock_surface_v1.link struct { struct wl_signal new_surface; // struct wlr_session_lock_surface_v1 struct wl_signal unlock; struct wl_signal destroy; } events; void *data; // private state bool locked_sent; }; struct wlr_session_lock_surface_v1_state { uint32_t width, height; uint32_t configure_serial; }; struct wlr_session_lock_surface_v1_configure { struct wl_list link; // wlr_session_lock_surface_v1.configure_list uint32_t serial; uint32_t width, height; }; struct wlr_session_lock_surface_v1 { struct wl_resource *resource; struct wl_list link; // wlr_session_lock_v1.surfaces struct wlr_output *output; struct wlr_surface *surface; bool configured; struct wl_list configure_list; // wlr_session_lock_surface_v1_configure.link struct wlr_session_lock_surface_v1_state current; struct wlr_session_lock_surface_v1_state pending; struct { struct wl_signal destroy; } events; void *data; // private state struct wl_listener output_destroy; }; struct wlr_session_lock_manager_v1 *wlr_session_lock_manager_v1_create( struct wl_display *display); void wlr_session_lock_v1_send_locked(struct wlr_session_lock_v1 *lock); void wlr_session_lock_v1_destroy(struct wlr_session_lock_v1 *lock); uint32_t wlr_session_lock_surface_v1_configure( struct wlr_session_lock_surface_v1 *lock_surface, uint32_t width, uint32_t height); /** * Get a struct wlr_session_lock_surface_v1 from a struct wlr_surface. * * Returns NULL if the surface has a different role or if the lock surface * has been destroyed. */ struct wlr_session_lock_surface_v1 *wlr_session_lock_surface_v1_try_from_wlr_surface( struct wlr_surface *surface); #endif wlroots-0.17.1/include/wlr/types/wlr_shm.h000066400000000000000000000021361454110342200205260ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_TYPES_WLR_SHM_H #define WLR_TYPES_WLR_SHM_H #include struct wlr_renderer; /** * Shared memory buffer interface. * * The buffers created via this interface are not safe to use from different * threads. * * Currently, accessing two buffers concurrently via * wlr_buffer_begin_data_ptr_access() will return an error. */ struct wlr_shm; /** * Create the wl_shm global. * * Compositors using struct wlr_renderer should use wlr_shm_create_with_renderer() * instead. */ struct wlr_shm *wlr_shm_create(struct wl_display *display, uint32_t version, const uint32_t *formats, size_t formats_len); /** * Create the wl_shm global. * * The pixel formats advertised to clients are taken from the struct wlr_renderer. */ struct wlr_shm *wlr_shm_create_with_renderer(struct wl_display *display, uint32_t version, struct wlr_renderer *renderer); #endif wlroots-0.17.1/include/wlr/types/wlr_single_pixel_buffer_v1.h000066400000000000000000000010071454110342200243540ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_TYPES_WLR_SINGLE_PIXEL_BUFFER_V1_H #define WLR_TYPES_WLR_SINGLE_PIXEL_BUFFER_V1_H #include struct wlr_single_pixel_buffer_manager_v1; struct wlr_single_pixel_buffer_manager_v1 *wlr_single_pixel_buffer_manager_v1_create( struct wl_display *display); #endif wlroots-0.17.1/include/wlr/types/wlr_subcompositor.h000066400000000000000000000030771454110342200226540ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_TYPES_WLR_SUBCOMPOSITOR_H #define WLR_TYPES_WLR_SUBCOMPOSITOR_H #include #include #include struct wlr_surface; /** * The sub-surface state describing the sub-surface's relationship with its * parent. Contrary to other states, this one is not applied on surface commit. * Instead, it's applied on parent surface commit. */ struct wlr_subsurface_parent_state { int32_t x, y; struct wl_list link; }; struct wlr_subsurface { struct wl_resource *resource; struct wlr_surface *surface; struct wlr_surface *parent; struct wlr_subsurface_parent_state current, pending; uint32_t cached_seq; bool has_cache; bool synchronized; bool reordered; bool added; struct wl_listener surface_client_commit; struct wl_listener parent_destroy; struct { struct wl_signal destroy; } events; void *data; }; struct wlr_subcompositor { struct wl_global *global; struct wl_listener display_destroy; struct { struct wl_signal destroy; } events; }; /** * Get a struct wlr_subsurface from a struct wlr_surface. * * Returns NULL if the surface doesn't have the subsurface role or if * the subsurface has been destroyed. */ struct wlr_subsurface *wlr_subsurface_try_from_wlr_surface( struct wlr_surface *surface); struct wlr_subcompositor *wlr_subcompositor_create(struct wl_display *display); #endif wlroots-0.17.1/include/wlr/types/wlr_switch.h000066400000000000000000000020441454110342200212360ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_TYPES_WLR_SWITCH_H #define WLR_TYPES_WLR_SWITCH_H #include #include #include struct wlr_switch_impl; struct wlr_switch { struct wlr_input_device base; const struct wlr_switch_impl *impl; struct { struct wl_signal toggle; } events; void *data; }; enum wlr_switch_type { WLR_SWITCH_TYPE_LID, WLR_SWITCH_TYPE_TABLET_MODE, }; enum wlr_switch_state { WLR_SWITCH_STATE_OFF = 0, WLR_SWITCH_STATE_ON, }; struct wlr_switch_toggle_event { uint32_t time_msec; enum wlr_switch_type switch_type; enum wlr_switch_state switch_state; }; /** * Get a struct wlr_switch from a struct wlr_input_device. * * Asserts that the input device is a switch. */ struct wlr_switch *wlr_switch_from_input_device( struct wlr_input_device *input_device); #endif wlroots-0.17.1/include/wlr/types/wlr_tablet_pad.h000066400000000000000000000042411454110342200220350ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_TYPES_WLR_TABLET_PAD_H #define WLR_TYPES_WLR_TABLET_PAD_H #include #include #include /* * NOTE: the wlr tablet pad implementation does not currently support tablets * with more than one mode. I don't own any such hardware so I cannot test it * and it is too complicated to make a meaningful implementation of blindly. */ struct wlr_tablet_pad_impl; struct wlr_tablet_pad { struct wlr_input_device base; const struct wlr_tablet_pad_impl *impl; struct { struct wl_signal button; struct wl_signal ring; struct wl_signal strip; struct wl_signal attach_tablet; // struct wlr_tablet_tool } events; size_t button_count; size_t ring_count; size_t strip_count; struct wl_list groups; // wlr_tablet_pad_group.link struct wl_array paths; // char * void *data; }; struct wlr_tablet_pad_group { struct wl_list link; size_t button_count; unsigned int *buttons; size_t strip_count; unsigned int *strips; size_t ring_count; unsigned int *rings; unsigned int mode_count; }; struct wlr_tablet_pad_button_event { uint32_t time_msec; uint32_t button; enum wlr_button_state state; unsigned int mode; unsigned int group; }; enum wlr_tablet_pad_ring_source { WLR_TABLET_PAD_RING_SOURCE_UNKNOWN, WLR_TABLET_PAD_RING_SOURCE_FINGER, }; struct wlr_tablet_pad_ring_event { uint32_t time_msec; enum wlr_tablet_pad_ring_source source; uint32_t ring; double position; unsigned int mode; }; enum wlr_tablet_pad_strip_source { WLR_TABLET_PAD_STRIP_SOURCE_UNKNOWN, WLR_TABLET_PAD_STRIP_SOURCE_FINGER, }; struct wlr_tablet_pad_strip_event { uint32_t time_msec; enum wlr_tablet_pad_strip_source source; uint32_t strip; double position; unsigned int mode; }; /** * Get a struct wlr_tablet_pad from a struct wlr_input_device. * * Asserts that the input device is a tablet pad. */ struct wlr_tablet_pad *wlr_tablet_pad_from_input_device( struct wlr_input_device *); #endif wlroots-0.17.1/include/wlr/types/wlr_tablet_tool.h000066400000000000000000000064261454110342200222550ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_TYPES_TABLET_TOOL_H #define WLR_TYPES_TABLET_TOOL_H #include #include #include /* * Copy+Paste from libinput, but this should neither use libinput, nor * tablet-unstable-v2 headers, so we can't include them */ enum wlr_tablet_tool_type { /** A generic pen */ WLR_TABLET_TOOL_TYPE_PEN = 1, /** Eraser */ WLR_TABLET_TOOL_TYPE_ERASER, /** A paintbrush-like tool */ WLR_TABLET_TOOL_TYPE_BRUSH, /** Physical drawing tool, e.g. Wacom Inking Pen */ WLR_TABLET_TOOL_TYPE_PENCIL, /** An airbrush-like tool */ WLR_TABLET_TOOL_TYPE_AIRBRUSH, /** A mouse bound to the tablet */ WLR_TABLET_TOOL_TYPE_MOUSE, /** A mouse tool with a lens */ WLR_TABLET_TOOL_TYPE_LENS, /** A rotary device with positional and rotation data */ WLR_TABLET_TOOL_TYPE_TOTEM, }; struct wlr_tablet_tool { enum wlr_tablet_tool_type type; uint64_t hardware_serial; uint64_t hardware_wacom; // Capabilities bool tilt; bool pressure; bool distance; bool rotation; bool slider; bool wheel; struct { struct wl_signal destroy; } events; void *data; }; struct wlr_tablet_impl; struct wlr_tablet { struct wlr_input_device base; const struct wlr_tablet_impl *impl; double width_mm, height_mm; struct { struct wl_signal axis; struct wl_signal proximity; struct wl_signal tip; struct wl_signal button; } events; struct wl_array paths; // char * void *data; }; enum wlr_tablet_tool_axes { WLR_TABLET_TOOL_AXIS_X = 1 << 0, WLR_TABLET_TOOL_AXIS_Y = 1 << 1, WLR_TABLET_TOOL_AXIS_DISTANCE = 1 << 2, WLR_TABLET_TOOL_AXIS_PRESSURE = 1 << 3, WLR_TABLET_TOOL_AXIS_TILT_X = 1 << 4, WLR_TABLET_TOOL_AXIS_TILT_Y = 1 << 5, WLR_TABLET_TOOL_AXIS_ROTATION = 1 << 6, WLR_TABLET_TOOL_AXIS_SLIDER = 1 << 7, WLR_TABLET_TOOL_AXIS_WHEEL = 1 << 8, }; struct wlr_tablet_tool_axis_event { struct wlr_tablet *tablet; struct wlr_tablet_tool *tool; uint32_t time_msec; uint32_t updated_axes; // From 0..1 double x, y; // Relative to last event double dx, dy; double pressure; double distance; double tilt_x, tilt_y; double rotation; double slider; double wheel_delta; }; enum wlr_tablet_tool_proximity_state { WLR_TABLET_TOOL_PROXIMITY_OUT, WLR_TABLET_TOOL_PROXIMITY_IN, }; struct wlr_tablet_tool_proximity_event { struct wlr_tablet *tablet; struct wlr_tablet_tool *tool; uint32_t time_msec; // From 0..1 double x, y; enum wlr_tablet_tool_proximity_state state; }; enum wlr_tablet_tool_tip_state { WLR_TABLET_TOOL_TIP_UP, WLR_TABLET_TOOL_TIP_DOWN, }; struct wlr_tablet_tool_tip_event { struct wlr_tablet *tablet; struct wlr_tablet_tool *tool; uint32_t time_msec; // From 0..1 double x, y; enum wlr_tablet_tool_tip_state state; }; struct wlr_tablet_tool_button_event { struct wlr_tablet *tablet; struct wlr_tablet_tool *tool; uint32_t time_msec; uint32_t button; enum wlr_button_state state; }; /** * Get a struct wlr_tablet from a struct wlr_input_device. * * Asserts that the input device is a tablet tool. */ struct wlr_tablet *wlr_tablet_from_input_device( struct wlr_input_device *input_device); #endif wlroots-0.17.1/include/wlr/types/wlr_tablet_v2.h000066400000000000000000000247401454110342200216260ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_TYPES_WLR_TABLET_V2_H #define WLR_TYPES_WLR_TABLET_V2_H #include #include #include "tablet-unstable-v2-protocol.h" /* This can probably be even lower,the tools don't have a lot of buttons */ #define WLR_TABLET_V2_TOOL_BUTTONS_CAP 16 struct wlr_input_device; struct wlr_tablet_pad_v2_grab_interface; struct wlr_tablet_pad_v2_grab { const struct wlr_tablet_pad_v2_grab_interface *interface; struct wlr_tablet_v2_tablet_pad *pad; void *data; }; struct wlr_tablet_tool_v2_grab_interface; struct wlr_tablet_tool_v2_grab { const struct wlr_tablet_tool_v2_grab_interface *interface; struct wlr_tablet_v2_tablet_tool *tool; void *data; }; struct wlr_tablet_client_v2; struct wlr_tablet_tool_client_v2; struct wlr_tablet_pad_client_v2; struct wlr_tablet_manager_v2 { struct wl_global *wl_global; struct wl_list clients; // wlr_tablet_manager_client_v2.link struct wl_list seats; // wlr_tablet_seat_v2.link struct wl_listener display_destroy; struct { struct wl_signal destroy; } events; void *data; }; struct wlr_tablet_v2_tablet { struct wl_list link; // wlr_tablet_seat_v2.tablets struct wlr_tablet *wlr_tablet; struct wlr_input_device *wlr_device; struct wl_list clients; // wlr_tablet_client_v2.tablet_link struct wl_listener tool_destroy; struct wlr_tablet_client_v2 *current_client; }; struct wlr_tablet_v2_tablet_tool { struct wl_list link; // wlr_tablet_seat_v2.tablets struct wlr_tablet_tool *wlr_tool; struct wl_list clients; // wlr_tablet_tool_client_v2.tool_link struct wl_listener tool_destroy; struct wlr_tablet_tool_client_v2 *current_client; struct wlr_surface *focused_surface; struct wl_listener surface_destroy; struct wlr_tablet_tool_v2_grab *grab; struct wlr_tablet_tool_v2_grab default_grab; uint32_t proximity_serial; bool is_down; uint32_t down_serial; size_t num_buttons; uint32_t pressed_buttons[WLR_TABLET_V2_TOOL_BUTTONS_CAP]; uint32_t pressed_serials[WLR_TABLET_V2_TOOL_BUTTONS_CAP]; struct { struct wl_signal set_cursor; // struct wlr_tablet_v2_event_cursor } events; }; struct wlr_tablet_v2_tablet_pad { struct wl_list link; // wlr_tablet_seat_v2.pads struct wlr_tablet_pad *wlr_pad; struct wlr_input_device *wlr_device; struct wl_list clients; // wlr_tablet_pad_client_v2.pad_link size_t group_count; uint32_t *groups; struct wl_listener pad_destroy; struct wlr_tablet_pad_client_v2 *current_client; struct wlr_tablet_pad_v2_grab *grab; struct wlr_tablet_pad_v2_grab default_grab; struct { struct wl_signal button_feedback; // struct wlr_tablet_v2_event_feedback struct wl_signal strip_feedback; // struct wlr_tablet_v2_event_feedback struct wl_signal ring_feedback; // struct wlr_tablet_v2_event_feedback } events; }; struct wlr_tablet_v2_event_cursor { struct wlr_surface *surface; uint32_t serial; int32_t hotspot_x; int32_t hotspot_y; struct wlr_seat_client *seat_client; }; struct wlr_tablet_v2_event_feedback { const char *description; size_t index; uint32_t serial; }; struct wlr_tablet_v2_tablet *wlr_tablet_create( struct wlr_tablet_manager_v2 *manager, struct wlr_seat *wlr_seat, struct wlr_input_device *wlr_device); struct wlr_tablet_v2_tablet_pad *wlr_tablet_pad_create( struct wlr_tablet_manager_v2 *manager, struct wlr_seat *wlr_seat, struct wlr_input_device *wlr_device); struct wlr_tablet_v2_tablet_tool *wlr_tablet_tool_create( struct wlr_tablet_manager_v2 *manager, struct wlr_seat *wlr_seat, struct wlr_tablet_tool *wlr_tool); struct wlr_tablet_manager_v2 *wlr_tablet_v2_create(struct wl_display *display); void wlr_send_tablet_v2_tablet_tool_proximity_in( struct wlr_tablet_v2_tablet_tool *tool, struct wlr_tablet_v2_tablet *tablet, struct wlr_surface *surface); void wlr_send_tablet_v2_tablet_tool_down(struct wlr_tablet_v2_tablet_tool *tool); void wlr_send_tablet_v2_tablet_tool_up(struct wlr_tablet_v2_tablet_tool *tool); void wlr_send_tablet_v2_tablet_tool_motion( struct wlr_tablet_v2_tablet_tool *tool, double x, double y); void wlr_send_tablet_v2_tablet_tool_pressure( struct wlr_tablet_v2_tablet_tool *tool, double pressure); void wlr_send_tablet_v2_tablet_tool_distance( struct wlr_tablet_v2_tablet_tool *tool, double distance); void wlr_send_tablet_v2_tablet_tool_tilt( struct wlr_tablet_v2_tablet_tool *tool, double x, double y); void wlr_send_tablet_v2_tablet_tool_rotation( struct wlr_tablet_v2_tablet_tool *tool, double degrees); void wlr_send_tablet_v2_tablet_tool_slider( struct wlr_tablet_v2_tablet_tool *tool, double position); void wlr_send_tablet_v2_tablet_tool_wheel( struct wlr_tablet_v2_tablet_tool *tool, double degrees, int32_t clicks); void wlr_send_tablet_v2_tablet_tool_proximity_out( struct wlr_tablet_v2_tablet_tool *tool); void wlr_send_tablet_v2_tablet_tool_button( struct wlr_tablet_v2_tablet_tool *tool, uint32_t button, enum zwp_tablet_pad_v2_button_state state); void wlr_tablet_v2_tablet_tool_notify_proximity_in( struct wlr_tablet_v2_tablet_tool *tool, struct wlr_tablet_v2_tablet *tablet, struct wlr_surface *surface); void wlr_tablet_v2_tablet_tool_notify_down(struct wlr_tablet_v2_tablet_tool *tool); void wlr_tablet_v2_tablet_tool_notify_up(struct wlr_tablet_v2_tablet_tool *tool); void wlr_tablet_v2_tablet_tool_notify_motion( struct wlr_tablet_v2_tablet_tool *tool, double x, double y); void wlr_tablet_v2_tablet_tool_notify_pressure( struct wlr_tablet_v2_tablet_tool *tool, double pressure); void wlr_tablet_v2_tablet_tool_notify_distance( struct wlr_tablet_v2_tablet_tool *tool, double distance); void wlr_tablet_v2_tablet_tool_notify_tilt( struct wlr_tablet_v2_tablet_tool *tool, double x, double y); void wlr_tablet_v2_tablet_tool_notify_rotation( struct wlr_tablet_v2_tablet_tool *tool, double degrees); void wlr_tablet_v2_tablet_tool_notify_slider( struct wlr_tablet_v2_tablet_tool *tool, double position); void wlr_tablet_v2_tablet_tool_notify_wheel( struct wlr_tablet_v2_tablet_tool *tool, double degrees, int32_t clicks); void wlr_tablet_v2_tablet_tool_notify_proximity_out( struct wlr_tablet_v2_tablet_tool *tool); void wlr_tablet_v2_tablet_tool_notify_button( struct wlr_tablet_v2_tablet_tool *tool, uint32_t button, enum zwp_tablet_pad_v2_button_state state); struct wlr_tablet_tool_v2_grab_interface { void (*proximity_in)( struct wlr_tablet_tool_v2_grab *grab, struct wlr_tablet_v2_tablet *tablet, struct wlr_surface *surface); void (*down)(struct wlr_tablet_tool_v2_grab *grab); void (*up)(struct wlr_tablet_tool_v2_grab *grab); void (*motion)(struct wlr_tablet_tool_v2_grab *grab, double x, double y); void (*pressure)(struct wlr_tablet_tool_v2_grab *grab, double pressure); void (*distance)(struct wlr_tablet_tool_v2_grab *grab, double distance); void (*tilt)(struct wlr_tablet_tool_v2_grab *grab, double x, double y); void (*rotation)(struct wlr_tablet_tool_v2_grab *grab, double degrees); void (*slider)(struct wlr_tablet_tool_v2_grab *grab, double position); void (*wheel)(struct wlr_tablet_tool_v2_grab *grab, double degrees, int32_t clicks); void (*proximity_out)(struct wlr_tablet_tool_v2_grab *grab); void (*button)( struct wlr_tablet_tool_v2_grab *grab, uint32_t button, enum zwp_tablet_pad_v2_button_state state); void (*cancel)(struct wlr_tablet_tool_v2_grab *grab); }; void wlr_tablet_tool_v2_start_grab(struct wlr_tablet_v2_tablet_tool *tool, struct wlr_tablet_tool_v2_grab *grab); void wlr_tablet_tool_v2_end_grab(struct wlr_tablet_v2_tablet_tool *tool); void wlr_tablet_tool_v2_start_implicit_grab(struct wlr_tablet_v2_tablet_tool *tool); bool wlr_tablet_tool_v2_has_implicit_grab( struct wlr_tablet_v2_tablet_tool *tool); uint32_t wlr_send_tablet_v2_tablet_pad_enter( struct wlr_tablet_v2_tablet_pad *pad, struct wlr_tablet_v2_tablet *tablet, struct wlr_surface *surface); void wlr_send_tablet_v2_tablet_pad_button( struct wlr_tablet_v2_tablet_pad *pad, size_t button, uint32_t time, enum zwp_tablet_pad_v2_button_state state); void wlr_send_tablet_v2_tablet_pad_strip(struct wlr_tablet_v2_tablet_pad *pad, uint32_t strip, double position, bool finger, uint32_t time); void wlr_send_tablet_v2_tablet_pad_ring(struct wlr_tablet_v2_tablet_pad *pad, uint32_t ring, double position, bool finger, uint32_t time); uint32_t wlr_send_tablet_v2_tablet_pad_leave(struct wlr_tablet_v2_tablet_pad *pad, struct wlr_surface *surface); uint32_t wlr_send_tablet_v2_tablet_pad_mode(struct wlr_tablet_v2_tablet_pad *pad, size_t group, uint32_t mode, uint32_t time); uint32_t wlr_tablet_v2_tablet_pad_notify_enter( struct wlr_tablet_v2_tablet_pad *pad, struct wlr_tablet_v2_tablet *tablet, struct wlr_surface *surface); void wlr_tablet_v2_tablet_pad_notify_button( struct wlr_tablet_v2_tablet_pad *pad, size_t button, uint32_t time, enum zwp_tablet_pad_v2_button_state state); void wlr_tablet_v2_tablet_pad_notify_strip( struct wlr_tablet_v2_tablet_pad *pad, uint32_t strip, double position, bool finger, uint32_t time); void wlr_tablet_v2_tablet_pad_notify_ring( struct wlr_tablet_v2_tablet_pad *pad, uint32_t ring, double position, bool finger, uint32_t time); uint32_t wlr_tablet_v2_tablet_pad_notify_leave( struct wlr_tablet_v2_tablet_pad *pad, struct wlr_surface *surface); uint32_t wlr_tablet_v2_tablet_pad_notify_mode( struct wlr_tablet_v2_tablet_pad *pad, size_t group, uint32_t mode, uint32_t time); struct wlr_tablet_pad_v2_grab_interface { uint32_t (*enter)( struct wlr_tablet_pad_v2_grab *grab, struct wlr_tablet_v2_tablet *tablet, struct wlr_surface *surface); void (*button)(struct wlr_tablet_pad_v2_grab *grab,size_t button, uint32_t time, enum zwp_tablet_pad_v2_button_state state); void (*strip)(struct wlr_tablet_pad_v2_grab *grab, uint32_t strip, double position, bool finger, uint32_t time); void (*ring)(struct wlr_tablet_pad_v2_grab *grab, uint32_t ring, double position, bool finger, uint32_t time); uint32_t (*leave)(struct wlr_tablet_pad_v2_grab *grab, struct wlr_surface *surface); uint32_t (*mode)(struct wlr_tablet_pad_v2_grab *grab, size_t group, uint32_t mode, uint32_t time); void (*cancel)(struct wlr_tablet_pad_v2_grab *grab); }; void wlr_tablet_v2_end_grab(struct wlr_tablet_v2_tablet_pad *pad); void wlr_tablet_v2_start_grab(struct wlr_tablet_v2_tablet_pad *pad, struct wlr_tablet_pad_v2_grab *grab); bool wlr_surface_accepts_tablet_v2(struct wlr_tablet_v2_tablet *tablet, struct wlr_surface *surface); #endif /* WLR_TYPES_WLR_TABLET_V2_H */ wlroots-0.17.1/include/wlr/types/wlr_tearing_control_v1.h000066400000000000000000000026401454110342200235360ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_TYPES_WLR_TEARING_CONTROL_MANAGER_V1_H #define WLR_TYPES_WLR_TEARING_CONTROL_MANAGER_V1_H #include #include #include #include #include "tearing-control-v1-protocol.h" struct wlr_tearing_control_v1 { uint32_t hint; struct wl_client *client; struct wl_list link; struct wl_resource *resource; struct { struct wl_signal set_hint; struct wl_signal destroy; } events; struct wlr_surface *surface; struct wlr_addon addon; }; struct wlr_tearing_control_manager_v1 { struct wl_global *global; struct wl_list surface_hints; // wlr_tearing_control_v1.link struct wl_listener display_destroy; struct { struct wl_signal new_object; // struct wlr_tearing_control_v1* struct wl_signal destroy; } events; void *data; }; struct wlr_tearing_control_manager_v1 *wlr_tearing_control_manager_v1_create( struct wl_display *display, uint32_t version); /** * Returns the tearing hint for a given surface */ enum wp_tearing_control_v1_presentation_hint wlr_tearing_control_manager_v1_surface_hint_from_surface( struct wlr_tearing_control_manager_v1 *manager, struct wlr_surface *surface); #endif wlroots-0.17.1/include/wlr/types/wlr_text_input_v3.h000066400000000000000000000060141454110342200225510ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_TYPES_WLR_TEXT_INPUT_V3_H #define WLR_TYPES_WLR_TEXT_INPUT_V3_H #include #include #include struct wlr_surface; enum wlr_text_input_v3_features { WLR_TEXT_INPUT_V3_FEATURE_SURROUNDING_TEXT = 1 << 0, WLR_TEXT_INPUT_V3_FEATURE_CONTENT_TYPE = 1 << 1, WLR_TEXT_INPUT_V3_FEATURE_CURSOR_RECTANGLE = 1 << 2, }; struct wlr_text_input_v3_state { struct { char *text; // NULL is allowed and equivalent to empty string uint32_t cursor; uint32_t anchor; } surrounding; uint32_t text_change_cause; struct { uint32_t hint; uint32_t purpose; } content_type; struct wlr_box cursor_rectangle; // Tracks which features were used in the current commit. // Useful in the enabling commit, where usage means support. uint32_t features; // bitfield of enum wlr_text_input_v3_features }; struct wlr_text_input_v3 { struct wlr_seat *seat; // becomes null when seat destroyed struct wl_resource *resource; struct wlr_surface *focused_surface; struct wlr_text_input_v3_state pending; struct wlr_text_input_v3_state current; uint32_t current_serial; // next in line to send bool pending_enabled; bool current_enabled; // supported in the current text input, more granular than surface uint32_t active_features; // bitfield of enum wlr_text_input_v3_features struct wl_list link; struct wl_listener surface_destroy; struct wl_listener seat_destroy; struct { struct wl_signal enable; // struct wlr_text_input_v3 struct wl_signal commit; // struct wlr_text_input_v3 struct wl_signal disable; // struct wlr_text_input_v3 struct wl_signal destroy; // struct wlr_text_input_v3 } events; }; struct wlr_text_input_manager_v3 { struct wl_global *global; struct wl_list text_inputs; // struct wlr_text_input_v3.resource.link struct wl_listener display_destroy; struct { struct wl_signal text_input; // struct wlr_text_input_v3 struct wl_signal destroy; // struct wlr_input_method_manager_v3 } events; }; struct wlr_text_input_manager_v3 *wlr_text_input_manager_v3_create( struct wl_display *wl_display); // Sends enter to the surface and saves it void wlr_text_input_v3_send_enter(struct wlr_text_input_v3 *text_input, struct wlr_surface *wlr_surface); // Sends leave to the currently focused surface and clears it void wlr_text_input_v3_send_leave(struct wlr_text_input_v3 *text_input); void wlr_text_input_v3_send_preedit_string(struct wlr_text_input_v3 *text_input, const char *text, int32_t cursor_begin, int32_t cursor_end); void wlr_text_input_v3_send_commit_string(struct wlr_text_input_v3 *text_input, const char *text); void wlr_text_input_v3_send_delete_surrounding_text( struct wlr_text_input_v3 *text_input, uint32_t before_length, uint32_t after_length); void wlr_text_input_v3_send_done(struct wlr_text_input_v3 *text_input); #endif wlroots-0.17.1/include/wlr/types/wlr_touch.h000066400000000000000000000027431454110342200210650ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_TYPES_WLR_TOUCH_H #define WLR_TYPES_WLR_TOUCH_H #include #include #include struct wlr_touch_impl; struct wlr_touch { struct wlr_input_device base; const struct wlr_touch_impl *impl; char *output_name; double width_mm, height_mm; struct { struct wl_signal down; // struct wlr_touch_down_event struct wl_signal up; // struct wlr_touch_up_event struct wl_signal motion; // struct wlr_touch_motion_event struct wl_signal cancel; // struct wlr_touch_cancel_event struct wl_signal frame; } events; void *data; }; struct wlr_touch_down_event { struct wlr_touch *touch; uint32_t time_msec; int32_t touch_id; // From 0..1 double x, y; }; struct wlr_touch_up_event { struct wlr_touch *touch; uint32_t time_msec; int32_t touch_id; }; struct wlr_touch_motion_event { struct wlr_touch *touch; uint32_t time_msec; int32_t touch_id; // From 0..1 double x, y; }; struct wlr_touch_cancel_event { struct wlr_touch *touch; uint32_t time_msec; int32_t touch_id; }; /** * Get a struct wlr_touch from a struct wlr_input_device. * * Asserts that the input device is a touch device. */ struct wlr_touch *wlr_touch_from_input_device( struct wlr_input_device *input_device); #endif wlroots-0.17.1/include/wlr/types/wlr_viewporter.h000066400000000000000000000017411454110342200221460ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_TYPES_WLR_VIEWPORTER_H #define WLR_TYPES_WLR_VIEWPORTER_H #include /** * Implementation for the viewporter protocol. * * When enabling viewporter, compositors need to update their rendering logic: * * - The size of the surface texture may not match the surface size anymore. * Compositors must use the surface size only. * - Compositors must call wlr_render_subtexture_with_matrix() when rendering a * surface texture with the source box returned by * wlr_surface_get_buffer_source_box(). */ struct wlr_viewporter { struct wl_global *global; struct { struct wl_signal destroy; } events; struct wl_listener display_destroy; }; struct wlr_viewporter *wlr_viewporter_create(struct wl_display *display); #endif wlroots-0.17.1/include/wlr/types/wlr_virtual_keyboard_v1.h000066400000000000000000000021461454110342200237140ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_TYPES_WLR_VIRTUAL_KEYBOARD_V1_H #define WLR_TYPES_WLR_VIRTUAL_KEYBOARD_V1_H #include #include struct wlr_virtual_keyboard_manager_v1 { struct wl_global *global; struct wl_list virtual_keyboards; // wlr_virtual_keyboard_v1.link struct wl_listener display_destroy; struct { struct wl_signal new_virtual_keyboard; // struct wlr_virtual_keyboard_v1 struct wl_signal destroy; } events; }; struct wlr_virtual_keyboard_v1 { struct wlr_keyboard keyboard; struct wl_resource *resource; struct wlr_seat *seat; bool has_keymap; struct wl_list link; // wlr_virtual_keyboard_manager_v1.virtual_keyboards }; struct wlr_virtual_keyboard_manager_v1* wlr_virtual_keyboard_manager_v1_create( struct wl_display *display); struct wlr_virtual_keyboard_v1 *wlr_input_device_get_virtual_keyboard( struct wlr_input_device *wlr_dev); #endif wlroots-0.17.1/include/wlr/types/wlr_virtual_pointer_v1.h000066400000000000000000000025671454110342200236030ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_TYPES_WLR_VIRTUAL_POINTER_V1_H #define WLR_TYPES_WLR_VIRTUAL_POINTER_V1_H #include #include #include #include struct wlr_virtual_pointer_manager_v1 { struct wl_global *global; struct wl_list virtual_pointers; // wlr_virtual_pointer_v1.link struct wl_listener display_destroy; struct { struct wl_signal new_virtual_pointer; // struct wlr_virtual_pointer_v1_new_pointer_event struct wl_signal destroy; } events; }; struct wlr_virtual_pointer_v1 { struct wlr_pointer pointer; struct wl_resource *resource; /* Vertical and horizontal */ struct wlr_pointer_axis_event axis_event[2]; enum wl_pointer_axis axis; bool axis_valid[2]; struct wl_list link; // wlr_virtual_pointer_manager_v1.virtual_pointers }; struct wlr_virtual_pointer_v1_new_pointer_event { struct wlr_virtual_pointer_v1 *new_pointer; /** Suggested by client; may be NULL. */ struct wlr_seat *suggested_seat; struct wlr_output *suggested_output; }; struct wlr_virtual_pointer_manager_v1* wlr_virtual_pointer_manager_v1_create( struct wl_display *display); #endif wlroots-0.17.1/include/wlr/types/wlr_xcursor_manager.h000066400000000000000000000033351454110342200231400ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_TYPES_WLR_XCURSOR_MANAGER_H #define WLR_TYPES_WLR_XCURSOR_MANAGER_H #include #include /** * An XCursor theme at a particular scale factor of the base size. */ struct wlr_xcursor_manager_theme { float scale; struct wlr_xcursor_theme *theme; struct wl_list link; }; /** * struct wlr_xcursor_manager dynamically loads xcursor themes at sizes necessary * for use on outputs at arbitrary scale factors. You should call * wlr_xcursor_manager_load() for each output you will show your cursor on, with * the scale factor parameter set to that output's scale factor. */ struct wlr_xcursor_manager { char *name; uint32_t size; struct wl_list scaled_themes; // wlr_xcursor_manager_theme.link }; /** * Creates a new XCursor manager with the given xcursor theme name and base size * (for use when scale=1). */ struct wlr_xcursor_manager *wlr_xcursor_manager_create(const char *name, uint32_t size); void wlr_xcursor_manager_destroy(struct wlr_xcursor_manager *manager); /** * Ensures an xcursor theme at the given scale factor is loaded in the manager. */ bool wlr_xcursor_manager_load(struct wlr_xcursor_manager *manager, float scale); /** * Retrieves a wlr_xcursor reference for the given cursor name at the given * scale factor, or NULL if this struct wlr_xcursor_manager has not loaded a * cursor theme at the requested scale. */ struct wlr_xcursor *wlr_xcursor_manager_get_xcursor( struct wlr_xcursor_manager *manager, const char *name, float scale); #endif wlroots-0.17.1/include/wlr/types/wlr_xdg_activation_v1.h000066400000000000000000000050561454110342200233540ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_TYPES_WLR_XDG_ACTIVATION_V1 #define WLR_TYPES_WLR_XDG_ACTIVATION_V1 #include struct wlr_xdg_activation_token_v1 { struct wlr_xdg_activation_v1 *activation; // The source surface that created the token. struct wlr_surface *surface; // can be NULL struct wlr_seat *seat; // can be NULL // The serial for the input event that created the token. uint32_t serial; // invalid if seat is NULL // The application ID to be activated. This is just a hint. char *app_id; // can be NULL struct wl_list link; // wlr_xdg_activation_v1.tokens void *data; struct { struct wl_signal destroy; } events; // private state char *token; struct wl_resource *resource; // can be NULL struct wl_event_source *timeout; // can be NULL struct wl_listener seat_destroy; struct wl_listener surface_destroy; }; struct wlr_xdg_activation_v1 { uint32_t token_timeout_msec; // token timeout in milliseconds (0 to disable) struct wl_list tokens; // wlr_xdg_activation_token_v1.link struct { struct wl_signal destroy; struct wl_signal request_activate; // struct wlr_xdg_activation_v1_request_activate_event struct wl_signal new_token; // struct wlr_xdg_activation_token_v1 } events; // private state struct wl_display *display; struct wl_global *global; struct wl_listener display_destroy; }; struct wlr_xdg_activation_v1_request_activate_event { struct wlr_xdg_activation_v1 *activation; // The token used to request activation. struct wlr_xdg_activation_token_v1 *token; // The surface requesting for activation. struct wlr_surface *surface; }; struct wlr_xdg_activation_v1 *wlr_xdg_activation_v1_create( struct wl_display *display); struct wlr_xdg_activation_token_v1 *wlr_xdg_activation_token_v1_create( struct wlr_xdg_activation_v1 *activation); void wlr_xdg_activation_token_v1_destroy( struct wlr_xdg_activation_token_v1 *token); struct wlr_xdg_activation_token_v1 *wlr_xdg_activation_v1_find_token( struct wlr_xdg_activation_v1 *activation, const char *token_str); // Get a string suitable for exporting to launched clients const char *wlr_xdg_activation_token_v1_get_name( struct wlr_xdg_activation_token_v1 *token); // Add a token to the pool of known tokens struct wlr_xdg_activation_token_v1 *wlr_xdg_activation_v1_add_token( struct wlr_xdg_activation_v1 *activation, const char *token_str); #endif wlroots-0.17.1/include/wlr/types/wlr_xdg_decoration_v1.h000066400000000000000000000037331454110342200233420ustar00rootroot00000000000000#ifndef WLR_TYPES_WLR_XDG_DECORATION_V1 #define WLR_TYPES_WLR_XDG_DECORATION_V1 #include #include enum wlr_xdg_toplevel_decoration_v1_mode { WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_NONE = 0, WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE = 1, WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE = 2, }; struct wlr_xdg_decoration_manager_v1 { struct wl_global *global; struct wl_list decorations; // wlr_xdg_toplevel_decoration.link struct wl_listener display_destroy; struct { struct wl_signal new_toplevel_decoration; // struct wlr_xdg_toplevel_decoration struct wl_signal destroy; } events; void *data; }; struct wlr_xdg_toplevel_decoration_v1_configure { struct wl_list link; // wlr_xdg_toplevel_decoration.configure_list struct wlr_xdg_surface_configure *surface_configure; enum wlr_xdg_toplevel_decoration_v1_mode mode; }; struct wlr_xdg_toplevel_decoration_v1_state { enum wlr_xdg_toplevel_decoration_v1_mode mode; }; struct wlr_xdg_toplevel_decoration_v1 { struct wl_resource *resource; struct wlr_xdg_toplevel * toplevel; struct wlr_xdg_decoration_manager_v1 *manager; struct wl_list link; // wlr_xdg_decoration_manager_v1.link struct wlr_xdg_toplevel_decoration_v1_state current, pending; enum wlr_xdg_toplevel_decoration_v1_mode scheduled_mode; enum wlr_xdg_toplevel_decoration_v1_mode requested_mode; bool added; struct wl_list configure_list; // wlr_xdg_toplevel_decoration_v1_configure.link struct { struct wl_signal destroy; struct wl_signal request_mode; } events; struct wl_listener surface_destroy; struct wl_listener surface_configure; struct wl_listener surface_ack_configure; struct wl_listener surface_commit; void *data; }; struct wlr_xdg_decoration_manager_v1 * wlr_xdg_decoration_manager_v1_create(struct wl_display *display); uint32_t wlr_xdg_toplevel_decoration_v1_set_mode( struct wlr_xdg_toplevel_decoration_v1 *decoration, enum wlr_xdg_toplevel_decoration_v1_mode mode); #endif wlroots-0.17.1/include/wlr/types/wlr_xdg_foreign_registry.h000066400000000000000000000041641454110342200241650ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_TYPES_WLR_XDG_FOREIGN_REGISTRY_H #define WLR_TYPES_WLR_XDG_FOREIGN_REGISTRY_H #include #define WLR_XDG_FOREIGN_HANDLE_SIZE 37 /** * struct wlr_xdg_foreign_registry is used for storing a list of exported * surfaces with the xdg-foreign family of protocols. * * It can be used to allow interoperability between clients using different * versions of the protocol (if all versions use the same registry). */ struct wlr_xdg_foreign_registry { struct wl_list exported_surfaces; // struct wlr_xdg_foreign_exported_surface struct wl_listener display_destroy; struct { struct wl_signal destroy; } events; }; struct wlr_xdg_foreign_exported { struct wl_list link; // wlr_xdg_foreign_registry.exported_surfaces struct wlr_xdg_foreign_registry *registry; struct wlr_surface *surface; char handle[WLR_XDG_FOREIGN_HANDLE_SIZE]; struct { struct wl_signal destroy; } events; }; /** * Create an empty struct wlr_xdg_foreign_registry. * * It will be destroyed when the associated display is destroyed. */ struct wlr_xdg_foreign_registry *wlr_xdg_foreign_registry_create( struct wl_display *display); /** * Add the given exported surface to the registry and assign it a unique handle. * The caller is responsible for removing the exported surface from the repository * if it is destroyed. * * Returns true if the initialization was successful. */ bool wlr_xdg_foreign_exported_init(struct wlr_xdg_foreign_exported *surface, struct wlr_xdg_foreign_registry *registry); /** * Find an exported surface with the given handle, or NULL if such a surface * does not exist. */ struct wlr_xdg_foreign_exported *wlr_xdg_foreign_registry_find_by_handle( struct wlr_xdg_foreign_registry *registry, const char *handle); /** * Remove the given surface from the registry it was previously added in. */ void wlr_xdg_foreign_exported_finish(struct wlr_xdg_foreign_exported *surface); #endif wlroots-0.17.1/include/wlr/types/wlr_xdg_foreign_v1.h000066400000000000000000000030541454110342200226400ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_TYPES_WLR_XDG_FOREIGN_V1_H #define WLR_TYPES_WLR_XDG_FOREIGN_V1_H #include #include struct wlr_xdg_foreign_v1 { struct { struct wl_global *global; struct wl_list objects; // wlr_xdg_exported_v1.link or wlr_xdg_imported_v1.link } exporter, importer; struct wl_listener foreign_registry_destroy; struct wl_listener display_destroy; struct wlr_xdg_foreign_registry *registry; struct { struct wl_signal destroy; } events; void *data; }; struct wlr_xdg_exported_v1 { struct wlr_xdg_foreign_exported base; struct wl_resource *resource; struct wl_listener xdg_surface_destroy; struct wl_list link; // wlr_xdg_foreign_v1.exporter.objects }; struct wlr_xdg_imported_v1 { struct wlr_xdg_foreign_exported *exported; struct wl_listener exported_destroyed; struct wl_resource *resource; struct wl_list link; // wlr_xdg_foreign_v1.importer.objects struct wl_list children; }; struct wlr_xdg_imported_child_v1 { struct wlr_xdg_imported_v1 *imported; struct wlr_surface *surface; struct wl_list link; // wlr_xdg_imported_v1.children struct wl_listener xdg_surface_destroy; struct wl_listener xdg_toplevel_set_parent; }; struct wlr_xdg_foreign_v1 *wlr_xdg_foreign_v1_create( struct wl_display *display, struct wlr_xdg_foreign_registry *registry); #endif wlroots-0.17.1/include/wlr/types/wlr_xdg_foreign_v2.h000066400000000000000000000030541454110342200226410ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_TYPES_WLR_XDG_FOREIGN_V2_H #define WLR_TYPES_WLR_XDG_FOREIGN_V2_H #include #include struct wlr_xdg_foreign_v2 { struct { struct wl_global *global; struct wl_list objects; // wlr_xdg_exported_v2.link or wlr_xdg_imported_v2.link } exporter, importer; struct wl_listener foreign_registry_destroy; struct wl_listener display_destroy; struct wlr_xdg_foreign_registry *registry; struct { struct wl_signal destroy; } events; void *data; }; struct wlr_xdg_exported_v2 { struct wlr_xdg_foreign_exported base; struct wl_resource *resource; struct wl_listener xdg_surface_destroy; struct wl_list link; // wlr_xdg_foreign_v2.exporter.objects }; struct wlr_xdg_imported_v2 { struct wlr_xdg_foreign_exported *exported; struct wl_listener exported_destroyed; struct wl_resource *resource; struct wl_list link; // wlr_xdg_foreign_v2.importer.objects struct wl_list children; }; struct wlr_xdg_imported_child_v2 { struct wlr_xdg_imported_v2 *imported; struct wlr_surface *surface; struct wl_list link; // wlr_xdg_imported_v2.children struct wl_listener xdg_surface_destroy; struct wl_listener xdg_toplevel_set_parent; }; struct wlr_xdg_foreign_v2 *wlr_xdg_foreign_v2_create( struct wl_display *display, struct wlr_xdg_foreign_registry *registry); #endif wlroots-0.17.1/include/wlr/types/wlr_xdg_output_v1.h000066400000000000000000000021241454110342200225440ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_TYPES_WLR_XDG_OUTPUT_V1_H #define WLR_TYPES_WLR_XDG_OUTPUT_V1_H #include #include struct wlr_xdg_output_v1 { struct wlr_xdg_output_manager_v1 *manager; struct wl_list resources; struct wl_list link; struct wlr_output_layout_output *layout_output; int32_t x, y; int32_t width, height; struct wl_listener destroy; struct wl_listener description; }; struct wlr_xdg_output_manager_v1 { struct wl_global *global; struct wlr_output_layout *layout; struct wl_list outputs; struct { struct wl_signal destroy; } events; struct wl_listener display_destroy; struct wl_listener layout_add; struct wl_listener layout_change; struct wl_listener layout_destroy; }; struct wlr_xdg_output_manager_v1 *wlr_xdg_output_manager_v1_create( struct wl_display *display, struct wlr_output_layout *layout); #endif wlroots-0.17.1/include/wlr/types/wlr_xdg_shell.h000066400000000000000000000367141454110342200217210ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_TYPES_WLR_XDG_SHELL_H #define WLR_TYPES_WLR_XDG_SHELL_H #include #include #include #include #include "xdg-shell-protocol.h" struct wlr_xdg_shell { struct wl_global *global; uint32_t version; struct wl_list clients; struct wl_list popup_grabs; uint32_t ping_timeout; struct wl_listener display_destroy; struct { struct wl_signal new_surface; // struct wlr_xdg_surface struct wl_signal destroy; } events; void *data; }; struct wlr_xdg_client { struct wlr_xdg_shell *shell; struct wl_resource *resource; struct wl_client *client; struct wl_list surfaces; struct wl_list link; // wlr_xdg_shell.clients uint32_t ping_serial; struct wl_event_source *ping_timer; }; struct wlr_xdg_positioner_rules { struct wlr_box anchor_rect; enum xdg_positioner_anchor anchor; enum xdg_positioner_gravity gravity; enum xdg_positioner_constraint_adjustment constraint_adjustment; bool reactive; bool has_parent_configure_serial; uint32_t parent_configure_serial; struct { int32_t width, height; } size, parent_size; struct { int32_t x, y; } offset; }; struct wlr_xdg_positioner { struct wl_resource *resource; struct wlr_xdg_positioner_rules rules; }; struct wlr_xdg_popup_state { // Position of the popup relative to the upper left corner of // the window geometry of the parent surface struct wlr_box geometry; bool reactive; }; enum wlr_xdg_popup_configure_field { WLR_XDG_POPUP_CONFIGURE_REPOSITION_TOKEN = 1 << 0, }; struct wlr_xdg_popup_configure { uint32_t fields; // enum wlr_xdg_popup_configure_field struct wlr_box geometry; struct wlr_xdg_positioner_rules rules; uint32_t reposition_token; }; struct wlr_xdg_popup { struct wlr_xdg_surface *base; struct wl_list link; struct wl_resource *resource; bool sent_initial_configure; struct wlr_surface *parent; struct wlr_seat *seat; struct wlr_xdg_popup_configure scheduled; struct wlr_xdg_popup_state current, pending; struct { struct wl_signal reposition; } events; struct wl_list grab_link; // wlr_xdg_popup_grab.popups }; // each seat gets a popup grab struct wlr_xdg_popup_grab { struct wl_client *client; struct wlr_seat_pointer_grab pointer_grab; struct wlr_seat_keyboard_grab keyboard_grab; struct wlr_seat_touch_grab touch_grab; struct wlr_seat *seat; struct wl_list popups; struct wl_list link; // wlr_xdg_shell.popup_grabs struct wl_listener seat_destroy; }; enum wlr_xdg_surface_role { WLR_XDG_SURFACE_ROLE_NONE, WLR_XDG_SURFACE_ROLE_TOPLEVEL, WLR_XDG_SURFACE_ROLE_POPUP, }; struct wlr_xdg_toplevel_state { bool maximized, fullscreen, resizing, activated, suspended; uint32_t tiled; // enum wlr_edges int32_t width, height; int32_t max_width, max_height; int32_t min_width, min_height; }; enum wlr_xdg_toplevel_wm_capabilities { WLR_XDG_TOPLEVEL_WM_CAPABILITIES_WINDOW_MENU = 1 << 0, WLR_XDG_TOPLEVEL_WM_CAPABILITIES_MAXIMIZE = 1 << 1, WLR_XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN = 1 << 2, WLR_XDG_TOPLEVEL_WM_CAPABILITIES_MINIMIZE = 1 << 3, }; enum wlr_xdg_toplevel_configure_field { WLR_XDG_TOPLEVEL_CONFIGURE_BOUNDS = 1 << 0, WLR_XDG_TOPLEVEL_CONFIGURE_WM_CAPABILITIES = 1 << 1, }; struct wlr_xdg_toplevel_configure { uint32_t fields; // enum wlr_xdg_toplevel_configure_field bool maximized, fullscreen, resizing, activated, suspended; uint32_t tiled; // enum wlr_edges int32_t width, height; struct { int32_t width, height; } bounds; uint32_t wm_capabilities; // enum wlr_xdg_toplevel_wm_capabilities }; struct wlr_xdg_toplevel_requested { bool maximized, minimized, fullscreen; struct wlr_output *fullscreen_output; struct wl_listener fullscreen_output_destroy; }; struct wlr_xdg_toplevel { struct wl_resource *resource; struct wlr_xdg_surface *base; struct wlr_xdg_toplevel *parent; struct wl_listener parent_unmap; struct wlr_xdg_toplevel_state current, pending; // Properties to be sent to the client in the next configure event. struct wlr_xdg_toplevel_configure scheduled; // Properties that the client has requested. Intended to be checked // by the compositor on surface map and state change requests (such as // xdg_toplevel.set_fullscreen) and handled accordingly. struct wlr_xdg_toplevel_requested requested; char *title; char *app_id; struct { // Note: as per xdg-shell protocol, the compositor has to // handle state requests by sending a configure event, // even if it didn't actually change the state. Therefore, // every compositor implementing xdg-shell support *must* // listen to these signals and schedule a configure event // immediately or at some time in the future; not doing so // is a protocol violation. struct wl_signal request_maximize; struct wl_signal request_fullscreen; struct wl_signal request_minimize; struct wl_signal request_move; struct wl_signal request_resize; struct wl_signal request_show_window_menu; struct wl_signal set_parent; struct wl_signal set_title; struct wl_signal set_app_id; } events; }; struct wlr_xdg_surface_configure { struct wlr_xdg_surface *surface; struct wl_list link; // wlr_xdg_surface.configure_list uint32_t serial; union { struct wlr_xdg_toplevel_configure *toplevel_configure; struct wlr_xdg_popup_configure *popup_configure; }; }; struct wlr_xdg_surface_state { uint32_t configure_serial; struct wlr_box geometry; }; /** * An xdg-surface is a user interface element requiring management by the * compositor. An xdg-surface alone isn't useful, a role should be assigned to * it in order to map it. */ struct wlr_xdg_surface { struct wlr_xdg_client *client; struct wl_resource *resource; struct wlr_surface *surface; struct wl_list link; // wlr_xdg_client.surfaces /** * The lifetime-bound role of the xdg_surface. WLR_XDG_SURFACE_ROLE_NONE * if the role was never set. */ enum wlr_xdg_surface_role role; /** * The role object representing the role. NULL if the object was destroyed. */ struct wl_resource *role_resource; // NULL if the role resource is inert union { struct wlr_xdg_toplevel *toplevel; struct wlr_xdg_popup *popup; }; struct wl_list popups; // wlr_xdg_popup.link bool added, configured; struct wl_event_source *configure_idle; uint32_t scheduled_serial; struct wl_list configure_list; struct wlr_xdg_surface_state current, pending; // Whether the surface is ready to receive configure events bool initialized; // Whether the latest commit is an initial commit bool initial_commit; struct { struct wl_signal destroy; struct wl_signal ping_timeout; struct wl_signal new_popup; // for protocol extensions struct wl_signal configure; // struct wlr_xdg_surface_configure struct wl_signal ack_configure; // struct wlr_xdg_surface_configure } events; void *data; // private state struct wl_listener role_resource_destroy; }; struct wlr_xdg_toplevel_move_event { struct wlr_xdg_toplevel *toplevel; struct wlr_seat_client *seat; uint32_t serial; }; struct wlr_xdg_toplevel_resize_event { struct wlr_xdg_toplevel *toplevel; struct wlr_seat_client *seat; uint32_t serial; uint32_t edges; }; struct wlr_xdg_toplevel_show_window_menu_event { struct wlr_xdg_toplevel *toplevel; struct wlr_seat_client *seat; uint32_t serial; int32_t x, y; }; /** * Create the xdg_wm_base global with the specified version. */ struct wlr_xdg_shell *wlr_xdg_shell_create(struct wl_display *display, uint32_t version); /** Get the corresponding struct wlr_xdg_surface from a resource. * * Aborts if the resource doesn't have the correct type. Returns NULL if the * resource is inert. */ struct wlr_xdg_surface *wlr_xdg_surface_from_resource( struct wl_resource *resource); /** Get the corresponding struct wlr_xdg_popup from a resource. * * Aborts if the resource doesn't have the correct type. Returns NULL if the * resource is inert. */ struct wlr_xdg_popup *wlr_xdg_popup_from_resource( struct wl_resource *resource); /** Get the corresponding struct wlr_xdg_toplevel from a resource. * * Aborts if the resource doesn't have the correct type. Returns NULL if the * resource is inert. */ struct wlr_xdg_toplevel *wlr_xdg_toplevel_from_resource( struct wl_resource *resource); /** Get the corresponding struct wlr_xdg_positioner from a resource. * * Aborts if the resource doesn't have the correct type. */ struct wlr_xdg_positioner *wlr_xdg_positioner_from_resource( struct wl_resource *resource); /** * Send a ping to the surface. If the surface does not respond in a reasonable * amount of time, the ping_timeout event will be emitted. */ void wlr_xdg_surface_ping(struct wlr_xdg_surface *surface); /** * Request that this toplevel surface be the given size. Returns the associated * configure serial. */ uint32_t wlr_xdg_toplevel_set_size(struct wlr_xdg_toplevel *toplevel, int32_t width, int32_t height); /** * Request that this toplevel show itself in an activated or deactivated * state. Returns the associated configure serial. */ uint32_t wlr_xdg_toplevel_set_activated(struct wlr_xdg_toplevel *toplevel, bool activated); /** * Request that this toplevel consider itself maximized or not * maximized. Returns the associated configure serial. */ uint32_t wlr_xdg_toplevel_set_maximized(struct wlr_xdg_toplevel *toplevel, bool maximized); /** * Request that this toplevel consider itself fullscreen or not * fullscreen. Returns the associated configure serial. */ uint32_t wlr_xdg_toplevel_set_fullscreen(struct wlr_xdg_toplevel *toplevel, bool fullscreen); /** * Request that this toplevel consider itself to be resizing or not * resizing. Returns the associated configure serial. */ uint32_t wlr_xdg_toplevel_set_resizing(struct wlr_xdg_toplevel *toplevel, bool resizing); /** * Request that this toplevel consider itself in a tiled layout and some * edges are adjacent to another part of the tiling grid. `tiled_edges` is a * bitfield of enum wlr_edges. Returns the associated configure serial. */ uint32_t wlr_xdg_toplevel_set_tiled(struct wlr_xdg_toplevel *toplevel, uint32_t tiled_edges); /** * Configure the recommended bounds for the client's window geometry size. * Returns the associated configure serial. */ uint32_t wlr_xdg_toplevel_set_bounds(struct wlr_xdg_toplevel *toplevel, int32_t width, int32_t height); /** * Configure the window manager capabilities for this toplevel. `caps` is a * bitfield of `enum wlr_xdg_toplevel_wm_capabilities`. Returns the associated * configure serial. */ uint32_t wlr_xdg_toplevel_set_wm_capabilities(struct wlr_xdg_toplevel *toplevel, uint32_t caps); /** * Request that this toplevel consider itself suspended or not * suspended. Returns the associated configure serial. */ uint32_t wlr_xdg_toplevel_set_suspended(struct wlr_xdg_toplevel *toplevel, bool suspended); /** * Request that this toplevel closes. */ void wlr_xdg_toplevel_send_close(struct wlr_xdg_toplevel *toplevel); /** * Sets the parent of this toplevel. Parent can be NULL. * * Returns true on success, false if setting the parent would create a loop. */ bool wlr_xdg_toplevel_set_parent(struct wlr_xdg_toplevel *toplevel, struct wlr_xdg_toplevel *parent); /** * Notify the client that the popup has been dismissed and destroy the * struct wlr_xdg_popup, rendering the resource inert. */ void wlr_xdg_popup_destroy(struct wlr_xdg_popup *popup); /** * Get the position for this popup in the surface parent's coordinate system. */ void wlr_xdg_popup_get_position(struct wlr_xdg_popup *popup, double *popup_sx, double *popup_sy); /** * Get the geometry based on positioner rules. */ void wlr_xdg_positioner_rules_get_geometry( const struct wlr_xdg_positioner_rules *rules, struct wlr_box *box); /** * Unconstrain the box from the constraint area according to positioner rules. */ void wlr_xdg_positioner_rules_unconstrain_box( const struct wlr_xdg_positioner_rules *rules, const struct wlr_box *constraint, struct wlr_box *box); /** * Convert the given coordinates in the popup coordinate system to the toplevel * surface coordinate system. */ void wlr_xdg_popup_get_toplevel_coords(struct wlr_xdg_popup *popup, int popup_sx, int popup_sy, int *toplevel_sx, int *toplevel_sy); /** * Set the geometry of this popup to unconstrain it according to its * xdg-positioner rules. The box should be in the popup's root toplevel parent * surface coordinate system. */ void wlr_xdg_popup_unconstrain_from_box(struct wlr_xdg_popup *popup, const struct wlr_box *toplevel_space_box); /** * Find a surface within this xdg-surface tree at the given surface-local * coordinates. Returns the surface and coordinates in the leaf surface * coordinate system or NULL if no surface is found at that location. */ struct wlr_surface *wlr_xdg_surface_surface_at( struct wlr_xdg_surface *surface, double sx, double sy, double *sub_x, double *sub_y); /** * Find a surface within this xdg-surface's popup tree at the given * surface-local coordinates. Returns the surface and coordinates in the leaf * surface coordinate system or NULL if no surface is found at that location. */ struct wlr_surface *wlr_xdg_surface_popup_surface_at( struct wlr_xdg_surface *surface, double sx, double sy, double *sub_x, double *sub_y); /** * Get a struct wlr_xdg_surface from a struct wlr_surface. * * Returns NULL if the surface doesn't have the xdg_surface role or * if the xdg_surface has been destroyed. */ struct wlr_xdg_surface *wlr_xdg_surface_try_from_wlr_surface(struct wlr_surface *surface); /** * Get a struct wlr_xdg_toplevel from a struct wlr_surface. * * Returns NULL if the surface doesn't have the xdg_surface role, the * xdg_surface is not a toplevel, or the xdg_surface/xdg_toplevel objects have * been destroyed. */ struct wlr_xdg_toplevel *wlr_xdg_toplevel_try_from_wlr_surface(struct wlr_surface *surface); /** * Get a struct wlr_xdg_popup from a struct wlr_surface. * * Returns NULL if the surface doesn't have the xdg_surface role, the * xdg_surface is not a popup, or the xdg_surface/xdg_popup objects have * been destroyed. */ struct wlr_xdg_popup *wlr_xdg_popup_try_from_wlr_surface(struct wlr_surface *surface); /** * Get the surface geometry. * * This is either the geometry as set by the client, or defaulted to the bounds * of the surface + the subsurfaces (as specified by the protocol). * * The x and y value can be < 0. */ void wlr_xdg_surface_get_geometry(struct wlr_xdg_surface *surface, struct wlr_box *box); /** * Call `iterator` on each mapped surface and popup in the xdg-surface tree * (whether or not this xdg-surface is mapped), with the surface's position * relative to the root xdg-surface. The function is called from root to leaves * (in rendering order). */ void wlr_xdg_surface_for_each_surface(struct wlr_xdg_surface *surface, wlr_surface_iterator_func_t iterator, void *user_data); /** * Call `iterator` on each mapped popup's surface and popup's subsurface in the * xdg-surface tree (whether or not this xdg-surface is mapped), with the * surfaces's position relative to the root xdg-surface. The function is called * from root to leaves (in rendering order). */ void wlr_xdg_surface_for_each_popup_surface(struct wlr_xdg_surface *surface, wlr_surface_iterator_func_t iterator, void *user_data); /** * Schedule a surface configuration. This should only be called by protocols * extending the shell. */ uint32_t wlr_xdg_surface_schedule_configure(struct wlr_xdg_surface *surface); #endif wlroots-0.17.1/include/wlr/util/000077500000000000000000000000001454110342200165115ustar00rootroot00000000000000wlroots-0.17.1/include/wlr/util/addon.h000066400000000000000000000020531454110342200177470ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_UTIL_ADDON_H #define WLR_UTIL_ADDON_H #include struct wlr_addon_set { // private state struct wl_list addons; }; struct wlr_addon; struct wlr_addon_interface { const char *name; // Has to call wlr_addon_finish() void (*destroy)(struct wlr_addon *addon); }; struct wlr_addon { const struct wlr_addon_interface *impl; // private state const void *owner; struct wl_list link; }; void wlr_addon_set_init(struct wlr_addon_set *set); void wlr_addon_set_finish(struct wlr_addon_set *set); void wlr_addon_init(struct wlr_addon *addon, struct wlr_addon_set *set, const void *owner, const struct wlr_addon_interface *impl); void wlr_addon_finish(struct wlr_addon *addon); struct wlr_addon *wlr_addon_find(struct wlr_addon_set *set, const void *owner, const struct wlr_addon_interface *impl); #endif wlroots-0.17.1/include/wlr/util/box.h000066400000000000000000000060351454110342200174560ustar00rootroot00000000000000/* * This is a stable interface of wlroots. Future changes will be limited to: * * - New functions * - New struct members * - New enum members * * Note that wlroots does not make an ABI compatibility promise - in the future, * the layout and size of structs used by wlroots may change, requiring code * depending on this header to be recompiled (but not edited). * * Breaking changes are announced in the release notes and follow a 1-year * deprecation schedule. */ #ifndef WLR_UTIL_BOX_H #define WLR_UTIL_BOX_H #include #include /** * A box representing a rectangle region in a 2D space. * * The x and y coordinates are inclusive, and the width and height lengths are * exclusive. In other words, the box starts from the coordinates (x, y), and * goes up to but not including (x + width, y + height). */ struct wlr_box { int x, y; int width, height; }; /** * A floating-point box representing a rectangle region in a 2D space. * * struct wlr_fbox has the same semantics as struct wlr_box. */ struct wlr_fbox { double x, y; double width, height; }; /** * Functions below accept NULL where a box is expected, which is treated * the same as an empty box. */ /** * Finds the closest point within the box bounds. * * Returns NAN if the box is empty. */ void wlr_box_closest_point(const struct wlr_box *box, double x, double y, double *dest_x, double *dest_y); /** * Gives the intersecting box between two struct wlr_box. * * Returns an empty box if the provided boxes don't intersect. */ bool wlr_box_intersection(struct wlr_box *dest, const struct wlr_box *box_a, const struct wlr_box *box_b); /** * Verifies if a point is contained within the bounds of a given struct wlr_box. * * For example: * * - A point at (100, 50) is not contained in the box (0, 0, 100, 50). * - A point at (10, 10) is contained in the box (10, 0, 50, 50). */ bool wlr_box_contains_point(const struct wlr_box *box, double x, double y); /** * Checks whether a box is empty or not. * * A box is considered empty if its width and/or height is zero or negative. */ bool wlr_box_empty(const struct wlr_box *box); /** * Transforms a box inside a (0, 0, width, height) box. */ void wlr_box_transform(struct wlr_box *dest, const struct wlr_box *box, enum wl_output_transform transform, int width, int height); /** * Checks whether a box is empty or not. * * A box is considered empty if its width and/or height is zero or negative. */ bool wlr_fbox_empty(const struct wlr_fbox *box); /** * Transforms a floating-point box inside a (0, 0, width, height) box. */ void wlr_fbox_transform(struct wlr_fbox *dest, const struct wlr_fbox *box, enum wl_output_transform transform, double width, double height); #ifdef WLR_USE_UNSTABLE /** * Returns true if the two boxes are equal, false otherwise. */ bool wlr_box_equal(const struct wlr_box *a, const struct wlr_box *b); /** * Returns true if the two boxes are equal, false otherwise. */ bool wlr_fbox_equal(const struct wlr_fbox *a, const struct wlr_fbox *b); #endif #endif wlroots-0.17.1/include/wlr/util/edges.h000066400000000000000000000012511454110342200177500ustar00rootroot00000000000000/* * This is a stable interface of wlroots. Future changes will be limited to: * * - New functions * - New struct members * - New enum members * * Note that wlroots does not make an ABI compatibility promise - in the future, * the layout and size of structs used by wlroots may change, requiring code * depending on this header to be recompiled (but not edited). * * Breaking changes are announced in the release notes and follow a 1-year * deprecation schedule. */ #ifndef WLR_UTIL_EDGES_H #define WLR_UTIL_EDGES_H enum wlr_edges { WLR_EDGE_NONE = 0, WLR_EDGE_TOP = 1 << 0, WLR_EDGE_BOTTOM = 1 << 1, WLR_EDGE_LEFT = 1 << 2, WLR_EDGE_RIGHT = 1 << 3, }; #endif wlroots-0.17.1/include/wlr/util/log.h000066400000000000000000000042521454110342200174460ustar00rootroot00000000000000/* * This is a stable interface of wlroots. Future changes will be limited to: * * - New functions * - New struct members * - New enum members * * Note that wlroots does not make an ABI compatibility promise - in the future, * the layout and size of structs used by wlroots may change, requiring code * depending on this header to be recompiled (but not edited). * * Breaking changes are announced in the release notes and follow a 1-year * deprecation schedule. */ #ifndef WLR_UTIL_LOG_H #define WLR_UTIL_LOG_H #include #include #include #include enum wlr_log_importance { WLR_SILENT = 0, WLR_ERROR = 1, WLR_INFO = 2, WLR_DEBUG = 3, WLR_LOG_IMPORTANCE_LAST, }; typedef void (*wlr_log_func_t)(enum wlr_log_importance importance, const char *fmt, va_list args); /** * Set the log verbosity and callback. * * Only messages less than or equal to the supplied verbosity will be logged. * If the callback is NULL, the default logger is used. * * This function can be called multiple times to update the verbosity or * callback function. */ void wlr_log_init(enum wlr_log_importance verbosity, wlr_log_func_t callback); /** * Get the current log verbosity configured by wlr_log_init(). */ enum wlr_log_importance wlr_log_get_verbosity(void); #ifdef __GNUC__ #define _WLR_ATTRIB_PRINTF(start, end) __attribute__((format(printf, start, end))) #else #define _WLR_ATTRIB_PRINTF(start, end) #endif void _wlr_log(enum wlr_log_importance verbosity, const char *format, ...) _WLR_ATTRIB_PRINTF(2, 3); void _wlr_vlog(enum wlr_log_importance verbosity, const char *format, va_list args) _WLR_ATTRIB_PRINTF(2, 0); #ifdef _WLR_REL_SRC_DIR // strip prefix from __FILE__, leaving the path relative to the project root #define _WLR_FILENAME ((const char *)__FILE__ + sizeof(_WLR_REL_SRC_DIR) - 1) #else #define _WLR_FILENAME __FILE__ #endif #define wlr_log(verb, fmt, ...) \ _wlr_log(verb, "[%s:%d] " fmt, _WLR_FILENAME, __LINE__, ##__VA_ARGS__) #define wlr_vlog(verb, fmt, args) \ _wlr_vlog(verb, "[%s:%d] " fmt, _WLR_FILENAME, __LINE__, args) #define wlr_log_errno(verb, fmt, ...) \ wlr_log(verb, fmt ": %s", ##__VA_ARGS__, strerror(errno)) #endif wlroots-0.17.1/include/wlr/util/region.h000066400000000000000000000034121454110342200201450ustar00rootroot00000000000000/* * This is a stable interface of wlroots. Future changes will be limited to: * * - New functions * - New struct members * - New enum members * * Note that wlroots does not make an ABI compatibility promise - in the future, * the layout and size of structs used by wlroots may change, requiring code * depending on this header to be recompiled (but not edited). * * Breaking changes are announced in the release notes and follow a 1-year * deprecation schedule. */ #ifndef WLR_UTIL_REGION_H #define WLR_UTIL_REGION_H #include #include #include /** * Scales a region, ie. multiplies all its coordinates by `scale`. * * The resulting coordinates are rounded up or down so that the new region is * at least as big as the original one. */ void wlr_region_scale(pixman_region32_t *dst, const pixman_region32_t *src, float scale); void wlr_region_scale_xy(pixman_region32_t *dst, const pixman_region32_t *src, float scale_x, float scale_y); /** * Applies a transform to a region inside a box of size `width` x `height`. */ void wlr_region_transform(pixman_region32_t *dst, const pixman_region32_t *src, enum wl_output_transform transform, int width, int height); /** * Expands the region by distance on both axis. distance must be * a non-negative number. */ void wlr_region_expand(pixman_region32_t *dst, const pixman_region32_t *src, int distance); /* * Builds the smallest possible region that contains the region rotated about * the point (ox, oy). */ void wlr_region_rotated_bounds(pixman_region32_t *dst, const pixman_region32_t *src, float rotation, int ox, int oy); bool wlr_region_confine(const pixman_region32_t *region, double x1, double y1, double x2, double y2, double *x2_out, double *y2_out); #endif wlroots-0.17.1/include/wlr/version.h.in000066400000000000000000000004241454110342200177770ustar00rootroot00000000000000#ifndef WLR_VERSION_H #define WLR_VERSION_H #mesondefine WLR_VERSION_STR #mesondefine WLR_VERSION_MAJOR #mesondefine WLR_VERSION_MINOR #mesondefine WLR_VERSION_MICRO #define WLR_VERSION_NUM ((WLR_VERSION_MAJOR << 16) | (WLR_VERSION_MINOR << 8) | WLR_VERSION_MICRO) #endif wlroots-0.17.1/include/wlr/xcursor.h000066400000000000000000000074621454110342200174230ustar00rootroot00000000000000/* * Copyright © 2012 Intel Corporation * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice (including the * next paragraph) shall be included in all copies or substantial * portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ /* * This is a stable interface of wlroots. Future changes will be limited to: * * - New functions * - New struct members * - New enum members * * Note that wlroots does not make an ABI compatibility promise - in the future, * the layout and size of structs used by wlroots may change, requiring code * depending on this header to be recompiled (but not edited). * * Breaking changes are announced in the release notes and follow a 1-year * deprecation schedule. */ #ifndef WLR_XCURSOR_H #define WLR_XCURSOR_H #include #include /** * A still cursor image. * * The buffer contains pixels layed out in a packed DRM_FORMAT_ARGB8888 format. */ struct wlr_xcursor_image { uint32_t width; /* actual width */ uint32_t height; /* actual height */ uint32_t hotspot_x; /* hot-spot x (must be inside image) */ uint32_t hotspot_y; /* hot-spot y (must be inside image) */ uint32_t delay; /* animation delay to next frame (ms) */ uint8_t *buffer; /* pixel data */ }; /** * A cursor. * * If the cursor is animated, it may contain more than a single image. */ struct wlr_xcursor { unsigned int image_count; struct wlr_xcursor_image **images; char *name; uint32_t total_delay; /* total duration of the animation in ms */ }; /** * Container for an Xcursor theme. */ struct wlr_xcursor_theme { unsigned int cursor_count; struct wlr_xcursor **cursors; char *name; int size; }; /** * Loads the named Xcursor theme. * * This is useful if you need cursor images for your compositor to use when a * client-side cursor is not available or you wish to override client-side * cursors for a particular UI interaction (such as using a grab cursor when * moving a window around). * * The size is given in pixels. * * If a cursor theme with the given name couldn't be loaded, a fallback theme * is loaded. * * On error, NULL is returned. */ struct wlr_xcursor_theme *wlr_xcursor_theme_load(const char *name, int size); /** * Destroy a cursor theme. * * This implicitly destroys all child cursors and cursor images. */ void wlr_xcursor_theme_destroy(struct wlr_xcursor_theme *theme); /** * Obtain a cursor for the specified name (e.g. "default"). * * If the cursor could not be found, NULL is returned. */ struct wlr_xcursor *wlr_xcursor_theme_get_cursor( struct wlr_xcursor_theme *theme, const char *name); /** * Find the frame for a given elapsed time in a cursor animation. * * This function converts a timestamp (in ms) to a cursor image index. */ int wlr_xcursor_frame(struct wlr_xcursor *cursor, uint32_t time); /** * Get the name of the resize cursor for the given edges. */ const char *wlr_xcursor_get_resize_name(enum wlr_edges edges); #endif wlroots-0.17.1/include/wlr/xwayland.h000066400000000000000000000001041454110342200175270ustar00rootroot00000000000000#include #include wlroots-0.17.1/include/wlr/xwayland/000077500000000000000000000000001454110342200173635ustar00rootroot00000000000000wlroots-0.17.1/include/wlr/xwayland/server.h000066400000000000000000000027761454110342200210560ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_XWAYLAND_SERVER_H #define WLR_XWAYLAND_SERVER_H #include #include #include #include struct wlr_xwayland_server_options { bool lazy; bool enable_wm; bool no_touch_pointer_emulation; bool force_xrandr_emulation; int terminate_delay; // in seconds, 0 to terminate immediately }; struct wlr_xwayland_server { pid_t pid; struct wl_client *client; struct wl_event_source *pipe_source; int wm_fd[2], wl_fd[2]; bool ready; time_t server_start; /* Anything above display is reset on Xwayland restart, rest is conserved */ int display; char display_name[16]; int x_fd[2]; struct wl_event_source *x_fd_read_event[2]; struct wlr_xwayland_server_options options; struct wl_display *wl_display; struct wl_event_source *idle_source; struct { struct wl_signal start; struct wl_signal ready; struct wl_signal destroy; } events; struct wl_listener client_destroy; struct wl_listener display_destroy; void *data; }; struct wlr_xwayland_server_ready_event { struct wlr_xwayland_server *server; int wm_fd; }; struct wlr_xwayland_server *wlr_xwayland_server_create( struct wl_display *display, struct wlr_xwayland_server_options *options); void wlr_xwayland_server_destroy(struct wlr_xwayland_server *server); #endif wlroots-0.17.1/include/wlr/xwayland/shell.h000066400000000000000000000035171454110342200206510ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_XWAYLAND_SHELL_H #define WLR_XWAYLAND_SHELL_H #include #include /** * The Xwayland shell. * * This is a shell only exposed to Xwayland. */ struct wlr_xwayland_shell_v1 { struct wl_global *global; struct { struct wl_signal destroy; struct wl_signal new_surface; // struct wlr_xwayland_surface_v1 } events; // private state struct wl_client *client; struct wl_list surfaces; // wlr_xwayland_surface_v1.link struct wl_listener display_destroy; struct wl_listener client_destroy; }; /** * An Xwayland shell surface. */ struct wlr_xwayland_surface_v1 { struct wlr_surface *surface; uint64_t serial; // private state struct wl_resource *resource; struct wl_list link; struct wlr_xwayland_shell_v1 *shell; bool added; }; /** * Create the xwayland_shell_v1 global. * * Compositors should add a global filter (see wl_display_set_global_filter()) * to only expose this global to Xwayland clients. */ struct wlr_xwayland_shell_v1 *wlr_xwayland_shell_v1_create( struct wl_display *display, uint32_t version); /** * Destroy the xwayland_shell_v1 global. */ void wlr_xwayland_shell_v1_destroy(struct wlr_xwayland_shell_v1 *shell); /** * Allow a client to bind to the xwayland_shell_v1 global. */ void wlr_xwayland_shell_v1_set_client(struct wlr_xwayland_shell_v1 *shell, struct wl_client *client); /** * Get a Wayland surface from an xwayland_shell_v1 serial. * * Returns NULL if the serial hasn't been associated with any surface. */ struct wlr_surface *wlr_xwayland_shell_v1_surface_from_serial( struct wlr_xwayland_shell_v1 *shell, uint64_t serial); #endif wlroots-0.17.1/include/wlr/xwayland/xwayland.h000066400000000000000000000213301454110342200213620ustar00rootroot00000000000000/* * This an unstable interface of wlroots. No guarantees are made regarding the * future consistency of this API. */ #ifndef WLR_USE_UNSTABLE #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif #ifndef WLR_XWAYLAND_XWAYLAND_H #define WLR_XWAYLAND_XWAYLAND_H #include #include #include #include #include #include struct wlr_box; struct wlr_xwm; struct wlr_data_source; struct wlr_drag; struct wlr_xwayland { struct wlr_xwayland_server *server; bool own_server; struct wlr_xwm *xwm; struct wlr_xwayland_shell_v1 *shell_v1; struct wlr_xwayland_cursor *cursor; const char *display_name; struct wl_display *wl_display; struct wlr_compositor *compositor; struct wlr_seat *seat; struct { struct wl_signal ready; struct wl_signal new_surface; // struct wlr_xwayland_surface struct wl_signal remove_startup_info; // struct wlr_xwayland_remove_startup_info_event } events; /** * Add a custom event handler to xwayland. Return 1 if the event was * handled or 0 to use the default wlr-xwayland handler. wlr-xwayland will * free the event. */ int (*user_event_handler)(struct wlr_xwm *xwm, xcb_generic_event_t *event); struct wl_listener server_start; struct wl_listener server_ready; struct wl_listener server_destroy; struct wl_listener seat_destroy; struct wl_listener shell_destroy; void *data; }; enum wlr_xwayland_surface_decorations { WLR_XWAYLAND_SURFACE_DECORATIONS_ALL = 0, WLR_XWAYLAND_SURFACE_DECORATIONS_NO_BORDER = 1, WLR_XWAYLAND_SURFACE_DECORATIONS_NO_TITLE = 2, }; /** * This represents the input focus described as follows: * * https://www.x.org/releases/X11R7.6/doc/xorg-docs/specs/ICCCM/icccm.html#input_focus */ enum wlr_xwayland_icccm_input_model { WLR_ICCCM_INPUT_MODEL_NONE = 0, WLR_ICCCM_INPUT_MODEL_PASSIVE = 1, WLR_ICCCM_INPUT_MODEL_LOCAL = 2, WLR_ICCCM_INPUT_MODEL_GLOBAL = 3, }; /** * An Xwayland user interface component. It has an absolute position in * layout-local coordinates. * * The inner struct wlr_surface is valid once the associate event is emitted. * Compositors can set up e.g. map and unmap listeners at this point. The * struct wlr_surface becomes invalid when the dissociate event is emitted. */ struct wlr_xwayland_surface { xcb_window_t window_id; struct wlr_xwm *xwm; uint32_t surface_id; uint64_t serial; struct wl_list link; struct wl_list stack_link; struct wl_list unpaired_link; struct wlr_surface *surface; struct wlr_addon surface_addon; struct wl_listener surface_commit; struct wl_listener surface_map; struct wl_listener surface_unmap; int16_t x, y; uint16_t width, height; uint16_t saved_width, saved_height; bool override_redirect; char *title; char *class; char *instance; char *role; char *startup_id; pid_t pid; bool has_utf8_title; struct wl_list children; // wlr_xwayland_surface.parent_link struct wlr_xwayland_surface *parent; struct wl_list parent_link; // wlr_xwayland_surface.children xcb_atom_t *window_type; size_t window_type_len; xcb_atom_t *protocols; size_t protocols_len; uint32_t decorations; xcb_icccm_wm_hints_t *hints; xcb_size_hints_t *size_hints; /* * _NET_WM_STRUT_PARTIAL (used by e.g. XWayland panels). * Note that right/bottom values are offsets from the lower * right corner of the X11 screen, and the exact relation * between X11 screen coordinates and the wlr_output_layout * depends on the XWayland implementation. */ xcb_ewmh_wm_strut_partial_t *strut_partial; bool pinging; struct wl_event_source *ping_timer; // _NET_WM_STATE bool modal; bool fullscreen; bool maximized_vert, maximized_horz; bool minimized; bool withdrawn; bool has_alpha; struct { struct wl_signal destroy; struct wl_signal request_configure; // struct wlr_xwayland_surface_configure_event struct wl_signal request_move; struct wl_signal request_resize; // struct wlr_xwayland_resize_event struct wl_signal request_minimize; // struct wlr_xwayland_minimize_event struct wl_signal request_maximize; struct wl_signal request_fullscreen; struct wl_signal request_activate; struct wl_signal associate; struct wl_signal dissociate; struct wl_signal set_title; struct wl_signal set_class; struct wl_signal set_role; struct wl_signal set_parent; struct wl_signal set_startup_id; struct wl_signal set_window_type; struct wl_signal set_hints; struct wl_signal set_decorations; struct wl_signal set_strut_partial; struct wl_signal set_override_redirect; struct wl_signal set_geometry; struct wl_signal ping_timeout; } events; void *data; }; struct wlr_xwayland_surface_configure_event { struct wlr_xwayland_surface *surface; int16_t x, y; uint16_t width, height; uint16_t mask; // xcb_config_window_t }; struct wlr_xwayland_remove_startup_info_event { const char *id; xcb_window_t window; }; struct wlr_xwayland_resize_event { struct wlr_xwayland_surface *surface; uint32_t edges; }; struct wlr_xwayland_minimize_event { struct wlr_xwayland_surface *surface; bool minimize; }; /** Create an Xwayland server and XWM. * * The server supports a lazy mode in which Xwayland is only started when a * client tries to connect. */ struct wlr_xwayland *wlr_xwayland_create(struct wl_display *wl_display, struct wlr_compositor *compositor, bool lazy); /** * Create an XWM from an existing Xwayland server. */ struct wlr_xwayland *wlr_xwayland_create_with_server(struct wl_display *display, struct wlr_compositor *compositor, struct wlr_xwayland_server *server); void wlr_xwayland_destroy(struct wlr_xwayland *wlr_xwayland); void wlr_xwayland_set_cursor(struct wlr_xwayland *wlr_xwayland, uint8_t *pixels, uint32_t stride, uint32_t width, uint32_t height, int32_t hotspot_x, int32_t hotspot_y); void wlr_xwayland_surface_activate(struct wlr_xwayland_surface *surface, bool activated); /** * Restack surface relative to sibling. * If sibling is NULL, then the surface is moved to the top or the bottom * of the stack (depending on the mode). */ void wlr_xwayland_surface_restack(struct wlr_xwayland_surface *surface, struct wlr_xwayland_surface *sibling, enum xcb_stack_mode_t mode); void wlr_xwayland_surface_configure(struct wlr_xwayland_surface *surface, int16_t x, int16_t y, uint16_t width, uint16_t height); void wlr_xwayland_surface_close(struct wlr_xwayland_surface *surface); void wlr_xwayland_surface_set_withdrawn(struct wlr_xwayland_surface *surface, bool withdrawn); void wlr_xwayland_surface_set_minimized(struct wlr_xwayland_surface *surface, bool minimized); void wlr_xwayland_surface_set_maximized(struct wlr_xwayland_surface *surface, bool maximized); void wlr_xwayland_surface_set_fullscreen(struct wlr_xwayland_surface *surface, bool fullscreen); void wlr_xwayland_set_seat(struct wlr_xwayland *xwayland, struct wlr_seat *seat); /** * Get a struct wlr_xwayland_surface from a struct wlr_surface. * * If the surface hasn't been created by Xwayland or has no X11 window * associated, NULL is returned. */ struct wlr_xwayland_surface *wlr_xwayland_surface_try_from_wlr_surface( struct wlr_surface *surface); void wlr_xwayland_surface_ping(struct wlr_xwayland_surface *surface); /** Metric to guess if an OR window should "receive" focus * * In the pure X setups, window managers usually straight up ignore override * redirect windows, and never touch them. (we have to handle them for mapping) * * When such a window wants to receive keyboard input (e.g. rofi/dzen) it will * use mechanics we don't support (sniffing/grabbing input). * [Sadly this is unrelated to xwayland-keyboard-grab] * * To still support these windows, while keeping general OR semantics as is, we * need to hand a subset of windows focus. * The dirty truth is, we need to hand focus to any Xwayland window, though * pretending this window has focus makes it easier to handle unmap. * * This function provides a handy metric based on the window type to guess if * the OR window wants focus. * It's probably not perfect, nor exactly intended but works in practice. * * Returns: true if the window should receive focus * false if it should be ignored */ bool wlr_xwayland_or_surface_wants_focus( const struct wlr_xwayland_surface *xsurface); enum wlr_xwayland_icccm_input_model wlr_xwayland_icccm_input_model( const struct wlr_xwayland_surface *xsurface); /** * Sets the _NET_WORKAREA root window property. The compositor should set * one workarea per virtual desktop. This indicates the usable geometry * (relative to the virtual desktop viewport) that is not covered by * panels, docks, etc. Unfortunately, it is not possible to specify * per-output workareas. */ void wlr_xwayland_set_workareas(struct wlr_xwayland *wlr_xwayland, const struct wlr_box *workareas, size_t num_workareas); #endif wlroots-0.17.1/include/xcursor/000077500000000000000000000000001454110342200164355ustar00rootroot00000000000000wlroots-0.17.1/include/xcursor/cursor_data.h000066400000000000000000001142171454110342200211220ustar00rootroot00000000000000/* * Copyright 1999 SuSE, Inc. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice (including the * next paragraph) shall be included in all copies or substantial * portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Author: Keith Packard, SuSE, Inc. */ #include #include static const uint32_t cursor_data[] = { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0xffffffff, 0xff000000, 0xffffffff, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0xff000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0x00000000, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0x00000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0x00000000, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0x00000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0xff000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0xff000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xff000000, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xff000000, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0xff000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0xffffffff, 0xff000000, 0xffffffff, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xff000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xff000000, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0xffffffff, 0xff000000, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0x00000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0xff000000, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0xff000000, 0xffffffff, 0xffffffff, 0xff000000, 0xffffffff, 0xff000000, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xff000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xff000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0xff000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xff000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xff000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0xff000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }; static const struct cursor_metadata { char *name; int width, height; int hotspot_x, hotspot_y; size_t offset; } cursor_metadata[] = { { "bottom_left_corner", 16, 16, 1, 14, 0 }, { "bottom_right_corner", 16, 16, 14, 14, 256 }, { "bottom_side", 15, 16, 7, 14, 512 }, { "grabbing", 16, 16, 8, 8, 752 }, { "left_ptr", 10, 16, 1, 1, 1008 }, { "left_side", 16, 15, 1, 7, 1168 }, { "right_side", 16, 15, 14, 7, 1408 }, { "top_left_corner", 16, 16, 1, 1, 1648 }, { "top_right_corner", 16, 16, 14, 1, 1904 }, { "top_side", 15, 16, 7, 1, 2160 }, { "xterm", 9, 16, 4, 8, 2400 }, { "hand1", 13, 16, 12, 0, 2544 }, { "watch", 16, 16, 15, 9, 2752 }, /* https://www.freedesktop.org/wiki/Specifications/cursor-spec/ */ { "sw-resize", 16, 16, 1, 14, 0 }, { "se-resize", 16, 16, 14, 14, 256 }, { "s-resize", 15, 16, 7, 14, 512 }, { "all-scroll", 16, 16, 8, 8, 752 }, { "default", 10, 16, 1, 1, 1008 }, { "w-resize", 16, 15, 1, 7, 1168 }, { "e-resize", 16, 15, 14, 7, 1408 }, { "nw-resize", 16, 16, 1, 1, 1648 }, { "ne-resize", 16, 16, 14, 1, 1904 }, { "n-resize", 15, 16, 7, 1, 2160 }, { "text", 9, 16, 4, 8, 2400 }, { "pointer", 13, 16, 12, 0, 2544 }, { "wait", 16, 16, 15, 9, 2752 }, }; wlroots-0.17.1/include/xcursor/xcursor.h000066400000000000000000000040271454110342200203160ustar00rootroot00000000000000/* * Copyright © 2002 Keith Packard * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice (including the * next paragraph) shall be included in all copies or substantial * portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef XCURSOR_H #define XCURSOR_H #include struct xcursor_image { uint32_t version; /* version of the image data */ uint32_t size; /* nominal size for matching */ uint32_t width; /* actual width */ uint32_t height; /* actual height */ uint32_t xhot; /* hot spot x (must be inside image) */ uint32_t yhot; /* hot spot y (must be inside image) */ uint32_t delay; /* animation delay to next frame (ms) */ uint32_t *pixels; /* pointer to pixels */ }; /* * Other data structures exposed by the library API */ struct xcursor_images { int nimage; /* number of images */ struct xcursor_image **images; /* array of XcursorImage pointers */ char *name; /* name used to load images */ }; void xcursor_images_destroy(struct xcursor_images *images); void xcursor_load_theme(const char *theme, int size, void (*load_callback)(struct xcursor_images *, void *), void *user_data); #endif wlroots-0.17.1/include/xwayland/000077500000000000000000000000001454110342200165575ustar00rootroot00000000000000wlroots-0.17.1/include/xwayland/selection.h000066400000000000000000000054421454110342200207220ustar00rootroot00000000000000#ifndef XWAYLAND_SELECTION_H #define XWAYLAND_SELECTION_H #include #include #include #define INCR_CHUNK_SIZE (64 * 1024) #define XDND_VERSION 5 struct wlr_primary_selection_source; struct wlr_xwm_selection; struct wlr_drag; struct wlr_data_source; struct wlr_xwm_selection_transfer { struct wlr_xwm_selection *selection; bool incr; bool flush_property_on_delete; bool property_set; struct wl_array source_data; int wl_client_fd; struct wl_event_source *event_source; struct wl_list link; // when sending to x11 xcb_selection_request_event_t request; // when receiving from x11 int property_start; xcb_get_property_reply_t *property_reply; xcb_window_t incoming_window; }; struct wlr_xwm_selection { struct wlr_xwm *xwm; xcb_atom_t atom; xcb_window_t window; xcb_window_t owner; xcb_timestamp_t timestamp; struct wl_list incoming; struct wl_list outgoing; }; struct wlr_xwm_selection_transfer * xwm_selection_find_incoming_transfer_by_window( struct wlr_xwm_selection *selection, xcb_window_t window); void xwm_selection_transfer_remove_event_source( struct wlr_xwm_selection_transfer *transfer); void xwm_selection_transfer_close_wl_client_fd( struct wlr_xwm_selection_transfer *transfer); void xwm_selection_transfer_destroy_property_reply( struct wlr_xwm_selection_transfer *transfer); void xwm_selection_transfer_init(struct wlr_xwm_selection_transfer *transfer, struct wlr_xwm_selection *selection); void xwm_selection_transfer_destroy( struct wlr_xwm_selection_transfer *transfer); void xwm_selection_transfer_destroy_outgoing( struct wlr_xwm_selection_transfer *transfer); xcb_atom_t xwm_mime_type_to_atom(struct wlr_xwm *xwm, char *mime_type); char *xwm_mime_type_from_atom(struct wlr_xwm *xwm, xcb_atom_t atom); struct wlr_xwm_selection *xwm_get_selection(struct wlr_xwm *xwm, xcb_atom_t selection_atom); void xwm_send_incr_chunk(struct wlr_xwm_selection_transfer *transfer); void xwm_handle_selection_request(struct wlr_xwm *xwm, xcb_selection_request_event_t *req); void xwm_handle_selection_destroy_notify(struct wlr_xwm *xwm, xcb_destroy_notify_event_t *event); void xwm_get_incr_chunk(struct wlr_xwm_selection_transfer *transfer); void xwm_handle_selection_notify(struct wlr_xwm *xwm, xcb_selection_notify_event_t *event); int xwm_handle_xfixes_selection_notify(struct wlr_xwm *xwm, xcb_xfixes_selection_notify_event_t *event); bool data_source_is_xwayland(struct wlr_data_source *wlr_source); bool primary_selection_source_is_xwayland( struct wlr_primary_selection_source *wlr_source); void xwm_seat_handle_start_drag(struct wlr_xwm *xwm, struct wlr_drag *drag); void xwm_selection_init(struct wlr_xwm_selection *selection, struct wlr_xwm *xwm, xcb_atom_t atom); void xwm_selection_finish(struct wlr_xwm_selection *selection); #endif wlroots-0.17.1/include/xwayland/xwm.h000066400000000000000000000077751454110342200175630ustar00rootroot00000000000000#ifndef XWAYLAND_XWM_H #define XWAYLAND_XWM_H #include #include #include #include #include "config.h" #include "xwayland/selection.h" #if HAVE_XCB_ERRORS #include #endif /* This is in xcb/xcb_event.h, but pulling xcb-util just for a constant * others redefine anyway is meh */ #define XCB_EVENT_RESPONSE_TYPE_MASK (0x7f) enum atom_name { WL_SURFACE_ID, WL_SURFACE_SERIAL, WM_DELETE_WINDOW, WM_PROTOCOLS, WM_HINTS, WM_NORMAL_HINTS, WM_SIZE_HINTS, WM_WINDOW_ROLE, MOTIF_WM_HINTS, UTF8_STRING, WM_S0, NET_SUPPORTED, NET_WM_CM_S0, NET_WM_PID, NET_WM_NAME, NET_WM_STATE, NET_WM_STRUT_PARTIAL, NET_WM_WINDOW_TYPE, WM_TAKE_FOCUS, WINDOW, NET_ACTIVE_WINDOW, NET_WM_MOVERESIZE, NET_SUPPORTING_WM_CHECK, NET_WM_STATE_FOCUSED, NET_WM_STATE_MODAL, NET_WM_STATE_FULLSCREEN, NET_WM_STATE_MAXIMIZED_VERT, NET_WM_STATE_MAXIMIZED_HORZ, NET_WM_STATE_HIDDEN, NET_WM_PING, WM_CHANGE_STATE, WM_STATE, CLIPBOARD, PRIMARY, WL_SELECTION, TARGETS, CLIPBOARD_MANAGER, INCR, TEXT, TIMESTAMP, DELETE, NET_STARTUP_ID, NET_STARTUP_INFO, NET_STARTUP_INFO_BEGIN, NET_WM_WINDOW_TYPE_NORMAL, NET_WM_WINDOW_TYPE_UTILITY, NET_WM_WINDOW_TYPE_TOOLTIP, NET_WM_WINDOW_TYPE_DND, NET_WM_WINDOW_TYPE_DROPDOWN_MENU, NET_WM_WINDOW_TYPE_POPUP_MENU, NET_WM_WINDOW_TYPE_COMBO, NET_WM_WINDOW_TYPE_MENU, NET_WM_WINDOW_TYPE_NOTIFICATION, NET_WM_WINDOW_TYPE_SPLASH, DND_SELECTION, DND_AWARE, DND_STATUS, DND_POSITION, DND_ENTER, DND_LEAVE, DND_DROP, DND_FINISHED, DND_PROXY, DND_TYPE_LIST, DND_ACTION_MOVE, DND_ACTION_COPY, DND_ACTION_ASK, DND_ACTION_PRIVATE, NET_CLIENT_LIST, NET_CLIENT_LIST_STACKING, NET_WORKAREA, ATOM_LAST // keep last }; struct wlr_xwm { struct wlr_xwayland *xwayland; struct wl_event_source *event_source; struct wlr_seat *seat; uint32_t ping_timeout; xcb_atom_t atoms[ATOM_LAST]; xcb_connection_t *xcb_conn; xcb_screen_t *screen; xcb_window_t window; xcb_visualid_t visual_id; xcb_colormap_t colormap; xcb_render_pictformat_t render_format_id; xcb_cursor_t cursor; struct wlr_xwm_selection clipboard_selection; struct wlr_xwm_selection primary_selection; struct wlr_xwm_selection dnd_selection; struct wlr_xwayland_surface *focus_surface; // Surfaces in creation order struct wl_list surfaces; // wlr_xwayland_surface.link // Surfaces in bottom-to-top stacking order, for _NET_CLIENT_LIST_STACKING struct wl_list surfaces_in_stack_order; // wlr_xwayland_surface.stack_link struct wl_list unpaired_surfaces; // wlr_xwayland_surface.unpaired_link struct wl_list pending_startup_ids; // pending_startup_id struct wlr_drag *drag; struct wlr_xwayland_surface *drag_focus; const xcb_query_extension_reply_t *xfixes; const xcb_query_extension_reply_t *xres; uint32_t xfixes_major_version; #if HAVE_XCB_ERRORS xcb_errors_context_t *errors_context; #endif unsigned int last_focus_seq; struct wl_listener compositor_new_surface; struct wl_listener compositor_destroy; struct wl_listener shell_v1_new_surface; struct wl_listener seat_set_selection; struct wl_listener seat_set_primary_selection; struct wl_listener seat_start_drag; struct wl_listener seat_drag_focus; struct wl_listener seat_drag_motion; struct wl_listener seat_drag_drop; struct wl_listener seat_drag_destroy; struct wl_listener seat_drag_source_destroy; }; struct wlr_xwm *xwm_create(struct wlr_xwayland *wlr_xwayland, int wm_fd); void xwm_destroy(struct wlr_xwm *xwm); void xwm_set_cursor(struct wlr_xwm *xwm, const uint8_t *pixels, uint32_t stride, uint32_t width, uint32_t height, int32_t hotspot_x, int32_t hotspot_y); int xwm_handle_selection_event(struct wlr_xwm *xwm, xcb_generic_event_t *event); int xwm_handle_selection_client_message(struct wlr_xwm *xwm, xcb_client_message_event_t *ev); void xwm_set_seat(struct wlr_xwm *xwm, struct wlr_seat *seat); char *xwm_get_atom_name(struct wlr_xwm *xwm, xcb_atom_t atom); bool xwm_atoms_contains(struct wlr_xwm *xwm, xcb_atom_t *atoms, size_t num_atoms, enum atom_name needle); #endif wlroots-0.17.1/meson.build000066400000000000000000000110371454110342200154510ustar00rootroot00000000000000project( 'wlroots', 'c', version: '0.17.1', license: 'MIT', meson_version: '>=0.59.0', default_options: [ 'c_std=c11', 'warning_level=2', 'werror=true', ], ) # When doing a major or minor release, *always* increase soversion. This isn't # necessary for bugfix releases. Increasing soversion is required because # wlroots never guarantees ABI stability -- only API stability is guaranteed # between minor releases. soversion = 12 little_endian = target_machine.endian() == 'little' big_endian = target_machine.endian() == 'big' add_project_arguments([ '-DWLR_USE_UNSTABLE', '-DWLR_LITTLE_ENDIAN=@0@'.format(little_endian.to_int()), '-DWLR_BIG_ENDIAN=@0@'.format(big_endian.to_int()), ], language: 'c') cc = meson.get_compiler('c') add_project_arguments(cc.get_supported_arguments([ '-Wundef', '-Wlogical-op', '-Wmissing-include-dirs', '-Wold-style-definition', '-Wpointer-arith', '-Winit-self', '-Wstrict-prototypes', '-Wimplicit-fallthrough=2', '-Wendif-labels', '-Wstrict-aliasing=2', '-Woverflow', '-Wmissing-prototypes', '-Walloca', '-Wno-missing-braces', '-Wno-missing-field-initializers', '-Wno-unused-parameter', ]), language: 'c') # Compute the relative path used by compiler invocations. source_root = meson.current_source_dir().split('/') build_root = meson.global_build_root().split('/') relative_dir_parts = [] i = 0 in_prefix = true foreach p : build_root if i >= source_root.length() or not in_prefix or p != source_root[i] in_prefix = false relative_dir_parts += '..' endif i += 1 endforeach i = 0 in_prefix = true foreach p : source_root if i >= build_root.length() or not in_prefix or build_root[i] != p in_prefix = false relative_dir_parts += p endif i += 1 endforeach relative_dir = join_paths(relative_dir_parts) + '/' # Strip relative path prefixes from the code if possible, otherwise hide them. if cc.has_argument('-fmacro-prefix-map=/prefix/to/hide=') add_project_arguments( '-fmacro-prefix-map=@0@='.format(relative_dir), language: 'c', ) else add_project_arguments( '-D_WLR_REL_SRC_DIR="@0@"'.format(relative_dir), language: 'c', ) endif features = { 'drm-backend': false, 'x11-backend': false, 'libinput-backend': false, 'xwayland': false, 'gles2-renderer': false, 'vulkan-renderer': false, 'gbm-allocator': false, 'session': false, } internal_features = { 'xcb-errors': false, 'egl': false, } internal_config = configuration_data() wayland_project_options = ['tests=false', 'documentation=false'] wayland_server = dependency('wayland-server', version: '>=1.22', fallback: 'wayland', default_options: wayland_project_options, ) drm = dependency('libdrm', version: '>=2.4.114', fallback: 'libdrm', default_options: [ 'intel=disabled', 'radeon=disabled', 'amdgpu=disabled', 'nouveau=disabled', 'vmwgfx=disabled', 'omap=disabled', 'exynos=disabled', 'freedreno=disabled', 'tegra=disabled', 'vc4=disabled', 'etnaviv=disabled', 'cairo-tests=disabled', 'man-pages=disabled', 'valgrind=disabled', 'tests=false', ], ) xkbcommon = dependency( 'xkbcommon', fallback: 'libxkbcommon', default_options: [ 'enable-tools=false', 'enable-x11=false', 'enable-docs=false', 'enable-xkbregistry=false', ], ) pixman = dependency('pixman-1', version: '>=0.42.0', fallback: 'pixman', default_options: ['werror=false'], ) math = cc.find_library('m') rt = cc.find_library('rt') wlr_files = [] wlr_deps = [ wayland_server, drm, xkbcommon, pixman, math, rt, ] subdir('protocol') subdir('render') subdir('backend') subdir('types') subdir('util') subdir('xcursor') subdir('xwayland') subdir('include') wlr_inc = include_directories('include') symbols_file = 'wlroots.syms' symbols_flag = '-Wl,--version-script,@0@/@1@'.format(meson.current_source_dir(), symbols_file) lib_wlr = library( meson.project_name(), wlr_files, soversion: soversion.to_string(), dependencies: wlr_deps, include_directories: [wlr_inc], install: true, link_args: symbols_flag, link_depends: symbols_file, ) wlr_vars = {} foreach name, have : features wlr_vars += { 'have_' + name.underscorify(): have.to_string() } endforeach wlroots = declare_dependency( link_with: lib_wlr, dependencies: wlr_deps, include_directories: wlr_inc, variables: wlr_vars, ) meson.override_dependency('wlroots', wlroots) summary(features + internal_features, bool_yn: true) if get_option('examples') subdir('examples') subdir('tinywl') endif pkgconfig = import('pkgconfig') pkgconfig.generate( lib_wlr, description: 'Wayland compositor library', url: 'https://gitlab.freedesktop.org/wlroots/wlroots', variables: wlr_vars, ) wlroots-0.17.1/meson_options.txt000066400000000000000000000016241454110342200167450ustar00rootroot00000000000000option('xcb-errors', type: 'feature', value: 'auto', description: 'Use xcb-errors util library') option('xwayland', type: 'feature', value: 'auto', yield: true, description: 'Enable support for X11 applications') option('examples', type: 'boolean', value: true, description: 'Build example applications') option('icon_directory', description: 'Location used to look for cursors (default: ${datadir}/icons)', type: 'string', value: '') option('renderers', type: 'array', choices: ['auto', 'gles2', 'vulkan'], value: ['auto'], description: 'Select built-in renderers') option('backends', type: 'array', choices: ['auto', 'drm', 'libinput', 'x11'], value: ['auto'], description: 'Select built-in backends') option('allocators', type: 'array', choices: ['auto', 'gbm'], value: ['auto'], description: 'Select built-in allocators') option('session', type: 'feature', value: 'auto', description: 'Enable session support') wlroots-0.17.1/protocol/000077500000000000000000000000001454110342200151465ustar00rootroot00000000000000wlroots-0.17.1/protocol/drm.xml000066400000000000000000000174471454110342200164670ustar00rootroot00000000000000 Copyright © 2008-2011 Kristian Høgsberg Copyright © 2010-2011 Intel Corporation Permission to use, copy, modify, distribute, and sell this software and its documentation for any purpose is hereby granted without fee, provided that\n the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of the copyright holders not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. The copyright holders make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. Bitmask of capabilities. wlroots-0.17.1/protocol/input-method-unstable-v2.xml000066400000000000000000000515521454110342200224550ustar00rootroot00000000000000 Copyright © 2008-2011 Kristian Høgsberg Copyright © 2010-2011 Intel Corporation Copyright © 2012-2013 Collabora, Ltd. Copyright © 2012, 2013 Intel Corporation Copyright © 2015, 2016 Jan Arne Petersen Copyright © 2017, 2018 Red Hat, Inc. Copyright © 2018 Purism SPC Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. This protocol allows applications to act as input methods for compositors. An input method context is used to manage the state of the input method. Text strings are UTF-8 encoded, their indices and lengths are in bytes. This document adheres to the RFC 2119 when using words like "must", "should", "may", etc. Warning! The protocol described in this file is experimental and backward incompatible changes may be made. Backward compatible changes may be added together with the corresponding interface version bump. Backward incompatible changes are done by bumping the version number in the protocol and interface names and resetting the interface version. Once the protocol is to be declared stable, the 'z' prefix and the version number in the protocol and interface names are removed and the interface version number is reset. An input method object allows for clients to compose text. The objects connects the client to a text input in an application, and lets the client to serve as an input method for a seat. The zwp_input_method_v2 object can occupy two distinct states: active and inactive. In the active state, the object is associated to and communicates with a text input. In the inactive state, there is no associated text input, and the only communication is with the compositor. Initially, the input method is in the inactive state. Requests issued in the inactive state must be accepted by the compositor. Because of the serial mechanism, and the state reset on activate event, they will not have any effect on the state of the next text input. There must be no more than one input method object per seat. Notification that a text input focused on this seat requested the input method to be activated. This event serves the purpose of providing the compositor with an active input method. This event resets all state associated with previous enable, disable, surrounding_text, text_change_cause, and content_type events, as well as the state associated with set_preedit_string, commit_string, and delete_surrounding_text requests. In addition, it marks the zwp_input_method_v2 object as active, and makes any existing zwp_input_popup_surface_v2 objects visible. The surrounding_text, and content_type events must follow before the next done event if the text input supports the respective functionality. State set with this event is double-buffered. It will get applied on the next zwp_input_method_v2.done event, and stay valid until changed. Notification that no focused text input currently needs an active input method on this seat. This event marks the zwp_input_method_v2 object as inactive. The compositor must make all existing zwp_input_popup_surface_v2 objects invisible until the next activate event. State set with this event is double-buffered. It will get applied on the next zwp_input_method_v2.done event, and stay valid until changed. Updates the surrounding plain text around the cursor, excluding the preedit text. If any preedit text is present, it is replaced with the cursor for the purpose of this event. The argument text is a buffer containing the preedit string, and must include the cursor position, and the complete selection. It should contain additional characters before and after these. There is a maximum length of wayland messages, so text can not be longer than 4000 bytes. cursor is the byte offset of the cursor within the text buffer. anchor is the byte offset of the selection anchor within the text buffer. If there is no selected text, anchor must be the same as cursor. If this event does not arrive before the first done event, the input method may assume that the text input does not support this functionality and ignore following surrounding_text events. Values set with this event are double-buffered. They will get applied and set to initial values on the next zwp_input_method_v2.done event. The initial state for affected fields is empty, meaning that the text input does not support sending surrounding text. If the empty values get applied, subsequent attempts to change them may have no effect. Tells the input method why the text surrounding the cursor changed. Whenever the client detects an external change in text, cursor, or anchor position, it must issue this request to the compositor. This request is intended to give the input method a chance to update the preedit text in an appropriate way, e.g. by removing it when the user starts typing with a keyboard. cause describes the source of the change. The value set with this event is double-buffered. It will get applied and set to its initial value on the next zwp_input_method_v2.done event. The initial value of cause is input_method. Indicates the content type and hint for the current zwp_input_method_v2 instance. Values set with this event are double-buffered. They will get applied on the next zwp_input_method_v2.done event. The initial value for hint is none, and the initial value for purpose is normal. Atomically applies state changes recently sent to the client. The done event establishes and updates the state of the client, and must be issued after any changes to apply them. Text input state (content purpose, content hint, surrounding text, and change cause) is conceptually double-buffered within an input method context. Events modify the pending state, as opposed to the current state in use by the input method. A done event atomically applies all pending state, replacing the current state. After done, the new pending state is as documented for each related request. Events must be applied in the order of arrival. Neither current nor pending state are modified unless noted otherwise. Send the commit string text for insertion to the application. Inserts a string at current cursor position (see commit event sequence). The string to commit could be either just a single character after a key press or the result of some composing. The argument text is a buffer containing the string to insert. There is a maximum length of wayland messages, so text can not be longer than 4000 bytes. Values set with this event are double-buffered. They must be applied and reset to initial on the next zwp_text_input_v3.commit request. The initial value of text is an empty string. Send the pre-edit string text to the application text input. Place a new composing text (pre-edit) at the current cursor position. Any previously set composing text must be removed. Any previously existing selected text must be removed. The cursor is moved to a new position within the preedit string. The argument text is a buffer containing the preedit string. There is a maximum length of wayland messages, so text can not be longer than 4000 bytes. The arguments cursor_begin and cursor_end are counted in bytes relative to the beginning of the submitted string buffer. Cursor should be hidden by the text input when both are equal to -1. cursor_begin indicates the beginning of the cursor. cursor_end indicates the end of the cursor. It may be equal or different than cursor_begin. Values set with this event are double-buffered. They must be applied on the next zwp_input_method_v2.commit event. The initial value of text is an empty string. The initial value of cursor_begin, and cursor_end are both 0. Remove the surrounding text. before_length and after_length are the number of bytes before and after the current cursor index (excluding the preedit text) to delete. If any preedit text is present, it is replaced with the cursor for the purpose of this event. In effect before_length is counted from the beginning of preedit text, and after_length from its end (see commit event sequence). Values set with this event are double-buffered. They must be applied and reset to initial on the next zwp_input_method_v2.commit request. The initial values of both before_length and after_length are 0. Apply state changes from commit_string, set_preedit_string and delete_surrounding_text requests. The state relating to these events is double-buffered, and each one modifies the pending state. This request replaces the current state with the pending state. The connected text input is expected to proceed by evaluating the changes in the following order: 1. Replace existing preedit string with the cursor. 2. Delete requested surrounding text. 3. Insert commit string with the cursor at its end. 4. Calculate surrounding text to send. 5. Insert new preedit text in cursor position. 6. Place cursor inside preedit text. The serial number reflects the last state of the zwp_input_method_v2 object known to the client. The value of the serial argument must be equal to the number of done events already issued by that object. When the compositor receives a commit request with a serial different than the number of past done events, it must proceed as normal, except it should not change the current state of the zwp_input_method_v2 object. Creates a new zwp_input_popup_surface_v2 object wrapping a given surface. The surface gets assigned the "input_popup" role. If the surface already has an assigned role, the compositor must issue a protocol error. Allow an input method to receive hardware keyboard input and process key events to generate text events (with pre-edit) over the wire. This allows input methods which compose multiple key events for inputting text like it is done for CJK languages. The compositor should send all keyboard events on the seat to the grab holder via the returned wl_keyboard object. Nevertheless, the compositor may decide not to forward any particular event. The compositor must not further process any event after it has been forwarded to the grab holder. Releasing the resulting wl_keyboard object releases the grab. The input method ceased to be available. The compositor must issue this event as the only event on the object if there was another input_method object associated with the same seat at the time of its creation. The compositor must issue this request when the object is no longer usable, e.g. due to seat removal. The input method context becomes inert and should be destroyed after deactivation is handled. Any further requests and events except for the destroy request must be ignored. Destroys the zwp_text_input_v2 object and any associated child objects, i.e. zwp_input_popup_surface_v2 and zwp_input_method_keyboard_grab_v2. This interface marks a surface as a popup for interacting with an input method. The compositor should place it near the active text input area. It must be visible if and only if the input method is in the active state. The client must not destroy the underlying wl_surface while the zwp_input_popup_surface_v2 object exists. Notify about the position of the area of the text input expressed as a rectangle in surface local coordinates. This is a hint to the input method telling it the relative position of the text being entered. The zwp_input_method_keyboard_grab_v2 interface represents an exclusive grab of the wl_keyboard interface associated with the seat. This event provides a file descriptor to the client which can be memory-mapped to provide a keyboard mapping description. A key was pressed or released. The time argument is a timestamp with millisecond granularity, with an undefined base. Notifies clients that the modifier and/or group state has changed, and it should update its local state. Informs the client about the keyboard's repeat rate and delay. This event is sent as soon as the zwp_input_method_keyboard_grab_v2 object has been created, and is guaranteed to be received by the client before any key press event. Negative values for either rate or delay are illegal. A rate of zero will disable any repeating (regardless of the value of delay). This event can be sent later on as well with a new value if necessary, so clients should continue listening for the event past the creation of zwp_input_method_keyboard_grab_v2. The input method manager allows the client to become the input method on a chosen seat. No more than one input method must be associated with any seat at any given time. Request a new input zwp_input_method_v2 object associated with a given seat. Destroys the zwp_input_method_manager_v2 object. The zwp_input_method_v2 objects originating from it remain valid. wlroots-0.17.1/protocol/meson.build000066400000000000000000000117101454110342200173100ustar00rootroot00000000000000wayland_protos = dependency('wayland-protocols', version: '>=1.32', fallback: 'wayland-protocols', default_options: ['tests=false'], ) wl_protocol_dir = wayland_protos.get_variable('pkgdatadir') wayland_scanner_dep = dependency('wayland-scanner', native: true) wayland_scanner = find_program( wayland_scanner_dep.get_variable('wayland_scanner'), native: true, ) protocols = { # Stable upstream protocols 'presentation-time': wl_protocol_dir / 'stable/presentation-time/presentation-time.xml', 'viewporter': wl_protocol_dir / 'stable/viewporter/viewporter.xml', 'xdg-shell': wl_protocol_dir / 'stable/xdg-shell/xdg-shell.xml', # Staging upstream protocols 'content-type-v1': wl_protocol_dir / 'staging/content-type/content-type-v1.xml', 'cursor-shape-v1': wl_protocol_dir / 'staging/cursor-shape/cursor-shape-v1.xml', 'drm-lease-v1': wl_protocol_dir / 'staging/drm-lease/drm-lease-v1.xml', 'ext-idle-notify-v1': wl_protocol_dir / 'staging/ext-idle-notify/ext-idle-notify-v1.xml', 'ext-session-lock-v1': wl_protocol_dir / 'staging/ext-session-lock/ext-session-lock-v1.xml', 'fractional-scale-v1': wl_protocol_dir / 'staging/fractional-scale/fractional-scale-v1.xml', 'security-context-v1': wl_protocol_dir / 'staging/security-context/security-context-v1.xml', 'single-pixel-buffer-v1': wl_protocol_dir / 'staging/single-pixel-buffer/single-pixel-buffer-v1.xml', 'xdg-activation-v1': wl_protocol_dir / 'staging/xdg-activation/xdg-activation-v1.xml', 'xwayland-shell-v1': wl_protocol_dir / 'staging/xwayland-shell/xwayland-shell-v1.xml', 'tearing-control-v1': wl_protocol_dir / 'staging/tearing-control/tearing-control-v1.xml', # Unstable upstream protocols 'fullscreen-shell-unstable-v1': wl_protocol_dir / 'unstable/fullscreen-shell/fullscreen-shell-unstable-v1.xml', 'idle-inhibit-unstable-v1': wl_protocol_dir / 'unstable/idle-inhibit/idle-inhibit-unstable-v1.xml', 'keyboard-shortcuts-inhibit-unstable-v1': wl_protocol_dir / 'unstable/keyboard-shortcuts-inhibit/keyboard-shortcuts-inhibit-unstable-v1.xml', 'linux-dmabuf-unstable-v1': wl_protocol_dir / 'unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml', 'pointer-constraints-unstable-v1': wl_protocol_dir / 'unstable/pointer-constraints/pointer-constraints-unstable-v1.xml', 'pointer-gestures-unstable-v1': wl_protocol_dir / 'unstable/pointer-gestures/pointer-gestures-unstable-v1.xml', 'primary-selection-unstable-v1': wl_protocol_dir / 'unstable/primary-selection/primary-selection-unstable-v1.xml', 'relative-pointer-unstable-v1': wl_protocol_dir / 'unstable/relative-pointer/relative-pointer-unstable-v1.xml', 'tablet-unstable-v2': wl_protocol_dir / 'unstable/tablet/tablet-unstable-v2.xml', 'text-input-unstable-v3': wl_protocol_dir / 'unstable/text-input/text-input-unstable-v3.xml', 'xdg-decoration-unstable-v1': wl_protocol_dir / 'unstable/xdg-decoration/xdg-decoration-unstable-v1.xml', 'xdg-foreign-unstable-v1': wl_protocol_dir / 'unstable/xdg-foreign/xdg-foreign-unstable-v1.xml', 'xdg-foreign-unstable-v2': wl_protocol_dir / 'unstable/xdg-foreign/xdg-foreign-unstable-v2.xml', 'xdg-output-unstable-v1': wl_protocol_dir / 'unstable/xdg-output/xdg-output-unstable-v1.xml', # Other protocols 'drm': 'drm.xml', 'input-method-unstable-v2': 'input-method-unstable-v2.xml', 'kde-server-decoration': 'server-decoration.xml', 'virtual-keyboard-unstable-v1': 'virtual-keyboard-unstable-v1.xml', 'wlr-data-control-unstable-v1': 'wlr-data-control-unstable-v1.xml', 'wlr-export-dmabuf-unstable-v1': 'wlr-export-dmabuf-unstable-v1.xml', 'wlr-foreign-toplevel-management-unstable-v1': 'wlr-foreign-toplevel-management-unstable-v1.xml', 'wlr-gamma-control-unstable-v1': 'wlr-gamma-control-unstable-v1.xml', 'wlr-input-inhibitor-unstable-v1': 'wlr-input-inhibitor-unstable-v1.xml', 'wlr-layer-shell-unstable-v1': 'wlr-layer-shell-unstable-v1.xml', 'wlr-output-management-unstable-v1': 'wlr-output-management-unstable-v1.xml', 'wlr-output-power-management-unstable-v1': 'wlr-output-power-management-unstable-v1.xml', 'wlr-screencopy-unstable-v1': 'wlr-screencopy-unstable-v1.xml', 'wlr-virtual-pointer-unstable-v1': 'wlr-virtual-pointer-unstable-v1.xml', } protocols_code = {} protocols_server_header = {} protocols_client_header = {} foreach name, path : protocols code = custom_target( name.underscorify() + '_c', input: path, output: '@BASENAME@-protocol.c', command: [wayland_scanner, 'private-code', '@INPUT@', '@OUTPUT@'], ) wlr_files += code server_header = custom_target( name.underscorify() + '_server_h', input: path, output: '@BASENAME@-protocol.h', command: [wayland_scanner, 'server-header', '@INPUT@', '@OUTPUT@'], ) wlr_files += server_header client_header = custom_target( name.underscorify() + '_client_h', input: path, output: '@BASENAME@-client-protocol.h', command: [wayland_scanner, 'client-header', '@INPUT@', '@OUTPUT@'], build_by_default: false, ) protocols_code += { name: code } protocols_server_header += { name: server_header } protocols_client_header += { name: client_header } endforeach wlroots-0.17.1/protocol/server-decoration.xml000066400000000000000000000124301454110342200213230ustar00rootroot00000000000000 . ]]> This interface allows to coordinate whether the server should create a server-side window decoration around a wl_surface representing a shell surface (wl_shell_surface or similar). By announcing support for this interface the server indicates that it supports server side decorations. When a client creates a server-side decoration object it indicates that it supports the protocol. The client is supposed to tell the server whether it wants server-side decorations or will provide client-side decorations. If the client does not create a server-side decoration object for a surface the server interprets this as lack of support for this protocol and considers it as client-side decorated. Nevertheless a client-side decorated surface should use this protocol to indicate to the server that it does not want a server-side deco. This event is emitted directly after binding the interface. It contains the default mode for the decoration. When a new server decoration object is created this new object will be in the default mode until the first request_mode is requested. The server may change the default mode at any time. This event is emitted directly after the decoration is created and represents the base decoration policy by the server. E.g. a server which wants all surfaces to be client-side decorated will send Client, a server which wants server-side decoration will send Server. The client can request a different mode through the decoration request. The server will acknowledge this by another event with the same mode. So even if a server prefers server-side decoration it's possible to force a client-side decoration. The server may emit this event at any time. In this case the client can again request a different mode. It's the responsibility of the server to prevent a feedback loop. wlroots-0.17.1/protocol/virtual-keyboard-unstable-v1.xml000066400000000000000000000114261454110342200233170ustar00rootroot00000000000000 Copyright © 2008-2011 Kristian Høgsberg Copyright © 2010-2013 Intel Corporation Copyright © 2012-2013 Collabora, Ltd. Copyright © 2018 Purism SPC Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. The virtual keyboard provides an application with requests which emulate the behaviour of a physical keyboard. This interface can be used by clients on its own to provide raw input events, or it can accompany the input method protocol. Provide a file descriptor to the compositor which can be memory-mapped to provide a keyboard mapping description. Format carries a value from the keymap_format enumeration. A key was pressed or released. The time argument is a timestamp with millisecond granularity, with an undefined base. All requests regarding a single object must share the same clock. Keymap must be set before issuing this request. State carries a value from the key_state enumeration. Notifies the compositor that the modifier and/or group state has changed, and it should update state. The client should use wl_keyboard.modifiers event to synchronize its internal state with seat state. Keymap must be set before issuing this request. A virtual keyboard manager allows an application to provide keyboard input events as if they came from a physical keyboard. Creates a new virtual keyboard associated to a seat. If the compositor enables a keyboard to perform arbitrary actions, it should present an error when an untrusted client requests a new keyboard. wlroots-0.17.1/protocol/wlr-data-control-unstable-v1.xml000066400000000000000000000274161454110342200232320ustar00rootroot00000000000000 Copyright © 2018 Simon Ser Copyright © 2019 Ivan Molodetskikh Permission to use, copy, modify, distribute, and sell this software and its documentation for any purpose is hereby granted without fee, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of the copyright holders not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. The copyright holders make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. This protocol allows a privileged client to control data devices. In particular, the client will be able to manage the current selection and take the role of a clipboard manager. Warning! The protocol described in this file is experimental and backward incompatible changes may be made. Backward compatible changes may be added together with the corresponding interface version bump. Backward incompatible changes are done by bumping the version number in the protocol and interface names and resetting the interface version. Once the protocol is to be declared stable, the 'z' prefix and the version number in the protocol and interface names are removed and the interface version number is reset. This interface is a manager that allows creating per-seat data device controls. Create a new data source. Create a data device that can be used to manage a seat's selection. All objects created by the manager will still remain valid, until their appropriate destroy request has been called. This interface allows a client to manage a seat's selection. When the seat is destroyed, this object becomes inert. This request asks the compositor to set the selection to the data from the source on behalf of the client. The given source may not be used in any further set_selection or set_primary_selection requests. Attempting to use a previously used source is a protocol error. To unset the selection, set the source to NULL. Destroys the data device object. The data_offer event introduces a new wlr_data_control_offer object, which will subsequently be used in either the wlr_data_control_device.selection event (for the regular clipboard selections) or the wlr_data_control_device.primary_selection event (for the primary clipboard selections). Immediately following the wlr_data_control_device.data_offer event, the new data_offer object will send out wlr_data_control_offer.offer events to describe the MIME types it offers. The selection event is sent out to notify the client of a new wlr_data_control_offer for the selection for this device. The wlr_data_control_device.data_offer and the wlr_data_control_offer.offer events are sent out immediately before this event to introduce the data offer object. The selection event is sent to a client when a new selection is set. The wlr_data_control_offer is valid until a new wlr_data_control_offer or NULL is received. The client must destroy the previous selection wlr_data_control_offer, if any, upon receiving this event. The first selection event is sent upon binding the wlr_data_control_device object. This data control object is no longer valid and should be destroyed by the client. The primary_selection event is sent out to notify the client of a new wlr_data_control_offer for the primary selection for this device. The wlr_data_control_device.data_offer and the wlr_data_control_offer.offer events are sent out immediately before this event to introduce the data offer object. The primary_selection event is sent to a client when a new primary selection is set. The wlr_data_control_offer is valid until a new wlr_data_control_offer or NULL is received. The client must destroy the previous primary selection wlr_data_control_offer, if any, upon receiving this event. If the compositor supports primary selection, the first primary_selection event is sent upon binding the wlr_data_control_device object. This request asks the compositor to set the primary selection to the data from the source on behalf of the client. The given source may not be used in any further set_selection or set_primary_selection requests. Attempting to use a previously used source is a protocol error. To unset the primary selection, set the source to NULL. The compositor will ignore this request if it does not support primary selection. The wlr_data_control_source object is the source side of a wlr_data_control_offer. It is created by the source client in a data transfer and provides a way to describe the offered data and a way to respond to requests to transfer the data. This request adds a MIME type to the set of MIME types advertised to targets. Can be called several times to offer multiple types. Calling this after wlr_data_control_device.set_selection is a protocol error. Destroys the data source object. Request for data from the client. Send the data as the specified MIME type over the passed file descriptor, then close it. This data source is no longer valid. The data source has been replaced by another data source. The client should clean up and destroy this data source. A wlr_data_control_offer represents a piece of data offered for transfer by another client (the source client). The offer describes the different MIME types that the data can be converted to and provides the mechanism for transferring the data directly from the source client. To transfer the offered data, the client issues this request and indicates the MIME type it wants to receive. The transfer happens through the passed file descriptor (typically created with the pipe system call). The source client writes the data in the MIME type representation requested and then closes the file descriptor. The receiving client reads from the read end of the pipe until EOF and then closes its end, at which point the transfer is complete. This request may happen multiple times for different MIME types. Destroys the data offer object. Sent immediately after creating the wlr_data_control_offer object. One event per offered MIME type. wlroots-0.17.1/protocol/wlr-export-dmabuf-unstable-v1.xml000066400000000000000000000217351454110342200234160ustar00rootroot00000000000000 Copyright © 2018 Rostislav Pehlivanov Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. An interface to capture surfaces in an efficient way by exporting DMA-BUFs. Warning! The protocol described in this file is experimental and backward incompatible changes may be made. Backward compatible changes may be added together with the corresponding interface version bump. Backward incompatible changes are done by bumping the version number in the protocol and interface names and resetting the interface version. Once the protocol is to be declared stable, the 'z' prefix and the version number in the protocol and interface names are removed and the interface version number is reset. This object is a manager with which to start capturing from sources. Capture the next frame of a an entire output. All objects created by the manager will still remain valid, until their appropriate destroy request has been called. This object represents a single DMA-BUF frame. If the capture is successful, the compositor will first send a "frame" event, followed by one or several "object". When the frame is available for readout, the "ready" event is sent. If the capture failed, the "cancel" event is sent. This can happen anytime before the "ready" event. Once either a "ready" or a "cancel" event is received, the client should destroy the frame. Once an "object" event is received, the client is responsible for closing the associated file descriptor. All frames are read-only and may not be written into or altered. Special flags that should be respected by the client. Main event supplying the client with information about the frame. If the capture didn't fail, this event is always emitted first before any other events. This event is followed by a number of "object" as specified by the "num_objects" argument. Event which serves to supply the client with the file descriptors containing the data for each object. After receiving this event, the client must always close the file descriptor as soon as they're done with it and even if the frame fails. This event is sent as soon as the frame is presented, indicating it is available for reading. This event includes the time at which presentation happened at. The timestamp is expressed as tv_sec_hi, tv_sec_lo, tv_nsec triples, each component being an unsigned 32-bit value. Whole seconds are in tv_sec which is a 64-bit value combined from tv_sec_hi and tv_sec_lo, and the additional fractional part in tv_nsec as nanoseconds. Hence, for valid timestamps tv_nsec must be in [0, 999999999]. The seconds part may have an arbitrary offset at start. After receiving this event, the client should destroy this object. Indicates reason for cancelling the frame. If the capture failed or if the frame is no longer valid after the "frame" event has been emitted, this event will be used to inform the client to scrap the frame. If the failure is temporary, the client may capture again the same source. If the failure is permanent, any further attempts to capture the same source will fail again. After receiving this event, the client should destroy this object. Unreferences the frame. This request must be called as soon as its no longer used. It can be called at any time by the client. The client will still have to close any FDs it has been given. wlroots-0.17.1/protocol/wlr-foreign-toplevel-management-unstable-v1.xml000066400000000000000000000264221454110342200262320ustar00rootroot00000000000000 Copyright © 2018 Ilia Bozhinov Permission to use, copy, modify, distribute, and sell this software and its documentation for any purpose is hereby granted without fee, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of the copyright holders not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. The copyright holders make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. The purpose of this protocol is to enable the creation of taskbars and docks by providing them with a list of opened applications and letting them request certain actions on them, like maximizing, etc. After a client binds the zwlr_foreign_toplevel_manager_v1, each opened toplevel window will be sent via the toplevel event This event is emitted whenever a new toplevel window is created. It is emitted for all toplevels, regardless of the app that has created them. All initial details of the toplevel(title, app_id, states, etc.) will be sent immediately after this event via the corresponding events in zwlr_foreign_toplevel_handle_v1. Indicates the client no longer wishes to receive events for new toplevels. However the compositor may emit further toplevel_created events, until the finished event is emitted. The client must not send any more requests after this one. This event indicates that the compositor is done sending events to the zwlr_foreign_toplevel_manager_v1. The server will destroy the object immediately after sending this request, so it will become invalid and the client should free any resources associated with it. A zwlr_foreign_toplevel_handle_v1 object represents an opened toplevel window. Each app may have multiple opened toplevels. Each toplevel has a list of outputs it is visible on, conveyed to the client with the output_enter and output_leave events. This event is emitted whenever the title of the toplevel changes. This event is emitted whenever the app-id of the toplevel changes. This event is emitted whenever the toplevel becomes visible on the given output. A toplevel may be visible on multiple outputs. This event is emitted whenever the toplevel stops being visible on the given output. It is guaranteed that an entered-output event with the same output has been emitted before this event. Requests that the toplevel be maximized. If the maximized state actually changes, this will be indicated by the state event. Requests that the toplevel be unmaximized. If the maximized state actually changes, this will be indicated by the state event. Requests that the toplevel be minimized. If the minimized state actually changes, this will be indicated by the state event. Requests that the toplevel be unminimized. If the minimized state actually changes, this will be indicated by the state event. Request that this toplevel be activated on the given seat. There is no guarantee the toplevel will be actually activated. The different states that a toplevel can have. These have the same meaning as the states with the same names defined in xdg-toplevel This event is emitted immediately after the zlw_foreign_toplevel_handle_v1 is created and each time the toplevel state changes, either because of a compositor action or because of a request in this protocol. This event is sent after all changes in the toplevel state have been sent. This allows changes to the zwlr_foreign_toplevel_handle_v1 properties to be seen as atomic, even if they happen via multiple events. Send a request to the toplevel to close itself. The compositor would typically use a shell-specific method to carry out this request, for example by sending the xdg_toplevel.close event. However, this gives no guarantees the toplevel will actually be destroyed. If and when this happens, the zwlr_foreign_toplevel_handle_v1.closed event will be emitted. The rectangle of the surface specified in this request corresponds to the place where the app using this protocol represents the given toplevel. It can be used by the compositor as a hint for some operations, e.g minimizing. The client is however not required to set this, in which case the compositor is free to decide some default value. If the client specifies more than one rectangle, only the last one is considered. The dimensions are given in surface-local coordinates. Setting width=height=0 removes the already-set rectangle. This event means the toplevel has been destroyed. It is guaranteed there won't be any more events for this zwlr_foreign_toplevel_handle_v1. The toplevel itself becomes inert so any requests will be ignored except the destroy request. Destroys the zwlr_foreign_toplevel_handle_v1 object. This request should be called either when the client does not want to use the toplevel anymore or after the closed event to finalize the destruction of the object. Requests that the toplevel be fullscreened on the given output. If the fullscreen state and/or the outputs the toplevel is visible on actually change, this will be indicated by the state and output_enter/leave events. The output parameter is only a hint to the compositor. Also, if output is NULL, the compositor should decide which output the toplevel will be fullscreened on, if at all. Requests that the toplevel be unfullscreened. If the fullscreen state actually changes, this will be indicated by the state event. This event is emitted whenever the parent of the toplevel changes. No event is emitted when the parent handle is destroyed by the client. wlroots-0.17.1/protocol/wlr-gamma-control-unstable-v1.xml000066400000000000000000000126061454110342200233760ustar00rootroot00000000000000 Copyright © 2015 Giulio camuffo Copyright © 2018 Simon Ser Permission to use, copy, modify, distribute, and sell this software and its documentation for any purpose is hereby granted without fee, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of the copyright holders not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. The copyright holders make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. This protocol allows a privileged client to set the gamma tables for outputs. Warning! The protocol described in this file is experimental and backward incompatible changes may be made. Backward compatible changes may be added together with the corresponding interface version bump. Backward incompatible changes are done by bumping the version number in the protocol and interface names and resetting the interface version. Once the protocol is to be declared stable, the 'z' prefix and the version number in the protocol and interface names are removed and the interface version number is reset. This interface is a manager that allows creating per-output gamma controls. Create a gamma control that can be used to adjust gamma tables for the provided output. All objects created by the manager will still remain valid, until their appropriate destroy request has been called. This interface allows a client to adjust gamma tables for a particular output. The client will receive the gamma size, and will then be able to set gamma tables. At any time the compositor can send a failed event indicating that this object is no longer valid. There must always be at most one gamma control object per output, which has exclusive access to this particular output. When the gamma control object is destroyed, the gamma table is restored to its original value. Advertise the size of each gamma ramp. This event is sent immediately when the gamma control object is created. Set the gamma table. The file descriptor can be memory-mapped to provide the raw gamma table, which contains successive gamma ramps for the red, green and blue channels. Each gamma ramp is an array of 16-byte unsigned integers which has the same length as the gamma size. The file descriptor data must have the same length as three times the gamma size. This event indicates that the gamma control is no longer valid. This can happen for a number of reasons, including: - The output doesn't support gamma tables - Setting the gamma tables failed - Another client already has exclusive gamma control for this output - The compositor has transferred gamma control to another client Upon receiving this event, the client should destroy this object. Destroys the gamma control object. If the object is still valid, this restores the original gamma tables. wlroots-0.17.1/protocol/wlr-input-inhibitor-unstable-v1.xml000066400000000000000000000061241454110342200237600ustar00rootroot00000000000000 Copyright © 2018 Drew DeVault Permission to use, copy, modify, distribute, and sell this software and its documentation for any purpose is hereby granted without fee, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of the copyright holders not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. The copyright holders make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. Clients can use this interface to prevent input events from being sent to any surfaces but its own, which is useful for example in lock screen software. It is assumed that access to this interface will be locked down to whitelisted clients by the compositor. Activates the input inhibitor. As long as the inhibitor is active, the compositor will not send input events to other clients. While this resource exists, input to clients other than the owner of the inhibitor resource will not receive input events. The client that owns this resource will receive all input events normally. The compositor will also disable all of its own input processing (such as keyboard shortcuts) while the inhibitor is active. The compositor may continue to send input events to selected clients, such as an on-screen keyboard (via the input-method protocol). Destroy the inhibitor and allow other clients to receive input. wlroots-0.17.1/protocol/wlr-layer-shell-unstable-v1.xml000066400000000000000000000440361454110342200230610ustar00rootroot00000000000000 Copyright © 2017 Drew DeVault Permission to use, copy, modify, distribute, and sell this software and its documentation for any purpose is hereby granted without fee, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of the copyright holders not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. The copyright holders make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. Clients can use this interface to assign the surface_layer role to wl_surfaces. Such surfaces are assigned to a "layer" of the output and rendered with a defined z-depth respective to each other. They may also be anchored to the edges and corners of a screen and specify input handling semantics. This interface should be suitable for the implementation of many desktop shell components, and a broad number of other applications that interact with the desktop. Create a layer surface for an existing surface. This assigns the role of layer_surface, or raises a protocol error if another role is already assigned. Creating a layer surface from a wl_surface which has a buffer attached or committed is a client error, and any attempts by a client to attach or manipulate a buffer prior to the first layer_surface.configure call must also be treated as errors. After creating a layer_surface object and setting it up, the client must perform an initial commit without any buffer attached. The compositor will reply with a layer_surface.configure event. The client must acknowledge it and is then allowed to attach a buffer to map the surface. You may pass NULL for output to allow the compositor to decide which output to use. Generally this will be the one that the user most recently interacted with. Clients can specify a namespace that defines the purpose of the layer surface. These values indicate which layers a surface can be rendered in. They are ordered by z depth, bottom-most first. Traditional shell surfaces will typically be rendered between the bottom and top layers. Fullscreen shell surfaces are typically rendered at the top layer. Multiple surfaces can share a single layer, and ordering within a single layer is undefined. This request indicates that the client will not use the layer_shell object any more. Objects that have been created through this instance are not affected. An interface that may be implemented by a wl_surface, for surfaces that are designed to be rendered as a layer of a stacked desktop-like environment. Layer surface state (layer, size, anchor, exclusive zone, margin, interactivity) is double-buffered, and will be applied at the time wl_surface.commit of the corresponding wl_surface is called. Attaching a null buffer to a layer surface unmaps it. Unmapping a layer_surface means that the surface cannot be shown by the compositor until it is explicitly mapped again. The layer_surface returns to the state it had right after layer_shell.get_layer_surface. The client can re-map the surface by performing a commit without any buffer attached, waiting for a configure event and handling it as usual. Sets the size of the surface in surface-local coordinates. The compositor will display the surface centered with respect to its anchors. If you pass 0 for either value, the compositor will assign it and inform you of the assignment in the configure event. You must set your anchor to opposite edges in the dimensions you omit; not doing so is a protocol error. Both values are 0 by default. Size is double-buffered, see wl_surface.commit. Requests that the compositor anchor the surface to the specified edges and corners. If two orthogonal edges are specified (e.g. 'top' and 'left'), then the anchor point will be the intersection of the edges (e.g. the top left corner of the output); otherwise the anchor point will be centered on that edge, or in the center if none is specified. Anchor is double-buffered, see wl_surface.commit. Requests that the compositor avoids occluding an area with other surfaces. The compositor's use of this information is implementation-dependent - do not assume that this region will not actually be occluded. A positive value is only meaningful if the surface is anchored to one edge or an edge and both perpendicular edges. If the surface is not anchored, anchored to only two perpendicular edges (a corner), anchored to only two parallel edges or anchored to all edges, a positive value will be treated the same as zero. A positive zone is the distance from the edge in surface-local coordinates to consider exclusive. Surfaces that do not wish to have an exclusive zone may instead specify how they should interact with surfaces that do. If set to zero, the surface indicates that it would like to be moved to avoid occluding surfaces with a positive exclusive zone. If set to -1, the surface indicates that it would not like to be moved to accommodate for other surfaces, and the compositor should extend it all the way to the edges it is anchored to. For example, a panel might set its exclusive zone to 10, so that maximized shell surfaces are not shown on top of it. A notification might set its exclusive zone to 0, so that it is moved to avoid occluding the panel, but shell surfaces are shown underneath it. A wallpaper or lock screen might set their exclusive zone to -1, so that they stretch below or over the panel. The default value is 0. Exclusive zone is double-buffered, see wl_surface.commit. Requests that the surface be placed some distance away from the anchor point on the output, in surface-local coordinates. Setting this value for edges you are not anchored to has no effect. The exclusive zone includes the margin. Margin is double-buffered, see wl_surface.commit. Types of keyboard interaction possible for layer shell surfaces. The rationale for this is twofold: (1) some applications are not interested in keyboard events and not allowing them to be focused can improve the desktop experience; (2) some applications will want to take exclusive keyboard focus. This value indicates that this surface is not interested in keyboard events and the compositor should never assign it the keyboard focus. This is the default value, set for newly created layer shell surfaces. This is useful for e.g. desktop widgets that display information or only have interaction with non-keyboard input devices. Request exclusive keyboard focus if this surface is above the shell surface layer. For the top and overlay layers, the seat will always give exclusive keyboard focus to the top-most layer which has keyboard interactivity set to exclusive. If this layer contains multiple surfaces with keyboard interactivity set to exclusive, the compositor determines the one receiving keyboard events in an implementation- defined manner. In this case, no guarantee is made when this surface will receive keyboard focus (if ever). For the bottom and background layers, the compositor is allowed to use normal focus semantics. This setting is mainly intended for applications that need to ensure they receive all keyboard events, such as a lock screen or a password prompt. This requests the compositor to allow this surface to be focused and unfocused by the user in an implementation-defined manner. The user should be able to unfocus this surface even regardless of the layer it is on. Typically, the compositor will want to use its normal mechanism to manage keyboard focus between layer shell surfaces with this setting and regular toplevels on the desktop layer (e.g. click to focus). Nevertheless, it is possible for a compositor to require a special interaction to focus or unfocus layer shell surfaces (e.g. requiring a click even if focus follows the mouse normally, or providing a keybinding to switch focus between layers). This setting is mainly intended for desktop shell components (e.g. panels) that allow keyboard interaction. Using this option can allow implementing a desktop shell that can be fully usable without the mouse. Set how keyboard events are delivered to this surface. By default, layer shell surfaces do not receive keyboard events; this request can be used to change this. This setting is inherited by child surfaces set by the get_popup request. Layer surfaces receive pointer, touch, and tablet events normally. If you do not want to receive them, set the input region on your surface to an empty region. Keyboard interactivity is double-buffered, see wl_surface.commit. This assigns an xdg_popup's parent to this layer_surface. This popup should have been created via xdg_surface::get_popup with the parent set to NULL, and this request must be invoked before committing the popup's initial state. See the documentation of xdg_popup for more details about what an xdg_popup is and how it is used. When a configure event is received, if a client commits the surface in response to the configure event, then the client must make an ack_configure request sometime before the commit request, passing along the serial of the configure event. If the client receives multiple configure events before it can respond to one, it only has to ack the last configure event. A client is not required to commit immediately after sending an ack_configure request - it may even ack_configure several times before its next surface commit. A client may send multiple ack_configure requests before committing, but only the last request sent before a commit indicates which configure event the client really is responding to. This request destroys the layer surface. The configure event asks the client to resize its surface. Clients should arrange their surface for the new states, and then send an ack_configure request with the serial sent in this configure event at some point before committing the new surface. The client is free to dismiss all but the last configure event it received. The width and height arguments specify the size of the window in surface-local coordinates. The size is a hint, in the sense that the client is free to ignore it if it doesn't resize, pick a smaller size (to satisfy aspect ratio or resize in steps of NxM pixels). If the client picks a smaller size and is anchored to two opposite anchors (e.g. 'top' and 'bottom'), the surface will be centered on this axis. If the width or height arguments are zero, it means the client should decide its own window dimension. The closed event is sent by the compositor when the surface will no longer be shown. The output may have been destroyed or the user may have asked for it to be removed. Further changes to the surface will be ignored. The client should destroy the resource after receiving this event, and create a new surface if they so choose. Change the layer that the surface is rendered on. Layer is double-buffered, see wl_surface.commit. wlroots-0.17.1/protocol/wlr-output-management-unstable-v1.xml000066400000000000000000000622661454110342200243170ustar00rootroot00000000000000 Copyright © 2019 Purism SPC Permission to use, copy, modify, distribute, and sell this software and its documentation for any purpose is hereby granted without fee, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of the copyright holders not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. The copyright holders make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. This protocol exposes interfaces to obtain and modify output device configuration. Warning! The protocol described in this file is experimental and backward incompatible changes may be made. Backward compatible changes may be added together with the corresponding interface version bump. Backward incompatible changes are done by bumping the version number in the protocol and interface names and resetting the interface version. Once the protocol is to be declared stable, the 'z' prefix and the version number in the protocol and interface names are removed and the interface version number is reset. This interface is a manager that allows reading and writing the current output device configuration. Output devices that display pixels (e.g. a physical monitor or a virtual output in a window) are represented as heads. Heads cannot be created nor destroyed by the client, but they can be enabled or disabled and their properties can be changed. Each head may have one or more available modes. Whenever a head appears (e.g. a monitor is plugged in), it will be advertised via the head event. Immediately after the output manager is bound, all current heads are advertised. Whenever a head's properties change, the relevant wlr_output_head events will be sent. Not all head properties will be sent: only properties that have changed need to. Whenever a head disappears (e.g. a monitor is unplugged), a wlr_output_head.finished event will be sent. After one or more heads appear, change or disappear, the done event will be sent. It carries a serial which can be used in a create_configuration request to update heads properties. The information obtained from this protocol should only be used for output configuration purposes. This protocol is not designed to be a generic output property advertisement protocol for regular clients. Instead, protocols such as xdg-output should be used. This event introduces a new head. This happens whenever a new head appears (e.g. a monitor is plugged in) or after the output manager is bound. This event is sent after all information has been sent after binding to the output manager object and after any subsequent changes. This applies to child head and mode objects as well. In other words, this event is sent whenever a head or mode is created or destroyed and whenever one of their properties has been changed. Not all state is re-sent each time the current configuration changes: only the actual changes are sent. This allows changes to the output configuration to be seen as atomic, even if they happen via multiple events. A serial is sent to be used in a future create_configuration request. Create a new output configuration object. This allows to update head properties. Indicates the client no longer wishes to receive events for output configuration changes. However the compositor may emit further events, until the finished event is emitted. The client must not send any more requests after this one. This event indicates that the compositor is done sending manager events. The compositor will destroy the object immediately after sending this event, so it will become invalid and the client should release any resources associated with it. A head is an output device. The difference between a wl_output object and a head is that heads are advertised even if they are turned off. A head object only advertises properties and cannot be used directly to change them. A head has some read-only properties: modes, name, description and physical_size. These cannot be changed by clients. Other properties can be updated via a wlr_output_configuration object. Properties sent via this interface are applied atomically via the wlr_output_manager.done event. No guarantees are made regarding the order in which properties are sent. This event describes the head name. The naming convention is compositor defined, but limited to alphanumeric characters and dashes (-). Each name is unique among all wlr_output_head objects, but if a wlr_output_head object is destroyed the same name may be reused later. The names will also remain consistent across sessions with the same hardware and software configuration. Examples of names include 'HDMI-A-1', 'WL-1', 'X11-1', etc. However, do not assume that the name is a reflection of an underlying DRM connector, X11 connection, etc. If the compositor implements the xdg-output protocol and this head is enabled, the xdg_output.name event must report the same name. The name event is sent after a wlr_output_head object is created. This event is only sent once per object, and the name does not change over the lifetime of the wlr_output_head object. This event describes a human-readable description of the head. The description is a UTF-8 string with no convention defined for its contents. Examples might include 'Foocorp 11" Display' or 'Virtual X11 output via :1'. However, do not assume that the name is a reflection of the make, model, serial of the underlying DRM connector or the display name of the underlying X11 connection, etc. If the compositor implements xdg-output and this head is enabled, the xdg_output.description must report the same description. The description event is sent after a wlr_output_head object is created. This event is only sent once per object, and the description does not change over the lifetime of the wlr_output_head object. This event describes the physical size of the head. This event is only sent if the head has a physical size (e.g. is not a projector or a virtual device). This event introduces a mode for this head. It is sent once per supported mode. This event describes whether the head is enabled. A disabled head is not mapped to a region of the global compositor space. When a head is disabled, some properties (current_mode, position, transform and scale) are irrelevant. This event describes the mode currently in use for this head. It is only sent if the output is enabled. This events describes the position of the head in the global compositor space. It is only sent if the output is enabled. This event describes the transformation currently applied to the head. It is only sent if the output is enabled. This events describes the scale of the head in the global compositor space. It is only sent if the output is enabled. This event indicates that the head is no longer available. The head object becomes inert. Clients should send a destroy request and release any resources associated with it. This event describes the manufacturer of the head. This must report the same make as the wl_output interface does in its geometry event. Together with the model and serial_number events the purpose is to allow clients to recognize heads from previous sessions and for example load head-specific configurations back. It is not guaranteed this event will be ever sent. A reason for that can be that the compositor does not have information about the make of the head or the definition of a make is not sensible in the current setup, for example in a virtual session. Clients can still try to identify the head by available information from other events but should be aware that there is an increased risk of false positives. It is not recommended to display the make string in UI to users. For that the string provided by the description event should be preferred. This event describes the model of the head. This must report the same model as the wl_output interface does in its geometry event. Together with the make and serial_number events the purpose is to allow clients to recognize heads from previous sessions and for example load head-specific configurations back. It is not guaranteed this event will be ever sent. A reason for that can be that the compositor does not have information about the model of the head or the definition of a model is not sensible in the current setup, for example in a virtual session. Clients can still try to identify the head by available information from other events but should be aware that there is an increased risk of false positives. It is not recommended to display the model string in UI to users. For that the string provided by the description event should be preferred. This event describes the serial number of the head. Together with the make and model events the purpose is to allow clients to recognize heads from previous sessions and for example load head- specific configurations back. It is not guaranteed this event will be ever sent. A reason for that can be that the compositor does not have information about the serial number of the head or the definition of a serial number is not sensible in the current setup. Clients can still try to identify the head by available information from other events but should be aware that there is an increased risk of false positives. It is not recommended to display the serial_number string in UI to users. For that the string provided by the description event should be preferred. This request indicates that the client will no longer use this head object. This event describes whether adaptive sync is currently enabled for the head or not. Adaptive sync is also known as Variable Refresh Rate or VRR. This object describes an output mode. Some heads don't support output modes, in which case modes won't be advertised. Properties sent via this interface are applied atomically via the wlr_output_manager.done event. No guarantees are made regarding the order in which properties are sent. This event describes the mode size. The size is given in physical hardware units of the output device. This is not necessarily the same as the output size in the global compositor space. For instance, the output may be scaled or transformed. This event describes the mode's fixed vertical refresh rate. It is only sent if the mode has a fixed refresh rate. This event advertises this mode as preferred. This event indicates that the mode is no longer available. The mode object becomes inert. Clients should send a destroy request and release any resources associated with it. This request indicates that the client will no longer use this mode object. This object is used by the client to describe a full output configuration. First, the client needs to setup the output configuration. Each head can be either enabled (and configured) or disabled. It is a protocol error to send two enable_head or disable_head requests with the same head. It is a protocol error to omit a head in a configuration. Then, the client can apply or test the configuration. The compositor will then reply with a succeeded, failed or cancelled event. Finally the client should destroy the configuration object. Enable a head. This request creates a head configuration object that can be used to change the head's properties. Disable a head. Apply the new output configuration. In case the configuration is successfully applied, there is no guarantee that the new output state matches completely the requested configuration. For instance, a compositor might round the scale if it doesn't support fractional scaling. After this request has been sent, the compositor must respond with an succeeded, failed or cancelled event. Sending a request that isn't the destructor is a protocol error. Test the new output configuration. The configuration won't be applied, but will only be validated. Even if the compositor succeeds to test a configuration, applying it may fail. After this request has been sent, the compositor must respond with an succeeded, failed or cancelled event. Sending a request that isn't the destructor is a protocol error. Sent after the compositor has successfully applied the changes or tested them. Upon receiving this event, the client should destroy this object. If the current configuration has changed, events to describe the changes will be sent followed by a wlr_output_manager.done event. Sent if the compositor rejects the changes or failed to apply them. The compositor should revert any changes made by the apply request that triggered this event. Upon receiving this event, the client should destroy this object. Sent if the compositor cancels the configuration because the state of an output changed and the client has outdated information (e.g. after an output has been hotplugged). The client can create a new configuration with a newer serial and try again. Upon receiving this event, the client should destroy this object. Using this request a client can tell the compositor that it is not going to use the configuration object anymore. Any changes to the outputs that have not been applied will be discarded. This request also destroys wlr_output_configuration_head objects created via this object. This object is used by the client to update a single head's configuration. It is a protocol error to set the same property twice. This request sets the head's mode. This request assigns a custom mode to the head. The size is given in physical hardware units of the output device. If set to zero, the refresh rate is unspecified. It is a protocol error to set both a mode and a custom mode. This request sets the head's position in the global compositor space. This request sets the head's transform. This request sets the head's scale. This request enables/disables adaptive sync. Adaptive sync is also known as Variable Refresh Rate or VRR. wlroots-0.17.1/protocol/wlr-output-power-management-unstable-v1.xml000066400000000000000000000127331454110342200254430ustar00rootroot00000000000000 Copyright © 2019 Purism SPC Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. This protocol allows clients to control power management modes of outputs that are currently part of the compositor space. The intent is to allow special clients like desktop shells to power down outputs when the system is idle. To modify outputs not currently part of the compositor space see wlr-output-management. Warning! The protocol described in this file is experimental and backward incompatible changes may be made. Backward compatible changes may be added together with the corresponding interface version bump. Backward incompatible changes are done by bumping the version number in the protocol and interface names and resetting the interface version. Once the protocol is to be declared stable, the 'z' prefix and the version number in the protocol and interface names are removed and the interface version number is reset. This interface is a manager that allows creating per-output power management mode controls. Create a output power management mode control that can be used to adjust the power management mode for a given output. All objects created by the manager will still remain valid, until their appropriate destroy request has been called. This object offers requests to set the power management mode of an output. Set an output's power save mode to the given mode. The mode change is effective immediately. If the output does not support the given mode a failed event is sent. Report the power management mode change of an output. The mode event is sent after an output changed its power management mode. The reason can be a client using set_mode or the compositor deciding to change an output's mode. This event is also sent immediately when the object is created so the client is informed about the current power management mode. This event indicates that the output power management mode control is no longer valid. This can happen for a number of reasons, including: - The output doesn't support power management - Another client already has exclusive power management mode control for this output - The output disappeared Upon receiving this event, the client should destroy this object. Destroys the output power management mode control object. wlroots-0.17.1/protocol/wlr-screencopy-unstable-v1.xml000066400000000000000000000236661454110342200230200ustar00rootroot00000000000000 Copyright © 2018 Simon Ser Copyright © 2019 Andri Yngvason Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. This protocol allows clients to ask the compositor to copy part of the screen content to a client buffer. Warning! The protocol described in this file is experimental and backward incompatible changes may be made. Backward compatible changes may be added together with the corresponding interface version bump. Backward incompatible changes are done by bumping the version number in the protocol and interface names and resetting the interface version. Once the protocol is to be declared stable, the 'z' prefix and the version number in the protocol and interface names are removed and the interface version number is reset. This object is a manager which offers requests to start capturing from a source. Capture the next frame of an entire output. Capture the next frame of an output's region. The region is given in output logical coordinates, see xdg_output.logical_size. The region will be clipped to the output's extents. All objects created by the manager will still remain valid, until their appropriate destroy request has been called. This object represents a single frame. When created, a series of buffer events will be sent, each representing a supported buffer type. The "buffer_done" event is sent afterwards to indicate that all supported buffer types have been enumerated. The client will then be able to send a "copy" request. If the capture is successful, the compositor will send a "flags" followed by a "ready" event. For objects version 2 or lower, wl_shm buffers are always supported, ie. the "buffer" event is guaranteed to be sent. If the capture failed, the "failed" event is sent. This can happen anytime before the "ready" event. Once either a "ready" or a "failed" event is received, the client should destroy the frame. Provides information about wl_shm buffer parameters that need to be used for this frame. This event is sent once after the frame is created if wl_shm buffers are supported. Copy the frame to the supplied buffer. The buffer must have a the correct size, see zwlr_screencopy_frame_v1.buffer and zwlr_screencopy_frame_v1.linux_dmabuf. The buffer needs to have a supported format. If the frame is successfully copied, a "flags" and a "ready" events are sent. Otherwise, a "failed" event is sent. Provides flags about the frame. This event is sent once before the "ready" event. Called as soon as the frame is copied, indicating it is available for reading. This event includes the time at which presentation happened at. The timestamp is expressed as tv_sec_hi, tv_sec_lo, tv_nsec triples, each component being an unsigned 32-bit value. Whole seconds are in tv_sec which is a 64-bit value combined from tv_sec_hi and tv_sec_lo, and the additional fractional part in tv_nsec as nanoseconds. Hence, for valid timestamps tv_nsec must be in [0, 999999999]. The seconds part may have an arbitrary offset at start. After receiving this event, the client should destroy the object. This event indicates that the attempted frame copy has failed. After receiving this event, the client should destroy the object. Destroys the frame. This request can be sent at any time by the client. Same as copy, except it waits until there is damage to copy. This event is sent right before the ready event when copy_with_damage is requested. It may be generated multiple times for each copy_with_damage request. The arguments describe a box around an area that has changed since the last copy request that was derived from the current screencopy manager instance. The union of all regions received between the call to copy_with_damage and a ready event is the total damage since the prior ready event. Provides information about linux-dmabuf buffer parameters that need to be used for this frame. This event is sent once after the frame is created if linux-dmabuf buffers are supported. This event is sent once after all buffer events have been sent. The client should proceed to create a buffer of one of the supported types, and send a "copy" request. wlroots-0.17.1/protocol/wlr-virtual-pointer-unstable-v1.xml000066400000000000000000000153571454110342200240100ustar00rootroot00000000000000 Copyright © 2019 Josef Gajdusek Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. This protocol allows clients to emulate a physical pointer device. The requests are mostly mirror opposites of those specified in wl_pointer. The pointer has moved by a relative amount to the previous request. Values are in the global compositor space. The pointer has moved in an absolute coordinate frame. Value of x can range from 0 to x_extent, value of y can range from 0 to y_extent. A button was pressed or released. Scroll and other axis requests. Indicates the set of events that logically belong together. Source information for scroll and other axis. Stop notification for scroll and other axes. Discrete step information for scroll and other axes. This event allows the client to extend data normally sent using the axis event with discrete value. This object allows clients to create individual virtual pointer objects. Creates a new virtual pointer. The optional seat is a suggestion to the compositor. Creates a new virtual pointer. The seat and the output arguments are optional. If the seat argument is set, the compositor should assign the input device to the requested seat. If the output argument is set, the compositor should map the input device to the requested output. wlroots-0.17.1/render/000077500000000000000000000000001454110342200145645ustar00rootroot00000000000000wlroots-0.17.1/render/allocator/000077500000000000000000000000001454110342200165445ustar00rootroot00000000000000wlroots-0.17.1/render/allocator/allocator.c000066400000000000000000000125451454110342200206770ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include #include #include #include "backend/backend.h" #include "render/allocator/allocator.h" #include "render/allocator/drm_dumb.h" #include "render/allocator/shm.h" #include "render/wlr_renderer.h" #if WLR_HAS_GBM_ALLOCATOR #include "render/allocator/gbm.h" #endif void wlr_allocator_init(struct wlr_allocator *alloc, const struct wlr_allocator_interface *impl, uint32_t buffer_caps) { assert(impl && impl->destroy && impl->create_buffer); *alloc = (struct wlr_allocator){ .impl = impl, .buffer_caps = buffer_caps, }; wl_signal_init(&alloc->events.destroy); } /* Re-open the DRM node to avoid GEM handle ref'counting issues. See: * https://gitlab.freedesktop.org/mesa/drm/-/merge_requests/110 */ static int reopen_drm_node(int drm_fd, bool allow_render_node) { if (drmIsMaster(drm_fd)) { // Only recent kernels support empty leases uint32_t lessee_id; int lease_fd = drmModeCreateLease(drm_fd, NULL, 0, O_CLOEXEC, &lessee_id); if (lease_fd >= 0) { return lease_fd; } else if (lease_fd != -EINVAL && lease_fd != -EOPNOTSUPP) { wlr_log_errno(WLR_ERROR, "drmModeCreateLease failed"); return -1; } wlr_log(WLR_DEBUG, "drmModeCreateLease failed, " "falling back to plain open"); } char *name = NULL; if (allow_render_node) { name = drmGetRenderDeviceNameFromFd(drm_fd); } if (name == NULL) { // Either the DRM device has no render node, either the caller wants // a primary node name = drmGetDeviceNameFromFd2(drm_fd); if (name == NULL) { wlr_log(WLR_ERROR, "drmGetDeviceNameFromFd2 failed"); return -1; } } int new_fd = open(name, O_RDWR | O_CLOEXEC); if (new_fd < 0) { wlr_log_errno(WLR_ERROR, "Failed to open DRM node '%s'", name); free(name); return -1; } free(name); // If we're using a DRM primary node and we are DRM master (e.g. because // we're running under the DRM backend), we need to use the legacy DRM // authentication mechanism to have the permission to manipulate DRM dumb // buffers. if (drmIsMaster(drm_fd) && drmGetNodeTypeFromFd(new_fd) == DRM_NODE_PRIMARY) { drm_magic_t magic; if (drmGetMagic(new_fd, &magic) < 0) { wlr_log_errno(WLR_ERROR, "drmGetMagic failed"); close(new_fd); return -1; } if (drmAuthMagic(drm_fd, magic) < 0) { wlr_log_errno(WLR_ERROR, "drmAuthMagic failed"); close(new_fd); return -1; } } return new_fd; } struct wlr_allocator *allocator_autocreate_with_drm_fd( struct wlr_backend *backend, struct wlr_renderer *renderer, int drm_fd) { uint32_t backend_caps = backend_get_buffer_caps(backend); uint32_t renderer_caps = renderer_get_render_buffer_caps(renderer); struct wlr_allocator *alloc = NULL; uint32_t gbm_caps = WLR_BUFFER_CAP_DMABUF; if ((backend_caps & gbm_caps) && (renderer_caps & gbm_caps) && drm_fd >= 0) { #if WLR_HAS_GBM_ALLOCATOR wlr_log(WLR_DEBUG, "Trying to create gbm allocator"); int gbm_fd = reopen_drm_node(drm_fd, true); if (gbm_fd < 0) { return NULL; } if ((alloc = wlr_gbm_allocator_create(gbm_fd)) != NULL) { return alloc; } close(gbm_fd); wlr_log(WLR_DEBUG, "Failed to create gbm allocator"); #else wlr_log(WLR_DEBUG, "Skipping gbm allocator: disabled at compile-time"); #endif } uint32_t shm_caps = WLR_BUFFER_CAP_SHM | WLR_BUFFER_CAP_DATA_PTR; if ((backend_caps & shm_caps) && (renderer_caps & shm_caps)) { wlr_log(WLR_DEBUG, "Trying to create shm allocator"); if ((alloc = wlr_shm_allocator_create()) != NULL) { return alloc; } wlr_log(WLR_DEBUG, "Failed to create shm allocator"); } uint32_t drm_caps = WLR_BUFFER_CAP_DMABUF | WLR_BUFFER_CAP_DATA_PTR; if ((backend_caps & drm_caps) && (renderer_caps & drm_caps) && drm_fd >= 0 && drmIsMaster(drm_fd)) { wlr_log(WLR_DEBUG, "Trying to create drm dumb allocator"); int dumb_fd = reopen_drm_node(drm_fd, false); if (dumb_fd < 0) { return NULL; } if ((alloc = wlr_drm_dumb_allocator_create(dumb_fd)) != NULL) { return alloc; } close(dumb_fd); wlr_log(WLR_DEBUG, "Failed to create drm dumb allocator"); } wlr_log(WLR_ERROR, "Failed to create allocator"); return NULL; } struct wlr_allocator *wlr_allocator_autocreate(struct wlr_backend *backend, struct wlr_renderer *renderer) { // Note, drm_fd may be negative if unavailable int drm_fd = wlr_backend_get_drm_fd(backend); if (drm_fd < 0) { drm_fd = wlr_renderer_get_drm_fd(renderer); } return allocator_autocreate_with_drm_fd(backend, renderer, drm_fd); } void wlr_allocator_destroy(struct wlr_allocator *alloc) { if (alloc == NULL) { return; } wl_signal_emit_mutable(&alloc->events.destroy, NULL); alloc->impl->destroy(alloc); } struct wlr_buffer *wlr_allocator_create_buffer(struct wlr_allocator *alloc, int width, int height, const struct wlr_drm_format *format) { struct wlr_buffer *buffer = alloc->impl->create_buffer(alloc, width, height, format); if (buffer == NULL) { return NULL; } if (alloc->buffer_caps & WLR_BUFFER_CAP_DATA_PTR) { assert(buffer->impl->begin_data_ptr_access && buffer->impl->end_data_ptr_access); } if (alloc->buffer_caps & WLR_BUFFER_CAP_DMABUF) { assert(buffer->impl->get_dmabuf); } if (alloc->buffer_caps & WLR_BUFFER_CAP_SHM) { assert(buffer->impl->get_shm); } return buffer; } wlroots-0.17.1/render/allocator/drm_dumb.c000066400000000000000000000147201454110342200205050ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "render/allocator/drm_dumb.h" #include "render/drm_format_set.h" #include "render/pixel_format.h" static const struct wlr_buffer_impl buffer_impl; static struct wlr_drm_dumb_buffer *drm_dumb_buffer_from_buffer( struct wlr_buffer *wlr_buf) { assert(wlr_buf->impl == &buffer_impl); struct wlr_drm_dumb_buffer *buf = wl_container_of(wlr_buf, buf, base); return buf; } static struct wlr_drm_dumb_buffer *create_buffer( struct wlr_drm_dumb_allocator *alloc, int width, int height, const struct wlr_drm_format *format) { if (!wlr_drm_format_has(format, DRM_FORMAT_MOD_INVALID) && !wlr_drm_format_has(format, DRM_FORMAT_MOD_LINEAR)) { wlr_log(WLR_ERROR, "DRM dumb allocator only supports INVALID and " "LINEAR modifiers"); return NULL; } const struct wlr_pixel_format_info *info = drm_get_pixel_format_info(format->format); if (info == NULL) { wlr_log(WLR_ERROR, "DRM format 0x%"PRIX32" not supported", format->format); return NULL; } else if (pixel_format_info_pixels_per_block(info) != 1) { wlr_log(WLR_ERROR, "Block formats are not supported"); return NULL; } struct wlr_drm_dumb_buffer *buffer = calloc(1, sizeof(*buffer)); if (buffer == NULL) { return NULL; } wlr_buffer_init(&buffer->base, &buffer_impl, width, height); wl_list_insert(&alloc->buffers, &buffer->link); buffer->drm_fd = alloc->drm_fd; uint32_t bpp = 8 * info->bytes_per_block; if (drmModeCreateDumbBuffer(alloc->drm_fd, width, height, bpp, 0, &buffer->handle, &buffer->stride, &buffer->size) != 0) { wlr_log_errno(WLR_ERROR, "Failed to create DRM dumb buffer"); goto create_destroy; } buffer->width = width; buffer->height = height; buffer->format = format->format; uint64_t offset; if (drmModeMapDumbBuffer(alloc->drm_fd, buffer->handle, &offset) != 0) { wlr_log_errno(WLR_ERROR, "Failed to map DRM dumb buffer"); goto create_destroy; } buffer->data = mmap(NULL, buffer->size, PROT_READ | PROT_WRITE, MAP_SHARED, alloc->drm_fd, offset); if (buffer->data == MAP_FAILED) { wlr_log_errno(WLR_ERROR, "Failed to mmap DRM dumb buffer"); goto create_destroy; } memset(buffer->data, 0, buffer->size); int prime_fd; if (drmPrimeHandleToFD(alloc->drm_fd, buffer->handle, DRM_CLOEXEC, &prime_fd) != 0) { wlr_log_errno(WLR_ERROR, "Failed to get PRIME handle from GEM handle"); goto create_destroy; } buffer->dmabuf = (struct wlr_dmabuf_attributes){ .width = buffer->width, .height = buffer->height, .format = format->format, .modifier = DRM_FORMAT_MOD_LINEAR, .n_planes = 1, .offset[0] = 0, .stride[0] = buffer->stride, .fd[0] = prime_fd, }; wlr_log(WLR_DEBUG, "Allocated %"PRIu32"x%"PRIu32" DRM dumb buffer", buffer->width, buffer->height); return buffer; create_destroy: wlr_buffer_drop(&buffer->base); return NULL; } static bool drm_dumb_buffer_begin_data_ptr_access(struct wlr_buffer *wlr_buffer, uint32_t flags, void **data, uint32_t *format, size_t *stride) { struct wlr_drm_dumb_buffer *buf = drm_dumb_buffer_from_buffer(wlr_buffer); *data = buf->data; *stride = buf->stride; *format = buf->format; return true; } static void drm_dumb_buffer_end_data_ptr_access(struct wlr_buffer *wlr_buffer) { // This space is intentionally left blank } static bool buffer_get_dmabuf(struct wlr_buffer *wlr_buffer, struct wlr_dmabuf_attributes *attribs) { struct wlr_drm_dumb_buffer *buf = drm_dumb_buffer_from_buffer(wlr_buffer); *attribs = buf->dmabuf; return true; } static void buffer_destroy(struct wlr_buffer *wlr_buffer) { struct wlr_drm_dumb_buffer *buf = drm_dumb_buffer_from_buffer(wlr_buffer); if (buf->data) { munmap(buf->data, buf->size); } wlr_dmabuf_attributes_finish(&buf->dmabuf); if (buf->drm_fd >= 0) { if (drmModeDestroyDumbBuffer(buf->drm_fd, buf->handle) != 0) { wlr_log_errno(WLR_ERROR, "Failed to destroy DRM dumb buffer"); } } wl_list_remove(&buf->link); free(buf); } static const struct wlr_buffer_impl buffer_impl = { .destroy = buffer_destroy, .get_dmabuf = buffer_get_dmabuf, .begin_data_ptr_access = drm_dumb_buffer_begin_data_ptr_access, .end_data_ptr_access = drm_dumb_buffer_end_data_ptr_access, }; static const struct wlr_allocator_interface allocator_impl; static struct wlr_drm_dumb_allocator *drm_dumb_alloc_from_alloc( struct wlr_allocator *wlr_alloc) { assert(wlr_alloc->impl == &allocator_impl); struct wlr_drm_dumb_allocator *alloc = wl_container_of(wlr_alloc, alloc, base); return alloc; } static struct wlr_buffer *allocator_create_buffer( struct wlr_allocator *wlr_alloc, int width, int height, const struct wlr_drm_format *drm_format) { struct wlr_drm_dumb_allocator *alloc = drm_dumb_alloc_from_alloc(wlr_alloc); struct wlr_drm_dumb_buffer *buffer = create_buffer(alloc, width, height, drm_format); if (buffer == NULL) { return NULL; } return &buffer->base; } static void allocator_destroy(struct wlr_allocator *wlr_alloc) { struct wlr_drm_dumb_allocator *alloc = drm_dumb_alloc_from_alloc(wlr_alloc); struct wlr_drm_dumb_buffer *buf, *buf_tmp; wl_list_for_each_safe(buf, buf_tmp, &alloc->buffers, link) { buf->drm_fd = -1; wl_list_remove(&buf->link); wl_list_init(&buf->link); } close(alloc->drm_fd); free(alloc); } static const struct wlr_allocator_interface allocator_impl = { .create_buffer = allocator_create_buffer, .destroy = allocator_destroy, }; struct wlr_allocator *wlr_drm_dumb_allocator_create(int drm_fd) { if (drmGetNodeTypeFromFd(drm_fd) != DRM_NODE_PRIMARY) { wlr_log(WLR_ERROR, "Cannot use DRM dumb buffers with non-primary DRM FD"); return NULL; } uint64_t has_dumb = 0; if (drmGetCap(drm_fd, DRM_CAP_DUMB_BUFFER, &has_dumb) < 0) { wlr_log(WLR_ERROR, "Failed to get DRM capabilities"); return NULL; } if (has_dumb == 0) { wlr_log(WLR_ERROR, "DRM dumb buffers not supported"); return NULL; } struct wlr_drm_dumb_allocator *alloc = calloc(1, sizeof(*alloc)); if (alloc == NULL) { return NULL; } wlr_allocator_init(&alloc->base, &allocator_impl, WLR_BUFFER_CAP_DATA_PTR | WLR_BUFFER_CAP_DMABUF); alloc->drm_fd = drm_fd; wl_list_init(&alloc->buffers); wlr_log(WLR_DEBUG, "Created DRM dumb allocator"); return &alloc->base; } wlroots-0.17.1/render/allocator/gbm.c000066400000000000000000000164351454110342200174660ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include #include #include #include "config.h" #include "render/allocator/gbm.h" #include "render/drm_format_set.h" static const struct wlr_buffer_impl buffer_impl; static struct wlr_gbm_buffer *get_gbm_buffer_from_buffer( struct wlr_buffer *wlr_buffer) { assert(wlr_buffer->impl == &buffer_impl); struct wlr_gbm_buffer *buffer = wl_container_of(wlr_buffer, buffer, base); return buffer; } static bool export_gbm_bo(struct gbm_bo *bo, struct wlr_dmabuf_attributes *out) { struct wlr_dmabuf_attributes attribs = {0}; attribs.n_planes = gbm_bo_get_plane_count(bo); if (attribs.n_planes > WLR_DMABUF_MAX_PLANES) { wlr_log(WLR_ERROR, "GBM BO contains too many planes (%d)", attribs.n_planes); return false; } attribs.width = gbm_bo_get_width(bo); attribs.height = gbm_bo_get_height(bo); attribs.format = gbm_bo_get_format(bo); attribs.modifier = gbm_bo_get_modifier(bo); int i; int32_t handle = -1; for (i = 0; i < attribs.n_planes; ++i) { #if HAVE_GBM_BO_GET_FD_FOR_PLANE (void)handle; attribs.fd[i] = gbm_bo_get_fd_for_plane(bo, i); if (attribs.fd[i] < 0) { wlr_log(WLR_ERROR, "gbm_bo_get_fd_for_plane failed"); goto error_fd; } #else // GBM is lacking a function to get a FD for a given plane. Instead, // check all planes have the same handle. We can't use // drmPrimeHandleToFD because that messes up handle ref'counting in // the user-space driver. union gbm_bo_handle plane_handle = gbm_bo_get_handle_for_plane(bo, i); if (plane_handle.s32 < 0) { wlr_log(WLR_ERROR, "gbm_bo_get_handle_for_plane failed"); goto error_fd; } if (i == 0) { handle = plane_handle.s32; } else if (plane_handle.s32 != handle) { wlr_log(WLR_ERROR, "Failed to export GBM BO: " "all planes don't have the same GEM handle"); goto error_fd; } attribs.fd[i] = gbm_bo_get_fd(bo); if (attribs.fd[i] < 0) { wlr_log(WLR_ERROR, "gbm_bo_get_fd failed"); goto error_fd; } #endif attribs.offset[i] = gbm_bo_get_offset(bo, i); attribs.stride[i] = gbm_bo_get_stride_for_plane(bo, i); } *out = attribs; return true; error_fd: for (int j = 0; j < i; ++j) { close(attribs.fd[j]); } return false; } static struct wlr_gbm_buffer *create_buffer(struct wlr_gbm_allocator *alloc, int width, int height, const struct wlr_drm_format *format) { struct gbm_device *gbm_device = alloc->gbm_device; assert(format->len > 0); bool has_modifier = true; uint64_t fallback_modifier = DRM_FORMAT_MOD_INVALID; struct gbm_bo *bo = gbm_bo_create_with_modifiers(gbm_device, width, height, format->format, format->modifiers, format->len); if (bo == NULL) { uint32_t usage = GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING; if (format->len == 1 && format->modifiers[0] == DRM_FORMAT_MOD_LINEAR) { usage |= GBM_BO_USE_LINEAR; fallback_modifier = DRM_FORMAT_MOD_LINEAR; } else if (!wlr_drm_format_has(format, DRM_FORMAT_MOD_INVALID)) { // If the format doesn't accept an implicit modifier, bail out. wlr_log(WLR_ERROR, "gbm_bo_create_with_modifiers failed"); return NULL; } bo = gbm_bo_create(gbm_device, width, height, format->format, usage); has_modifier = false; } if (bo == NULL) { wlr_log(WLR_ERROR, "gbm_bo_create failed"); return NULL; } struct wlr_gbm_buffer *buffer = calloc(1, sizeof(*buffer)); if (buffer == NULL) { gbm_bo_destroy(bo); return NULL; } wlr_buffer_init(&buffer->base, &buffer_impl, width, height); buffer->gbm_bo = bo; wl_list_insert(&alloc->buffers, &buffer->link); if (!export_gbm_bo(bo, &buffer->dmabuf)) { free(buffer); gbm_bo_destroy(bo); return NULL; } // If the buffer has been allocated with an implicit modifier, make sure we // don't populate the modifier field: other parts of the stack may not // understand modifiers, and they can't strip the modifier. if (!has_modifier) { buffer->dmabuf.modifier = fallback_modifier; } char *format_name = drmGetFormatName(buffer->dmabuf.format); char *modifier_name = drmGetFormatModifierName(buffer->dmabuf.modifier); wlr_log(WLR_DEBUG, "Allocated %dx%d GBM buffer " "with format %s (0x%08"PRIX32"), modifier %s (0x%016"PRIX64")", buffer->base.width, buffer->base.height, format_name ? format_name : "", buffer->dmabuf.format, modifier_name ? modifier_name : "", buffer->dmabuf.modifier); free(format_name); free(modifier_name); return buffer; } static void buffer_destroy(struct wlr_buffer *wlr_buffer) { struct wlr_gbm_buffer *buffer = get_gbm_buffer_from_buffer(wlr_buffer); wlr_dmabuf_attributes_finish(&buffer->dmabuf); if (buffer->gbm_bo != NULL) { gbm_bo_destroy(buffer->gbm_bo); } wl_list_remove(&buffer->link); free(buffer); } static bool buffer_get_dmabuf(struct wlr_buffer *wlr_buffer, struct wlr_dmabuf_attributes *attribs) { struct wlr_gbm_buffer *buffer = get_gbm_buffer_from_buffer(wlr_buffer); *attribs = buffer->dmabuf; return true; } static const struct wlr_buffer_impl buffer_impl = { .destroy = buffer_destroy, .get_dmabuf = buffer_get_dmabuf, }; static const struct wlr_allocator_interface allocator_impl; static struct wlr_gbm_allocator *get_gbm_alloc_from_alloc( struct wlr_allocator *wlr_alloc) { assert(wlr_alloc->impl == &allocator_impl); struct wlr_gbm_allocator *alloc = wl_container_of(wlr_alloc, alloc, base); return alloc; } struct wlr_allocator *wlr_gbm_allocator_create(int fd) { uint64_t cap; if (drmGetCap(fd, DRM_CAP_PRIME, &cap) || !(cap & DRM_PRIME_CAP_EXPORT)) { wlr_log(WLR_ERROR, "PRIME export not supported"); return NULL; } struct wlr_gbm_allocator *alloc = calloc(1, sizeof(*alloc)); if (alloc == NULL) { return NULL; } wlr_allocator_init(&alloc->base, &allocator_impl, WLR_BUFFER_CAP_DMABUF); alloc->fd = fd; wl_list_init(&alloc->buffers); alloc->gbm_device = gbm_create_device(fd); if (alloc->gbm_device == NULL) { wlr_log(WLR_ERROR, "gbm_create_device failed"); free(alloc); return NULL; } wlr_log(WLR_DEBUG, "Created GBM allocator with backend %s", gbm_device_get_backend_name(alloc->gbm_device)); char *drm_name = drmGetDeviceNameFromFd2(fd); wlr_log(WLR_DEBUG, "Using DRM node %s", drm_name); free(drm_name); return &alloc->base; } static void allocator_destroy(struct wlr_allocator *wlr_alloc) { struct wlr_gbm_allocator *alloc = get_gbm_alloc_from_alloc(wlr_alloc); // The gbm_bo objects need to be destroyed before the gbm_device struct wlr_gbm_buffer *buf, *buf_tmp; wl_list_for_each_safe(buf, buf_tmp, &alloc->buffers, link) { gbm_bo_destroy(buf->gbm_bo); buf->gbm_bo = NULL; wl_list_remove(&buf->link); wl_list_init(&buf->link); } gbm_device_destroy(alloc->gbm_device); close(alloc->fd); free(alloc); } static struct wlr_buffer *allocator_create_buffer( struct wlr_allocator *wlr_alloc, int width, int height, const struct wlr_drm_format *format) { struct wlr_gbm_allocator *alloc = get_gbm_alloc_from_alloc(wlr_alloc); struct wlr_gbm_buffer *buffer = create_buffer(alloc, width, height, format); if (buffer == NULL) { return NULL; } return &buffer->base; } static const struct wlr_allocator_interface allocator_impl = { .destroy = allocator_destroy, .create_buffer = allocator_create_buffer, }; wlroots-0.17.1/render/allocator/meson.build000066400000000000000000000012341454110342200207060ustar00rootroot00000000000000allocators = get_option('allocators') if 'auto' in allocators and get_option('auto_features').enabled() allocators = ['gbm'] elif 'auto' in allocators and get_option('auto_features').disabled() allocators = [] endif wlr_files += files( 'allocator.c', 'shm.c', 'drm_dumb.c', ) gbm = disabler() if 'gbm' in allocators or 'auto' in allocators gbm = dependency('gbm', version: '>=17.1.0', required: 'gbm' in allocators) endif if gbm.found() wlr_files += files('gbm.c') wlr_deps += gbm features += { 'gbm-allocator': true } has = cc.has_function('gbm_bo_get_fd_for_plane', dependencies: [gbm]) internal_config.set10('HAVE_GBM_BO_GET_FD_FOR_PLANE', has) endif wlroots-0.17.1/render/allocator/shm.c000066400000000000000000000066361454110342200175120ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include "render/pixel_format.h" #include "render/allocator/shm.h" #include "util/shm.h" static const struct wlr_buffer_impl buffer_impl; static struct wlr_shm_buffer *shm_buffer_from_buffer( struct wlr_buffer *wlr_buffer) { assert(wlr_buffer->impl == &buffer_impl); struct wlr_shm_buffer *buffer = wl_container_of(wlr_buffer, buffer, base); return buffer; } static void buffer_destroy(struct wlr_buffer *wlr_buffer) { struct wlr_shm_buffer *buffer = shm_buffer_from_buffer(wlr_buffer); munmap(buffer->data, buffer->size); close(buffer->shm.fd); free(buffer); } static bool buffer_get_shm(struct wlr_buffer *wlr_buffer, struct wlr_shm_attributes *shm) { struct wlr_shm_buffer *buffer = shm_buffer_from_buffer(wlr_buffer); *shm = buffer->shm; return true; } static bool shm_buffer_begin_data_ptr_access(struct wlr_buffer *wlr_buffer, uint32_t flags, void **data, uint32_t *format, size_t *stride) { struct wlr_shm_buffer *buffer = shm_buffer_from_buffer(wlr_buffer); *data = buffer->data; *format = buffer->shm.format; *stride = buffer->shm.stride; return true; } static void shm_buffer_end_data_ptr_access(struct wlr_buffer *wlr_buffer) { // This space is intentionally left blank } static const struct wlr_buffer_impl buffer_impl = { .destroy = buffer_destroy, .get_shm = buffer_get_shm, .begin_data_ptr_access = shm_buffer_begin_data_ptr_access, .end_data_ptr_access = shm_buffer_end_data_ptr_access, }; static struct wlr_buffer *allocator_create_buffer( struct wlr_allocator *wlr_allocator, int width, int height, const struct wlr_drm_format *format) { const struct wlr_pixel_format_info *info = drm_get_pixel_format_info(format->format); if (info == NULL) { wlr_log(WLR_ERROR, "Unsupported pixel format 0x%"PRIX32, format->format); return NULL; } struct wlr_shm_buffer *buffer = calloc(1, sizeof(*buffer)); if (buffer == NULL) { return NULL; } wlr_buffer_init(&buffer->base, &buffer_impl, width, height); // TODO: consider using a single file for multiple buffers int stride = pixel_format_info_min_stride(info, width); // TODO: align? buffer->size = stride * height; buffer->shm.fd = allocate_shm_file(buffer->size); if (buffer->shm.fd < 0) { free(buffer); return NULL; } buffer->shm.format = format->format; buffer->shm.width = width; buffer->shm.height = height; buffer->shm.stride = stride; buffer->shm.offset = 0; buffer->data = mmap(NULL, buffer->size, PROT_READ | PROT_WRITE, MAP_SHARED, buffer->shm.fd, 0); if (buffer->data == MAP_FAILED) { wlr_log_errno(WLR_ERROR, "mmap failed"); close(buffer->shm.fd); free(buffer); return NULL; } return &buffer->base; } static void allocator_destroy(struct wlr_allocator *wlr_allocator) { free(wlr_allocator); } static const struct wlr_allocator_interface allocator_impl = { .destroy = allocator_destroy, .create_buffer = allocator_create_buffer, }; struct wlr_allocator *wlr_shm_allocator_create(void) { struct wlr_shm_allocator *allocator = calloc(1, sizeof(*allocator)); if (allocator == NULL) { return NULL; } wlr_allocator_init(&allocator->base, &allocator_impl, WLR_BUFFER_CAP_DATA_PTR | WLR_BUFFER_CAP_SHM); wlr_log(WLR_DEBUG, "Created shm allocator"); return &allocator->base; } wlroots-0.17.1/render/dmabuf.c000066400000000000000000000014721454110342200161720ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200809L #include #include #include #include #include "render/dmabuf.h" void wlr_dmabuf_attributes_finish(struct wlr_dmabuf_attributes *attribs) { for (int i = 0; i < attribs->n_planes; ++i) { close(attribs->fd[i]); attribs->fd[i] = -1; } attribs->n_planes = 0; } bool wlr_dmabuf_attributes_copy(struct wlr_dmabuf_attributes *dst, const struct wlr_dmabuf_attributes *src) { *dst = *src; int i; for (i = 0; i < src->n_planes; ++i) { dst->fd[i] = fcntl(src->fd[i], F_DUPFD_CLOEXEC, 0); if (dst->fd[i] < 0) { wlr_log_errno(WLR_ERROR, "fcntl(F_DUPFD_CLOEXEC) failed"); goto error; } } return true; error: for (int j = 0; j < i; j++) { close(dst->fd[j]); dst->fd[j] = -1; } dst->n_planes = 0; return false; } wlroots-0.17.1/render/dmabuf_fallback.c000066400000000000000000000007231454110342200200070ustar00rootroot00000000000000#include #include "render/dmabuf.h" bool dmabuf_check_sync_file_import_export(void) { return false; } bool dmabuf_import_sync_file(int dmabuf_fd, uint32_t flags, int sync_file_fd) { wlr_log(WLR_ERROR, "DMA-BUF sync_file import IOCTL not available on this system"); return false; } int dmabuf_export_sync_file(int dmabuf_fd, uint32_t flags) { wlr_log(WLR_ERROR, "DMA-BUF sync_file export IOCTL not available on this system"); return false; } wlroots-0.17.1/render/dmabuf_linux.c000066400000000000000000000044071454110342200174120ustar00rootroot00000000000000#include #include #include #include #include #include #include "render/dmabuf.h" bool dmabuf_check_sync_file_import_export(void) { /* Unfortunately there's no better way to check the availability of the * IOCTL than to check the kernel version. See the discussion at: * https://lore.kernel.org/dri-devel/20220601161303.64797-1-contact@emersion.fr/ */ struct utsname utsname = {0}; if (uname(&utsname) != 0) { wlr_log_errno(WLR_ERROR, "uname failed"); return false; } if (strcmp(utsname.sysname, "Linux") != 0) { return false; } // Trim release suffix if any, e.g. "-arch1-1" for (size_t i = 0; utsname.release[i] != '\0'; i++) { char ch = utsname.release[i]; if ((ch < '0' || ch > '9') && ch != '.') { utsname.release[i] = '\0'; break; } } char *rel = strtok(utsname.release, "."); int major = atoi(rel); int minor = 0; rel = strtok(NULL, "."); if (rel != NULL) { minor = atoi(rel); } int patch = 0; rel = strtok(NULL, "."); if (rel != NULL) { patch = atoi(rel); } return KERNEL_VERSION(major, minor, patch) >= KERNEL_VERSION(5, 20, 0); } // TODO: drop these definitions once widespread #if !defined(DMA_BUF_IOCTL_IMPORT_SYNC_FILE) struct dma_buf_import_sync_file { __u32 flags; __s32 fd; }; #define DMA_BUF_IOCTL_IMPORT_SYNC_FILE _IOW(DMA_BUF_BASE, 3, struct dma_buf_import_sync_file) #endif #if !defined(DMA_BUF_IOCTL_EXPORT_SYNC_FILE) struct dma_buf_export_sync_file { __u32 flags; __s32 fd; }; #define DMA_BUF_IOCTL_EXPORT_SYNC_FILE _IOWR(DMA_BUF_BASE, 2, struct dma_buf_export_sync_file) #endif bool dmabuf_import_sync_file(int dmabuf_fd, uint32_t flags, int sync_file_fd) { struct dma_buf_import_sync_file data = { .flags = flags, .fd = sync_file_fd, }; if (drmIoctl(dmabuf_fd, DMA_BUF_IOCTL_IMPORT_SYNC_FILE, &data) != 0) { wlr_log_errno(WLR_ERROR, "drmIoctl(IMPORT_SYNC_FILE) failed"); return false; } return true; } int dmabuf_export_sync_file(int dmabuf_fd, uint32_t flags) { struct dma_buf_export_sync_file data = { .flags = flags, .fd = -1, }; if (drmIoctl(dmabuf_fd, DMA_BUF_IOCTL_EXPORT_SYNC_FILE, &data) != 0) { wlr_log_errno(WLR_ERROR, "drmIoctl(EXPORT_SYNC_FILE) failed"); return -1; } return data.fd; } wlroots-0.17.1/render/drm_format_set.c000066400000000000000000000153201454110342200177360ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include "render/drm_format_set.h" void wlr_drm_format_finish(struct wlr_drm_format *format) { if (!format) { return; } free(format->modifiers); } void wlr_drm_format_set_finish(struct wlr_drm_format_set *set) { for (size_t i = 0; i < set->len; ++i) { wlr_drm_format_finish(&set->formats[i]); } free(set->formats); set->len = 0; set->capacity = 0; set->formats = NULL; } static struct wlr_drm_format *format_set_get(const struct wlr_drm_format_set *set, uint32_t format) { for (size_t i = 0; i < set->len; ++i) { if (set->formats[i].format == format) { return &set->formats[i]; } } return NULL; } const struct wlr_drm_format *wlr_drm_format_set_get( const struct wlr_drm_format_set *set, uint32_t format) { return format_set_get(set, format); } bool wlr_drm_format_set_has(const struct wlr_drm_format_set *set, uint32_t format, uint64_t modifier) { const struct wlr_drm_format *fmt = wlr_drm_format_set_get(set, format); if (!fmt) { return false; } return wlr_drm_format_has(fmt, modifier); } bool wlr_drm_format_set_add(struct wlr_drm_format_set *set, uint32_t format, uint64_t modifier) { assert(format != DRM_FORMAT_INVALID); struct wlr_drm_format *existing = format_set_get(set, format); if (existing) { return wlr_drm_format_add(existing, modifier); } struct wlr_drm_format fmt; wlr_drm_format_init(&fmt, format); if (!wlr_drm_format_add(&fmt, modifier)) { wlr_drm_format_finish(&fmt); return false; } if (set->len == set->capacity) { size_t capacity = set->capacity ? set->capacity * 2 : 4; struct wlr_drm_format *fmts = realloc(set->formats, sizeof(*fmts) * capacity); if (!fmts) { wlr_log_errno(WLR_ERROR, "Allocation failed"); return false; } set->capacity = capacity; set->formats = fmts; } set->formats[set->len++] = fmt; return true; } void wlr_drm_format_init(struct wlr_drm_format *fmt, uint32_t format) { *fmt = (struct wlr_drm_format){ .format = format, }; } bool wlr_drm_format_has(const struct wlr_drm_format *fmt, uint64_t modifier) { for (size_t i = 0; i < fmt->len; ++i) { if (fmt->modifiers[i] == modifier) { return true; } } return false; } bool wlr_drm_format_add(struct wlr_drm_format *fmt, uint64_t modifier) { if (wlr_drm_format_has(fmt, modifier)) { return true; } if (fmt->len == fmt->capacity) { size_t capacity = fmt->capacity ? fmt->capacity * 2 : 4; uint64_t *new_modifiers = realloc(fmt->modifiers, sizeof(*fmt->modifiers) * capacity); if (!new_modifiers) { wlr_log_errno(WLR_ERROR, "Allocation failed"); return false; } fmt->capacity = capacity; fmt->modifiers = new_modifiers; } fmt->modifiers[fmt->len++] = modifier; return true; } bool wlr_drm_format_copy(struct wlr_drm_format *dst, const struct wlr_drm_format *src) { assert(src->len <= src->capacity); uint64_t *modifiers = malloc(sizeof(*modifiers) * src->len); if (!modifiers) { return false; } memcpy(modifiers, src->modifiers, sizeof(*modifiers) * src->len); wlr_drm_format_finish(dst); dst->capacity = src->len; dst->len = src->len; dst->format = src->format; dst->modifiers = modifiers; return true; } bool wlr_drm_format_set_copy(struct wlr_drm_format_set *dst, const struct wlr_drm_format_set *src) { struct wlr_drm_format *formats = malloc(src->len * sizeof(formats[0])); if (formats == NULL) { return false; } struct wlr_drm_format_set out = { .len = 0, .capacity = src->len, .formats = formats, }; size_t i; for (i = 0; i < src->len; i++) { out.formats[out.len] = (struct wlr_drm_format){0}; if (!wlr_drm_format_copy(&out.formats[out.len], &src->formats[i])) { wlr_drm_format_set_finish(&out); return false; } out.len++; } *dst = out; return true; } bool wlr_drm_format_intersect(struct wlr_drm_format *dst, const struct wlr_drm_format *a, const struct wlr_drm_format *b) { assert(a->format == b->format); size_t capacity = a->len < b->len ? a->len : b->len; uint64_t *modifiers = malloc(sizeof(*modifiers) * capacity); if (!modifiers) { return false; } struct wlr_drm_format fmt = { .capacity = capacity, .len = 0, .modifiers = modifiers, .format = a->format, }; for (size_t i = 0; i < a->len; i++) { for (size_t j = 0; j < b->len; j++) { if (a->modifiers[i] == b->modifiers[j]) { assert(fmt.len < fmt.capacity); fmt.modifiers[fmt.len++] = a->modifiers[i]; break; } } } wlr_drm_format_finish(dst); *dst = fmt; return true; } bool wlr_drm_format_set_intersect(struct wlr_drm_format_set *dst, const struct wlr_drm_format_set *a, const struct wlr_drm_format_set *b) { struct wlr_drm_format_set out = {0}; out.capacity = a->len < b->len ? a->len : b->len; out.formats = malloc(sizeof(*out.formats) * out.capacity); if (out.formats == NULL) { wlr_log_errno(WLR_ERROR, "Allocation failed"); return false; } for (size_t i = 0; i < a->len; i++) { for (size_t j = 0; j < b->len; j++) { if (a->formats[i].format == b->formats[j].format) { // When the two formats have no common modifier, keep // intersecting the rest of the formats: they may be compatible // with each other out.formats[out.len] = (struct wlr_drm_format){0}; if (!wlr_drm_format_intersect(&out.formats[out.len], &a->formats[i], &b->formats[j])) { wlr_drm_format_set_finish(&out); return false; } if (out.formats[out.len].len == 0) { wlr_drm_format_finish(&out.formats[out.len]); } else { out.len++; } break; } } } if (out.len == 0) { wlr_drm_format_set_finish(&out); return false; } wlr_drm_format_set_finish(dst); *dst = out; return true; } static bool drm_format_set_extend(struct wlr_drm_format_set *dst, const struct wlr_drm_format_set *src) { for (size_t i = 0; i < src->len; i++) { struct wlr_drm_format *format = &src->formats[i]; for (size_t j = 0; j < format->len; j++) { if (!wlr_drm_format_set_add(dst, format->format, format->modifiers[j])) { wlr_log_errno(WLR_ERROR, "Adding format/modifier to set failed"); return false; } } } return true; } bool wlr_drm_format_set_union(struct wlr_drm_format_set *dst, const struct wlr_drm_format_set *a, const struct wlr_drm_format_set *b) { struct wlr_drm_format_set out = {0}; out.capacity = a->len + b->len; out.formats = malloc(sizeof(*out.formats) * out.capacity); if (out.formats == NULL) { wlr_log_errno(WLR_ERROR, "Allocation failed"); return false; } // Add both a and b sets into out if (!drm_format_set_extend(&out, a)) { return false; } if (!drm_format_set_extend(&out, b)) { return false; } wlr_drm_format_set_finish(dst); *dst = out; return true; } wlroots-0.17.1/render/egl.c000066400000000000000000000712071454110342200155060ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include #include #include #include #include "render/egl.h" #include "util/env.h" static enum wlr_log_importance egl_log_importance_to_wlr(EGLint type) { switch (type) { case EGL_DEBUG_MSG_CRITICAL_KHR: return WLR_ERROR; case EGL_DEBUG_MSG_ERROR_KHR: return WLR_ERROR; case EGL_DEBUG_MSG_WARN_KHR: return WLR_ERROR; case EGL_DEBUG_MSG_INFO_KHR: return WLR_INFO; default: return WLR_INFO; } } static const char *egl_error_str(EGLint error) { switch (error) { case EGL_SUCCESS: return "EGL_SUCCESS"; case EGL_NOT_INITIALIZED: return "EGL_NOT_INITIALIZED"; case EGL_BAD_ACCESS: return "EGL_BAD_ACCESS"; case EGL_BAD_ALLOC: return "EGL_BAD_ALLOC"; case EGL_BAD_ATTRIBUTE: return "EGL_BAD_ATTRIBUTE"; case EGL_BAD_CONTEXT: return "EGL_BAD_CONTEXT"; case EGL_BAD_CONFIG: return "EGL_BAD_CONFIG"; case EGL_BAD_CURRENT_SURFACE: return "EGL_BAD_CURRENT_SURFACE"; case EGL_BAD_DISPLAY: return "EGL_BAD_DISPLAY"; case EGL_BAD_DEVICE_EXT: return "EGL_BAD_DEVICE_EXT"; case EGL_BAD_SURFACE: return "EGL_BAD_SURFACE"; case EGL_BAD_MATCH: return "EGL_BAD_MATCH"; case EGL_BAD_PARAMETER: return "EGL_BAD_PARAMETER"; case EGL_BAD_NATIVE_PIXMAP: return "EGL_BAD_NATIVE_PIXMAP"; case EGL_BAD_NATIVE_WINDOW: return "EGL_BAD_NATIVE_WINDOW"; case EGL_CONTEXT_LOST: return "EGL_CONTEXT_LOST"; } return "unknown error"; } static void egl_log(EGLenum error, const char *command, EGLint msg_type, EGLLabelKHR thread, EGLLabelKHR obj, const char *msg) { _wlr_log(egl_log_importance_to_wlr(msg_type), "[EGL] command: %s, error: %s (0x%x), message: \"%s\"", command, egl_error_str(error), error, msg); } static bool check_egl_ext(const char *exts, const char *ext) { size_t extlen = strlen(ext); const char *end = exts + strlen(exts); while (exts < end) { if (*exts == ' ') { exts++; continue; } size_t n = strcspn(exts, " "); if (n == extlen && strncmp(ext, exts, n) == 0) { return true; } exts += n; } return false; } static void load_egl_proc(void *proc_ptr, const char *name) { void *proc = (void *)eglGetProcAddress(name); if (proc == NULL) { wlr_log(WLR_ERROR, "eglGetProcAddress(%s) failed", name); abort(); } *(void **)proc_ptr = proc; } static int get_egl_dmabuf_formats(struct wlr_egl *egl, EGLint **formats); static int get_egl_dmabuf_modifiers(struct wlr_egl *egl, EGLint format, uint64_t **modifiers, EGLBoolean **external_only); static void log_modifier(uint64_t modifier, bool external_only) { char *mod_name = drmGetFormatModifierName(modifier); wlr_log(WLR_DEBUG, " %s (0x%016"PRIX64"): ✓ texture %s render", mod_name ? mod_name : "", modifier, external_only ? "✗" : "✓"); free(mod_name); } static void init_dmabuf_formats(struct wlr_egl *egl) { bool no_modifiers = env_parse_bool("WLR_EGL_NO_MODIFIERS"); if (no_modifiers) { wlr_log(WLR_INFO, "WLR_EGL_NO_MODIFIERS set, disabling modifiers for EGL"); } EGLint *formats; int formats_len = get_egl_dmabuf_formats(egl, &formats); if (formats_len < 0) { return; } wlr_log(WLR_DEBUG, "Supported DMA-BUF formats:"); bool has_modifiers = false; for (int i = 0; i < formats_len; i++) { EGLint fmt = formats[i]; uint64_t *modifiers = NULL; EGLBoolean *external_only = NULL; int modifiers_len = 0; if (!no_modifiers) { modifiers_len = get_egl_dmabuf_modifiers(egl, fmt, &modifiers, &external_only); } if (modifiers_len < 0) { continue; } has_modifiers = has_modifiers || modifiers_len > 0; bool all_external_only = true; for (int j = 0; j < modifiers_len; j++) { wlr_drm_format_set_add(&egl->dmabuf_texture_formats, fmt, modifiers[j]); if (!external_only[j]) { wlr_drm_format_set_add(&egl->dmabuf_render_formats, fmt, modifiers[j]); all_external_only = false; } } // EGL always supports implicit modifiers. If at least one modifier supports rendering, // assume the implicit modifier supports rendering too. wlr_drm_format_set_add(&egl->dmabuf_texture_formats, fmt, DRM_FORMAT_MOD_INVALID); if (modifiers_len == 0 || !all_external_only) { wlr_drm_format_set_add(&egl->dmabuf_render_formats, fmt, DRM_FORMAT_MOD_INVALID); } if (modifiers_len == 0) { // Asume the linear layout is supported if the driver doesn't // explicitly say otherwise wlr_drm_format_set_add(&egl->dmabuf_texture_formats, fmt, DRM_FORMAT_MOD_LINEAR); wlr_drm_format_set_add(&egl->dmabuf_render_formats, fmt, DRM_FORMAT_MOD_LINEAR); } if (wlr_log_get_verbosity() >= WLR_DEBUG) { char *fmt_name = drmGetFormatName(fmt); wlr_log(WLR_DEBUG, " %s (0x%08"PRIX32")", fmt_name ? fmt_name : "", fmt); free(fmt_name); log_modifier(DRM_FORMAT_MOD_INVALID, false); if (modifiers_len == 0) { log_modifier(DRM_FORMAT_MOD_LINEAR, false); } for (int j = 0; j < modifiers_len; j++) { log_modifier(modifiers[j], external_only[j]); } } free(modifiers); free(external_only); } free(formats); egl->has_modifiers = has_modifiers; if (!no_modifiers) { wlr_log(WLR_DEBUG, "EGL DMA-BUF format modifiers %s", has_modifiers ? "supported" : "unsupported"); } } static struct wlr_egl *egl_create(void) { const char *client_exts_str = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS); if (client_exts_str == NULL) { if (eglGetError() == EGL_BAD_DISPLAY) { wlr_log(WLR_ERROR, "EGL_EXT_client_extensions not supported"); } else { wlr_log(WLR_ERROR, "Failed to query EGL client extensions"); } return NULL; } wlr_log(WLR_INFO, "Supported EGL client extensions: %s", client_exts_str); if (!check_egl_ext(client_exts_str, "EGL_EXT_platform_base")) { wlr_log(WLR_ERROR, "EGL_EXT_platform_base not supported"); return NULL; } struct wlr_egl *egl = calloc(1, sizeof(*egl)); if (egl == NULL) { wlr_log_errno(WLR_ERROR, "Allocation failed"); return NULL; } load_egl_proc(&egl->procs.eglGetPlatformDisplayEXT, "eglGetPlatformDisplayEXT"); egl->exts.KHR_platform_gbm = check_egl_ext(client_exts_str, "EGL_KHR_platform_gbm"); egl->exts.EXT_platform_device = check_egl_ext(client_exts_str, "EGL_EXT_platform_device"); egl->exts.KHR_display_reference = check_egl_ext(client_exts_str, "EGL_KHR_display_reference"); if (check_egl_ext(client_exts_str, "EGL_EXT_device_base") || check_egl_ext(client_exts_str, "EGL_EXT_device_enumeration")) { load_egl_proc(&egl->procs.eglQueryDevicesEXT, "eglQueryDevicesEXT"); } if (check_egl_ext(client_exts_str, "EGL_EXT_device_base") || check_egl_ext(client_exts_str, "EGL_EXT_device_query")) { egl->exts.EXT_device_query = true; load_egl_proc(&egl->procs.eglQueryDeviceStringEXT, "eglQueryDeviceStringEXT"); load_egl_proc(&egl->procs.eglQueryDisplayAttribEXT, "eglQueryDisplayAttribEXT"); } if (check_egl_ext(client_exts_str, "EGL_KHR_debug")) { load_egl_proc(&egl->procs.eglDebugMessageControlKHR, "eglDebugMessageControlKHR"); static const EGLAttrib debug_attribs[] = { EGL_DEBUG_MSG_CRITICAL_KHR, EGL_TRUE, EGL_DEBUG_MSG_ERROR_KHR, EGL_TRUE, EGL_DEBUG_MSG_WARN_KHR, EGL_TRUE, EGL_DEBUG_MSG_INFO_KHR, EGL_TRUE, EGL_NONE, }; egl->procs.eglDebugMessageControlKHR(egl_log, debug_attribs); } if (eglBindAPI(EGL_OPENGL_ES_API) == EGL_FALSE) { wlr_log(WLR_ERROR, "Failed to bind to the OpenGL ES API"); free(egl); return NULL; } return egl; } static bool egl_init_display(struct wlr_egl *egl, EGLDisplay display) { egl->display = display; EGLint major, minor; if (eglInitialize(egl->display, &major, &minor) == EGL_FALSE) { wlr_log(WLR_ERROR, "Failed to initialize EGL"); return false; } const char *display_exts_str = eglQueryString(egl->display, EGL_EXTENSIONS); if (display_exts_str == NULL) { wlr_log(WLR_ERROR, "Failed to query EGL display extensions"); return false; } if (check_egl_ext(display_exts_str, "EGL_KHR_image_base")) { egl->exts.KHR_image_base = true; load_egl_proc(&egl->procs.eglCreateImageKHR, "eglCreateImageKHR"); load_egl_proc(&egl->procs.eglDestroyImageKHR, "eglDestroyImageKHR"); } egl->exts.EXT_image_dma_buf_import = check_egl_ext(display_exts_str, "EGL_EXT_image_dma_buf_import"); if (check_egl_ext(display_exts_str, "EGL_EXT_image_dma_buf_import_modifiers")) { egl->exts.EXT_image_dma_buf_import_modifiers = true; load_egl_proc(&egl->procs.eglQueryDmaBufFormatsEXT, "eglQueryDmaBufFormatsEXT"); load_egl_proc(&egl->procs.eglQueryDmaBufModifiersEXT, "eglQueryDmaBufModifiersEXT"); } egl->exts.EXT_create_context_robustness = check_egl_ext(display_exts_str, "EGL_EXT_create_context_robustness"); const char *device_exts_str = NULL, *driver_name = NULL; if (egl->exts.EXT_device_query) { EGLAttrib device_attrib; if (!egl->procs.eglQueryDisplayAttribEXT(egl->display, EGL_DEVICE_EXT, &device_attrib)) { wlr_log(WLR_ERROR, "eglQueryDisplayAttribEXT(EGL_DEVICE_EXT) failed"); return false; } egl->device = (EGLDeviceEXT)device_attrib; device_exts_str = egl->procs.eglQueryDeviceStringEXT(egl->device, EGL_EXTENSIONS); if (device_exts_str == NULL) { wlr_log(WLR_ERROR, "eglQueryDeviceStringEXT(EGL_EXTENSIONS) failed"); return false; } if (check_egl_ext(device_exts_str, "EGL_MESA_device_software")) { if (env_parse_bool("WLR_RENDERER_ALLOW_SOFTWARE")) { wlr_log(WLR_INFO, "Using software rendering"); } else { wlr_log(WLR_ERROR, "Software rendering detected, please use " "the WLR_RENDERER_ALLOW_SOFTWARE environment variable " "to proceed"); return false; } } #ifdef EGL_DRIVER_NAME_EXT if (check_egl_ext(device_exts_str, "EGL_EXT_device_persistent_id")) { driver_name = egl->procs.eglQueryDeviceStringEXT(egl->device, EGL_DRIVER_NAME_EXT); } #endif egl->exts.EXT_device_drm = check_egl_ext(device_exts_str, "EGL_EXT_device_drm"); egl->exts.EXT_device_drm_render_node = check_egl_ext(device_exts_str, "EGL_EXT_device_drm_render_node"); } if (!check_egl_ext(display_exts_str, "EGL_KHR_no_config_context") && !check_egl_ext(display_exts_str, "EGL_MESA_configless_context")) { wlr_log(WLR_ERROR, "EGL_KHR_no_config_context or " "EGL_MESA_configless_context not supported"); return false; } if (!check_egl_ext(display_exts_str, "EGL_KHR_surfaceless_context")) { wlr_log(WLR_ERROR, "EGL_KHR_surfaceless_context not supported"); return false; } egl->exts.IMG_context_priority = check_egl_ext(display_exts_str, "EGL_IMG_context_priority"); wlr_log(WLR_INFO, "Using EGL %d.%d", (int)major, (int)minor); wlr_log(WLR_INFO, "Supported EGL display extensions: %s", display_exts_str); if (device_exts_str != NULL) { wlr_log(WLR_INFO, "Supported EGL device extensions: %s", device_exts_str); } wlr_log(WLR_INFO, "EGL vendor: %s", eglQueryString(egl->display, EGL_VENDOR)); if (driver_name != NULL) { wlr_log(WLR_INFO, "EGL driver name: %s", driver_name); } init_dmabuf_formats(egl); return true; } static bool egl_init(struct wlr_egl *egl, EGLenum platform, void *remote_display) { EGLint display_attribs[3] = {0}; size_t display_attribs_len = 0; if (egl->exts.KHR_display_reference) { display_attribs[display_attribs_len++] = EGL_TRACK_REFERENCES_KHR; display_attribs[display_attribs_len++] = EGL_TRUE; } display_attribs[display_attribs_len++] = EGL_NONE; assert(display_attribs_len < sizeof(display_attribs) / sizeof(display_attribs[0])); EGLDisplay display = egl->procs.eglGetPlatformDisplayEXT(platform, remote_display, display_attribs); if (display == EGL_NO_DISPLAY) { wlr_log(WLR_ERROR, "Failed to create EGL display"); return false; } if (!egl_init_display(egl, display)) { if (egl->exts.KHR_display_reference) { eglTerminate(display); } return false; } size_t atti = 0; EGLint attribs[7]; attribs[atti++] = EGL_CONTEXT_CLIENT_VERSION; attribs[atti++] = 2; // Request a high priority context if possible // TODO: only do this if we're running as the DRM master bool request_high_priority = egl->exts.IMG_context_priority; // Try to reschedule all of our rendering to be completed first. If it // fails, it will fallback to the default priority (MEDIUM). if (request_high_priority) { attribs[atti++] = EGL_CONTEXT_PRIORITY_LEVEL_IMG; attribs[atti++] = EGL_CONTEXT_PRIORITY_HIGH_IMG; } if (egl->exts.EXT_create_context_robustness) { attribs[atti++] = EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_EXT; attribs[atti++] = EGL_LOSE_CONTEXT_ON_RESET_EXT; } attribs[atti++] = EGL_NONE; assert(atti <= sizeof(attribs)/sizeof(attribs[0])); egl->context = eglCreateContext(egl->display, EGL_NO_CONFIG_KHR, EGL_NO_CONTEXT, attribs); if (egl->context == EGL_NO_CONTEXT) { wlr_log(WLR_ERROR, "Failed to create EGL context"); return false; } if (request_high_priority) { EGLint priority = EGL_CONTEXT_PRIORITY_MEDIUM_IMG; eglQueryContext(egl->display, egl->context, EGL_CONTEXT_PRIORITY_LEVEL_IMG, &priority); if (priority != EGL_CONTEXT_PRIORITY_HIGH_IMG) { wlr_log(WLR_INFO, "Failed to obtain a high priority context"); } else { wlr_log(WLR_DEBUG, "Obtained high priority context"); } } return true; } static bool device_has_name(const drmDevice *device, const char *name); static EGLDeviceEXT get_egl_device_from_drm_fd(struct wlr_egl *egl, int drm_fd) { if (egl->procs.eglQueryDevicesEXT == NULL) { wlr_log(WLR_DEBUG, "EGL_EXT_device_enumeration not supported"); return EGL_NO_DEVICE_EXT; } else if (!egl->exts.EXT_device_query) { wlr_log(WLR_DEBUG, "EGL_EXT_device_query not supported"); return EGL_NO_DEVICE_EXT; } EGLint nb_devices = 0; if (!egl->procs.eglQueryDevicesEXT(0, NULL, &nb_devices)) { wlr_log(WLR_ERROR, "Failed to query EGL devices"); return EGL_NO_DEVICE_EXT; } EGLDeviceEXT *devices = calloc(nb_devices, sizeof(*devices)); if (devices == NULL) { wlr_log_errno(WLR_ERROR, "Failed to allocate EGL device list"); return EGL_NO_DEVICE_EXT; } if (!egl->procs.eglQueryDevicesEXT(nb_devices, devices, &nb_devices)) { wlr_log(WLR_ERROR, "Failed to query EGL devices"); return EGL_NO_DEVICE_EXT; } drmDevice *device = NULL; int ret = drmGetDevice(drm_fd, &device); if (ret < 0) { wlr_log(WLR_ERROR, "Failed to get DRM device: %s", strerror(-ret)); return EGL_NO_DEVICE_EXT; } EGLDeviceEXT egl_device = NULL; for (int i = 0; i < nb_devices; i++) { const char *egl_device_name = egl->procs.eglQueryDeviceStringEXT( devices[i], EGL_DRM_DEVICE_FILE_EXT); if (egl_device_name == NULL) { continue; } if (device_has_name(device, egl_device_name)) { wlr_log(WLR_DEBUG, "Using EGL device %s", egl_device_name); egl_device = devices[i]; break; } } drmFreeDevice(&device); free(devices); return egl_device; } static int open_render_node(int drm_fd) { char *render_name = drmGetRenderDeviceNameFromFd(drm_fd); if (render_name == NULL) { // This can happen on split render/display platforms, fallback to // primary node render_name = drmGetPrimaryDeviceNameFromFd(drm_fd); if (render_name == NULL) { wlr_log_errno(WLR_ERROR, "drmGetPrimaryDeviceNameFromFd failed"); return -1; } wlr_log(WLR_DEBUG, "DRM device '%s' has no render node, " "falling back to primary node", render_name); } int render_fd = open(render_name, O_RDWR | O_CLOEXEC); if (render_fd < 0) { wlr_log_errno(WLR_ERROR, "Failed to open DRM node '%s'", render_name); } free(render_name); return render_fd; } struct wlr_egl *wlr_egl_create_with_drm_fd(int drm_fd) { struct wlr_egl *egl = egl_create(); if (egl == NULL) { wlr_log(WLR_ERROR, "Failed to create EGL context"); return NULL; } if (egl->exts.EXT_platform_device) { /* * Search for the EGL device matching the DRM fd using the * EXT_device_enumeration extension. */ EGLDeviceEXT egl_device = get_egl_device_from_drm_fd(egl, drm_fd); if (egl_device != EGL_NO_DEVICE_EXT) { if (egl_init(egl, EGL_PLATFORM_DEVICE_EXT, egl_device)) { wlr_log(WLR_DEBUG, "Using EGL_PLATFORM_DEVICE_EXT"); return egl; } goto error; } /* Falls back on GBM in case the device was not found */ } else { wlr_log(WLR_DEBUG, "EXT_platform_device not supported"); } if (egl->exts.KHR_platform_gbm) { int gbm_fd = open_render_node(drm_fd); if (gbm_fd < 0) { wlr_log(WLR_ERROR, "Failed to open DRM render node"); goto error; } egl->gbm_device = gbm_create_device(gbm_fd); if (!egl->gbm_device) { close(gbm_fd); wlr_log(WLR_ERROR, "Failed to create GBM device"); goto error; } if (egl_init(egl, EGL_PLATFORM_GBM_KHR, egl->gbm_device)) { wlr_log(WLR_DEBUG, "Using EGL_PLATFORM_GBM_KHR"); return egl; } gbm_device_destroy(egl->gbm_device); close(gbm_fd); } else { wlr_log(WLR_DEBUG, "KHR_platform_gbm not supported"); } error: wlr_log(WLR_ERROR, "Failed to initialize EGL context"); free(egl); eglReleaseThread(); return NULL; } struct wlr_egl *wlr_egl_create_with_context(EGLDisplay display, EGLContext context) { EGLint client_type; if (!eglQueryContext(display, context, EGL_CONTEXT_CLIENT_TYPE, &client_type) || client_type != EGL_OPENGL_ES_API) { wlr_log(WLR_ERROR, "Unsupported EGL context client type (need OpenGL ES)"); return NULL; } EGLint client_version; if (!eglQueryContext(display, context, EGL_CONTEXT_CLIENT_VERSION, &client_version) || client_version < 2) { wlr_log(WLR_ERROR, "Unsupported EGL context client version (need OpenGL ES >= 2)"); return NULL; } struct wlr_egl *egl = egl_create(); if (egl == NULL) { return NULL; } if (!egl_init_display(egl, display)) { free(egl); return NULL; } egl->context = context; return egl; } void wlr_egl_destroy(struct wlr_egl *egl) { if (egl == NULL) { return; } wlr_drm_format_set_finish(&egl->dmabuf_render_formats); wlr_drm_format_set_finish(&egl->dmabuf_texture_formats); eglMakeCurrent(egl->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); eglDestroyContext(egl->display, egl->context); if (egl->exts.KHR_display_reference) { eglTerminate(egl->display); } eglReleaseThread(); if (egl->gbm_device) { int gbm_fd = gbm_device_get_fd(egl->gbm_device); gbm_device_destroy(egl->gbm_device); close(gbm_fd); } free(egl); } EGLDisplay wlr_egl_get_display(struct wlr_egl *egl) { return egl->display; } EGLContext wlr_egl_get_context(struct wlr_egl *egl) { return egl->context; } bool wlr_egl_destroy_image(struct wlr_egl *egl, EGLImage image) { if (!egl->exts.KHR_image_base) { return false; } if (!image) { return true; } return egl->procs.eglDestroyImageKHR(egl->display, image); } bool wlr_egl_make_current(struct wlr_egl *egl) { if (!eglMakeCurrent(egl->display, EGL_NO_SURFACE, EGL_NO_SURFACE, egl->context)) { wlr_log(WLR_ERROR, "eglMakeCurrent failed"); return false; } return true; } bool wlr_egl_unset_current(struct wlr_egl *egl) { if (!eglMakeCurrent(egl->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) { wlr_log(WLR_ERROR, "eglMakeCurrent failed"); return false; } return true; } bool wlr_egl_is_current(struct wlr_egl *egl) { return eglGetCurrentContext() == egl->context; } void wlr_egl_save_context(struct wlr_egl_context *context) { context->display = eglGetCurrentDisplay(); context->context = eglGetCurrentContext(); context->draw_surface = eglGetCurrentSurface(EGL_DRAW); context->read_surface = eglGetCurrentSurface(EGL_READ); } bool wlr_egl_restore_context(struct wlr_egl_context *context) { // If the saved context is a null-context, we must use the current // display instead of the saved display because eglMakeCurrent() can't // handle EGL_NO_DISPLAY. EGLDisplay display = context->display == EGL_NO_DISPLAY ? eglGetCurrentDisplay() : context->display; // If the current display is also EGL_NO_DISPLAY, we assume that there // is currently no context set and no action needs to be taken to unset // the context. if (display == EGL_NO_DISPLAY) { return true; } return eglMakeCurrent(display, context->draw_surface, context->read_surface, context->context); } EGLImageKHR wlr_egl_create_image_from_dmabuf(struct wlr_egl *egl, struct wlr_dmabuf_attributes *attributes, bool *external_only) { if (!egl->exts.KHR_image_base || !egl->exts.EXT_image_dma_buf_import) { wlr_log(WLR_ERROR, "dmabuf import extension not present"); return NULL; } if (attributes->modifier != DRM_FORMAT_MOD_INVALID && attributes->modifier != DRM_FORMAT_MOD_LINEAR && !egl->has_modifiers) { wlr_log(WLR_ERROR, "EGL implementation doesn't support modifiers"); return NULL; } unsigned int atti = 0; EGLint attribs[50]; attribs[atti++] = EGL_WIDTH; attribs[atti++] = attributes->width; attribs[atti++] = EGL_HEIGHT; attribs[atti++] = attributes->height; attribs[atti++] = EGL_LINUX_DRM_FOURCC_EXT; attribs[atti++] = attributes->format; struct { EGLint fd; EGLint offset; EGLint pitch; EGLint mod_lo; EGLint mod_hi; } attr_names[WLR_DMABUF_MAX_PLANES] = { { EGL_DMA_BUF_PLANE0_FD_EXT, EGL_DMA_BUF_PLANE0_OFFSET_EXT, EGL_DMA_BUF_PLANE0_PITCH_EXT, EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT, EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT }, { EGL_DMA_BUF_PLANE1_FD_EXT, EGL_DMA_BUF_PLANE1_OFFSET_EXT, EGL_DMA_BUF_PLANE1_PITCH_EXT, EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT, EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT }, { EGL_DMA_BUF_PLANE2_FD_EXT, EGL_DMA_BUF_PLANE2_OFFSET_EXT, EGL_DMA_BUF_PLANE2_PITCH_EXT, EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT, EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT }, { EGL_DMA_BUF_PLANE3_FD_EXT, EGL_DMA_BUF_PLANE3_OFFSET_EXT, EGL_DMA_BUF_PLANE3_PITCH_EXT, EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT, EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT } }; for (int i = 0; i < attributes->n_planes; i++) { attribs[atti++] = attr_names[i].fd; attribs[atti++] = attributes->fd[i]; attribs[atti++] = attr_names[i].offset; attribs[atti++] = attributes->offset[i]; attribs[atti++] = attr_names[i].pitch; attribs[atti++] = attributes->stride[i]; if (egl->has_modifiers && attributes->modifier != DRM_FORMAT_MOD_INVALID) { attribs[atti++] = attr_names[i].mod_lo; attribs[atti++] = attributes->modifier & 0xFFFFFFFF; attribs[atti++] = attr_names[i].mod_hi; attribs[atti++] = attributes->modifier >> 32; } } // Our clients don't expect our usage to trash the buffer contents attribs[atti++] = EGL_IMAGE_PRESERVED_KHR; attribs[atti++] = EGL_TRUE; attribs[atti++] = EGL_NONE; assert(atti < sizeof(attribs)/sizeof(attribs[0])); EGLImageKHR image = egl->procs.eglCreateImageKHR(egl->display, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, NULL, attribs); if (image == EGL_NO_IMAGE_KHR) { wlr_log(WLR_ERROR, "eglCreateImageKHR failed"); return EGL_NO_IMAGE_KHR; } *external_only = !wlr_drm_format_set_has(&egl->dmabuf_render_formats, attributes->format, attributes->modifier); return image; } static int get_egl_dmabuf_formats(struct wlr_egl *egl, EGLint **formats) { if (!egl->exts.EXT_image_dma_buf_import) { wlr_log(WLR_DEBUG, "DMA-BUF import extension not present"); return -1; } // when we only have the image_dmabuf_import extension we can't query // which formats are supported. These two are on almost always // supported; it's the intended way to just try to create buffers. // Just a guess but better than not supporting dmabufs at all, // given that the modifiers extension isn't supported everywhere. if (!egl->exts.EXT_image_dma_buf_import_modifiers) { static const EGLint fallback_formats[] = { DRM_FORMAT_ARGB8888, DRM_FORMAT_XRGB8888, }; int num = sizeof(fallback_formats) / sizeof(fallback_formats[0]); *formats = calloc(num, sizeof(**formats)); if (!*formats) { wlr_log_errno(WLR_ERROR, "Allocation failed"); return -1; } memcpy(*formats, fallback_formats, num * sizeof(**formats)); return num; } EGLint num; if (!egl->procs.eglQueryDmaBufFormatsEXT(egl->display, 0, NULL, &num)) { wlr_log(WLR_ERROR, "Failed to query number of dmabuf formats"); return -1; } *formats = calloc(num, sizeof(**formats)); if (*formats == NULL) { wlr_log(WLR_ERROR, "Allocation failed: %s", strerror(errno)); return -1; } if (!egl->procs.eglQueryDmaBufFormatsEXT(egl->display, num, *formats, &num)) { wlr_log(WLR_ERROR, "Failed to query dmabuf format"); free(*formats); return -1; } return num; } static int get_egl_dmabuf_modifiers(struct wlr_egl *egl, EGLint format, uint64_t **modifiers, EGLBoolean **external_only) { *modifiers = NULL; *external_only = NULL; if (!egl->exts.EXT_image_dma_buf_import) { wlr_log(WLR_DEBUG, "DMA-BUF extension not present"); return -1; } if (!egl->exts.EXT_image_dma_buf_import_modifiers) { return 0; } EGLint num; if (!egl->procs.eglQueryDmaBufModifiersEXT(egl->display, format, 0, NULL, NULL, &num)) { wlr_log(WLR_ERROR, "Failed to query dmabuf number of modifiers"); return -1; } if (num == 0) { return 0; } *modifiers = calloc(num, sizeof(**modifiers)); if (*modifiers == NULL) { wlr_log_errno(WLR_ERROR, "Allocation failed"); return -1; } *external_only = calloc(num, sizeof(**external_only)); if (*external_only == NULL) { wlr_log_errno(WLR_ERROR, "Allocation failed"); free(*modifiers); *modifiers = NULL; return -1; } if (!egl->procs.eglQueryDmaBufModifiersEXT(egl->display, format, num, *modifiers, *external_only, &num)) { wlr_log(WLR_ERROR, "Failed to query dmabuf modifiers"); free(*modifiers); free(*external_only); return -1; } return num; } const struct wlr_drm_format_set *wlr_egl_get_dmabuf_texture_formats( struct wlr_egl *egl) { return &egl->dmabuf_texture_formats; } const struct wlr_drm_format_set *wlr_egl_get_dmabuf_render_formats( struct wlr_egl *egl) { return &egl->dmabuf_render_formats; } static bool device_has_name(const drmDevice *device, const char *name) { for (size_t i = 0; i < DRM_NODE_MAX; i++) { if (!(device->available_nodes & (1 << i))) { continue; } if (strcmp(device->nodes[i], name) == 0) { return true; } } return false; } static char *get_render_name(const char *name) { uint32_t flags = 0; int devices_len = drmGetDevices2(flags, NULL, 0); if (devices_len < 0) { wlr_log(WLR_ERROR, "drmGetDevices2 failed: %s", strerror(-devices_len)); return NULL; } drmDevice **devices = calloc(devices_len, sizeof(*devices)); if (devices == NULL) { wlr_log_errno(WLR_ERROR, "Allocation failed"); return NULL; } devices_len = drmGetDevices2(flags, devices, devices_len); if (devices_len < 0) { free(devices); wlr_log(WLR_ERROR, "drmGetDevices2 failed: %s", strerror(-devices_len)); return NULL; } const drmDevice *match = NULL; for (int i = 0; i < devices_len; i++) { if (device_has_name(devices[i], name)) { match = devices[i]; break; } } char *render_name = NULL; if (match == NULL) { wlr_log(WLR_ERROR, "Cannot find DRM device %s", name); } else if (!(match->available_nodes & (1 << DRM_NODE_RENDER))) { // Likely a split display/render setup. Pick the primary node and hope // Mesa will open the right render node under-the-hood. wlr_log(WLR_DEBUG, "DRM device %s has no render node, " "falling back to primary node", name); assert(match->available_nodes & (1 << DRM_NODE_PRIMARY)); render_name = strdup(match->nodes[DRM_NODE_PRIMARY]); } else { render_name = strdup(match->nodes[DRM_NODE_RENDER]); } for (int i = 0; i < devices_len; i++) { drmFreeDevice(&devices[i]); } free(devices); return render_name; } static int dup_egl_device_drm_fd(struct wlr_egl *egl) { if (egl->device == EGL_NO_DEVICE_EXT || (!egl->exts.EXT_device_drm && !egl->exts.EXT_device_drm_render_node)) { return -1; } char *render_name = NULL; #ifdef EGL_EXT_device_drm_render_node if (egl->exts.EXT_device_drm_render_node) { const char *name = egl->procs.eglQueryDeviceStringEXT(egl->device, EGL_DRM_RENDER_NODE_FILE_EXT); if (name == NULL) { wlr_log(WLR_DEBUG, "EGL device has no render node"); return -1; } render_name = strdup(name); } #endif if (render_name == NULL) { const char *primary_name = egl->procs.eglQueryDeviceStringEXT(egl->device, EGL_DRM_DEVICE_FILE_EXT); if (primary_name == NULL) { wlr_log(WLR_ERROR, "eglQueryDeviceStringEXT(EGL_DRM_DEVICE_FILE_EXT) failed"); return -1; } render_name = get_render_name(primary_name); if (render_name == NULL) { wlr_log(WLR_ERROR, "Can't find render node name for device %s", primary_name); return -1; } } int render_fd = open(render_name, O_RDWR | O_NONBLOCK | O_CLOEXEC); if (render_fd < 0) { wlr_log_errno(WLR_ERROR, "Failed to open DRM render node %s", render_name); free(render_name); return -1; } free(render_name); return render_fd; } int wlr_egl_dup_drm_fd(struct wlr_egl *egl) { int fd = dup_egl_device_drm_fd(egl); if (fd >= 0) { return fd; } // Fallback to GBM's FD if we can't use EGLDevice if (egl->gbm_device == NULL) { return -1; } fd = fcntl(gbm_device_get_fd(egl->gbm_device), F_DUPFD_CLOEXEC, 0); if (fd < 0) { wlr_log_errno(WLR_ERROR, "Failed to dup GBM FD"); } return fd; } wlroots-0.17.1/render/gles2/000077500000000000000000000000001454110342200156005ustar00rootroot00000000000000wlroots-0.17.1/render/gles2/meson.build000066400000000000000000000004511454110342200177420ustar00rootroot00000000000000glesv2 = dependency('glesv2', required: 'gles2' in renderers) if not (glesv2.found() and internal_features['egl']) subdir_done() endif features += { 'gles2-renderer': true } wlr_deps += glesv2 wlr_files += files( 'pass.c', 'pixel_format.c', 'renderer.c', 'texture.c', ) subdir('shaders') wlroots-0.17.1/render/gles2/pass.c000066400000000000000000000214571454110342200167230ustar00rootroot00000000000000#define _POSIX_C_SOURCE 199309L #include #include #include #include #include #include "render/gles2.h" #include "types/wlr_matrix.h" #define MAX_QUADS 86 // 4kb static const struct wlr_render_pass_impl render_pass_impl; static struct wlr_gles2_render_pass *get_render_pass(struct wlr_render_pass *wlr_pass) { assert(wlr_pass->impl == &render_pass_impl); struct wlr_gles2_render_pass *pass = wl_container_of(wlr_pass, pass, base); return pass; } static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { struct wlr_gles2_render_pass *pass = get_render_pass(wlr_pass); struct wlr_gles2_renderer *renderer = pass->buffer->renderer; struct wlr_gles2_render_timer *timer = pass->timer; push_gles2_debug(renderer); if (timer) { // clear disjoint flag GLint64 disjoint; renderer->procs.glGetInteger64vEXT(GL_GPU_DISJOINT_EXT, &disjoint); // set up the query renderer->procs.glQueryCounterEXT(timer->id, GL_TIMESTAMP_EXT); // get end-of-CPU-work time in GL time domain renderer->procs.glGetInteger64vEXT(GL_TIMESTAMP_EXT, &timer->gl_cpu_end); // get end-of-CPU-work time in CPU time domain clock_gettime(CLOCK_MONOTONIC, &timer->cpu_end); } glFlush(); glBindFramebuffer(GL_FRAMEBUFFER, 0); pop_gles2_debug(renderer); wlr_buffer_unlock(pass->buffer->buffer); free(pass); return true; } static void render(const struct wlr_box *box, const pixman_region32_t *clip, GLint attrib) { pixman_region32_t region; pixman_region32_init_rect(®ion, box->x, box->y, box->width, box->height); if (clip) { pixman_region32_intersect(®ion, ®ion, clip); } int rects_len; const pixman_box32_t *rects = pixman_region32_rectangles(®ion, &rects_len); if (rects_len == 0) { pixman_region32_fini(®ion); return; } glEnableVertexAttribArray(attrib); for (int i = 0; i < rects_len;) { int batch = rects_len - i < MAX_QUADS ? rects_len - i : MAX_QUADS; int batch_end = batch + i; size_t vert_index = 0; GLfloat verts[MAX_QUADS * 6 * 2]; for (; i < batch_end; i++) { const pixman_box32_t *rect = &rects[i]; verts[vert_index++] = (GLfloat)(rect->x1 - box->x) / box->width; verts[vert_index++] = (GLfloat)(rect->y1 - box->y) / box->height; verts[vert_index++] = (GLfloat)(rect->x2 - box->x) / box->width; verts[vert_index++] = (GLfloat)(rect->y1 - box->y) / box->height; verts[vert_index++] = (GLfloat)(rect->x1 - box->x) / box->width; verts[vert_index++] = (GLfloat)(rect->y2 - box->y) / box->height; verts[vert_index++] = (GLfloat)(rect->x2 - box->x) / box->width; verts[vert_index++] = (GLfloat)(rect->y1 - box->y) / box->height; verts[vert_index++] = (GLfloat)(rect->x2 - box->x) / box->width; verts[vert_index++] = (GLfloat)(rect->y2 - box->y) / box->height; verts[vert_index++] = (GLfloat)(rect->x1 - box->x) / box->width; verts[vert_index++] = (GLfloat)(rect->y2 - box->y) / box->height; } glVertexAttribPointer(attrib, 2, GL_FLOAT, GL_FALSE, 0, verts); glDrawArrays(GL_TRIANGLES, 0, batch * 6); } glDisableVertexAttribArray(attrib); pixman_region32_fini(®ion); } static void set_proj_matrix(GLint loc, float proj[9], const struct wlr_box *box) { float gl_matrix[9]; wlr_matrix_identity(gl_matrix); wlr_matrix_translate(gl_matrix, box->x, box->y); wlr_matrix_scale(gl_matrix, box->width, box->height); wlr_matrix_multiply(gl_matrix, proj, gl_matrix); glUniformMatrix3fv(loc, 1, GL_FALSE, gl_matrix); } static void set_tex_matrix(GLint loc, enum wl_output_transform trans, const struct wlr_fbox *box) { float tex_matrix[9]; wlr_matrix_identity(tex_matrix); wlr_matrix_translate(tex_matrix, box->x, box->y); wlr_matrix_scale(tex_matrix, box->width, box->height); wlr_matrix_translate(tex_matrix, .5, .5); // since textures have a different origin point we have to transform // differently if we are rotating if (trans & WL_OUTPUT_TRANSFORM_90) { wlr_matrix_transform(tex_matrix, wlr_output_transform_invert(trans)); } else { wlr_matrix_transform(tex_matrix, trans); } wlr_matrix_translate(tex_matrix, -.5, -.5); glUniformMatrix3fv(loc, 1, GL_FALSE, tex_matrix); } static void setup_blending(enum wlr_render_blend_mode mode) { switch (mode) { case WLR_RENDER_BLEND_MODE_PREMULTIPLIED: glEnable(GL_BLEND); break; case WLR_RENDER_BLEND_MODE_NONE: glDisable(GL_BLEND); break; } } static void render_pass_add_texture(struct wlr_render_pass *wlr_pass, const struct wlr_render_texture_options *options) { struct wlr_gles2_render_pass *pass = get_render_pass(wlr_pass); struct wlr_gles2_renderer *renderer = pass->buffer->renderer; struct wlr_gles2_texture *texture = gles2_get_texture(options->texture); struct wlr_gles2_tex_shader *shader = NULL; switch (texture->target) { case GL_TEXTURE_2D: if (texture->has_alpha) { shader = &renderer->shaders.tex_rgba; } else { shader = &renderer->shaders.tex_rgbx; } break; case GL_TEXTURE_EXTERNAL_OES: // EGL_EXT_image_dma_buf_import_modifiers requires // GL_OES_EGL_image_external assert(renderer->exts.OES_egl_image_external); shader = &renderer->shaders.tex_ext; break; default: abort(); } struct wlr_box dst_box; struct wlr_fbox src_fbox; wlr_render_texture_options_get_src_box(options, &src_fbox); wlr_render_texture_options_get_dst_box(options, &dst_box); float alpha = wlr_render_texture_options_get_alpha(options); src_fbox.x /= options->texture->width; src_fbox.y /= options->texture->height; src_fbox.width /= options->texture->width; src_fbox.height /= options->texture->height; push_gles2_debug(renderer); setup_blending(!texture->has_alpha && alpha == 1.0 ? WLR_RENDER_BLEND_MODE_NONE : options->blend_mode); glUseProgram(shader->program); glActiveTexture(GL_TEXTURE0); glBindTexture(texture->target, texture->tex); switch (options->filter_mode) { case WLR_SCALE_FILTER_BILINEAR: glTexParameteri(texture->target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(texture->target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); break; case WLR_SCALE_FILTER_NEAREST: glTexParameteri(texture->target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(texture->target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); break; } glUniform1i(shader->tex, 0); glUniform1f(shader->alpha, alpha); set_proj_matrix(shader->proj, pass->projection_matrix, &dst_box); set_tex_matrix(shader->tex_proj, options->transform, &src_fbox); render(&dst_box, options->clip, shader->pos_attrib); glBindTexture(texture->target, 0); pop_gles2_debug(renderer); } static void render_pass_add_rect(struct wlr_render_pass *wlr_pass, const struct wlr_render_rect_options *options) { struct wlr_gles2_render_pass *pass = get_render_pass(wlr_pass); struct wlr_gles2_renderer *renderer = pass->buffer->renderer; const struct wlr_render_color *color = &options->color; struct wlr_box box; wlr_render_rect_options_get_box(options, pass->buffer->buffer, &box); push_gles2_debug(renderer); setup_blending(color->a == 1.0 ? WLR_RENDER_BLEND_MODE_NONE : options->blend_mode); glUseProgram(renderer->shaders.quad.program); set_proj_matrix(renderer->shaders.quad.proj, pass->projection_matrix, &box); glUniform4f(renderer->shaders.quad.color, color->r, color->g, color->b, color->a); render(&box, options->clip, renderer->shaders.quad.pos_attrib); pop_gles2_debug(renderer); } static const struct wlr_render_pass_impl render_pass_impl = { .submit = render_pass_submit, .add_texture = render_pass_add_texture, .add_rect = render_pass_add_rect, }; static const char *reset_status_str(GLenum status) { switch (status) { case GL_GUILTY_CONTEXT_RESET_KHR: return "guilty"; case GL_INNOCENT_CONTEXT_RESET_KHR: return "innocent"; case GL_UNKNOWN_CONTEXT_RESET_KHR: return "unknown"; default: return ""; } } struct wlr_gles2_render_pass *begin_gles2_buffer_pass(struct wlr_gles2_buffer *buffer, struct wlr_gles2_render_timer *timer) { struct wlr_gles2_renderer *renderer = buffer->renderer; struct wlr_buffer *wlr_buffer = buffer->buffer; if (renderer->procs.glGetGraphicsResetStatusKHR) { GLenum status = renderer->procs.glGetGraphicsResetStatusKHR(); if (status != GL_NO_ERROR) { wlr_log(WLR_ERROR, "GPU reset (%s)", reset_status_str(status)); wl_signal_emit_mutable(&renderer->wlr_renderer.events.lost, NULL); return NULL; } } struct wlr_gles2_render_pass *pass = calloc(1, sizeof(*pass)); if (pass == NULL) { return NULL; } wlr_render_pass_init(&pass->base, &render_pass_impl); wlr_buffer_lock(wlr_buffer); pass->buffer = buffer; pass->timer = timer; matrix_projection(pass->projection_matrix, wlr_buffer->width, wlr_buffer->height, WL_OUTPUT_TRANSFORM_FLIPPED_180); push_gles2_debug(renderer); glBindFramebuffer(GL_FRAMEBUFFER, buffer->fbo); glViewport(0, 0, wlr_buffer->width, wlr_buffer->height); glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); glDisable(GL_SCISSOR_TEST); pop_gles2_debug(renderer); return pass; } wlroots-0.17.1/render/gles2/pixel_format.c000066400000000000000000000104221454110342200204340ustar00rootroot00000000000000#include #include #include #include "render/gles2.h" /* * The DRM formats are little endian while the GL formats are big endian, * so DRM_FORMAT_ARGB8888 is actually compatible with GL_BGRA_EXT. */ static const struct wlr_gles2_pixel_format formats[] = { { .drm_format = DRM_FORMAT_ARGB8888, .gl_format = GL_BGRA_EXT, .gl_type = GL_UNSIGNED_BYTE, .has_alpha = true, }, { .drm_format = DRM_FORMAT_XRGB8888, .gl_format = GL_BGRA_EXT, .gl_type = GL_UNSIGNED_BYTE, .has_alpha = false, }, { .drm_format = DRM_FORMAT_XBGR8888, .gl_format = GL_RGBA, .gl_type = GL_UNSIGNED_BYTE, .has_alpha = false, }, { .drm_format = DRM_FORMAT_ABGR8888, .gl_format = GL_RGBA, .gl_type = GL_UNSIGNED_BYTE, .has_alpha = true, }, { .drm_format = DRM_FORMAT_BGR888, .gl_format = GL_RGB, .gl_type = GL_UNSIGNED_BYTE, .has_alpha = false, }, #if WLR_LITTLE_ENDIAN { .drm_format = DRM_FORMAT_RGBX4444, .gl_format = GL_RGBA, .gl_type = GL_UNSIGNED_SHORT_4_4_4_4, .has_alpha = false, }, { .drm_format = DRM_FORMAT_RGBA4444, .gl_format = GL_RGBA, .gl_type = GL_UNSIGNED_SHORT_4_4_4_4, .has_alpha = true, }, { .drm_format = DRM_FORMAT_RGBX5551, .gl_format = GL_RGBA, .gl_type = GL_UNSIGNED_SHORT_5_5_5_1, .has_alpha = false, }, { .drm_format = DRM_FORMAT_RGBA5551, .gl_format = GL_RGBA, .gl_type = GL_UNSIGNED_SHORT_5_5_5_1, .has_alpha = true, }, { .drm_format = DRM_FORMAT_RGB565, .gl_format = GL_RGB, .gl_type = GL_UNSIGNED_SHORT_5_6_5, .has_alpha = false, }, { .drm_format = DRM_FORMAT_XBGR2101010, .gl_format = GL_RGBA, .gl_type = GL_UNSIGNED_INT_2_10_10_10_REV_EXT, .has_alpha = false, }, { .drm_format = DRM_FORMAT_ABGR2101010, .gl_format = GL_RGBA, .gl_type = GL_UNSIGNED_INT_2_10_10_10_REV_EXT, .has_alpha = true, }, { .drm_format = DRM_FORMAT_XBGR16161616F, .gl_format = GL_RGBA, .gl_type = GL_HALF_FLOAT_OES, .has_alpha = false, }, { .drm_format = DRM_FORMAT_ABGR16161616F, .gl_format = GL_RGBA, .gl_type = GL_HALF_FLOAT_OES, .has_alpha = true, }, { .drm_format = DRM_FORMAT_XBGR16161616, .gl_internalformat = GL_RGBA16_EXT, .gl_format = GL_RGBA, .gl_type = GL_UNSIGNED_SHORT, .has_alpha = false, }, { .drm_format = DRM_FORMAT_ABGR16161616, .gl_internalformat = GL_RGBA16_EXT, .gl_format = GL_RGBA, .gl_type = GL_UNSIGNED_SHORT, .has_alpha = true, }, #endif }; // TODO: more pixel formats /* * Return true if supported for texturing, even if other operations like * reading aren't supported. */ bool is_gles2_pixel_format_supported(const struct wlr_gles2_renderer *renderer, const struct wlr_gles2_pixel_format *format) { if (format->gl_type == GL_UNSIGNED_INT_2_10_10_10_REV_EXT && !renderer->exts.EXT_texture_type_2_10_10_10_REV) { return false; } if (format->gl_type == GL_HALF_FLOAT_OES && !renderer->exts.OES_texture_half_float_linear) { return false; } if (format->gl_type == GL_UNSIGNED_SHORT && !renderer->exts.EXT_texture_norm16) { return false; } /* * Note that we don't need to check for GL_EXT_texture_format_BGRA8888 * here, since we've already checked if we have it at renderer creation * time and bailed out if not. We do the check there because Wayland * requires all compositors to support SHM buffers in that format. */ return true; } const struct wlr_gles2_pixel_format *get_gles2_format_from_drm(uint32_t fmt) { for (size_t i = 0; i < sizeof(formats) / sizeof(*formats); ++i) { if (formats[i].drm_format == fmt) { return &formats[i]; } } return NULL; } const struct wlr_gles2_pixel_format *get_gles2_format_from_gl( GLint gl_format, GLint gl_type, bool alpha) { for (size_t i = 0; i < sizeof(formats) / sizeof(*formats); ++i) { if (formats[i].gl_format == gl_format && formats[i].gl_type == gl_type && formats[i].has_alpha == alpha) { return &formats[i]; } } return NULL; } const uint32_t *get_gles2_shm_formats(const struct wlr_gles2_renderer *renderer, size_t *len) { static uint32_t shm_formats[sizeof(formats) / sizeof(formats[0])]; size_t j = 0; for (size_t i = 0; i < sizeof(formats) / sizeof(formats[0]); i++) { if (!is_gles2_pixel_format_supported(renderer, &formats[i])) { continue; } shm_formats[j++] = formats[i].drm_format; } *len = j; return shm_formats; } wlroots-0.17.1/render/gles2/renderer.c000066400000000000000000000733731454110342200175670ustar00rootroot00000000000000#define _POSIX_C_SOURCE 199309L #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "render/egl.h" #include "render/gles2.h" #include "render/pixel_format.h" #include "types/wlr_matrix.h" #include "util/time.h" #include "common_vert_src.h" #include "quad_frag_src.h" #include "tex_rgba_frag_src.h" #include "tex_rgbx_frag_src.h" #include "tex_external_frag_src.h" static const GLfloat verts[] = { 1, 0, // top right 0, 0, // top left 1, 1, // bottom right 0, 1, // bottom left }; static const struct wlr_renderer_impl renderer_impl; static const struct wlr_render_timer_impl render_timer_impl; bool wlr_renderer_is_gles2(struct wlr_renderer *wlr_renderer) { return wlr_renderer->impl == &renderer_impl; } struct wlr_gles2_renderer *gles2_get_renderer( struct wlr_renderer *wlr_renderer) { assert(wlr_renderer_is_gles2(wlr_renderer)); struct wlr_gles2_renderer *renderer = wl_container_of(wlr_renderer, renderer, wlr_renderer); return renderer; } static struct wlr_gles2_renderer *gles2_get_renderer_in_context( struct wlr_renderer *wlr_renderer) { struct wlr_gles2_renderer *renderer = gles2_get_renderer(wlr_renderer); assert(wlr_egl_is_current(renderer->egl)); assert(renderer->current_buffer != NULL); return renderer; } bool wlr_render_timer_is_gles2(struct wlr_render_timer *timer) { return timer->impl == &render_timer_impl; } struct wlr_gles2_render_timer *gles2_get_render_timer(struct wlr_render_timer *wlr_timer) { assert(wlr_render_timer_is_gles2(wlr_timer)); struct wlr_gles2_render_timer *timer = wl_container_of(wlr_timer, timer, base); return timer; } static void destroy_buffer(struct wlr_gles2_buffer *buffer) { wl_list_remove(&buffer->link); wlr_addon_finish(&buffer->addon); struct wlr_egl_context prev_ctx; wlr_egl_save_context(&prev_ctx); wlr_egl_make_current(buffer->renderer->egl); push_gles2_debug(buffer->renderer); glDeleteFramebuffers(1, &buffer->fbo); glDeleteRenderbuffers(1, &buffer->rbo); pop_gles2_debug(buffer->renderer); wlr_egl_destroy_image(buffer->renderer->egl, buffer->image); wlr_egl_restore_context(&prev_ctx); free(buffer); } static void handle_buffer_destroy(struct wlr_addon *addon) { struct wlr_gles2_buffer *buffer = wl_container_of(addon, buffer, addon); destroy_buffer(buffer); } static const struct wlr_addon_interface buffer_addon_impl = { .name = "wlr_gles2_buffer", .destroy = handle_buffer_destroy, }; static struct wlr_gles2_buffer *get_or_create_buffer(struct wlr_gles2_renderer *renderer, struct wlr_buffer *wlr_buffer) { struct wlr_addon *addon = wlr_addon_find(&wlr_buffer->addons, renderer, &buffer_addon_impl); if (addon) { struct wlr_gles2_buffer *buffer = wl_container_of(addon, buffer, addon); return buffer; } struct wlr_gles2_buffer *buffer = calloc(1, sizeof(*buffer)); if (buffer == NULL) { wlr_log_errno(WLR_ERROR, "Allocation failed"); return NULL; } buffer->buffer = wlr_buffer; buffer->renderer = renderer; struct wlr_dmabuf_attributes dmabuf = {0}; if (!wlr_buffer_get_dmabuf(wlr_buffer, &dmabuf)) { goto error_buffer; } bool external_only; buffer->image = wlr_egl_create_image_from_dmabuf(renderer->egl, &dmabuf, &external_only); if (buffer->image == EGL_NO_IMAGE_KHR) { goto error_buffer; } push_gles2_debug(renderer); glGenRenderbuffers(1, &buffer->rbo); glBindRenderbuffer(GL_RENDERBUFFER, buffer->rbo); renderer->procs.glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER, buffer->image); glBindRenderbuffer(GL_RENDERBUFFER, 0); glGenFramebuffers(1, &buffer->fbo); glBindFramebuffer(GL_FRAMEBUFFER, buffer->fbo); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, buffer->rbo); GLenum fb_status = glCheckFramebufferStatus(GL_FRAMEBUFFER); glBindFramebuffer(GL_FRAMEBUFFER, 0); pop_gles2_debug(renderer); if (fb_status != GL_FRAMEBUFFER_COMPLETE) { wlr_log(WLR_ERROR, "Failed to create FBO"); goto error_image; } wlr_addon_init(&buffer->addon, &wlr_buffer->addons, renderer, &buffer_addon_impl); wl_list_insert(&renderer->buffers, &buffer->link); wlr_log(WLR_DEBUG, "Created GL FBO for buffer %dx%d", wlr_buffer->width, wlr_buffer->height); return buffer; error_image: wlr_egl_destroy_image(renderer->egl, buffer->image); error_buffer: free(buffer); return NULL; } static bool gles2_bind_buffer(struct wlr_renderer *wlr_renderer, struct wlr_buffer *wlr_buffer) { struct wlr_gles2_renderer *renderer = gles2_get_renderer(wlr_renderer); if (renderer->current_buffer != NULL) { assert(wlr_egl_is_current(renderer->egl)); push_gles2_debug(renderer); glFlush(); glBindFramebuffer(GL_FRAMEBUFFER, 0); pop_gles2_debug(renderer); wlr_buffer_unlock(renderer->current_buffer->buffer); renderer->current_buffer = NULL; } if (wlr_buffer == NULL) { wlr_egl_unset_current(renderer->egl); return true; } wlr_egl_make_current(renderer->egl); struct wlr_gles2_buffer *buffer = get_or_create_buffer(renderer, wlr_buffer); if (buffer == NULL) { return false; } wlr_buffer_lock(wlr_buffer); renderer->current_buffer = buffer; push_gles2_debug(renderer); glBindFramebuffer(GL_FRAMEBUFFER, renderer->current_buffer->fbo); pop_gles2_debug(renderer); return true; } static const char *reset_status_str(GLenum status) { switch (status) { case GL_GUILTY_CONTEXT_RESET_KHR: return "guilty"; case GL_INNOCENT_CONTEXT_RESET_KHR: return "innocent"; case GL_UNKNOWN_CONTEXT_RESET_KHR: return "unknown"; default: return ""; } } static bool gles2_begin(struct wlr_renderer *wlr_renderer, uint32_t width, uint32_t height) { struct wlr_gles2_renderer *renderer = gles2_get_renderer_in_context(wlr_renderer); push_gles2_debug(renderer); if (renderer->procs.glGetGraphicsResetStatusKHR) { GLenum status = renderer->procs.glGetGraphicsResetStatusKHR(); if (status != GL_NO_ERROR) { wlr_log(WLR_ERROR, "GPU reset (%s)", reset_status_str(status)); wl_signal_emit_mutable(&wlr_renderer->events.lost, NULL); return false; } } glViewport(0, 0, width, height); renderer->viewport_width = width; renderer->viewport_height = height; // refresh projection matrix matrix_projection(renderer->projection, width, height, WL_OUTPUT_TRANSFORM_FLIPPED_180); glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); // XXX: maybe we should save output projection and remove some of the need // for users to sling matricies themselves pop_gles2_debug(renderer); return true; } static void gles2_end(struct wlr_renderer *wlr_renderer) { gles2_get_renderer_in_context(wlr_renderer); // no-op } static void gles2_clear(struct wlr_renderer *wlr_renderer, const float color[static 4]) { struct wlr_gles2_renderer *renderer = gles2_get_renderer_in_context(wlr_renderer); push_gles2_debug(renderer); glClearColor(color[0], color[1], color[2], color[3]); glClear(GL_COLOR_BUFFER_BIT); pop_gles2_debug(renderer); } static void gles2_scissor(struct wlr_renderer *wlr_renderer, struct wlr_box *box) { struct wlr_gles2_renderer *renderer = gles2_get_renderer_in_context(wlr_renderer); push_gles2_debug(renderer); if (box != NULL) { glScissor(box->x, box->y, box->width, box->height); glEnable(GL_SCISSOR_TEST); } else { glDisable(GL_SCISSOR_TEST); } pop_gles2_debug(renderer); } static bool gles2_render_subtexture_with_matrix( struct wlr_renderer *wlr_renderer, struct wlr_texture *wlr_texture, const struct wlr_fbox *box, const float matrix[static 9], float alpha) { struct wlr_gles2_renderer *renderer = gles2_get_renderer_in_context(wlr_renderer); struct wlr_gles2_texture *texture = gles2_get_texture(wlr_texture); assert(texture->renderer == renderer); struct wlr_gles2_tex_shader *shader = NULL; switch (texture->target) { case GL_TEXTURE_2D: if (texture->has_alpha) { shader = &renderer->shaders.tex_rgba; } else { shader = &renderer->shaders.tex_rgbx; } break; case GL_TEXTURE_EXTERNAL_OES: // EGL_EXT_image_dma_buf_import_modifiers requires // GL_OES_EGL_image_external assert(renderer->exts.OES_egl_image_external); shader = &renderer->shaders.tex_ext; break; default: abort(); } float gl_matrix[9]; wlr_matrix_multiply(gl_matrix, renderer->projection, matrix); push_gles2_debug(renderer); if (!texture->has_alpha && alpha == 1.0) { glDisable(GL_BLEND); } else { glEnable(GL_BLEND); } glActiveTexture(GL_TEXTURE0); glBindTexture(texture->target, texture->tex); glTexParameteri(texture->target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glUseProgram(shader->program); glUniformMatrix3fv(shader->proj, 1, GL_FALSE, gl_matrix); glUniform1i(shader->tex, 0); glUniform1f(shader->alpha, alpha); float tex_matrix[9]; wlr_matrix_identity(tex_matrix); wlr_matrix_translate(tex_matrix, box->x / texture->wlr_texture.width, box->y / texture->wlr_texture.height); wlr_matrix_scale(tex_matrix, box->width / texture->wlr_texture.width, box->height / texture->wlr_texture.height); glUniformMatrix3fv(shader->tex_proj, 1, GL_FALSE, tex_matrix); glVertexAttribPointer(shader->pos_attrib, 2, GL_FLOAT, GL_FALSE, 0, verts); glEnableVertexAttribArray(shader->pos_attrib); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); glDisableVertexAttribArray(shader->pos_attrib); glBindTexture(texture->target, 0); pop_gles2_debug(renderer); return true; } static void gles2_render_quad_with_matrix(struct wlr_renderer *wlr_renderer, const float color[static 4], const float matrix[static 9]) { struct wlr_gles2_renderer *renderer = gles2_get_renderer_in_context(wlr_renderer); float gl_matrix[9]; wlr_matrix_multiply(gl_matrix, renderer->projection, matrix); push_gles2_debug(renderer); if (color[3] == 1.0) { glDisable(GL_BLEND); } else { glEnable(GL_BLEND); } glUseProgram(renderer->shaders.quad.program); glUniformMatrix3fv(renderer->shaders.quad.proj, 1, GL_FALSE, gl_matrix); glUniform4f(renderer->shaders.quad.color, color[0], color[1], color[2], color[3]); glVertexAttribPointer(renderer->shaders.quad.pos_attrib, 2, GL_FLOAT, GL_FALSE, 0, verts); glEnableVertexAttribArray(renderer->shaders.quad.pos_attrib); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); glDisableVertexAttribArray(renderer->shaders.quad.pos_attrib); pop_gles2_debug(renderer); } static const uint32_t *gles2_get_shm_texture_formats( struct wlr_renderer *wlr_renderer, size_t *len) { struct wlr_gles2_renderer *renderer = gles2_get_renderer(wlr_renderer); return get_gles2_shm_formats(renderer, len); } static const struct wlr_drm_format_set *gles2_get_dmabuf_texture_formats( struct wlr_renderer *wlr_renderer) { struct wlr_gles2_renderer *renderer = gles2_get_renderer(wlr_renderer); return wlr_egl_get_dmabuf_texture_formats(renderer->egl); } static const struct wlr_drm_format_set *gles2_get_render_formats( struct wlr_renderer *wlr_renderer) { struct wlr_gles2_renderer *renderer = gles2_get_renderer(wlr_renderer); return wlr_egl_get_dmabuf_render_formats(renderer->egl); } static uint32_t gles2_preferred_read_format( struct wlr_renderer *wlr_renderer) { struct wlr_gles2_renderer *renderer = gles2_get_renderer_in_context(wlr_renderer); push_gles2_debug(renderer); GLint gl_format = -1, gl_type = -1, alpha_size = -1; glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT, &gl_format); glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE, &gl_type); glGetIntegerv(GL_ALPHA_BITS, &alpha_size); pop_gles2_debug(renderer); const struct wlr_gles2_pixel_format *fmt = get_gles2_format_from_gl(gl_format, gl_type, alpha_size > 0); if (fmt != NULL) { return fmt->drm_format; } if (renderer->exts.EXT_read_format_bgra) { return DRM_FORMAT_XRGB8888; } return DRM_FORMAT_XBGR8888; } static bool gles2_read_pixels(struct wlr_renderer *wlr_renderer, uint32_t drm_format, uint32_t stride, uint32_t width, uint32_t height, uint32_t src_x, uint32_t src_y, uint32_t dst_x, uint32_t dst_y, void *data) { struct wlr_gles2_renderer *renderer = gles2_get_renderer_in_context(wlr_renderer); const struct wlr_gles2_pixel_format *fmt = get_gles2_format_from_drm(drm_format); if (fmt == NULL || !is_gles2_pixel_format_supported(renderer, fmt)) { wlr_log(WLR_ERROR, "Cannot read pixels: unsupported pixel format 0x%"PRIX32, drm_format); return false; } if (fmt->gl_format == GL_BGRA_EXT && !renderer->exts.EXT_read_format_bgra) { wlr_log(WLR_ERROR, "Cannot read pixels: missing GL_EXT_read_format_bgra extension"); return false; } const struct wlr_pixel_format_info *drm_fmt = drm_get_pixel_format_info(fmt->drm_format); assert(drm_fmt); if (pixel_format_info_pixels_per_block(drm_fmt) != 1) { wlr_log(WLR_ERROR, "Cannot read pixels: block formats are not supported"); return false; } push_gles2_debug(renderer); // Make sure any pending drawing is finished before we try to read it glFinish(); glGetError(); // Clear the error flag unsigned char *p = (unsigned char *)data + dst_y * stride; glPixelStorei(GL_PACK_ALIGNMENT, 1); uint32_t pack_stride = pixel_format_info_min_stride(drm_fmt, width); if (pack_stride == stride && dst_x == 0) { // Under these particular conditions, we can read the pixels with only // one glReadPixels call glReadPixels(src_x, src_y, width, height, fmt->gl_format, fmt->gl_type, p); } else { // Unfortunately GLES2 doesn't support GL_PACK_ROW_LENGTH, so we have to read // the lines out row by row for (size_t i = 0; i < height; ++i) { uint32_t y = src_y + i; glReadPixels(src_x, y, width, 1, fmt->gl_format, fmt->gl_type, p + i * stride + dst_x * drm_fmt->bytes_per_block); } } pop_gles2_debug(renderer); return glGetError() == GL_NO_ERROR; } static int gles2_get_drm_fd(struct wlr_renderer *wlr_renderer) { struct wlr_gles2_renderer *renderer = gles2_get_renderer(wlr_renderer); if (renderer->drm_fd < 0) { renderer->drm_fd = wlr_egl_dup_drm_fd(renderer->egl); } return renderer->drm_fd; } static uint32_t gles2_get_render_buffer_caps(struct wlr_renderer *wlr_renderer) { return WLR_BUFFER_CAP_DMABUF; } struct wlr_egl *wlr_gles2_renderer_get_egl(struct wlr_renderer *wlr_renderer) { struct wlr_gles2_renderer *renderer = gles2_get_renderer(wlr_renderer); return renderer->egl; } static void gles2_destroy(struct wlr_renderer *wlr_renderer) { struct wlr_gles2_renderer *renderer = gles2_get_renderer(wlr_renderer); wlr_egl_make_current(renderer->egl); struct wlr_gles2_buffer *buffer, *buffer_tmp; wl_list_for_each_safe(buffer, buffer_tmp, &renderer->buffers, link) { destroy_buffer(buffer); } struct wlr_gles2_texture *tex, *tex_tmp; wl_list_for_each_safe(tex, tex_tmp, &renderer->textures, link) { gles2_texture_destroy(tex); } push_gles2_debug(renderer); glDeleteProgram(renderer->shaders.quad.program); glDeleteProgram(renderer->shaders.tex_rgba.program); glDeleteProgram(renderer->shaders.tex_rgbx.program); glDeleteProgram(renderer->shaders.tex_ext.program); pop_gles2_debug(renderer); if (renderer->exts.KHR_debug) { glDisable(GL_DEBUG_OUTPUT_KHR); renderer->procs.glDebugMessageCallbackKHR(NULL, NULL); } wlr_egl_unset_current(renderer->egl); wlr_egl_destroy(renderer->egl); if (renderer->drm_fd >= 0) { close(renderer->drm_fd); } free(renderer); } static struct wlr_render_pass *gles2_begin_buffer_pass(struct wlr_renderer *wlr_renderer, struct wlr_buffer *wlr_buffer, const struct wlr_buffer_pass_options *options) { struct wlr_gles2_renderer *renderer = gles2_get_renderer(wlr_renderer); if (!wlr_egl_make_current(renderer->egl)) { return NULL; } struct wlr_gles2_render_timer *timer = NULL; if (options->timer) { timer = gles2_get_render_timer(options->timer); clock_gettime(CLOCK_MONOTONIC, &timer->cpu_start); } struct wlr_gles2_buffer *buffer = get_or_create_buffer(renderer, wlr_buffer); if (!buffer) { return NULL; } struct wlr_gles2_render_pass *pass = begin_gles2_buffer_pass(buffer, timer); if (!pass) { return NULL; } return &pass->base; } static struct wlr_render_timer *gles2_render_timer_create(struct wlr_renderer *wlr_renderer) { struct wlr_gles2_renderer *renderer = gles2_get_renderer(wlr_renderer); if (!renderer->exts.EXT_disjoint_timer_query) { wlr_log(WLR_ERROR, "can't create timer, EXT_disjoint_timer_query not available"); return NULL; } struct wlr_gles2_render_timer *timer = calloc(1, sizeof(*timer)); if (!timer) { return NULL; } timer->base.impl = &render_timer_impl; timer->renderer = renderer; struct wlr_egl_context prev_ctx; wlr_egl_save_context(&prev_ctx); wlr_egl_make_current(renderer->egl); renderer->procs.glGenQueriesEXT(1, &timer->id); wlr_egl_restore_context(&prev_ctx); return &timer->base; } static int gles2_get_render_time(struct wlr_render_timer *wlr_timer) { struct wlr_gles2_render_timer *timer = gles2_get_render_timer(wlr_timer); struct wlr_gles2_renderer *renderer = timer->renderer; struct wlr_egl_context prev_ctx; wlr_egl_save_context(&prev_ctx); wlr_egl_make_current(renderer->egl); GLint64 disjoint; renderer->procs.glGetInteger64vEXT(GL_GPU_DISJOINT_EXT, &disjoint); if (disjoint) { wlr_log(WLR_ERROR, "a disjoint operation occurred and the render timer is invalid"); wlr_egl_restore_context(&prev_ctx); return -1; } GLint available; renderer->procs.glGetQueryObjectivEXT(timer->id, GL_QUERY_RESULT_AVAILABLE_EXT, &available); if (!available) { wlr_log(WLR_ERROR, "timer was read too early, gpu isn't done!"); wlr_egl_restore_context(&prev_ctx); return -1; } GLuint64 gl_render_end; renderer->procs.glGetQueryObjectui64vEXT(timer->id, GL_QUERY_RESULT_EXT, &gl_render_end); int64_t cpu_nsec_total = timespec_to_nsec(&timer->cpu_end) - timespec_to_nsec(&timer->cpu_start); wlr_egl_restore_context(&prev_ctx); return gl_render_end - timer->gl_cpu_end + cpu_nsec_total; } static void gles2_render_timer_destroy(struct wlr_render_timer *wlr_timer) { struct wlr_gles2_render_timer *timer = wl_container_of(wlr_timer, timer, base); struct wlr_gles2_renderer *renderer = timer->renderer; struct wlr_egl_context prev_ctx; wlr_egl_save_context(&prev_ctx); wlr_egl_make_current(renderer->egl); renderer->procs.glDeleteQueriesEXT(1, &timer->id); wlr_egl_restore_context(&prev_ctx); free(timer); } static const struct wlr_renderer_impl renderer_impl = { .destroy = gles2_destroy, .bind_buffer = gles2_bind_buffer, .begin = gles2_begin, .end = gles2_end, .clear = gles2_clear, .scissor = gles2_scissor, .render_subtexture_with_matrix = gles2_render_subtexture_with_matrix, .render_quad_with_matrix = gles2_render_quad_with_matrix, .get_shm_texture_formats = gles2_get_shm_texture_formats, .get_dmabuf_texture_formats = gles2_get_dmabuf_texture_formats, .get_render_formats = gles2_get_render_formats, .preferred_read_format = gles2_preferred_read_format, .read_pixels = gles2_read_pixels, .get_drm_fd = gles2_get_drm_fd, .get_render_buffer_caps = gles2_get_render_buffer_caps, .texture_from_buffer = gles2_texture_from_buffer, .begin_buffer_pass = gles2_begin_buffer_pass, .render_timer_create = gles2_render_timer_create, }; static const struct wlr_render_timer_impl render_timer_impl = { .get_duration_ns = gles2_get_render_time, .destroy = gles2_render_timer_destroy, }; void push_gles2_debug_(struct wlr_gles2_renderer *renderer, const char *file, const char *func) { if (!renderer->procs.glPushDebugGroupKHR) { return; } int len = snprintf(NULL, 0, "%s:%s", file, func) + 1; char str[len]; snprintf(str, len, "%s:%s", file, func); renderer->procs.glPushDebugGroupKHR(GL_DEBUG_SOURCE_APPLICATION_KHR, 1, -1, str); } void pop_gles2_debug(struct wlr_gles2_renderer *renderer) { if (renderer->procs.glPopDebugGroupKHR) { renderer->procs.glPopDebugGroupKHR(); } } static enum wlr_log_importance gles2_log_importance_to_wlr(GLenum type) { switch (type) { case GL_DEBUG_TYPE_ERROR_KHR: return WLR_ERROR; case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_KHR: return WLR_DEBUG; case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_KHR: return WLR_ERROR; case GL_DEBUG_TYPE_PORTABILITY_KHR: return WLR_DEBUG; case GL_DEBUG_TYPE_PERFORMANCE_KHR: return WLR_DEBUG; case GL_DEBUG_TYPE_OTHER_KHR: return WLR_DEBUG; case GL_DEBUG_TYPE_MARKER_KHR: return WLR_DEBUG; case GL_DEBUG_TYPE_PUSH_GROUP_KHR: return WLR_DEBUG; case GL_DEBUG_TYPE_POP_GROUP_KHR: return WLR_DEBUG; default: return WLR_DEBUG; } } static void gles2_log(GLenum src, GLenum type, GLuint id, GLenum severity, GLsizei len, const GLchar *msg, const void *user) { _wlr_log(gles2_log_importance_to_wlr(type), "[GLES2] %s", msg); } static GLuint compile_shader(struct wlr_gles2_renderer *renderer, GLenum type, const GLchar *src) { push_gles2_debug(renderer); GLuint shader = glCreateShader(type); glShaderSource(shader, 1, &src, NULL); glCompileShader(shader); GLint ok; glGetShaderiv(shader, GL_COMPILE_STATUS, &ok); if (ok == GL_FALSE) { wlr_log(WLR_ERROR, "Failed to compile shader"); glDeleteShader(shader); shader = 0; } pop_gles2_debug(renderer); return shader; } static GLuint link_program(struct wlr_gles2_renderer *renderer, const GLchar *vert_src, const GLchar *frag_src) { push_gles2_debug(renderer); GLuint vert = compile_shader(renderer, GL_VERTEX_SHADER, vert_src); if (!vert) { goto error; } GLuint frag = compile_shader(renderer, GL_FRAGMENT_SHADER, frag_src); if (!frag) { glDeleteShader(vert); goto error; } GLuint prog = glCreateProgram(); glAttachShader(prog, vert); glAttachShader(prog, frag); glLinkProgram(prog); glDetachShader(prog, vert); glDetachShader(prog, frag); glDeleteShader(vert); glDeleteShader(frag); GLint ok; glGetProgramiv(prog, GL_LINK_STATUS, &ok); if (ok == GL_FALSE) { wlr_log(WLR_ERROR, "Failed to link shader"); glDeleteProgram(prog); goto error; } pop_gles2_debug(renderer); return prog; error: pop_gles2_debug(renderer); return 0; } static bool check_gl_ext(const char *exts, const char *ext) { size_t extlen = strlen(ext); const char *end = exts + strlen(exts); while (exts < end) { if (exts[0] == ' ') { exts++; continue; } size_t n = strcspn(exts, " "); if (n == extlen && strncmp(ext, exts, n) == 0) { return true; } exts += n; } return false; } static void load_gl_proc(void *proc_ptr, const char *name) { void *proc = (void *)eglGetProcAddress(name); if (proc == NULL) { wlr_log(WLR_ERROR, "eglGetProcAddress(%s) failed", name); abort(); } *(void **)proc_ptr = proc; } struct wlr_renderer *wlr_gles2_renderer_create_with_drm_fd(int drm_fd) { struct wlr_egl *egl = wlr_egl_create_with_drm_fd(drm_fd); if (egl == NULL) { wlr_log(WLR_ERROR, "Could not initialize EGL"); return NULL; } struct wlr_renderer *renderer = wlr_gles2_renderer_create(egl); if (!renderer) { wlr_log(WLR_ERROR, "Failed to create GLES2 renderer"); wlr_egl_destroy(egl); return NULL; } return renderer; } struct wlr_renderer *wlr_gles2_renderer_create(struct wlr_egl *egl) { if (!wlr_egl_make_current(egl)) { return NULL; } const char *exts_str = (const char *)glGetString(GL_EXTENSIONS); if (exts_str == NULL) { wlr_log(WLR_ERROR, "Failed to get GL_EXTENSIONS"); return NULL; } struct wlr_gles2_renderer *renderer = calloc(1, sizeof(*renderer)); if (renderer == NULL) { return NULL; } wlr_renderer_init(&renderer->wlr_renderer, &renderer_impl); wl_list_init(&renderer->buffers); wl_list_init(&renderer->textures); renderer->egl = egl; renderer->exts_str = exts_str; renderer->drm_fd = -1; wlr_log(WLR_INFO, "Creating GLES2 renderer"); wlr_log(WLR_INFO, "Using %s", glGetString(GL_VERSION)); wlr_log(WLR_INFO, "GL vendor: %s", glGetString(GL_VENDOR)); wlr_log(WLR_INFO, "GL renderer: %s", glGetString(GL_RENDERER)); wlr_log(WLR_INFO, "Supported GLES2 extensions: %s", exts_str); if (!renderer->egl->exts.EXT_image_dma_buf_import) { wlr_log(WLR_ERROR, "EGL_EXT_image_dma_buf_import not supported"); free(renderer); return NULL; } if (!check_gl_ext(exts_str, "GL_EXT_texture_format_BGRA8888")) { wlr_log(WLR_ERROR, "BGRA8888 format not supported by GLES2"); free(renderer); return NULL; } if (!check_gl_ext(exts_str, "GL_EXT_unpack_subimage")) { wlr_log(WLR_ERROR, "GL_EXT_unpack_subimage not supported"); free(renderer); return NULL; } renderer->exts.EXT_read_format_bgra = check_gl_ext(exts_str, "GL_EXT_read_format_bgra"); renderer->exts.EXT_texture_type_2_10_10_10_REV = check_gl_ext(exts_str, "GL_EXT_texture_type_2_10_10_10_REV"); renderer->exts.OES_texture_half_float_linear = check_gl_ext(exts_str, "GL_OES_texture_half_float_linear"); renderer->exts.EXT_texture_norm16 = check_gl_ext(exts_str, "GL_EXT_texture_norm16"); if (check_gl_ext(exts_str, "GL_KHR_debug")) { renderer->exts.KHR_debug = true; load_gl_proc(&renderer->procs.glDebugMessageCallbackKHR, "glDebugMessageCallbackKHR"); load_gl_proc(&renderer->procs.glDebugMessageControlKHR, "glDebugMessageControlKHR"); } if (check_gl_ext(exts_str, "GL_OES_EGL_image_external")) { renderer->exts.OES_egl_image_external = true; load_gl_proc(&renderer->procs.glEGLImageTargetTexture2DOES, "glEGLImageTargetTexture2DOES"); } if (check_gl_ext(exts_str, "GL_OES_EGL_image")) { renderer->exts.OES_egl_image = true; load_gl_proc(&renderer->procs.glEGLImageTargetRenderbufferStorageOES, "glEGLImageTargetRenderbufferStorageOES"); } if (check_gl_ext(exts_str, "GL_KHR_robustness")) { GLint notif_strategy = 0; glGetIntegerv(GL_RESET_NOTIFICATION_STRATEGY_KHR, ¬if_strategy); switch (notif_strategy) { case GL_LOSE_CONTEXT_ON_RESET_KHR: wlr_log(WLR_DEBUG, "GPU reset notifications are enabled"); load_gl_proc(&renderer->procs.glGetGraphicsResetStatusKHR, "glGetGraphicsResetStatusKHR"); break; case GL_NO_RESET_NOTIFICATION_KHR: wlr_log(WLR_DEBUG, "GPU reset notifications are disabled"); break; } } if (check_gl_ext(exts_str, "GL_EXT_disjoint_timer_query")) { renderer->exts.EXT_disjoint_timer_query = true; load_gl_proc(&renderer->procs.glGenQueriesEXT, "glGenQueriesEXT"); load_gl_proc(&renderer->procs.glDeleteQueriesEXT, "glDeleteQueriesEXT"); load_gl_proc(&renderer->procs.glQueryCounterEXT, "glQueryCounterEXT"); load_gl_proc(&renderer->procs.glGetQueryObjectivEXT, "glGetQueryObjectivEXT"); load_gl_proc(&renderer->procs.glGetQueryObjectui64vEXT, "glGetQueryObjectui64vEXT"); load_gl_proc(&renderer->procs.glGetInteger64vEXT, "glGetInteger64vEXT"); } if (renderer->exts.KHR_debug) { glEnable(GL_DEBUG_OUTPUT_KHR); glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_KHR); renderer->procs.glDebugMessageCallbackKHR(gles2_log, NULL); // Silence unwanted message types renderer->procs.glDebugMessageControlKHR(GL_DONT_CARE, GL_DEBUG_TYPE_POP_GROUP_KHR, GL_DONT_CARE, 0, NULL, GL_FALSE); renderer->procs.glDebugMessageControlKHR(GL_DONT_CARE, GL_DEBUG_TYPE_PUSH_GROUP_KHR, GL_DONT_CARE, 0, NULL, GL_FALSE); } push_gles2_debug(renderer); GLuint prog; renderer->shaders.quad.program = prog = link_program(renderer, common_vert_src, quad_frag_src); if (!renderer->shaders.quad.program) { goto error; } renderer->shaders.quad.proj = glGetUniformLocation(prog, "proj"); renderer->shaders.quad.color = glGetUniformLocation(prog, "color"); renderer->shaders.quad.pos_attrib = glGetAttribLocation(prog, "pos"); renderer->shaders.tex_rgba.program = prog = link_program(renderer, common_vert_src, tex_rgba_frag_src); if (!renderer->shaders.tex_rgba.program) { goto error; } renderer->shaders.tex_rgba.proj = glGetUniformLocation(prog, "proj"); renderer->shaders.tex_rgba.tex_proj = glGetUniformLocation(prog, "tex_proj"); renderer->shaders.tex_rgba.tex = glGetUniformLocation(prog, "tex"); renderer->shaders.tex_rgba.alpha = glGetUniformLocation(prog, "alpha"); renderer->shaders.tex_rgba.pos_attrib = glGetAttribLocation(prog, "pos"); renderer->shaders.tex_rgbx.program = prog = link_program(renderer, common_vert_src, tex_rgbx_frag_src); if (!renderer->shaders.tex_rgbx.program) { goto error; } renderer->shaders.tex_rgbx.proj = glGetUniformLocation(prog, "proj"); renderer->shaders.tex_rgbx.tex_proj = glGetUniformLocation(prog, "tex_proj"); renderer->shaders.tex_rgbx.tex = glGetUniformLocation(prog, "tex"); renderer->shaders.tex_rgbx.alpha = glGetUniformLocation(prog, "alpha"); renderer->shaders.tex_rgbx.pos_attrib = glGetAttribLocation(prog, "pos"); if (renderer->exts.OES_egl_image_external) { renderer->shaders.tex_ext.program = prog = link_program(renderer, common_vert_src, tex_external_frag_src); if (!renderer->shaders.tex_ext.program) { goto error; } renderer->shaders.tex_ext.proj = glGetUniformLocation(prog, "proj"); renderer->shaders.tex_ext.tex_proj = glGetUniformLocation(prog, "tex_proj"); renderer->shaders.tex_ext.tex = glGetUniformLocation(prog, "tex"); renderer->shaders.tex_ext.alpha = glGetUniformLocation(prog, "alpha"); renderer->shaders.tex_ext.pos_attrib = glGetAttribLocation(prog, "pos"); } pop_gles2_debug(renderer); wlr_egl_unset_current(renderer->egl); return &renderer->wlr_renderer; error: glDeleteProgram(renderer->shaders.quad.program); glDeleteProgram(renderer->shaders.tex_rgba.program); glDeleteProgram(renderer->shaders.tex_rgbx.program); glDeleteProgram(renderer->shaders.tex_ext.program); pop_gles2_debug(renderer); if (renderer->exts.KHR_debug) { glDisable(GL_DEBUG_OUTPUT_KHR); renderer->procs.glDebugMessageCallbackKHR(NULL, NULL); } wlr_egl_unset_current(renderer->egl); free(renderer); return NULL; } bool wlr_gles2_renderer_check_ext(struct wlr_renderer *wlr_renderer, const char *ext) { struct wlr_gles2_renderer *renderer = gles2_get_renderer(wlr_renderer); return check_gl_ext(renderer->exts_str, ext); } GLuint wlr_gles2_renderer_get_current_fbo(struct wlr_renderer *wlr_renderer) { struct wlr_gles2_renderer *renderer = gles2_get_renderer(wlr_renderer); assert(renderer->current_buffer); return renderer->current_buffer->fbo; } wlroots-0.17.1/render/gles2/shaders/000077500000000000000000000000001454110342200172315ustar00rootroot00000000000000wlroots-0.17.1/render/gles2/shaders/common.vert000066400000000000000000000003201454110342200214160ustar00rootroot00000000000000uniform mat3 proj; uniform mat3 tex_proj; attribute vec2 pos; varying vec2 v_texcoord; void main() { vec3 pos3 = vec3(pos, 1.0); gl_Position = vec4(pos3 * proj, 1.0); v_texcoord = (pos3 * tex_proj).xy; } wlroots-0.17.1/render/gles2/shaders/embed.sh000077500000000000000000000002411454110342200206410ustar00rootroot00000000000000#!/bin/sh -eu var=${1:-data} hex="$(od -A n -t x1 -v)" echo "static const char $var[] = {" for byte in $hex; do echo " 0x$byte," done echo " 0x00," echo "};" wlroots-0.17.1/render/gles2/shaders/meson.build000066400000000000000000000006131454110342200213730ustar00rootroot00000000000000embed = find_program('./embed.sh', native: true) shaders = [ 'common.vert', 'quad.frag', 'tex_rgba.frag', 'tex_rgbx.frag', 'tex_external.frag', ] foreach name : shaders output = name.underscorify() + '_src.h' var = name.underscorify() + '_src' wlr_files += custom_target( output, command: [embed, var], input: name, output: output, feed: true, capture: true, ) endforeach wlroots-0.17.1/render/gles2/shaders/quad.frag000066400000000000000000000003131454110342200210210ustar00rootroot00000000000000#ifdef GL_FRAGMENT_PRECISION_HIGH precision highp float; #else precision mediump float; #endif varying vec4 v_color; varying vec2 v_texcoord; uniform vec4 color; void main() { gl_FragColor = color; } wlroots-0.17.1/render/gles2/shaders/tex_external.frag000066400000000000000000000004551454110342200226000ustar00rootroot00000000000000#extension GL_OES_EGL_image_external : require #ifdef GL_FRAGMENT_PRECISION_HIGH precision highp float; #else precision mediump float; #endif varying vec2 v_texcoord; uniform samplerExternalOES texture0; uniform float alpha; void main() { gl_FragColor = texture2D(texture0, v_texcoord) * alpha; } wlroots-0.17.1/render/gles2/shaders/tex_rgba.frag000066400000000000000000000003521454110342200216650ustar00rootroot00000000000000#ifdef GL_FRAGMENT_PRECISION_HIGH precision highp float; #else precision mediump float; #endif varying vec2 v_texcoord; uniform sampler2D tex; uniform float alpha; void main() { gl_FragColor = texture2D(tex, v_texcoord) * alpha; } wlroots-0.17.1/render/gles2/shaders/tex_rgbx.frag000066400000000000000000000003711454110342200217150ustar00rootroot00000000000000#ifdef GL_FRAGMENT_PRECISION_HIGH precision highp float; #else precision mediump float; #endif varying vec2 v_texcoord; uniform sampler2D tex; uniform float alpha; void main() { gl_FragColor = vec4(texture2D(tex, v_texcoord).rgb, 1.0) * alpha; } wlroots-0.17.1/render/gles2/texture.c000066400000000000000000000257771454110342200174660ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include "render/egl.h" #include "render/gles2.h" #include "render/pixel_format.h" #include "types/wlr_buffer.h" static const struct wlr_texture_impl texture_impl; bool wlr_texture_is_gles2(struct wlr_texture *wlr_texture) { return wlr_texture->impl == &texture_impl; } struct wlr_gles2_texture *gles2_get_texture( struct wlr_texture *wlr_texture) { assert(wlr_texture_is_gles2(wlr_texture)); struct wlr_gles2_texture *texture = wl_container_of(wlr_texture, texture, wlr_texture); return texture; } static bool gles2_texture_update_from_buffer(struct wlr_texture *wlr_texture, struct wlr_buffer *buffer, const pixman_region32_t *damage) { struct wlr_gles2_texture *texture = gles2_get_texture(wlr_texture); if (texture->target != GL_TEXTURE_2D || texture->image != EGL_NO_IMAGE_KHR) { return false; } void *data; uint32_t format; size_t stride; if (!wlr_buffer_begin_data_ptr_access(buffer, WLR_BUFFER_DATA_PTR_ACCESS_READ, &data, &format, &stride)) { return false; } if (format != texture->drm_format) { wlr_buffer_end_data_ptr_access(buffer); return false; } const struct wlr_gles2_pixel_format *fmt = get_gles2_format_from_drm(texture->drm_format); assert(fmt); const struct wlr_pixel_format_info *drm_fmt = drm_get_pixel_format_info(texture->drm_format); assert(drm_fmt); if (pixel_format_info_pixels_per_block(drm_fmt) != 1) { wlr_buffer_end_data_ptr_access(buffer); wlr_log(WLR_ERROR, "Cannot update texture: block formats are not supported"); return false; } if (!pixel_format_info_check_stride(drm_fmt, stride, buffer->width)) { wlr_buffer_end_data_ptr_access(buffer); return false; } struct wlr_egl_context prev_ctx; wlr_egl_save_context(&prev_ctx); wlr_egl_make_current(texture->renderer->egl); push_gles2_debug(texture->renderer); glBindTexture(GL_TEXTURE_2D, texture->tex); int rects_len = 0; const pixman_box32_t *rects = pixman_region32_rectangles(damage, &rects_len); for (int i = 0; i < rects_len; i++) { pixman_box32_t rect = rects[i]; glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, stride / drm_fmt->bytes_per_block); glPixelStorei(GL_UNPACK_SKIP_PIXELS_EXT, rect.x1); glPixelStorei(GL_UNPACK_SKIP_ROWS_EXT, rect.y1); int width = rect.x2 - rect.x1; int height = rect.y2 - rect.y1; glTexSubImage2D(GL_TEXTURE_2D, 0, rect.x1, rect.y1, width, height, fmt->gl_format, fmt->gl_type, data); } glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, 0); glPixelStorei(GL_UNPACK_SKIP_PIXELS_EXT, 0); glPixelStorei(GL_UNPACK_SKIP_ROWS_EXT, 0); glBindTexture(GL_TEXTURE_2D, 0); pop_gles2_debug(texture->renderer); wlr_egl_restore_context(&prev_ctx); wlr_buffer_end_data_ptr_access(buffer); return true; } static bool gles2_texture_invalidate(struct wlr_gles2_texture *texture) { if (texture->image == EGL_NO_IMAGE_KHR) { return false; } if (texture->target == GL_TEXTURE_EXTERNAL_OES) { // External changes are immediately made visible by the GL implementation return true; } struct wlr_egl_context prev_ctx; wlr_egl_save_context(&prev_ctx); wlr_egl_make_current(texture->renderer->egl); push_gles2_debug(texture->renderer); glBindTexture(texture->target, texture->tex); texture->renderer->procs.glEGLImageTargetTexture2DOES(texture->target, texture->image); glBindTexture(texture->target, 0); pop_gles2_debug(texture->renderer); wlr_egl_restore_context(&prev_ctx); return true; } void gles2_texture_destroy(struct wlr_gles2_texture *texture) { wl_list_remove(&texture->link); if (texture->buffer != NULL) { wlr_addon_finish(&texture->buffer_addon); } struct wlr_egl_context prev_ctx; wlr_egl_save_context(&prev_ctx); wlr_egl_make_current(texture->renderer->egl); push_gles2_debug(texture->renderer); glDeleteTextures(1, &texture->tex); wlr_egl_destroy_image(texture->renderer->egl, texture->image); pop_gles2_debug(texture->renderer); wlr_egl_restore_context(&prev_ctx); free(texture); } static void gles2_texture_unref(struct wlr_texture *wlr_texture) { struct wlr_gles2_texture *texture = gles2_get_texture(wlr_texture); if (texture->buffer != NULL) { // Keep the texture around, in case the buffer is re-used later. We're // still listening to the buffer's destroy event. wlr_buffer_unlock(texture->buffer); } else { gles2_texture_destroy(texture); } } static const struct wlr_texture_impl texture_impl = { .update_from_buffer = gles2_texture_update_from_buffer, .destroy = gles2_texture_unref, }; static struct wlr_gles2_texture *gles2_texture_create( struct wlr_gles2_renderer *renderer, uint32_t width, uint32_t height) { struct wlr_gles2_texture *texture = calloc(1, sizeof(*texture)); if (texture == NULL) { wlr_log_errno(WLR_ERROR, "Allocation failed"); return NULL; } wlr_texture_init(&texture->wlr_texture, &renderer->wlr_renderer, &texture_impl, width, height); texture->renderer = renderer; wl_list_insert(&renderer->textures, &texture->link); return texture; } static struct wlr_texture *gles2_texture_from_pixels( struct wlr_renderer *wlr_renderer, uint32_t drm_format, uint32_t stride, uint32_t width, uint32_t height, const void *data) { struct wlr_gles2_renderer *renderer = gles2_get_renderer(wlr_renderer); const struct wlr_gles2_pixel_format *fmt = get_gles2_format_from_drm(drm_format); if (fmt == NULL) { wlr_log(WLR_ERROR, "Unsupported pixel format 0x%"PRIX32, drm_format); return NULL; } const struct wlr_pixel_format_info *drm_fmt = drm_get_pixel_format_info(drm_format); assert(drm_fmt); if (pixel_format_info_pixels_per_block(drm_fmt) != 1) { wlr_log(WLR_ERROR, "Cannot upload texture: block formats are not supported"); return NULL; } if (!pixel_format_info_check_stride(drm_fmt, stride, width)) { return NULL; } struct wlr_gles2_texture *texture = gles2_texture_create(renderer, width, height); if (texture == NULL) { return NULL; } texture->target = GL_TEXTURE_2D; texture->has_alpha = fmt->has_alpha; texture->drm_format = fmt->drm_format; GLint internal_format = fmt->gl_internalformat; if (!internal_format) { internal_format = fmt->gl_format; } struct wlr_egl_context prev_ctx; wlr_egl_save_context(&prev_ctx); wlr_egl_make_current(renderer->egl); push_gles2_debug(renderer); glGenTextures(1, &texture->tex); glBindTexture(GL_TEXTURE_2D, texture->tex); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, stride / drm_fmt->bytes_per_block); glTexImage2D(GL_TEXTURE_2D, 0, internal_format, width, height, 0, fmt->gl_format, fmt->gl_type, data); glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, 0); glBindTexture(GL_TEXTURE_2D, 0); pop_gles2_debug(renderer); wlr_egl_restore_context(&prev_ctx); return &texture->wlr_texture; } static struct wlr_texture *gles2_texture_from_dmabuf( struct wlr_renderer *wlr_renderer, struct wlr_dmabuf_attributes *attribs) { struct wlr_gles2_renderer *renderer = gles2_get_renderer(wlr_renderer); if (!renderer->procs.glEGLImageTargetTexture2DOES) { return NULL; } struct wlr_gles2_texture *texture = gles2_texture_create(renderer, attribs->width, attribs->height); if (texture == NULL) { return NULL; } texture->drm_format = DRM_FORMAT_INVALID; // texture can't be written anyways const struct wlr_pixel_format_info *drm_fmt = drm_get_pixel_format_info(attribs->format); if (drm_fmt != NULL) { texture->has_alpha = drm_fmt->has_alpha; } else { // We don't know, assume the texture has an alpha channel texture->has_alpha = true; } struct wlr_egl_context prev_ctx; wlr_egl_save_context(&prev_ctx); wlr_egl_make_current(renderer->egl); bool external_only; texture->image = wlr_egl_create_image_from_dmabuf(renderer->egl, attribs, &external_only); if (texture->image == EGL_NO_IMAGE_KHR) { wlr_log(WLR_ERROR, "Failed to create EGL image from DMA-BUF"); wlr_egl_restore_context(&prev_ctx); wl_list_remove(&texture->link); free(texture); return NULL; } texture->target = external_only ? GL_TEXTURE_EXTERNAL_OES : GL_TEXTURE_2D; push_gles2_debug(renderer); glGenTextures(1, &texture->tex); glBindTexture(texture->target, texture->tex); glTexParameteri(texture->target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(texture->target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); renderer->procs.glEGLImageTargetTexture2DOES(texture->target, texture->image); glBindTexture(texture->target, 0); pop_gles2_debug(renderer); wlr_egl_restore_context(&prev_ctx); return &texture->wlr_texture; } static void texture_handle_buffer_destroy(struct wlr_addon *addon) { struct wlr_gles2_texture *texture = wl_container_of(addon, texture, buffer_addon); gles2_texture_destroy(texture); } static const struct wlr_addon_interface texture_addon_impl = { .name = "wlr_gles2_texture", .destroy = texture_handle_buffer_destroy, }; static struct wlr_texture *gles2_texture_from_dmabuf_buffer( struct wlr_gles2_renderer *renderer, struct wlr_buffer *buffer, struct wlr_dmabuf_attributes *dmabuf) { struct wlr_addon *addon = wlr_addon_find(&buffer->addons, renderer, &texture_addon_impl); if (addon != NULL) { struct wlr_gles2_texture *texture = wl_container_of(addon, texture, buffer_addon); if (!gles2_texture_invalidate(texture)) { wlr_log(WLR_ERROR, "Failed to invalidate texture"); return false; } wlr_buffer_lock(texture->buffer); return &texture->wlr_texture; } struct wlr_texture *wlr_texture = gles2_texture_from_dmabuf(&renderer->wlr_renderer, dmabuf); if (wlr_texture == NULL) { return false; } struct wlr_gles2_texture *texture = gles2_get_texture(wlr_texture); texture->buffer = wlr_buffer_lock(buffer); wlr_addon_init(&texture->buffer_addon, &buffer->addons, renderer, &texture_addon_impl); return &texture->wlr_texture; } struct wlr_texture *gles2_texture_from_buffer(struct wlr_renderer *wlr_renderer, struct wlr_buffer *buffer) { struct wlr_gles2_renderer *renderer = gles2_get_renderer(wlr_renderer); void *data; uint32_t format; size_t stride; struct wlr_dmabuf_attributes dmabuf; if (wlr_buffer_get_dmabuf(buffer, &dmabuf)) { return gles2_texture_from_dmabuf_buffer(renderer, buffer, &dmabuf); } else if (wlr_buffer_begin_data_ptr_access(buffer, WLR_BUFFER_DATA_PTR_ACCESS_READ, &data, &format, &stride)) { struct wlr_texture *tex = gles2_texture_from_pixels(wlr_renderer, format, stride, buffer->width, buffer->height, data); wlr_buffer_end_data_ptr_access(buffer); return tex; } else { return NULL; } } void wlr_gles2_texture_get_attribs(struct wlr_texture *wlr_texture, struct wlr_gles2_texture_attribs *attribs) { struct wlr_gles2_texture *texture = gles2_get_texture(wlr_texture); *attribs = (struct wlr_gles2_texture_attribs){ .target = texture->target, .tex = texture->tex, .has_alpha = texture->has_alpha, }; } wlroots-0.17.1/render/meson.build000066400000000000000000000017001454110342200167240ustar00rootroot00000000000000renderers = get_option('renderers') if 'auto' in renderers and get_option('auto_features').enabled() renderers = ['gles2', 'vulkan'] elif 'auto' in renderers and get_option('auto_features').disabled() renderers = [] endif wlr_files += files( 'dmabuf.c', 'drm_format_set.c', 'pass.c', 'pixel_format.c', 'swapchain.c', 'wlr_renderer.c', 'wlr_texture.c', ) if cc.has_header('linux/dma-buf.h') and target_machine.system() == 'linux' wlr_files += files('dmabuf_linux.c') else wlr_files += files('dmabuf_fallback.c') endif if 'gles2' in renderers or 'auto' in renderers egl = dependency('egl', required: 'gles2' in renderers) gbm = dependency('gbm', required: 'gles2' in renderers) if egl.found() and gbm.found() wlr_deps += [egl, gbm] wlr_files += files('egl.c') internal_features += { 'egl': true } endif subdir('gles2') endif if 'vulkan' in renderers or 'auto' in renderers subdir('vulkan') endif subdir('pixman') subdir('allocator') wlroots-0.17.1/render/pass.c000066400000000000000000000144161454110342200157040ustar00rootroot00000000000000#include #include #include #include #include #include #include "render/pass.h" struct wlr_render_pass_legacy { struct wlr_render_pass base; struct wlr_renderer *renderer; int width, height; }; void wlr_render_pass_init(struct wlr_render_pass *render_pass, const struct wlr_render_pass_impl *impl) { assert(impl->submit && impl->add_texture && impl->add_rect); *render_pass = (struct wlr_render_pass){ .impl = impl, }; } bool wlr_render_pass_submit(struct wlr_render_pass *render_pass) { return render_pass->impl->submit(render_pass); } void wlr_render_pass_add_texture(struct wlr_render_pass *render_pass, const struct wlr_render_texture_options *options) { // make sure the texture source box does not try and sample outside of the // texture if (!wlr_fbox_empty(&options->src_box)) { const struct wlr_fbox *box = &options->src_box; assert(box->x >= 0 && box->y >= 0 && box->x + box->width <= options->texture->width && box->y + box->height <= options->texture->height); } render_pass->impl->add_texture(render_pass, options); } void wlr_render_pass_add_rect(struct wlr_render_pass *render_pass, const struct wlr_render_rect_options *options) { assert(options->box.width >= 0 && options->box.height >= 0); render_pass->impl->add_rect(render_pass, options); } void wlr_render_texture_options_get_src_box(const struct wlr_render_texture_options *options, struct wlr_fbox *box) { *box = options->src_box; if (wlr_fbox_empty(box)) { *box = (struct wlr_fbox){ .width = options->texture->width, .height = options->texture->height, }; } } void wlr_render_texture_options_get_dst_box(const struct wlr_render_texture_options *options, struct wlr_box *box) { *box = options->dst_box; if (wlr_box_empty(box)) { box->width = options->texture->width; box->height = options->texture->height; } } float wlr_render_texture_options_get_alpha(const struct wlr_render_texture_options *options) { if (options->alpha == NULL) { return 1; } return *options->alpha; } void wlr_render_rect_options_get_box(const struct wlr_render_rect_options *options, const struct wlr_buffer *buffer, struct wlr_box *box) { if (wlr_box_empty(&options->box)) { *box = (struct wlr_box){ .width = buffer->width, .height = buffer->height, }; return; } *box = options->box; } static const struct wlr_render_pass_impl legacy_impl; static struct wlr_render_pass_legacy *legacy_pass_from_pass(struct wlr_render_pass *pass) { assert(pass->impl == &legacy_impl); struct wlr_render_pass_legacy *legacy = wl_container_of(pass, legacy, base); return legacy; } static bool legacy_submit(struct wlr_render_pass *wlr_pass) { struct wlr_render_pass_legacy *pass = legacy_pass_from_pass(wlr_pass); wlr_renderer_end(pass->renderer); free(pass); return true; } static void get_clip_region(struct wlr_render_pass_legacy *pass, const pixman_region32_t *in, pixman_region32_t *out) { if (in != NULL) { pixman_region32_init(out); pixman_region32_copy(out, in); } else { pixman_region32_init_rect(out, 0, 0, pass->width, pass->height); } } static void scissor(struct wlr_renderer *renderer, const pixman_box32_t *rect) { struct wlr_box box = { .x = rect->x1, .y = rect->y1, .width = rect->x2 - rect->x1, .height = rect->y2 - rect->y1, }; wlr_renderer_scissor(renderer, &box); } static void legacy_add_texture(struct wlr_render_pass *wlr_pass, const struct wlr_render_texture_options *options) { struct wlr_render_pass_legacy *pass = legacy_pass_from_pass(wlr_pass); struct wlr_texture *texture = options->texture; struct wlr_fbox src_box; wlr_render_texture_options_get_src_box(options, &src_box); struct wlr_box dst_box; wlr_render_texture_options_get_dst_box(options, &dst_box); float alpha = wlr_render_texture_options_get_alpha(options); float proj[9], matrix[9]; wlr_matrix_identity(proj); wlr_matrix_project_box(matrix, &dst_box, options->transform, 0.0, proj); pixman_region32_t clip; get_clip_region(pass, options->clip, &clip); float black[4] = {0}; int rects_len = 0; const pixman_box32_t *rects = pixman_region32_rectangles(&clip, &rects_len); for (int i = 0; i < rects_len; i++) { scissor(pass->renderer, &rects[i]); if (options->blend_mode == WLR_RENDER_BLEND_MODE_NONE) { wlr_renderer_clear(pass->renderer, black); } wlr_render_subtexture_with_matrix(pass->renderer, texture, &src_box, matrix, alpha); } wlr_renderer_scissor(pass->renderer, NULL); pixman_region32_fini(&clip); } static void legacy_add_rect(struct wlr_render_pass *wlr_pass, const struct wlr_render_rect_options *options) { struct wlr_render_pass_legacy *pass = legacy_pass_from_pass(wlr_pass); float proj[9], matrix[9]; wlr_matrix_identity(proj); wlr_matrix_project_box(matrix, &options->box, WL_OUTPUT_TRANSFORM_NORMAL, 0.0, proj); pixman_region32_t clip; get_clip_region(pass, options->clip, &clip); pixman_region32_intersect_rect(&clip, &clip, options->box.x, options->box.y, options->box.width, options->box.height); float color[4] = { options->color.r, options->color.g, options->color.b, options->color.a, }; int rects_len = 0; const pixman_box32_t *rects = pixman_region32_rectangles(&clip, &rects_len); for (int i = 0; i < rects_len; i++) { scissor(pass->renderer, &rects[i]); switch (options->blend_mode) { case WLR_RENDER_BLEND_MODE_PREMULTIPLIED: wlr_render_quad_with_matrix(pass->renderer, color, matrix); break; case WLR_RENDER_BLEND_MODE_NONE: wlr_renderer_clear(pass->renderer, color); break; } } wlr_renderer_scissor(pass->renderer, NULL); pixman_region32_fini(&clip); } static const struct wlr_render_pass_impl legacy_impl = { .submit = legacy_submit, .add_texture = legacy_add_texture, .add_rect = legacy_add_rect, }; struct wlr_render_pass *begin_legacy_buffer_render_pass(struct wlr_renderer *renderer, struct wlr_buffer *buffer) { if (renderer->rendering) { return NULL; } struct wlr_render_pass_legacy *pass = calloc(1, sizeof(*pass)); if (pass == NULL) { return NULL; } wlr_render_pass_init(&pass->base, &legacy_impl); pass->renderer = renderer; pass->width = buffer->width; pass->height = buffer->height; if (!wlr_renderer_begin_with_buffer(renderer, buffer)) { free(pass); return NULL; } return &pass->base; } wlroots-0.17.1/render/pixel_format.c000066400000000000000000000130671454110342200174300ustar00rootroot00000000000000#include #include #include #include "render/pixel_format.h" static const struct wlr_pixel_format_info pixel_format_info[] = { { .drm_format = DRM_FORMAT_XRGB8888, .bytes_per_block = 4, }, { .drm_format = DRM_FORMAT_ARGB8888, .opaque_substitute = DRM_FORMAT_XRGB8888, .bytes_per_block = 4, .has_alpha = true, }, { .drm_format = DRM_FORMAT_XBGR8888, .bytes_per_block = 4, }, { .drm_format = DRM_FORMAT_ABGR8888, .opaque_substitute = DRM_FORMAT_XBGR8888, .bytes_per_block = 4, .has_alpha = true, }, { .drm_format = DRM_FORMAT_RGBX8888, .bytes_per_block = 4, }, { .drm_format = DRM_FORMAT_RGBA8888, .opaque_substitute = DRM_FORMAT_RGBX8888, .bytes_per_block = 4, .has_alpha = true, }, { .drm_format = DRM_FORMAT_BGRX8888, .bytes_per_block = 4, }, { .drm_format = DRM_FORMAT_BGRA8888, .opaque_substitute = DRM_FORMAT_BGRX8888, .bytes_per_block = 4, .has_alpha = true, }, { .drm_format = DRM_FORMAT_R8, .bytes_per_block = 1, }, { .drm_format = DRM_FORMAT_GR88, .bytes_per_block = 2, }, { .drm_format = DRM_FORMAT_RGB888, .bytes_per_block = 3, }, { .drm_format = DRM_FORMAT_BGR888, .bytes_per_block = 3, }, { .drm_format = DRM_FORMAT_RGBX4444, .bytes_per_block = 2, }, { .drm_format = DRM_FORMAT_RGBA4444, .opaque_substitute = DRM_FORMAT_RGBX4444, .bytes_per_block = 2, .has_alpha = true, }, { .drm_format = DRM_FORMAT_BGRX4444, .bytes_per_block = 2, }, { .drm_format = DRM_FORMAT_BGRA4444, .opaque_substitute = DRM_FORMAT_BGRX4444, .bytes_per_block = 2, .has_alpha = true, }, { .drm_format = DRM_FORMAT_RGBX5551, .bytes_per_block = 2, }, { .drm_format = DRM_FORMAT_RGBA5551, .opaque_substitute = DRM_FORMAT_RGBX5551, .bytes_per_block = 2, .has_alpha = true, }, { .drm_format = DRM_FORMAT_BGRX5551, .bytes_per_block = 2, }, { .drm_format = DRM_FORMAT_BGRA5551, .opaque_substitute = DRM_FORMAT_BGRX5551, .bytes_per_block = 2, .has_alpha = true, }, { .drm_format = DRM_FORMAT_XRGB1555, .bytes_per_block = 2, }, { .drm_format = DRM_FORMAT_ARGB1555, .opaque_substitute = DRM_FORMAT_XRGB1555, .bytes_per_block = 2, .has_alpha = true, }, { .drm_format = DRM_FORMAT_RGB565, .bytes_per_block = 2, }, { .drm_format = DRM_FORMAT_BGR565, .bytes_per_block = 2, }, { .drm_format = DRM_FORMAT_XRGB2101010, .bytes_per_block = 4, }, { .drm_format = DRM_FORMAT_ARGB2101010, .opaque_substitute = DRM_FORMAT_XRGB2101010, .bytes_per_block = 4, .has_alpha = true, }, { .drm_format = DRM_FORMAT_XBGR2101010, .bytes_per_block = 4, }, { .drm_format = DRM_FORMAT_ABGR2101010, .opaque_substitute = DRM_FORMAT_XBGR2101010, .bytes_per_block = 4, .has_alpha = true, }, { .drm_format = DRM_FORMAT_XBGR16161616F, .bytes_per_block = 8, }, { .drm_format = DRM_FORMAT_ABGR16161616F, .opaque_substitute = DRM_FORMAT_XBGR16161616F, .bytes_per_block = 8, .has_alpha = true, }, { .drm_format = DRM_FORMAT_XBGR16161616, .bytes_per_block = 8, }, { .drm_format = DRM_FORMAT_ABGR16161616, .opaque_substitute = DRM_FORMAT_XBGR16161616, .bytes_per_block = 8, .has_alpha = true, }, { .drm_format = DRM_FORMAT_YVYU, .bytes_per_block = 4, .block_width = 2, .block_height = 1, }, { .drm_format = DRM_FORMAT_VYUY, .bytes_per_block = 4, .block_width = 2, .block_height = 1, }, }; static const size_t pixel_format_info_size = sizeof(pixel_format_info) / sizeof(pixel_format_info[0]); const struct wlr_pixel_format_info *drm_get_pixel_format_info(uint32_t fmt) { for (size_t i = 0; i < pixel_format_info_size; ++i) { if (pixel_format_info[i].drm_format == fmt) { return &pixel_format_info[i]; } } return NULL; } uint32_t convert_wl_shm_format_to_drm(enum wl_shm_format fmt) { switch (fmt) { case WL_SHM_FORMAT_XRGB8888: return DRM_FORMAT_XRGB8888; case WL_SHM_FORMAT_ARGB8888: return DRM_FORMAT_ARGB8888; default: return (uint32_t)fmt; } } enum wl_shm_format convert_drm_format_to_wl_shm(uint32_t fmt) { switch (fmt) { case DRM_FORMAT_XRGB8888: return WL_SHM_FORMAT_XRGB8888; case DRM_FORMAT_ARGB8888: return WL_SHM_FORMAT_ARGB8888; default: return (enum wl_shm_format)fmt; } } uint32_t pixel_format_info_pixels_per_block(const struct wlr_pixel_format_info *info) { uint32_t pixels = info->block_width * info->block_height; return pixels > 0 ? pixels : 1; } static int32_t div_round_up(int32_t dividend, int32_t divisor) { int32_t quotient = dividend / divisor; if (dividend % divisor != 0) { quotient++; } return quotient; } int32_t pixel_format_info_min_stride(const struct wlr_pixel_format_info *fmt, int32_t width) { int32_t pixels_per_block = (int32_t)pixel_format_info_pixels_per_block(fmt); int32_t bytes_per_block = (int32_t)fmt->bytes_per_block; if (width > INT32_MAX / bytes_per_block) { wlr_log(WLR_DEBUG, "Invalid width %d (overflow)", width); return 0; } return div_round_up(width * bytes_per_block, pixels_per_block); } bool pixel_format_info_check_stride(const struct wlr_pixel_format_info *fmt, int32_t stride, int32_t width) { int32_t bytes_per_block = (int32_t)fmt->bytes_per_block; if (stride % bytes_per_block != 0) { wlr_log(WLR_DEBUG, "Invalid stride %d (incompatible with %d " "bytes-per-block)", stride, bytes_per_block); return false; } int32_t min_stride = pixel_format_info_min_stride(fmt, width); if (min_stride <= 0) { return false; } else if (stride < min_stride) { wlr_log(WLR_DEBUG, "Invalid stride %d (too small for %d " "bytes-per-block and width %d)", stride, bytes_per_block, width); return false; } return true; } wlroots-0.17.1/render/pixman/000077500000000000000000000000001454110342200160605ustar00rootroot00000000000000wlroots-0.17.1/render/pixman/meson.build000066400000000000000000000001701454110342200202200ustar00rootroot00000000000000pixman = dependency('pixman-1') wlr_deps += pixman wlr_files += files( 'pass.c', 'pixel_format.c', 'renderer.c', ) wlroots-0.17.1/render/pixman/pass.c000066400000000000000000000147571454110342200172100ustar00rootroot00000000000000#include #include #include "render/pixman.h" static const struct wlr_render_pass_impl render_pass_impl; static struct wlr_pixman_render_pass *get_render_pass(struct wlr_render_pass *wlr_pass) { assert(wlr_pass->impl == &render_pass_impl); struct wlr_pixman_render_pass *pass = wl_container_of(wlr_pass, pass, base); return pass; } static struct wlr_pixman_texture *get_texture(struct wlr_texture *wlr_texture) { assert(wlr_texture_is_pixman(wlr_texture)); struct wlr_pixman_texture *texture = wl_container_of(wlr_texture, texture, wlr_texture); return texture; } static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { struct wlr_pixman_render_pass *pass = get_render_pass(wlr_pass); wlr_buffer_end_data_ptr_access(pass->buffer->buffer); wlr_buffer_unlock(pass->buffer->buffer); free(pass); return true; } static pixman_op_t get_pixman_blending(enum wlr_render_blend_mode mode) { switch (mode) { case WLR_RENDER_BLEND_MODE_PREMULTIPLIED: return PIXMAN_OP_OVER; case WLR_RENDER_BLEND_MODE_NONE: return PIXMAN_OP_SRC; } abort(); } static void render_pass_add_texture(struct wlr_render_pass *wlr_pass, const struct wlr_render_texture_options *options) { struct wlr_pixman_render_pass *pass = get_render_pass(wlr_pass); struct wlr_pixman_texture *texture = get_texture(options->texture); struct wlr_pixman_buffer *buffer = pass->buffer; if (texture->buffer != NULL && !begin_pixman_data_ptr_access(texture->buffer, &texture->image, WLR_BUFFER_DATA_PTR_ACCESS_READ)) { return; } struct wlr_fbox src_fbox; wlr_render_texture_options_get_src_box(options, &src_fbox); struct wlr_box src_box = { .x = roundf(src_fbox.x), .y = roundf(src_fbox.y), .width = roundf(src_fbox.width), .height = roundf(src_fbox.height), }; struct wlr_box dst_box; wlr_render_texture_options_get_dst_box(options, &dst_box); pixman_image_t *mask = NULL; float alpha = wlr_render_texture_options_get_alpha(options); if (alpha != 1) { mask = pixman_image_create_solid_fill(&(struct pixman_color){ .alpha = 0xFFFF * alpha, }); } struct wlr_box orig_box; wlr_box_transform(&orig_box, &dst_box, options->transform, buffer->buffer->width, buffer->buffer->height); int32_t dest_x, dest_y, width, height; if (options->transform != WL_OUTPUT_TRANSFORM_NORMAL || orig_box.width != src_box.width || orig_box.height != src_box.height) { // Cosinus/sinus values are extact integers for enum wl_output_transform entries int tr_cos = 1, tr_sin = 0, tr_x = 0, tr_y = 0; switch (options->transform) { case WL_OUTPUT_TRANSFORM_NORMAL: case WL_OUTPUT_TRANSFORM_FLIPPED: break; case WL_OUTPUT_TRANSFORM_90: case WL_OUTPUT_TRANSFORM_FLIPPED_90: tr_cos = 0; tr_sin = 1; tr_x = buffer->buffer->height; break; case WL_OUTPUT_TRANSFORM_180: case WL_OUTPUT_TRANSFORM_FLIPPED_180: tr_cos = -1; tr_sin = 0; tr_x = buffer->buffer->width; tr_y = buffer->buffer->height; break; case WL_OUTPUT_TRANSFORM_270: case WL_OUTPUT_TRANSFORM_FLIPPED_270: tr_cos = 0; tr_sin = -1; tr_y = buffer->buffer->width; break; } struct pixman_transform transform; pixman_transform_init_identity(&transform); pixman_transform_rotate(&transform, NULL, pixman_int_to_fixed(tr_cos), pixman_int_to_fixed(tr_sin)); if (options->transform >= WL_OUTPUT_TRANSFORM_FLIPPED) { pixman_transform_scale(&transform, NULL, pixman_int_to_fixed(-1), pixman_int_to_fixed(1)); } pixman_transform_translate(&transform, NULL, pixman_int_to_fixed(tr_x), pixman_int_to_fixed(tr_y)); pixman_transform_translate(&transform, NULL, -pixman_int_to_fixed(orig_box.x), -pixman_int_to_fixed(orig_box.y)); pixman_transform_scale(&transform, NULL, pixman_double_to_fixed(src_box.width / (double)orig_box.width), pixman_double_to_fixed(src_box.height / (double)orig_box.height)); pixman_image_set_transform(texture->image, &transform); dest_x = dest_y = 0; width = buffer->buffer->width; height = buffer->buffer->height; } else { pixman_image_set_transform(texture->image, NULL); dest_x = dst_box.x; dest_y = dst_box.y; width = src_box.width; height = src_box.height; } switch (options->filter_mode) { case WLR_SCALE_FILTER_BILINEAR: pixman_image_set_filter(texture->image, PIXMAN_FILTER_BILINEAR, NULL, 0); break; case WLR_SCALE_FILTER_NEAREST: pixman_image_set_filter(texture->image, PIXMAN_FILTER_NEAREST, NULL, 0); break; } pixman_op_t op = get_pixman_blending(options->blend_mode); pixman_image_set_clip_region32(buffer->image, (pixman_region32_t *)options->clip); pixman_image_composite32(op, texture->image, mask, buffer->image, src_box.x, src_box.y, 0, 0, dest_x, dest_y, width, height); pixman_image_set_clip_region32(buffer->image, NULL); pixman_image_set_transform(texture->image, NULL); if (texture->buffer != NULL) { wlr_buffer_end_data_ptr_access(texture->buffer); } if (mask != NULL) { pixman_image_unref(mask); } } static void render_pass_add_rect(struct wlr_render_pass *wlr_pass, const struct wlr_render_rect_options *options) { struct wlr_pixman_render_pass *pass = get_render_pass(wlr_pass); struct wlr_pixman_buffer *buffer = pass->buffer; struct wlr_box box; wlr_render_rect_options_get_box(options, pass->buffer->buffer, &box); pixman_op_t op = get_pixman_blending(options->color.a == 1 ? WLR_RENDER_BLEND_MODE_NONE : options->blend_mode); struct pixman_color color = { .red = options->color.r * 0xFFFF, .green = options->color.g * 0xFFFF, .blue = options->color.b * 0xFFFF, .alpha = options->color.a * 0xFFFF, }; pixman_image_t *fill = pixman_image_create_solid_fill(&color); pixman_image_set_clip_region32(buffer->image, (pixman_region32_t *)options->clip); pixman_image_composite32(op, fill, NULL, buffer->image, 0, 0, 0, 0, box.x, box.y, box.width, box.height); pixman_image_set_clip_region32(buffer->image, NULL); pixman_image_unref(fill); } static const struct wlr_render_pass_impl render_pass_impl = { .submit = render_pass_submit, .add_texture = render_pass_add_texture, .add_rect = render_pass_add_rect, }; struct wlr_pixman_render_pass *begin_pixman_render_pass( struct wlr_pixman_buffer *buffer) { struct wlr_pixman_render_pass *pass = calloc(1, sizeof(*pass)); if (pass == NULL) { return NULL; } wlr_render_pass_init(&pass->base, &render_pass_impl); if (!begin_pixman_data_ptr_access(buffer->buffer, &buffer->image, WLR_BUFFER_DATA_PTR_ACCESS_READ | WLR_BUFFER_DATA_PTR_ACCESS_WRITE)) { free(pass); return NULL; } wlr_buffer_lock(buffer->buffer); pass->buffer = buffer; return pass; } wlroots-0.17.1/render/pixman/pixel_format.c000066400000000000000000000055701454110342200207240ustar00rootroot00000000000000#include #include #include "render/pixman.h" static const struct wlr_pixman_pixel_format formats[] = { { .drm_format = DRM_FORMAT_ARGB8888, #if WLR_BIG_ENDIAN .pixman_format = PIXMAN_b8g8r8a8, #else .pixman_format = PIXMAN_a8r8g8b8, #endif }, { .drm_format = DRM_FORMAT_XBGR8888, #if WLR_BIG_ENDIAN .pixman_format = PIXMAN_r8g8b8x8, #else .pixman_format = PIXMAN_x8b8g8r8, #endif }, { .drm_format = DRM_FORMAT_XRGB8888, #if WLR_BIG_ENDIAN .pixman_format = PIXMAN_b8g8r8x8, #else .pixman_format = PIXMAN_x8r8g8b8, #endif }, { .drm_format = DRM_FORMAT_ABGR8888, #if WLR_BIG_ENDIAN .pixman_format = PIXMAN_r8g8b8a8, #else .pixman_format = PIXMAN_a8b8g8r8, #endif }, { .drm_format = DRM_FORMAT_RGBA8888, #if WLR_BIG_ENDIAN .pixman_format = PIXMAN_a8b8g8r8, #else .pixman_format = PIXMAN_r8g8b8a8, #endif }, { .drm_format = DRM_FORMAT_RGBX8888, #if WLR_BIG_ENDIAN .pixman_format = PIXMAN_x8b8g8r8, #else .pixman_format = PIXMAN_r8g8b8x8, #endif }, { .drm_format = DRM_FORMAT_BGRA8888, #if WLR_BIG_ENDIAN .pixman_format = PIXMAN_a8r8g8b8, #else .pixman_format = PIXMAN_b8g8r8a8, #endif }, { .drm_format = DRM_FORMAT_BGRX8888, #if WLR_BIG_ENDIAN .pixman_format = PIXMAN_x8r8g8b8, #else .pixman_format = PIXMAN_b8g8r8x8, #endif }, #if WLR_LITTLE_ENDIAN // Since DRM formats are always little-endian, they don't have an // equivalent on big-endian if their components are spanning across // multiple bytes. { .drm_format = DRM_FORMAT_RGB565, .pixman_format = PIXMAN_r5g6b5, }, { .drm_format = DRM_FORMAT_BGR565, .pixman_format = PIXMAN_b5g6r5, }, { .drm_format = DRM_FORMAT_ARGB2101010, .pixman_format = PIXMAN_a2r10g10b10, }, { .drm_format = DRM_FORMAT_XRGB2101010, .pixman_format = PIXMAN_x2r10g10b10, }, { .drm_format = DRM_FORMAT_ABGR2101010, .pixman_format = PIXMAN_a2b10g10r10, }, { .drm_format = DRM_FORMAT_XBGR2101010, .pixman_format = PIXMAN_x2b10g10r10, }, #endif }; pixman_format_code_t get_pixman_format_from_drm(uint32_t fmt) { for (size_t i = 0; i < sizeof(formats) / sizeof(*formats); ++i) { if (formats[i].drm_format == fmt) { return formats[i].pixman_format; } } wlr_log(WLR_ERROR, "DRM format 0x%"PRIX32" has no pixman equivalent", fmt); return 0; } uint32_t get_drm_format_from_pixman(pixman_format_code_t fmt) { for (size_t i = 0; i < sizeof(formats) / sizeof(*formats); ++i) { if (formats[i].pixman_format == fmt) { return formats[i].drm_format; } } wlr_log(WLR_ERROR, "pixman format 0x%"PRIX32" has no drm equivalent", fmt); return DRM_FORMAT_INVALID; } const uint32_t *get_pixman_drm_formats(size_t *len) { static uint32_t drm_formats[sizeof(formats) / sizeof(formats[0])]; *len = sizeof(formats) / sizeof(formats[0]); for (size_t i = 0; i < sizeof(formats) / sizeof(formats[0]); i++) { drm_formats[i] = formats[i].drm_format; } return drm_formats; } wlroots-0.17.1/render/pixman/renderer.c000066400000000000000000000404761454110342200200450ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include "render/pixman.h" #include "types/wlr_buffer.h" static const struct wlr_renderer_impl renderer_impl; bool wlr_renderer_is_pixman(struct wlr_renderer *wlr_renderer) { return wlr_renderer->impl == &renderer_impl; } static struct wlr_pixman_renderer *get_renderer( struct wlr_renderer *wlr_renderer) { assert(wlr_renderer_is_pixman(wlr_renderer)); struct wlr_pixman_renderer *renderer = wl_container_of(wlr_renderer, renderer, wlr_renderer); return renderer; } bool begin_pixman_data_ptr_access(struct wlr_buffer *wlr_buffer, pixman_image_t **image_ptr, uint32_t flags) { pixman_image_t *image = *image_ptr; void *data = NULL; uint32_t drm_format; size_t stride; if (!wlr_buffer_begin_data_ptr_access(wlr_buffer, flags, &data, &drm_format, &stride)) { return false; } // If the data pointer has changed, re-create the Pixman image. This can // happen if it's a client buffer and the wl_shm_pool has been resized. if (data != pixman_image_get_data(image)) { pixman_format_code_t format = get_pixman_format_from_drm(drm_format); assert(format != 0); pixman_image_t *new_image = pixman_image_create_bits_no_clear(format, wlr_buffer->width, wlr_buffer->height, data, stride); if (image == NULL) { wlr_buffer_end_data_ptr_access(wlr_buffer); return false; } pixman_image_unref(image); image = new_image; } *image_ptr = image; return true; } static struct wlr_pixman_buffer *get_buffer( struct wlr_pixman_renderer *renderer, struct wlr_buffer *wlr_buffer) { struct wlr_pixman_buffer *buffer; wl_list_for_each(buffer, &renderer->buffers, link) { if (buffer->buffer == wlr_buffer) { return buffer; } } return NULL; } static const struct wlr_texture_impl texture_impl; bool wlr_texture_is_pixman(struct wlr_texture *texture) { return texture->impl == &texture_impl; } static struct wlr_pixman_texture *get_texture( struct wlr_texture *wlr_texture) { assert(wlr_texture_is_pixman(wlr_texture)); struct wlr_pixman_texture *texture = wl_container_of(wlr_texture, texture, wlr_texture); return texture; } static void texture_destroy(struct wlr_texture *wlr_texture) { struct wlr_pixman_texture *texture = get_texture(wlr_texture); wl_list_remove(&texture->link); pixman_image_unref(texture->image); wlr_buffer_unlock(texture->buffer); free(texture->data); free(texture); } static const struct wlr_texture_impl texture_impl = { .destroy = texture_destroy, }; static void destroy_buffer(struct wlr_pixman_buffer *buffer) { wl_list_remove(&buffer->link); wl_list_remove(&buffer->buffer_destroy.link); pixman_image_unref(buffer->image); free(buffer); } static void handle_destroy_buffer(struct wl_listener *listener, void *data) { struct wlr_pixman_buffer *buffer = wl_container_of(listener, buffer, buffer_destroy); destroy_buffer(buffer); } static struct wlr_pixman_buffer *create_buffer( struct wlr_pixman_renderer *renderer, struct wlr_buffer *wlr_buffer) { struct wlr_pixman_buffer *buffer = calloc(1, sizeof(*buffer)); if (buffer == NULL) { wlr_log_errno(WLR_ERROR, "Allocation failed"); return NULL; } buffer->buffer = wlr_buffer; buffer->renderer = renderer; void *data = NULL; uint32_t drm_format; size_t stride; if (!wlr_buffer_begin_data_ptr_access(wlr_buffer, WLR_BUFFER_DATA_PTR_ACCESS_READ | WLR_BUFFER_DATA_PTR_ACCESS_WRITE, &data, &drm_format, &stride)) { wlr_log(WLR_ERROR, "Failed to get buffer data"); goto error_buffer; } wlr_buffer_end_data_ptr_access(wlr_buffer); pixman_format_code_t format = get_pixman_format_from_drm(drm_format); if (format == 0) { wlr_log(WLR_ERROR, "Unsupported pixman drm format 0x%"PRIX32, drm_format); goto error_buffer; } buffer->image = pixman_image_create_bits(format, wlr_buffer->width, wlr_buffer->height, data, stride); if (!buffer->image) { wlr_log(WLR_ERROR, "Failed to allocate pixman image"); goto error_buffer; } buffer->buffer_destroy.notify = handle_destroy_buffer; wl_signal_add(&wlr_buffer->events.destroy, &buffer->buffer_destroy); wl_list_insert(&renderer->buffers, &buffer->link); wlr_log(WLR_DEBUG, "Created pixman buffer %dx%d", wlr_buffer->width, wlr_buffer->height); return buffer; error_buffer: free(buffer); return NULL; } static bool pixman_begin(struct wlr_renderer *wlr_renderer, uint32_t width, uint32_t height) { struct wlr_pixman_renderer *renderer = get_renderer(wlr_renderer); renderer->width = width; renderer->height = height; struct wlr_pixman_buffer *buffer = renderer->current_buffer; assert(buffer != NULL); return begin_pixman_data_ptr_access(buffer->buffer, &buffer->image, WLR_BUFFER_DATA_PTR_ACCESS_READ | WLR_BUFFER_DATA_PTR_ACCESS_WRITE); } static void pixman_end(struct wlr_renderer *wlr_renderer) { struct wlr_pixman_renderer *renderer = get_renderer(wlr_renderer); assert(renderer->current_buffer != NULL); wlr_buffer_end_data_ptr_access(renderer->current_buffer->buffer); } static void pixman_clear(struct wlr_renderer *wlr_renderer, const float color[static 4]) { struct wlr_pixman_renderer *renderer = get_renderer(wlr_renderer); struct wlr_pixman_buffer *buffer = renderer->current_buffer; const struct pixman_color colour = { .red = color[0] * 0xFFFF, .green = color[1] * 0xFFFF, .blue = color[2] * 0xFFFF, .alpha = color[3] * 0xFFFF, }; pixman_image_t *fill = pixman_image_create_solid_fill(&colour); pixman_image_composite32(PIXMAN_OP_SRC, fill, NULL, buffer->image, 0, 0, 0, 0, 0, 0, renderer->width, renderer->height); pixman_image_unref(fill); } static void pixman_scissor(struct wlr_renderer *wlr_renderer, struct wlr_box *box) { struct wlr_pixman_renderer *renderer = get_renderer(wlr_renderer); struct wlr_pixman_buffer *buffer = renderer->current_buffer; if (box != NULL) { struct pixman_region32 region = {0}; pixman_region32_init_rect(®ion, box->x, box->y, box->width, box->height); pixman_image_set_clip_region32(buffer->image, ®ion); pixman_region32_fini(®ion); } else { pixman_image_set_clip_region32(buffer->image, NULL); } } static void matrix_to_pixman_transform(struct pixman_transform *transform, const float mat[static 9]) { struct pixman_f_transform ftr; ftr.m[0][0] = mat[0]; ftr.m[0][1] = mat[1]; ftr.m[0][2] = mat[2]; ftr.m[1][0] = mat[3]; ftr.m[1][1] = mat[4]; ftr.m[1][2] = mat[5]; ftr.m[2][0] = mat[6]; ftr.m[2][1] = mat[7]; ftr.m[2][2] = mat[8]; pixman_transform_from_pixman_f_transform(transform, &ftr); } static bool pixman_render_subtexture_with_matrix( struct wlr_renderer *wlr_renderer, struct wlr_texture *wlr_texture, const struct wlr_fbox *fbox, const float matrix[static 9], float alpha) { struct wlr_pixman_renderer *renderer = get_renderer(wlr_renderer); struct wlr_pixman_texture *texture = get_texture(wlr_texture); struct wlr_pixman_buffer *buffer = renderer->current_buffer; if (texture->buffer != NULL && !begin_pixman_data_ptr_access(texture->buffer, &texture->image, WLR_BUFFER_DATA_PTR_ACCESS_READ)) { return false; } pixman_image_t *mask = NULL; if (alpha != 1.0) { struct pixman_color mask_colour = {0}; mask_colour.alpha = 0xFFFF * alpha; mask = pixman_image_create_solid_fill(&mask_colour); } float m[9]; memcpy(m, matrix, sizeof(m)); wlr_matrix_scale(m, 1.0 / fbox->width, 1.0 / fbox->height); struct pixman_transform transform = {0}; matrix_to_pixman_transform(&transform, m); pixman_transform_invert(&transform, &transform); pixman_image_set_transform(texture->image, &transform); // TODO clip properly with src_x and src_y pixman_image_composite32(PIXMAN_OP_OVER, texture->image, mask, buffer->image, 0, 0, 0, 0, 0, 0, renderer->width, renderer->height); if (texture->buffer != NULL) { wlr_buffer_end_data_ptr_access(texture->buffer); } if (mask != NULL) { pixman_image_unref(mask); } return true; } static void pixman_render_quad_with_matrix(struct wlr_renderer *wlr_renderer, const float color[static 4], const float matrix[static 9]) { struct wlr_pixman_renderer *renderer = get_renderer(wlr_renderer); struct wlr_pixman_buffer *buffer = renderer->current_buffer; struct pixman_color colour = { .red = color[0] * 0xFFFF, .green = color[1] * 0xFFFF, .blue = color[2] * 0xFFFF, .alpha = color[3] * 0xFFFF, }; pixman_image_t *fill = pixman_image_create_solid_fill(&colour); float m[9]; memcpy(m, matrix, sizeof(m)); // TODO get the width/height from the caller instead of extracting them float width, height; if (matrix[1] == 0.0 && matrix[3] == 0.0) { width = fabs(matrix[0]); height = fabs(matrix[4]); } else { width = sqrt(matrix[0] * matrix[0] + matrix[1] * matrix[1]); height = sqrt(matrix[3] * matrix[3] + matrix[4] * matrix[4]); } wlr_matrix_scale(m, 1.0 / width, 1.0 / height); pixman_image_t *image = pixman_image_create_bits(PIXMAN_a8r8g8b8, width, height, NULL, 0); // TODO find a way to fill the image without allocating 2 images pixman_image_composite32(PIXMAN_OP_SRC, fill, NULL, image, 0, 0, 0, 0, 0, 0, width, height); pixman_image_unref(fill); struct pixman_transform transform = {0}; matrix_to_pixman_transform(&transform, m); pixman_transform_invert(&transform, &transform); pixman_image_set_transform(image, &transform); pixman_image_composite32(PIXMAN_OP_OVER, image, NULL, buffer->image, 0, 0, 0, 0, 0, 0, renderer->width, renderer->height); pixman_image_unref(image); } static const uint32_t *pixman_get_shm_texture_formats( struct wlr_renderer *wlr_renderer, size_t *len) { return get_pixman_drm_formats(len); } static const struct wlr_drm_format_set *pixman_get_render_formats( struct wlr_renderer *wlr_renderer) { struct wlr_pixman_renderer *renderer = get_renderer(wlr_renderer); return &renderer->drm_formats; } static struct wlr_pixman_texture *pixman_texture_create( struct wlr_pixman_renderer *renderer, uint32_t drm_format, uint32_t width, uint32_t height) { struct wlr_pixman_texture *texture = calloc(1, sizeof(*texture)); if (texture == NULL) { wlr_log_errno(WLR_ERROR, "Failed to allocate pixman texture"); return NULL; } wlr_texture_init(&texture->wlr_texture, &renderer->wlr_renderer, &texture_impl, width, height); texture->format_info = drm_get_pixel_format_info(drm_format); if (!texture->format_info) { wlr_log(WLR_ERROR, "Unsupported drm format 0x%"PRIX32, drm_format); free(texture); return NULL; } texture->format = get_pixman_format_from_drm(drm_format); if (texture->format == 0) { wlr_log(WLR_ERROR, "Unsupported pixman drm format 0x%"PRIX32, drm_format); free(texture); return NULL; } wl_list_insert(&renderer->textures, &texture->link); return texture; } static struct wlr_texture *pixman_texture_from_buffer( struct wlr_renderer *wlr_renderer, struct wlr_buffer *buffer) { struct wlr_pixman_renderer *renderer = get_renderer(wlr_renderer); void *data = NULL; uint32_t drm_format; size_t stride; if (!wlr_buffer_begin_data_ptr_access(buffer, WLR_BUFFER_DATA_PTR_ACCESS_READ, &data, &drm_format, &stride)) { return NULL; } wlr_buffer_end_data_ptr_access(buffer); struct wlr_pixman_texture *texture = pixman_texture_create(renderer, drm_format, buffer->width, buffer->height); if (texture == NULL) { return NULL; } texture->image = pixman_image_create_bits_no_clear(texture->format, buffer->width, buffer->height, data, stride); if (!texture->image) { wlr_log(WLR_ERROR, "Failed to create pixman image"); wl_list_remove(&texture->link); free(texture); return NULL; } texture->buffer = wlr_buffer_lock(buffer); return &texture->wlr_texture; } static bool pixman_bind_buffer(struct wlr_renderer *wlr_renderer, struct wlr_buffer *wlr_buffer) { struct wlr_pixman_renderer *renderer = get_renderer(wlr_renderer); if (renderer->current_buffer != NULL) { wlr_buffer_unlock(renderer->current_buffer->buffer); renderer->current_buffer = NULL; } if (wlr_buffer == NULL) { return true; } struct wlr_pixman_buffer *buffer = get_buffer(renderer, wlr_buffer); if (buffer == NULL) { buffer = create_buffer(renderer, wlr_buffer); } if (buffer == NULL) { return false; } wlr_buffer_lock(wlr_buffer); renderer->current_buffer = buffer; return true; } static void pixman_destroy(struct wlr_renderer *wlr_renderer) { struct wlr_pixman_renderer *renderer = get_renderer(wlr_renderer); struct wlr_pixman_buffer *buffer, *buffer_tmp; wl_list_for_each_safe(buffer, buffer_tmp, &renderer->buffers, link) { destroy_buffer(buffer); } struct wlr_pixman_texture *tex, *tex_tmp; wl_list_for_each_safe(tex, tex_tmp, &renderer->textures, link) { wlr_texture_destroy(&tex->wlr_texture); } wlr_drm_format_set_finish(&renderer->drm_formats); free(renderer); } static uint32_t pixman_preferred_read_format( struct wlr_renderer *wlr_renderer) { struct wlr_pixman_renderer *renderer = get_renderer(wlr_renderer); struct wlr_pixman_buffer *buffer = renderer->current_buffer; pixman_format_code_t pixman_format = pixman_image_get_format( buffer->image); return get_drm_format_from_pixman(pixman_format); } static bool pixman_read_pixels(struct wlr_renderer *wlr_renderer, uint32_t drm_format, uint32_t stride, uint32_t width, uint32_t height, uint32_t src_x, uint32_t src_y, uint32_t dst_x, uint32_t dst_y, void *data) { struct wlr_pixman_renderer *renderer = get_renderer(wlr_renderer); struct wlr_pixman_buffer *buffer = renderer->current_buffer; pixman_format_code_t fmt = get_pixman_format_from_drm(drm_format); if (fmt == 0) { wlr_log(WLR_ERROR, "Cannot read pixels: unsupported pixel format"); return false; } const struct wlr_pixel_format_info *drm_fmt = drm_get_pixel_format_info(drm_format); assert(drm_fmt); pixman_image_t *dst = pixman_image_create_bits_no_clear(fmt, width, height, data, stride); pixman_image_composite32(PIXMAN_OP_SRC, buffer->image, NULL, dst, src_x, src_y, 0, 0, dst_x, dst_y, width, height); pixman_image_unref(dst); return true; } static uint32_t pixman_get_render_buffer_caps(struct wlr_renderer *renderer) { return WLR_BUFFER_CAP_DATA_PTR; } static struct wlr_render_pass *pixman_begin_buffer_pass(struct wlr_renderer *wlr_renderer, struct wlr_buffer *wlr_buffer, const struct wlr_buffer_pass_options *options) { struct wlr_pixman_renderer *renderer = get_renderer(wlr_renderer); struct wlr_pixman_buffer *buffer = get_buffer(renderer, wlr_buffer); if (buffer == NULL) { buffer = create_buffer(renderer, wlr_buffer); } if (buffer == NULL) { return NULL; } struct wlr_pixman_render_pass *pass = begin_pixman_render_pass(buffer); if (pass == NULL) { return NULL; } return &pass->base; } static const struct wlr_renderer_impl renderer_impl = { .begin = pixman_begin, .end = pixman_end, .clear = pixman_clear, .scissor = pixman_scissor, .render_subtexture_with_matrix = pixman_render_subtexture_with_matrix, .render_quad_with_matrix = pixman_render_quad_with_matrix, .get_shm_texture_formats = pixman_get_shm_texture_formats, .get_render_formats = pixman_get_render_formats, .texture_from_buffer = pixman_texture_from_buffer, .bind_buffer = pixman_bind_buffer, .destroy = pixman_destroy, .preferred_read_format = pixman_preferred_read_format, .read_pixels = pixman_read_pixels, .get_render_buffer_caps = pixman_get_render_buffer_caps, .begin_buffer_pass = pixman_begin_buffer_pass, }; struct wlr_renderer *wlr_pixman_renderer_create(void) { struct wlr_pixman_renderer *renderer = calloc(1, sizeof(*renderer)); if (renderer == NULL) { return NULL; } wlr_log(WLR_INFO, "Creating pixman renderer"); wlr_renderer_init(&renderer->wlr_renderer, &renderer_impl); wl_list_init(&renderer->buffers); wl_list_init(&renderer->textures); size_t len = 0; const uint32_t *formats = get_pixman_drm_formats(&len); for (size_t i = 0; i < len; ++i) { wlr_drm_format_set_add(&renderer->drm_formats, formats[i], DRM_FORMAT_MOD_INVALID); wlr_drm_format_set_add(&renderer->drm_formats, formats[i], DRM_FORMAT_MOD_LINEAR); } return &renderer->wlr_renderer; } pixman_image_t *wlr_pixman_texture_get_image(struct wlr_texture *wlr_texture) { struct wlr_pixman_texture *texture = get_texture(wlr_texture); return texture->image; } pixman_image_t *wlr_pixman_renderer_get_current_image( struct wlr_renderer *wlr_renderer) { struct wlr_pixman_renderer *renderer = get_renderer(wlr_renderer); assert(renderer->current_buffer); return renderer->current_buffer->image; } wlroots-0.17.1/render/swapchain.c000066400000000000000000000075441454110342200167170ustar00rootroot00000000000000#include #include #include #include #include #include "render/allocator/allocator.h" #include "render/drm_format_set.h" static void swapchain_handle_allocator_destroy(struct wl_listener *listener, void *data) { struct wlr_swapchain *swapchain = wl_container_of(listener, swapchain, allocator_destroy); swapchain->allocator = NULL; wl_list_remove(&swapchain->allocator_destroy.link); wl_list_init(&swapchain->allocator_destroy.link); } struct wlr_swapchain *wlr_swapchain_create( struct wlr_allocator *alloc, int width, int height, const struct wlr_drm_format *format) { struct wlr_swapchain *swapchain = calloc(1, sizeof(*swapchain)); if (swapchain == NULL) { return NULL; } swapchain->allocator = alloc; swapchain->width = width; swapchain->height = height; if (!wlr_drm_format_copy(&swapchain->format, format)) { free(swapchain); return NULL; } swapchain->allocator_destroy.notify = swapchain_handle_allocator_destroy; wl_signal_add(&alloc->events.destroy, &swapchain->allocator_destroy); return swapchain; } static void slot_reset(struct wlr_swapchain_slot *slot) { if (slot->acquired) { wl_list_remove(&slot->release.link); } wlr_buffer_drop(slot->buffer); *slot = (struct wlr_swapchain_slot){0}; } void wlr_swapchain_destroy(struct wlr_swapchain *swapchain) { if (swapchain == NULL) { return; } for (size_t i = 0; i < WLR_SWAPCHAIN_CAP; i++) { slot_reset(&swapchain->slots[i]); } wl_list_remove(&swapchain->allocator_destroy.link); wlr_drm_format_finish(&swapchain->format); free(swapchain); } static void slot_handle_release(struct wl_listener *listener, void *data) { struct wlr_swapchain_slot *slot = wl_container_of(listener, slot, release); wl_list_remove(&slot->release.link); slot->acquired = false; } static struct wlr_buffer *slot_acquire(struct wlr_swapchain *swapchain, struct wlr_swapchain_slot *slot, int *age) { assert(!slot->acquired); assert(slot->buffer != NULL); slot->acquired = true; slot->release.notify = slot_handle_release; wl_signal_add(&slot->buffer->events.release, &slot->release); if (age != NULL) { *age = slot->age; } return wlr_buffer_lock(slot->buffer); } struct wlr_buffer *wlr_swapchain_acquire(struct wlr_swapchain *swapchain, int *age) { struct wlr_swapchain_slot *free_slot = NULL; for (size_t i = 0; i < WLR_SWAPCHAIN_CAP; i++) { struct wlr_swapchain_slot *slot = &swapchain->slots[i]; if (slot->acquired) { continue; } if (slot->buffer != NULL) { return slot_acquire(swapchain, slot, age); } free_slot = slot; } if (free_slot == NULL) { wlr_log(WLR_ERROR, "No free output buffer slot"); return NULL; } if (swapchain->allocator == NULL) { return NULL; } wlr_log(WLR_DEBUG, "Allocating new swapchain buffer"); free_slot->buffer = wlr_allocator_create_buffer(swapchain->allocator, swapchain->width, swapchain->height, &swapchain->format); if (free_slot->buffer == NULL) { wlr_log(WLR_ERROR, "Failed to allocate buffer"); return NULL; } return slot_acquire(swapchain, free_slot, age); } static bool swapchain_has_buffer(struct wlr_swapchain *swapchain, struct wlr_buffer *buffer) { for (size_t i = 0; i < WLR_SWAPCHAIN_CAP; i++) { struct wlr_swapchain_slot *slot = &swapchain->slots[i]; if (slot->buffer == buffer) { return true; } } return false; } void wlr_swapchain_set_buffer_submitted(struct wlr_swapchain *swapchain, struct wlr_buffer *buffer) { assert(buffer != NULL); if (!swapchain_has_buffer(swapchain, buffer)) { return; } // See the algorithm described in: // https://www.khronos.org/registry/EGL/extensions/EXT/EGL_EXT_buffer_age.txt for (size_t i = 0; i < WLR_SWAPCHAIN_CAP; i++) { struct wlr_swapchain_slot *slot = &swapchain->slots[i]; if (slot->buffer == buffer) { slot->age = 1; } else if (slot->age > 0) { slot->age++; } } } wlroots-0.17.1/render/vulkan/000077500000000000000000000000001454110342200160645ustar00rootroot00000000000000wlroots-0.17.1/render/vulkan/meson.build000066400000000000000000000022301454110342200202230ustar00rootroot00000000000000msg = [] if 'vulkan' in renderers msg += 'Install "@0@" or pass "-Dvulkan=disabled" to disable it.' else msg += 'Required for vulkan renderer support.' endif dep_vulkan = dependency('vulkan', version: '>=1.2.182', required: 'vulkan' in renderers, not_found_message: '\n'.join(msg).format('vulkan') ) if not dep_vulkan.found() subdir_done() endif # Vulkan headers are installed separately from the loader (which ships the # pkg-config file) if not cc.check_header('vulkan/vulkan.h', dependencies: dep_vulkan) if 'vulkan' in renderers error('\n'.join(msg).format('vulkan-headers')) else subdir_done() endif endif glslang = find_program('glslang', 'glslangValidator', native: true, required: false) if not glslang.found() if 'vulkan' in renderers error('\n'.join(msg).format('glslang')) else subdir_done() endif endif glslang_version_info = run_command(glslang, '--version', check: true).stdout() glslang_version = glslang_version_info.split('\n')[0].split(':')[-1] wlr_files += files( 'pass.c', 'renderer.c', 'texture.c', 'vulkan.c', 'util.c', 'pixel_format.c', ) wlr_deps += dep_vulkan features += { 'vulkan-renderer': true } subdir('shaders') wlroots-0.17.1/render/vulkan/pass.c000066400000000000000000000550251454110342200172050ustar00rootroot00000000000000#include #include #include #include #include "render/vulkan.h" #include "types/wlr_matrix.h" static const struct wlr_render_pass_impl render_pass_impl; static struct wlr_vk_render_pass *get_render_pass(struct wlr_render_pass *wlr_pass) { assert(wlr_pass->impl == &render_pass_impl); struct wlr_vk_render_pass *pass = wl_container_of(wlr_pass, pass, base); return pass; } static void bind_pipeline(struct wlr_vk_render_pass *pass, VkPipeline pipeline) { if (pipeline == pass->bound_pipeline) { return; } vkCmdBindPipeline(pass->command_buffer->vk, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); pass->bound_pipeline = pipeline; } static void get_clip_region(struct wlr_vk_render_pass *pass, const pixman_region32_t *in, pixman_region32_t *out) { if (in != NULL) { pixman_region32_init(out); pixman_region32_copy(out, in); } else { struct wlr_buffer *buffer = pass->render_buffer->wlr_buffer; pixman_region32_init_rect(out, 0, 0, buffer->width, buffer->height); } } static void convert_pixman_box_to_vk_rect(const pixman_box32_t *box, VkRect2D *rect) { *rect = (VkRect2D){ .offset = { .x = box->x1, .y = box->y1 }, .extent = { .width = box->x2 - box->x1, .height = box->y2 - box->y1 }, }; } static float color_to_linear(float non_linear) { // See https://www.w3.org/Graphics/Color/srgb return (non_linear > 0.04045) ? pow((non_linear + 0.055) / 1.055, 2.4) : non_linear / 12.92; } static void mat3_to_mat4(const float mat3[9], float mat4[4][4]) { memset(mat4, 0, sizeof(float) * 16); mat4[0][0] = mat3[0]; mat4[0][1] = mat3[1]; mat4[0][3] = mat3[2]; mat4[1][0] = mat3[3]; mat4[1][1] = mat3[4]; mat4[1][3] = mat3[5]; mat4[2][2] = 1.f; mat4[3][3] = 1.f; } static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { struct wlr_vk_render_pass *pass = get_render_pass(wlr_pass); struct wlr_vk_renderer *renderer = pass->renderer; struct wlr_vk_command_buffer *render_cb = pass->command_buffer; struct wlr_vk_render_buffer *render_buffer = pass->render_buffer; struct wlr_vk_command_buffer *stage_cb = NULL; VkSemaphoreSubmitInfoKHR *render_wait = NULL; bool device_lost = false; if (pass->failed) { goto error; } if (vulkan_record_stage_cb(renderer) == VK_NULL_HANDLE) { goto error; } stage_cb = renderer->stage.cb; assert(stage_cb != NULL); renderer->stage.cb = NULL; if (render_buffer->blend_image) { // Apply output shader to map blend image to actual output image vkCmdNextSubpass(render_cb->vk, VK_SUBPASS_CONTENTS_INLINE); int width = pass->render_buffer->wlr_buffer->width; int height = pass->render_buffer->wlr_buffer->height; float final_matrix[9] = { width, 0, -1, 0, height, -1, 0, 0, 0, }; struct wlr_vk_vert_pcr_data vert_pcr_data = { .uv_off = { 0, 0 }, .uv_size = { 1, 1 }, }; mat3_to_mat4(final_matrix, vert_pcr_data.mat4); bind_pipeline(pass, render_buffer->render_setup->output_pipe); vkCmdPushConstants(render_cb->vk, renderer->output_pipe_layout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(vert_pcr_data), &vert_pcr_data); vkCmdBindDescriptorSets(render_cb->vk, VK_PIPELINE_BIND_POINT_GRAPHICS, renderer->output_pipe_layout, 0, 1, &render_buffer->blend_descriptor_set, 0, NULL); const pixman_region32_t *clip = rect_union_evaluate(&pass->updated_region); int clip_rects_len; const pixman_box32_t *clip_rects = pixman_region32_rectangles( clip, &clip_rects_len); for (int i = 0; i < clip_rects_len; i++) { VkRect2D rect; convert_pixman_box_to_vk_rect(&clip_rects[i], &rect); vkCmdSetScissor(render_cb->vk, 0, 1, &rect); vkCmdDraw(render_cb->vk, 4, 1, 0, 0); } } vkCmdEndRenderPass(render_cb->vk); // insert acquire and release barriers for dmabuf-images uint32_t barrier_count = wl_list_length(&renderer->foreign_textures) + 1; VkImageMemoryBarrier *acquire_barriers = calloc(barrier_count, sizeof(*acquire_barriers)); VkImageMemoryBarrier *release_barriers = calloc(barrier_count, sizeof(*release_barriers)); render_wait = calloc(barrier_count * WLR_DMABUF_MAX_PLANES, sizeof(*render_wait)); if (acquire_barriers == NULL || release_barriers == NULL || render_wait == NULL) { wlr_log_errno(WLR_ERROR, "Allocation failed"); free(acquire_barriers); free(release_barriers); free(render_wait); goto error; } struct wlr_vk_texture *texture, *tmp_tex; size_t idx = 0; uint32_t render_wait_len = 0; wl_list_for_each_safe(texture, tmp_tex, &renderer->foreign_textures, foreign_link) { VkImageLayout src_layout = VK_IMAGE_LAYOUT_GENERAL; if (!texture->transitioned) { src_layout = VK_IMAGE_LAYOUT_UNDEFINED; texture->transitioned = true; } // acquire acquire_barriers[idx] = (VkImageMemoryBarrier){ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_FOREIGN_EXT, .dstQueueFamilyIndex = renderer->dev->queue_family, .image = texture->image, .oldLayout = src_layout, .newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, .srcAccessMask = 0, // ignored anyways .dstAccessMask = VK_ACCESS_SHADER_READ_BIT, .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .subresourceRange.layerCount = 1, .subresourceRange.levelCount = 1, }; // release release_barriers[idx] = (VkImageMemoryBarrier){ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .srcQueueFamilyIndex = renderer->dev->queue_family, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_FOREIGN_EXT, .image = texture->image, .oldLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, .newLayout = VK_IMAGE_LAYOUT_GENERAL, .srcAccessMask = VK_ACCESS_SHADER_READ_BIT, .dstAccessMask = 0, // ignored anyways .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .subresourceRange.layerCount = 1, .subresourceRange.levelCount = 1, }; ++idx; if (!vulkan_sync_foreign_texture(texture)) { wlr_log(WLR_ERROR, "Failed to wait for foreign texture DMA-BUF fence"); } else { for (size_t i = 0; i < WLR_DMABUF_MAX_PLANES; i++) { if (texture->foreign_semaphores[i] != VK_NULL_HANDLE) { assert(render_wait_len < barrier_count * WLR_DMABUF_MAX_PLANES); render_wait[render_wait_len++] = (VkSemaphoreSubmitInfoKHR){ .sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO_KHR, .semaphore = texture->foreign_semaphores[i], .stageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT_KHR, }; } } } wl_list_remove(&texture->foreign_link); texture->owned = false; } // also add acquire/release barriers for the current render buffer VkImageLayout src_layout = VK_IMAGE_LAYOUT_GENERAL; if (!render_buffer->transitioned) { src_layout = VK_IMAGE_LAYOUT_PREINITIALIZED; render_buffer->transitioned = true; } if (render_buffer->blend_image) { // The render pass changes the blend image layout from // color attachment to read only, so on each frame, before // the render pass starts, we change it back VkImageLayout blend_src_layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; if (!render_buffer->blend_transitioned) { blend_src_layout = VK_IMAGE_LAYOUT_UNDEFINED; render_buffer->blend_transitioned = true; } VkImageMemoryBarrier blend_acq_barrier = { .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .image = render_buffer->blend_image, .oldLayout = blend_src_layout, .newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, .srcAccessMask = VK_ACCESS_SHADER_READ_BIT, .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, .subresourceRange = { .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .layerCount = 1, .levelCount = 1, }, }; vkCmdPipelineBarrier(stage_cb->vk, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, 0, NULL, 0, NULL, 1, &blend_acq_barrier); } // acquire render buffer before rendering acquire_barriers[idx] = (VkImageMemoryBarrier){ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_FOREIGN_EXT, .dstQueueFamilyIndex = renderer->dev->queue_family, .image = render_buffer->image, .oldLayout = src_layout, .newLayout = VK_IMAGE_LAYOUT_GENERAL, .srcAccessMask = 0, // ignored anyways .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .subresourceRange.layerCount = 1, .subresourceRange.levelCount = 1, }; // release render buffer after rendering release_barriers[idx] = (VkImageMemoryBarrier){ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .srcQueueFamilyIndex = renderer->dev->queue_family, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_FOREIGN_EXT, .image = render_buffer->image, .oldLayout = VK_IMAGE_LAYOUT_GENERAL, .newLayout = VK_IMAGE_LAYOUT_GENERAL, .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, .dstAccessMask = 0, // ignored anyways .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .subresourceRange.layerCount = 1, .subresourceRange.levelCount = 1, }; ++idx; vkCmdPipelineBarrier(stage_cb->vk, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, 0, NULL, 0, NULL, barrier_count, acquire_barriers); vkCmdPipelineBarrier(render_cb->vk, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, NULL, 0, NULL, barrier_count, release_barriers); free(acquire_barriers); free(release_barriers); // No semaphores needed here. // We don't need a semaphore from the stage/transfer submission // to the render submissions since they are on the same queue // and we have a renderpass dependency for that. uint64_t stage_timeline_point = vulkan_end_command_buffer(stage_cb, renderer); if (stage_timeline_point == 0) { goto error; } VkCommandBufferSubmitInfoKHR stage_cb_info = { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO_KHR, .commandBuffer = stage_cb->vk, }; VkSemaphoreSubmitInfoKHR stage_signal = { .sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO_KHR, .semaphore = renderer->timeline_semaphore, .value = stage_timeline_point, }; VkSubmitInfo2KHR stage_submit = { .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO_2_KHR, .commandBufferInfoCount = 1, .pCommandBufferInfos = &stage_cb_info, .signalSemaphoreInfoCount = 1, .pSignalSemaphoreInfos = &stage_signal, }; VkSemaphoreSubmitInfoKHR stage_wait; if (renderer->stage.last_timeline_point > 0) { stage_wait = (VkSemaphoreSubmitInfoKHR){ .sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO_KHR, .semaphore = renderer->timeline_semaphore, .value = renderer->stage.last_timeline_point, .stageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT_KHR, }; stage_submit.waitSemaphoreInfoCount = 1; stage_submit.pWaitSemaphoreInfos = &stage_wait; } renderer->stage.last_timeline_point = stage_timeline_point; uint64_t render_timeline_point = vulkan_end_command_buffer(render_cb, renderer); if (render_timeline_point == 0) { goto error; } uint32_t render_signal_len = 1; VkSemaphoreSubmitInfoKHR render_signal[2] = {0}; render_signal[0] = (VkSemaphoreSubmitInfoKHR){ .sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO_KHR, .semaphore = renderer->timeline_semaphore, .value = render_timeline_point, }; if (renderer->dev->implicit_sync_interop) { if (render_cb->binary_semaphore == VK_NULL_HANDLE) { VkExportSemaphoreCreateInfo export_info = { .sType = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO, .handleTypes = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT, }; VkSemaphoreCreateInfo semaphore_info = { .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, .pNext = &export_info, }; VkResult res = vkCreateSemaphore(renderer->dev->dev, &semaphore_info, NULL, &render_cb->binary_semaphore); if (res != VK_SUCCESS) { wlr_vk_error("vkCreateSemaphore", res); goto error; } } render_signal[render_signal_len++] = (VkSemaphoreSubmitInfoKHR){ .sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO_KHR, .semaphore = render_cb->binary_semaphore, }; } VkCommandBufferSubmitInfoKHR render_cb_info = { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO_KHR, .commandBuffer = render_cb->vk, }; VkSubmitInfo2KHR render_submit = { .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO_2_KHR, .waitSemaphoreInfoCount = render_wait_len, .pWaitSemaphoreInfos = render_wait, .commandBufferInfoCount = 1, .pCommandBufferInfos = &render_cb_info, .signalSemaphoreInfoCount = render_signal_len, .pSignalSemaphoreInfos = render_signal, }; VkSubmitInfo2KHR submit_info[] = { stage_submit, render_submit }; VkResult res = renderer->dev->api.vkQueueSubmit2KHR(renderer->dev->queue, 2, submit_info, VK_NULL_HANDLE); if (res != VK_SUCCESS) { device_lost = res == VK_ERROR_DEVICE_LOST; wlr_vk_error("vkQueueSubmit", res); goto error; } free(render_wait); struct wlr_vk_shared_buffer *stage_buf, *stage_buf_tmp; wl_list_for_each_safe(stage_buf, stage_buf_tmp, &renderer->stage.buffers, link) { if (stage_buf->allocs.size == 0) { continue; } wl_list_remove(&stage_buf->link); wl_list_insert(&stage_cb->stage_buffers, &stage_buf->link); } if (!vulkan_sync_render_buffer(renderer, render_buffer, render_cb)) { wlr_log(WLR_ERROR, "Failed to sync render buffer"); } wlr_buffer_unlock(render_buffer->wlr_buffer); rect_union_finish(&pass->updated_region); free(pass); return true; error: free(render_wait); vulkan_reset_command_buffer(stage_cb); vulkan_reset_command_buffer(render_cb); wlr_buffer_unlock(render_buffer->wlr_buffer); rect_union_finish(&pass->updated_region); free(pass); if (device_lost) { wl_signal_emit_mutable(&renderer->wlr_renderer.events.lost, NULL); } return false; } static void render_pass_mark_box_updated(struct wlr_vk_render_pass *pass, const struct wlr_box *box) { if (!pass->render_buffer->blend_image) { return; } pixman_box32_t pixman_box = { .x1 = box->x, .x2 = box->x + box->width, .y1 = box->y, .y2 = box->y + box->height, }; rect_union_add(&pass->updated_region, pixman_box); } static void render_pass_add_rect(struct wlr_render_pass *wlr_pass, const struct wlr_render_rect_options *options) { struct wlr_vk_render_pass *pass = get_render_pass(wlr_pass); VkCommandBuffer cb = pass->command_buffer->vk; // Input color values are given in sRGB space, shader expects // them in linear space. The shader does all computation in linear // space and expects in inputs in linear space since it outputs // colors in linear space as well (and vulkan then automatically // does the conversion for out sRGB render targets). float linear_color[] = { color_to_linear(options->color.r), color_to_linear(options->color.g), color_to_linear(options->color.b), options->color.a, // no conversion for alpha }; pixman_region32_t clip; get_clip_region(pass, options->clip, &clip); int clip_rects_len; const pixman_box32_t *clip_rects = pixman_region32_rectangles(&clip, &clip_rects_len); // Record regions possibly updated for use in second subpass for (int i = 0; i < clip_rects_len; i++) { struct wlr_box clip_box = { .x = clip_rects[i].x1, .y = clip_rects[i].y1, .width = clip_rects[i].x2 - clip_rects[i].x1, .height = clip_rects[i].y2 - clip_rects[i].y1, }; struct wlr_box intersection; if (!wlr_box_intersection(&intersection, &options->box, &clip_box)) { continue; } render_pass_mark_box_updated(pass, &intersection); } struct wlr_box box; wlr_render_rect_options_get_box(options, pass->render_buffer->wlr_buffer, &box); switch (options->blend_mode) { case WLR_RENDER_BLEND_MODE_PREMULTIPLIED:; float proj[9], matrix[9]; wlr_matrix_identity(proj); wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL, 0, proj); wlr_matrix_multiply(matrix, pass->projection, matrix); struct wlr_vk_pipeline *pipe = setup_get_or_create_pipeline( pass->render_buffer->render_setup, &(struct wlr_vk_pipeline_key) { .source = WLR_VK_SHADER_SOURCE_SINGLE_COLOR, .layout = { .ycbcr_format = NULL }, }); if (!pipe) { pass->failed = true; break; } struct wlr_vk_vert_pcr_data vert_pcr_data = { .uv_off = { 0, 0 }, .uv_size = { 1, 1 }, }; mat3_to_mat4(matrix, vert_pcr_data.mat4); bind_pipeline(pass, pipe->vk); vkCmdPushConstants(cb, pipe->layout->vk, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(vert_pcr_data), &vert_pcr_data); vkCmdPushConstants(cb, pipe->layout->vk, VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(vert_pcr_data), sizeof(float) * 4, linear_color); for (int i = 0; i < clip_rects_len; i++) { VkRect2D rect; convert_pixman_box_to_vk_rect(&clip_rects[i], &rect); vkCmdSetScissor(cb, 0, 1, &rect); vkCmdDraw(cb, 4, 1, 0, 0); } break; case WLR_RENDER_BLEND_MODE_NONE:; VkClearAttachment clear_att = { .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .colorAttachment = 0, .clearValue.color.float32 = { linear_color[0], linear_color[1], linear_color[2], linear_color[3], }, }; VkClearRect clear_rect = { .rect = { .offset = { box.x, box.y }, .extent = { box.width, box.height }, }, .layerCount = 1, }; for (int i = 0; i < clip_rects_len; i++) { VkRect2D rect; convert_pixman_box_to_vk_rect(&clip_rects[i], &rect); vkCmdSetScissor(cb, 0, 1, &rect); vkCmdClearAttachments(cb, 1, &clear_att, 1, &clear_rect); } break; } pixman_region32_fini(&clip); } static void render_pass_add_texture(struct wlr_render_pass *wlr_pass, const struct wlr_render_texture_options *options) { struct wlr_vk_render_pass *pass = get_render_pass(wlr_pass); struct wlr_vk_renderer *renderer = pass->renderer; struct wlr_vk_render_buffer *render_buffer = pass->render_buffer; VkCommandBuffer cb = pass->command_buffer->vk; struct wlr_vk_texture *texture = vulkan_get_texture(options->texture); assert(texture->renderer == renderer); if (texture->dmabuf_imported && !texture->owned) { // Store this texture in the list of textures that need to be // acquired before rendering and released after rendering. // We don't do it here immediately since barriers inside // a renderpass are suboptimal (would require additional renderpass // dependency and potentially multiple barriers) and it's // better to issue one barrier for all used textures anyways. texture->owned = true; assert(texture->foreign_link.prev == NULL); assert(texture->foreign_link.next == NULL); wl_list_insert(&renderer->foreign_textures, &texture->foreign_link); } struct wlr_fbox src_box; wlr_render_texture_options_get_src_box(options, &src_box); struct wlr_box dst_box; wlr_render_texture_options_get_dst_box(options, &dst_box); float alpha = wlr_render_texture_options_get_alpha(options); pixman_region32_t clip; get_clip_region(pass, options->clip, &clip); float proj[9], matrix[9]; wlr_matrix_identity(proj); wlr_matrix_project_box(matrix, &dst_box, options->transform, 0, proj); wlr_matrix_multiply(matrix, pass->projection, matrix); struct wlr_vk_vert_pcr_data vert_pcr_data = { .uv_off = { src_box.x / options->texture->width, src_box.y / options->texture->height, }, .uv_size = { src_box.width / options->texture->width, src_box.height / options->texture->height, }, }; mat3_to_mat4(matrix, vert_pcr_data.mat4); struct wlr_vk_pipeline *pipe = setup_get_or_create_pipeline( render_buffer->render_setup, &(struct wlr_vk_pipeline_key) { .source = WLR_VK_SHADER_SOURCE_TEXTURE, .layout = { .ycbcr_format = texture->format->is_ycbcr ? texture->format : NULL, .filter_mode = options->filter_mode, }, .texture_transform = texture->transform, .blend_mode = !texture->has_alpha && alpha == 1.0 ? WLR_RENDER_BLEND_MODE_NONE : options->blend_mode, }); if (!pipe) { pass->failed = true; return; } struct wlr_vk_texture_view *view = vulkan_texture_get_or_create_view(texture, pipe->layout); if (!view) { pass->failed = true; return; } bind_pipeline(pass, pipe->vk); vkCmdBindDescriptorSets(cb, VK_PIPELINE_BIND_POINT_GRAPHICS, pipe->layout->vk, 0, 1, &view->ds, 0, NULL); vkCmdPushConstants(cb, pipe->layout->vk, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(vert_pcr_data), &vert_pcr_data); vkCmdPushConstants(cb, pipe->layout->vk, VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(vert_pcr_data), sizeof(float), &alpha); int clip_rects_len; const pixman_box32_t *clip_rects = pixman_region32_rectangles(&clip, &clip_rects_len); for (int i = 0; i < clip_rects_len; i++) { VkRect2D rect; convert_pixman_box_to_vk_rect(&clip_rects[i], &rect); vkCmdSetScissor(cb, 0, 1, &rect); vkCmdDraw(cb, 4, 1, 0, 0); struct wlr_box clip_box = { .x = clip_rects[i].x1, .y = clip_rects[i].y1, .width = clip_rects[i].x2 - clip_rects[i].x1, .height = clip_rects[i].y2 - clip_rects[i].y1, }; struct wlr_box intersection; if (!wlr_box_intersection(&intersection, &dst_box, &clip_box)) { continue; } render_pass_mark_box_updated(pass, &intersection); } texture->last_used_cb = pass->command_buffer; } static const struct wlr_render_pass_impl render_pass_impl = { .submit = render_pass_submit, .add_rect = render_pass_add_rect, .add_texture = render_pass_add_texture, }; struct wlr_vk_render_pass *vulkan_begin_render_pass(struct wlr_vk_renderer *renderer, struct wlr_vk_render_buffer *buffer) { struct wlr_vk_render_pass *pass = calloc(1, sizeof(*pass)); if (pass == NULL) { return NULL; } wlr_render_pass_init(&pass->base, &render_pass_impl); pass->renderer = renderer; rect_union_init(&pass->updated_region); struct wlr_vk_command_buffer *cb = vulkan_acquire_command_buffer(renderer); if (cb == NULL) { free(pass); return NULL; } VkCommandBufferBeginInfo begin_info = { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, }; VkResult res = vkBeginCommandBuffer(cb->vk, &begin_info); if (res != VK_SUCCESS) { wlr_vk_error("vkBeginCommandBuffer", res); vulkan_reset_command_buffer(cb); free(pass); return NULL; } int width = buffer->wlr_buffer->width; int height = buffer->wlr_buffer->height; VkRect2D rect = { .extent = { width, height } }; VkRenderPassBeginInfo rp_info = { .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, .renderArea = rect, .renderPass = buffer->render_setup->render_pass, .framebuffer = buffer->framebuffer, .clearValueCount = 0, }; vkCmdBeginRenderPass(cb->vk, &rp_info, VK_SUBPASS_CONTENTS_INLINE); vkCmdSetViewport(cb->vk, 0, 1, &(VkViewport){ .width = width, .height = height, .maxDepth = 1, }); // matrix_projection() assumes a GL coordinate system so we need // to pass WL_OUTPUT_TRANSFORM_FLIPPED_180 to adjust it for vulkan. matrix_projection(pass->projection, width, height, WL_OUTPUT_TRANSFORM_FLIPPED_180); wlr_buffer_lock(buffer->wlr_buffer); pass->render_buffer = buffer; pass->command_buffer = cb; return pass; } wlroots-0.17.1/render/vulkan/pixel_format.c000066400000000000000000000353541454110342200207330ustar00rootroot00000000000000#include #include #include #include #include #include #include "render/pixel_format.h" #include "render/vulkan.h" static const struct wlr_vk_format formats[] = { // Vulkan non-packed 8-bits-per-channel formats have an inverted channel // order compared to the DRM formats, because DRM format channel order // is little-endian while Vulkan format channel order is in memory byte // order. { .drm = DRM_FORMAT_R8, .vk = VK_FORMAT_R8_SRGB, .is_srgb = true, }, { .drm = DRM_FORMAT_GR88, .vk = VK_FORMAT_R8G8_SRGB, .is_srgb = true, }, { .drm = DRM_FORMAT_RGB888, .vk = VK_FORMAT_B8G8R8_SRGB, .is_srgb = true, }, { .drm = DRM_FORMAT_BGR888, .vk = VK_FORMAT_R8G8B8_SRGB, .is_srgb = true, }, { .drm = DRM_FORMAT_XRGB8888, .vk = VK_FORMAT_B8G8R8A8_SRGB, .is_srgb = true, }, { .drm = DRM_FORMAT_XBGR8888, .vk = VK_FORMAT_R8G8B8A8_SRGB, .is_srgb = true, }, // The Vulkan _SRGB formats correspond to unpremultiplied alpha, but // the Wayland protocol specifies premultiplied alpha on electrical values { .drm = DRM_FORMAT_ARGB8888, .vk = VK_FORMAT_B8G8R8A8_UNORM, }, { .drm = DRM_FORMAT_ABGR8888, .vk = VK_FORMAT_R8G8B8A8_UNORM, }, // Vulkan packed formats have the same channel order as DRM formats on // little endian systems. #if WLR_LITTLE_ENDIAN { .drm = DRM_FORMAT_RGBA4444, .vk = VK_FORMAT_R4G4B4A4_UNORM_PACK16, }, { .drm = DRM_FORMAT_RGBX4444, .vk = VK_FORMAT_R4G4B4A4_UNORM_PACK16, }, { .drm = DRM_FORMAT_BGRA4444, .vk = VK_FORMAT_B4G4R4A4_UNORM_PACK16, }, { .drm = DRM_FORMAT_BGRX4444, .vk = VK_FORMAT_B4G4R4A4_UNORM_PACK16, }, { .drm = DRM_FORMAT_RGB565, .vk = VK_FORMAT_R5G6B5_UNORM_PACK16, }, { .drm = DRM_FORMAT_BGR565, .vk = VK_FORMAT_B5G6R5_UNORM_PACK16, }, { .drm = DRM_FORMAT_RGBA5551, .vk = VK_FORMAT_R5G5B5A1_UNORM_PACK16, }, { .drm = DRM_FORMAT_RGBX5551, .vk = VK_FORMAT_R5G5B5A1_UNORM_PACK16, }, { .drm = DRM_FORMAT_BGRA5551, .vk = VK_FORMAT_B5G5R5A1_UNORM_PACK16, }, { .drm = DRM_FORMAT_BGRX5551, .vk = VK_FORMAT_B5G5R5A1_UNORM_PACK16, }, { .drm = DRM_FORMAT_ARGB1555, .vk = VK_FORMAT_A1R5G5B5_UNORM_PACK16, }, { .drm = DRM_FORMAT_XRGB1555, .vk = VK_FORMAT_A1R5G5B5_UNORM_PACK16, }, { .drm = DRM_FORMAT_ARGB2101010, .vk = VK_FORMAT_A2R10G10B10_UNORM_PACK32, }, { .drm = DRM_FORMAT_XRGB2101010, .vk = VK_FORMAT_A2R10G10B10_UNORM_PACK32, }, { .drm = DRM_FORMAT_ABGR2101010, .vk = VK_FORMAT_A2B10G10R10_UNORM_PACK32, }, { .drm = DRM_FORMAT_XBGR2101010, .vk = VK_FORMAT_A2B10G10R10_UNORM_PACK32, }, #endif // Vulkan 16-bits-per-channel formats have an inverted channel order // compared to DRM formats, just like the 8-bits-per-channel ones. // On little endian systems the memory representation of each channel // matches the DRM formats'. #if WLR_LITTLE_ENDIAN { .drm = DRM_FORMAT_ABGR16161616, .vk = VK_FORMAT_R16G16B16A16_UNORM, }, { .drm = DRM_FORMAT_XBGR16161616, .vk = VK_FORMAT_R16G16B16A16_UNORM, }, { .drm = DRM_FORMAT_ABGR16161616F, .vk = VK_FORMAT_R16G16B16A16_SFLOAT, }, { .drm = DRM_FORMAT_XBGR16161616F, .vk = VK_FORMAT_R16G16B16A16_SFLOAT, }, #endif // YCbCr formats // R -> V, G -> Y, B -> U // 420 -> 2x2 subsampled, 422 -> 2x1 subsampled, 444 -> non-subsampled { .drm = DRM_FORMAT_UYVY, .vk = VK_FORMAT_B8G8R8G8_422_UNORM, .is_ycbcr = true, }, { .drm = DRM_FORMAT_YUYV, .vk = VK_FORMAT_G8B8G8R8_422_UNORM, .is_ycbcr = true, }, { .drm = DRM_FORMAT_NV12, .vk = VK_FORMAT_G8_B8R8_2PLANE_420_UNORM, .is_ycbcr = true, }, { .drm = DRM_FORMAT_NV16, .vk = VK_FORMAT_G8_B8R8_2PLANE_422_UNORM, .is_ycbcr = true, }, { .drm = DRM_FORMAT_YUV420, .vk = VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM, .is_ycbcr = true, }, { .drm = DRM_FORMAT_YUV422, .vk = VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM, .is_ycbcr = true, }, { .drm = DRM_FORMAT_YUV444, .vk = VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM, .is_ycbcr = true, }, // 3PACK16 formats split the memory in three 16-bit words, so they have an // inverted channel order compared to DRM formats. #if WLR_LITTLE_ENDIAN { .drm = DRM_FORMAT_P010, .vk = VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16, .is_ycbcr = true, }, { .drm = DRM_FORMAT_P210, .vk = VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16, .is_ycbcr = true, }, { .drm = DRM_FORMAT_P012, .vk = VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16, .is_ycbcr = true, }, { .drm = DRM_FORMAT_P016, .vk = VK_FORMAT_G16_B16R16_2PLANE_420_UNORM, .is_ycbcr = true, }, { .drm = DRM_FORMAT_Q410, .vk = VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16, .is_ycbcr = true, }, #endif // TODO: add DRM_FORMAT_NV24/VK_FORMAT_G8_B8R8_2PLANE_444_UNORM (requires // Vulkan 1.3 or VK_EXT_ycbcr_2plane_444_formats) }; const struct wlr_vk_format *vulkan_get_format_list(size_t *len) { *len = sizeof(formats) / sizeof(formats[0]); return formats; } const struct wlr_vk_format *vulkan_get_format_from_drm(uint32_t drm_format) { for (unsigned i = 0; i < sizeof(formats) / sizeof(formats[0]); ++i) { if (formats[i].drm == drm_format) { return &formats[i]; } } return NULL; } const VkImageUsageFlags vulkan_render_usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; const VkImageUsageFlags vulkan_shm_tex_usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; const VkImageUsageFlags vulkan_dma_tex_usage = VK_IMAGE_USAGE_SAMPLED_BIT; static const VkFormatFeatureFlags render_features = VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT; static const VkFormatFeatureFlags shm_tex_features = VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | // NOTE: we don't strictly require this, we could create a NEAREST // sampler for formats that need it, in case this ever makes problems. VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT; static const VkFormatFeatureFlags dma_tex_features = VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | // NOTE: we don't strictly require this, we could create a NEAREST // sampler for formats that need it, in case this ever makes problems. VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT; static const VkFormatFeatureFlags ycbcr_tex_features = VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_LINEAR_FILTER_BIT | VK_FORMAT_FEATURE_MIDPOINT_CHROMA_SAMPLES_BIT; static bool query_modifier_usage_support(struct wlr_vk_device *dev, VkFormat vk_format, VkImageUsageFlags usage, const VkDrmFormatModifierPropertiesEXT *m, struct wlr_vk_format_modifier_props *out, const char **errmsg) { VkResult res; *errmsg = NULL; VkPhysicalDeviceImageDrmFormatModifierInfoEXT modi = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_DRM_FORMAT_MODIFIER_INFO_EXT, .drmFormatModifier = m->drmFormatModifier, .sharingMode = VK_SHARING_MODE_EXCLUSIVE, }; VkPhysicalDeviceExternalImageFormatInfo efmti = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO, .handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT, .pNext = &modi, }; VkPhysicalDeviceImageFormatInfo2 fmti = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2, .type = VK_IMAGE_TYPE_2D, .format = vk_format, .usage = usage, .tiling = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT, .pNext = &efmti, }; VkExternalImageFormatProperties efmtp = { .sType = VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES, }; VkImageFormatProperties2 ifmtp = { .sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2, .pNext = &efmtp, }; const VkExternalMemoryProperties *emp = &efmtp.externalMemoryProperties; res = vkGetPhysicalDeviceImageFormatProperties2(dev->phdev, &fmti, &ifmtp); if (res != VK_SUCCESS) { if (res == VK_ERROR_FORMAT_NOT_SUPPORTED) { *errmsg = "unsupported format"; } else { wlr_vk_error("vkGetPhysicalDeviceImageFormatProperties2", res); *errmsg = "failed to get format properties"; } return false; } else if (!(emp->externalMemoryFeatures & VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT)) { *errmsg = "import not supported"; return false; } VkExtent3D me = ifmtp.imageFormatProperties.maxExtent; *out = (struct wlr_vk_format_modifier_props){ .props = *m, .max_extent.width = me.width, .max_extent.height = me.height, }; return true; } static bool query_modifier_support(struct wlr_vk_device *dev, struct wlr_vk_format_props *props, size_t modifier_count) { VkDrmFormatModifierPropertiesListEXT modp = { .sType = VK_STRUCTURE_TYPE_DRM_FORMAT_MODIFIER_PROPERTIES_LIST_EXT, .drmFormatModifierCount = modifier_count, }; VkFormatProperties2 fmtp = { .sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2, .pNext = &modp, }; modp.pDrmFormatModifierProperties = calloc(modifier_count, sizeof(*modp.pDrmFormatModifierProperties)); if (!modp.pDrmFormatModifierProperties) { wlr_log_errno(WLR_ERROR, "Allocation failed"); return false; } vkGetPhysicalDeviceFormatProperties2(dev->phdev, props->format.vk, &fmtp); props->dmabuf.render_mods = calloc(modp.drmFormatModifierCount, sizeof(*props->dmabuf.render_mods)); props->dmabuf.texture_mods = calloc(modp.drmFormatModifierCount, sizeof(*props->dmabuf.texture_mods)); if (!props->dmabuf.render_mods || !props->dmabuf.texture_mods) { wlr_log_errno(WLR_ERROR, "Allocation failed"); free(modp.pDrmFormatModifierProperties); free(props->dmabuf.render_mods); free(props->dmabuf.texture_mods); return false; } bool found = false; for (uint32_t i = 0; i < modp.drmFormatModifierCount; ++i) { VkDrmFormatModifierPropertiesEXT m = modp.pDrmFormatModifierProperties[i]; char render_status[256], texture_status[256]; // check that specific modifier for render usage const char *errmsg = "unknown error"; if ((m.drmFormatModifierTilingFeatures & render_features) == render_features && !props->format.is_ycbcr) { struct wlr_vk_format_modifier_props p = {0}; if (query_modifier_usage_support(dev, props->format.vk, vulkan_render_usage, &m, &p, &errmsg)) { props->dmabuf.render_mods[props->dmabuf.render_mod_count++] = p; wlr_drm_format_set_add(&dev->dmabuf_render_formats, props->format.drm, m.drmFormatModifier); found = true; } } else { errmsg = "missing required features"; } if (errmsg != NULL) { snprintf(render_status, sizeof(render_status), "✗ render (%s)", errmsg); } else { snprintf(render_status, sizeof(render_status), "✓ render"); } // check that specific modifier for texture usage errmsg = "unknown error"; VkFormatFeatureFlags features = dma_tex_features; if (props->format.is_ycbcr) { features |= ycbcr_tex_features; } if ((m.drmFormatModifierTilingFeatures & features) == features) { struct wlr_vk_format_modifier_props p = {0}; if (query_modifier_usage_support(dev, props->format.vk, vulkan_dma_tex_usage, &m, &p, &errmsg)) { props->dmabuf.texture_mods[props->dmabuf.texture_mod_count++] = p; wlr_drm_format_set_add(&dev->dmabuf_texture_formats, props->format.drm, m.drmFormatModifier); found = true; } } else { errmsg = "missing required features"; } if (errmsg != NULL) { snprintf(texture_status, sizeof(texture_status), "✗ texture (%s)", errmsg); } else { snprintf(texture_status, sizeof(texture_status), "✓ texture"); } char *modifier_name = drmGetFormatModifierName(m.drmFormatModifier); wlr_log(WLR_DEBUG, " DMA-BUF modifier %s " "(0x%016"PRIX64", %"PRIu32" planes): %s %s", modifier_name ? modifier_name : "", m.drmFormatModifier, m.drmFormatModifierPlaneCount, texture_status, render_status); free(modifier_name); } free(modp.pDrmFormatModifierProperties); return found; } void vulkan_format_props_query(struct wlr_vk_device *dev, const struct wlr_vk_format *format) { VkResult res; if (format->is_ycbcr && !dev->sampler_ycbcr_conversion) { return; } char *format_name = drmGetFormatName(format->drm); wlr_log(WLR_DEBUG, " %s (0x%08"PRIX32")", format_name ? format_name : "", format->drm); free(format_name); VkDrmFormatModifierPropertiesListEXT modp = { .sType = VK_STRUCTURE_TYPE_DRM_FORMAT_MODIFIER_PROPERTIES_LIST_EXT, }; VkFormatProperties2 fmtp = { .sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2, .pNext = &modp, }; vkGetPhysicalDeviceFormatProperties2(dev->phdev, format->vk, &fmtp); bool add_fmt_props = false; struct wlr_vk_format_props props = {0}; props.format = *format; const struct wlr_pixel_format_info *format_info = drm_get_pixel_format_info(format->drm); // shm texture properties const char *shm_texture_status; if ((fmtp.formatProperties.optimalTilingFeatures & shm_tex_features) == shm_tex_features && !format->is_ycbcr && format_info != NULL) { VkPhysicalDeviceImageFormatInfo2 fmti = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2, .type = VK_IMAGE_TYPE_2D, .format = format->vk, .tiling = VK_IMAGE_TILING_OPTIMAL, .usage = vulkan_shm_tex_usage, }; VkImageFormatProperties2 ifmtp = { .sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2, }; res = vkGetPhysicalDeviceImageFormatProperties2(dev->phdev, &fmti, &ifmtp); if (res != VK_SUCCESS) { if (res == VK_ERROR_FORMAT_NOT_SUPPORTED) { shm_texture_status = "✗ texture (unsupported format)"; } else { wlr_vk_error("vkGetPhysicalDeviceImageFormatProperties2", res); shm_texture_status = "✗ texture (failed to get format properties)"; } } else { VkExtent3D me = ifmtp.imageFormatProperties.maxExtent; props.shm.max_extent.width = me.width; props.shm.max_extent.height = me.height; props.shm.features = fmtp.formatProperties.optimalTilingFeatures; shm_texture_status = "✓ texture"; dev->shm_formats[dev->shm_format_count] = format->drm; ++dev->shm_format_count; add_fmt_props = true; } } else { shm_texture_status = "✗ texture (missing required features)"; } wlr_log(WLR_DEBUG, " Shared memory: %s", shm_texture_status); if (modp.drmFormatModifierCount > 0) { add_fmt_props |= query_modifier_support(dev, &props, modp.drmFormatModifierCount); } if (add_fmt_props) { dev->format_props[dev->format_prop_count] = props; ++dev->format_prop_count; } else { vulkan_format_props_finish(&props); } } void vulkan_format_props_finish(struct wlr_vk_format_props *props) { free(props->dmabuf.texture_mods); free(props->dmabuf.render_mods); } const struct wlr_vk_format_modifier_props *vulkan_format_props_find_modifier( struct wlr_vk_format_props *props, uint64_t mod, bool render) { uint32_t len; const struct wlr_vk_format_modifier_props *mods; if (render) { len = props->dmabuf.render_mod_count; mods = props->dmabuf.render_mods; } else { len = props->dmabuf.texture_mod_count; mods = props->dmabuf.texture_mods; } for (uint32_t i = 0; i < len; ++i) { if (mods[i].props.drmFormatModifier == mod) { return &mods[i]; } } return NULL; } wlroots-0.17.1/render/vulkan/renderer.c000066400000000000000000002575721454110342200200600ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "render/dmabuf.h" #include "render/pixel_format.h" #include "render/vulkan.h" #include "render/vulkan/shaders/common.vert.h" #include "render/vulkan/shaders/texture.frag.h" #include "render/vulkan/shaders/quad.frag.h" #include "render/vulkan/shaders/output.frag.h" #include "types/wlr_buffer.h" #include "types/wlr_matrix.h" // TODO: // - simplify stage allocation, don't track allocations but use ringbuffer-like // - use a pipeline cache (not sure when to save though, after every pipeline // creation?) // - create pipelines as derivatives of each other // - evaluate if creating VkDeviceMemory pools is a good idea. // We can expect wayland client images to be fairly large (and shouldn't // have more than 4k of those I guess) but pooling memory allocations // might still be a good idea. static const VkDeviceSize min_stage_size = 1024 * 1024; // 1MB static const VkDeviceSize max_stage_size = 256 * min_stage_size; // 256MB static const size_t start_descriptor_pool_size = 256u; static bool default_debug = true; static const struct wlr_renderer_impl renderer_impl; bool wlr_renderer_is_vk(struct wlr_renderer *wlr_renderer) { return wlr_renderer->impl == &renderer_impl; } struct wlr_vk_renderer *vulkan_get_renderer(struct wlr_renderer *wlr_renderer) { assert(wlr_renderer_is_vk(wlr_renderer)); struct wlr_vk_renderer *renderer = wl_container_of(wlr_renderer, renderer, wlr_renderer); return renderer; } static struct wlr_vk_render_format_setup *find_or_create_render_setup( struct wlr_vk_renderer *renderer, const struct wlr_vk_format *format, bool has_blending_buffer); // https://www.w3.org/Graphics/Color/srgb static float color_to_linear(float non_linear) { return (non_linear > 0.04045) ? pow((non_linear + 0.055) / 1.055, 2.4) : non_linear / 12.92; } static void mat3_to_mat4(const float mat3[9], float mat4[4][4]) { memset(mat4, 0, sizeof(float) * 16); mat4[0][0] = mat3[0]; mat4[0][1] = mat3[1]; mat4[0][3] = mat3[2]; mat4[1][0] = mat3[3]; mat4[1][1] = mat3[4]; mat4[1][3] = mat3[5]; mat4[2][2] = 1.f; mat4[3][3] = 1.f; } static struct wlr_vk_descriptor_pool *alloc_ds( struct wlr_vk_renderer *renderer, VkDescriptorSet *ds, VkDescriptorType type, const VkDescriptorSetLayout *layout, struct wl_list *pool_list, size_t *last_pool_size) { VkResult res; bool found = false; struct wlr_vk_descriptor_pool *pool; wl_list_for_each(pool, pool_list, link) { if (pool->free > 0) { found = true; break; } } if (!found) { // create new pool pool = calloc(1, sizeof(*pool)); if (!pool) { wlr_log_errno(WLR_ERROR, "allocation failed"); return NULL; } size_t count = 2 * (*last_pool_size); if (!count) { count = start_descriptor_pool_size; } pool->free = count; VkDescriptorPoolSize pool_size = { .descriptorCount = count, .type = type, }; VkDescriptorPoolCreateInfo dpool_info = { .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, .maxSets = count, .poolSizeCount = 1, .pPoolSizes = &pool_size, .flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, }; res = vkCreateDescriptorPool(renderer->dev->dev, &dpool_info, NULL, &pool->pool); if (res != VK_SUCCESS) { wlr_vk_error("vkCreateDescriptorPool", res); free(pool); return NULL; } *last_pool_size = count; wl_list_insert(pool_list, &pool->link); } VkDescriptorSetAllocateInfo ds_info = { .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, .descriptorSetCount = 1, .pSetLayouts = layout, .descriptorPool = pool->pool, }; res = vkAllocateDescriptorSets(renderer->dev->dev, &ds_info, ds); if (res != VK_SUCCESS) { wlr_vk_error("vkAllocateDescriptorSets", res); return NULL; } --pool->free; return pool; } struct wlr_vk_descriptor_pool *vulkan_alloc_texture_ds( struct wlr_vk_renderer *renderer, VkDescriptorSetLayout ds_layout, VkDescriptorSet *ds) { return alloc_ds(renderer, ds, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, &ds_layout, &renderer->descriptor_pools, &renderer->last_pool_size); } struct wlr_vk_descriptor_pool *vulkan_alloc_blend_ds( struct wlr_vk_renderer *renderer, VkDescriptorSet *ds) { return alloc_ds(renderer, ds, VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, &renderer->output_ds_layout, &renderer->output_descriptor_pools, &renderer->last_output_pool_size); } void vulkan_free_ds(struct wlr_vk_renderer *renderer, struct wlr_vk_descriptor_pool *pool, VkDescriptorSet ds) { vkFreeDescriptorSets(renderer->dev->dev, pool->pool, 1, &ds); ++pool->free; } static void destroy_render_format_setup(struct wlr_vk_renderer *renderer, struct wlr_vk_render_format_setup *setup) { if (!setup) { return; } VkDevice dev = renderer->dev->dev; vkDestroyRenderPass(dev, setup->render_pass, NULL); vkDestroyPipeline(dev, setup->output_pipe, NULL); struct wlr_vk_pipeline *pipeline, *tmp_pipeline; wl_list_for_each_safe(pipeline, tmp_pipeline, &setup->pipelines, link) { vkDestroyPipeline(dev, pipeline->vk, NULL); free(pipeline); } } static void shared_buffer_destroy(struct wlr_vk_renderer *r, struct wlr_vk_shared_buffer *buffer) { if (!buffer) { return; } if (buffer->allocs.size > 0) { wlr_log(WLR_ERROR, "shared_buffer_finish: %zu allocations left", buffer->allocs.size / sizeof(struct wlr_vk_allocation)); } wl_array_release(&buffer->allocs); if (buffer->buffer) { vkDestroyBuffer(r->dev->dev, buffer->buffer, NULL); } if (buffer->memory) { vkFreeMemory(r->dev->dev, buffer->memory, NULL); } wl_list_remove(&buffer->link); free(buffer); } struct wlr_vk_buffer_span vulkan_get_stage_span(struct wlr_vk_renderer *r, VkDeviceSize size, VkDeviceSize alignment) { // try to find free span // simple greedy allocation algorithm - should be enough for this usecase // since all allocations are freed together after the frame struct wlr_vk_shared_buffer *buf; wl_list_for_each_reverse(buf, &r->stage.buffers, link) { VkDeviceSize start = 0u; if (buf->allocs.size > 0) { const struct wlr_vk_allocation *allocs = buf->allocs.data; size_t allocs_len = buf->allocs.size / sizeof(struct wlr_vk_allocation); const struct wlr_vk_allocation *last = &allocs[allocs_len - 1]; start = last->start + last->size; } assert(start <= buf->buf_size); // ensure the proposed start is a multiple of alignment start += alignment - 1 - ((start + alignment - 1) % alignment); if (buf->buf_size - start < size) { continue; } struct wlr_vk_allocation *a = wl_array_add(&buf->allocs, sizeof(*a)); if (a == NULL) { wlr_log_errno(WLR_ERROR, "Allocation failed"); goto error_alloc; } *a = (struct wlr_vk_allocation){ .start = start, .size = size, }; return (struct wlr_vk_buffer_span) { .buffer = buf, .alloc = *a, }; } if (size > max_stage_size) { wlr_log(WLR_ERROR, "cannot vulkan stage buffer: " "requested size (%zu bytes) exceeds maximum (%zu bytes)", (size_t)size, (size_t)max_stage_size); goto error_alloc; } // we didn't find a free buffer - create one // size = clamp(max(size * 2, prev_size * 2), min_size, max_size) VkDeviceSize bsize = size * 2; bsize = bsize < min_stage_size ? min_stage_size : bsize; if (!wl_list_empty(&r->stage.buffers)) { struct wl_list *last_link = r->stage.buffers.prev; struct wlr_vk_shared_buffer *prev = wl_container_of( last_link, prev, link); VkDeviceSize last_size = 2 * prev->buf_size; bsize = bsize < last_size ? last_size : bsize; } if (bsize > max_stage_size) { wlr_log(WLR_INFO, "vulkan stage buffers have reached max size"); bsize = max_stage_size; } // create buffer buf = calloc(1, sizeof(*buf)); if (!buf) { wlr_log_errno(WLR_ERROR, "Allocation failed"); goto error_alloc; } VkResult res; VkBufferCreateInfo buf_info = { .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, .size = bsize, .usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT, .sharingMode = VK_SHARING_MODE_EXCLUSIVE, }; res = vkCreateBuffer(r->dev->dev, &buf_info, NULL, &buf->buffer); if (res != VK_SUCCESS) { wlr_vk_error("vkCreateBuffer", res); goto error; } VkMemoryRequirements mem_reqs; vkGetBufferMemoryRequirements(r->dev->dev, buf->buffer, &mem_reqs); int mem_type_index = vulkan_find_mem_type(r->dev, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, mem_reqs.memoryTypeBits); if (mem_type_index < 0) { wlr_log(WLR_ERROR, "Failed to find memory type"); goto error; } VkMemoryAllocateInfo mem_info = { .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, .allocationSize = mem_reqs.size, .memoryTypeIndex = (uint32_t)mem_type_index, }; res = vkAllocateMemory(r->dev->dev, &mem_info, NULL, &buf->memory); if (res != VK_SUCCESS) { wlr_vk_error("vkAllocatorMemory", res); goto error; } res = vkBindBufferMemory(r->dev->dev, buf->buffer, buf->memory, 0); if (res != VK_SUCCESS) { wlr_vk_error("vkBindBufferMemory", res); goto error; } struct wlr_vk_allocation *a = wl_array_add(&buf->allocs, sizeof(*a)); if (a == NULL) { wlr_log_errno(WLR_ERROR, "Allocation failed"); goto error; } wlr_log(WLR_DEBUG, "Created new vk staging buffer of size %" PRIu64, bsize); buf->buf_size = bsize; wl_list_insert(&r->stage.buffers, &buf->link); *a = (struct wlr_vk_allocation){ .start = 0, .size = size, }; return (struct wlr_vk_buffer_span) { .buffer = buf, .alloc = *a, }; error: shared_buffer_destroy(r, buf); error_alloc: return (struct wlr_vk_buffer_span) { .buffer = NULL, .alloc = (struct wlr_vk_allocation) {0, 0}, }; } VkCommandBuffer vulkan_record_stage_cb(struct wlr_vk_renderer *renderer) { if (renderer->stage.cb == NULL) { renderer->stage.cb = vulkan_acquire_command_buffer(renderer); if (renderer->stage.cb == NULL) { return VK_NULL_HANDLE; } VkCommandBufferBeginInfo begin_info = { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, }; vkBeginCommandBuffer(renderer->stage.cb->vk, &begin_info); } return renderer->stage.cb->vk; } bool vulkan_submit_stage_wait(struct wlr_vk_renderer *renderer) { if (renderer->stage.cb == NULL) { return false; } struct wlr_vk_command_buffer *cb = renderer->stage.cb; renderer->stage.cb = NULL; uint64_t timeline_point = vulkan_end_command_buffer(cb, renderer); if (timeline_point == 0) { return false; } VkTimelineSemaphoreSubmitInfoKHR timeline_submit_info = { .sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO_KHR, .signalSemaphoreValueCount = 1, .pSignalSemaphoreValues = &timeline_point, }; VkSubmitInfo submit_info = { .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, .pNext = &timeline_submit_info, .commandBufferCount = 1, .pCommandBuffers = &cb->vk, .signalSemaphoreCount = 1, .pSignalSemaphores = &renderer->timeline_semaphore, }; VkResult res = vkQueueSubmit(renderer->dev->queue, 1, &submit_info, VK_NULL_HANDLE); if (res != VK_SUCCESS) { wlr_vk_error("vkQueueSubmit", res); return false; } // NOTE: don't release stage allocations here since they may still be // used for reading. Will be done next frame. return vulkan_wait_command_buffer(cb, renderer); } struct wlr_vk_format_props *vulkan_format_props_from_drm( struct wlr_vk_device *dev, uint32_t drm_fmt) { for (size_t i = 0u; i < dev->format_prop_count; ++i) { if (dev->format_props[i].format.drm == drm_fmt) { return &dev->format_props[i]; } } return NULL; } static bool init_command_buffer(struct wlr_vk_command_buffer *cb, struct wlr_vk_renderer *renderer) { VkResult res; VkCommandBuffer vk_cb = VK_NULL_HANDLE; VkCommandBufferAllocateInfo cmd_buf_info = { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, .commandPool = renderer->command_pool, .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, .commandBufferCount = 1, }; res = vkAllocateCommandBuffers(renderer->dev->dev, &cmd_buf_info, &vk_cb); if (res != VK_SUCCESS) { wlr_vk_error("vkAllocateCommandBuffers", res); return false; } *cb = (struct wlr_vk_command_buffer){ .vk = vk_cb, }; wl_list_init(&cb->destroy_textures); wl_list_init(&cb->stage_buffers); return true; } bool vulkan_wait_command_buffer(struct wlr_vk_command_buffer *cb, struct wlr_vk_renderer *renderer) { VkResult res; assert(cb->vk != VK_NULL_HANDLE && !cb->recording); VkSemaphoreWaitInfoKHR wait_info = { .sType = VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO_KHR, .semaphoreCount = 1, .pSemaphores = &renderer->timeline_semaphore, .pValues = &cb->timeline_point, }; res = renderer->dev->api.vkWaitSemaphoresKHR(renderer->dev->dev, &wait_info, UINT64_MAX); if (res != VK_SUCCESS) { wlr_vk_error("vkWaitSemaphoresKHR", res); return false; } return true; } static void release_command_buffer_resources(struct wlr_vk_command_buffer *cb, struct wlr_vk_renderer *renderer) { struct wlr_vk_texture *texture, *texture_tmp; wl_list_for_each_safe(texture, texture_tmp, &cb->destroy_textures, destroy_link) { wl_list_remove(&texture->destroy_link); texture->last_used_cb = NULL; wlr_texture_destroy(&texture->wlr_texture); } struct wlr_vk_shared_buffer *buf, *buf_tmp; wl_list_for_each_safe(buf, buf_tmp, &cb->stage_buffers, link) { buf->allocs.size = 0; wl_list_remove(&buf->link); wl_list_insert(&renderer->stage.buffers, &buf->link); } } static struct wlr_vk_command_buffer *get_command_buffer( struct wlr_vk_renderer *renderer) { VkResult res; uint64_t current_point; res = renderer->dev->api.vkGetSemaphoreCounterValueKHR(renderer->dev->dev, renderer->timeline_semaphore, ¤t_point); if (res != VK_SUCCESS) { wlr_vk_error("vkGetSemaphoreCounterValueKHR", res); return NULL; } // Destroy textures for completed command buffers for (size_t i = 0; i < VULKAN_COMMAND_BUFFERS_CAP; i++) { struct wlr_vk_command_buffer *cb = &renderer->command_buffers[i]; if (cb->vk != VK_NULL_HANDLE && !cb->recording && cb->timeline_point <= current_point) { release_command_buffer_resources(cb, renderer); } } // First try to find an existing command buffer which isn't busy struct wlr_vk_command_buffer *unused = NULL; struct wlr_vk_command_buffer *wait = NULL; for (size_t i = 0; i < VULKAN_COMMAND_BUFFERS_CAP; i++) { struct wlr_vk_command_buffer *cb = &renderer->command_buffers[i]; if (cb->vk == VK_NULL_HANDLE) { unused = cb; break; } if (cb->recording) { continue; } if (cb->timeline_point <= current_point) { return cb; } if (wait == NULL || cb->timeline_point < wait->timeline_point) { wait = cb; } } // If there is an unused slot, initialize it if (unused != NULL) { if (!init_command_buffer(unused, renderer)) { return NULL; } return unused; } // Block until a busy command buffer becomes available if (!vulkan_wait_command_buffer(wait, renderer)) { return NULL; } return wait; } struct wlr_vk_command_buffer *vulkan_acquire_command_buffer( struct wlr_vk_renderer *renderer) { struct wlr_vk_command_buffer *cb = get_command_buffer(renderer); if (cb == NULL) { return NULL; } assert(!cb->recording); cb->recording = true; return cb; } uint64_t vulkan_end_command_buffer(struct wlr_vk_command_buffer *cb, struct wlr_vk_renderer *renderer) { assert(cb->recording); cb->recording = false; VkResult res = vkEndCommandBuffer(cb->vk); if (res != VK_SUCCESS) { wlr_vk_error("vkEndCommandBuffer", res); return 0; } renderer->timeline_point++; cb->timeline_point = renderer->timeline_point; return cb->timeline_point; } void vulkan_reset_command_buffer(struct wlr_vk_command_buffer *cb) { if (cb == NULL) { return; } cb->recording = false; VkResult res = vkResetCommandBuffer(cb->vk, 0); if (res != VK_SUCCESS) { wlr_vk_error("vkResetCommandBuffer", res); } } static void destroy_render_buffer(struct wlr_vk_render_buffer *buffer) { wl_list_remove(&buffer->link); wlr_addon_finish(&buffer->addon); assert(buffer->renderer->current_render_buffer != buffer); VkDevice dev = buffer->renderer->dev->dev; // TODO: asynchronously wait for the command buffers using this render // buffer to complete (just like we do for textures) VkResult res = vkQueueWaitIdle(buffer->renderer->dev->queue); if (res != VK_SUCCESS) { wlr_vk_error("vkQueueWaitIdle", res); } vkDestroyFramebuffer(dev, buffer->framebuffer, NULL); vkDestroyImageView(dev, buffer->image_view, NULL); vkDestroyImage(dev, buffer->image, NULL); for (size_t i = 0u; i < buffer->mem_count; ++i) { vkFreeMemory(dev, buffer->memories[i], NULL); } vkDestroyImage(dev, buffer->blend_image, NULL); vkFreeMemory(dev, buffer->blend_memory, NULL); vkDestroyImageView(dev, buffer->blend_image_view, NULL); if (buffer->blend_attachment_pool) { vulkan_free_ds(buffer->renderer, buffer->blend_attachment_pool, buffer->blend_descriptor_set); } free(buffer); } static void handle_render_buffer_destroy(struct wlr_addon *addon) { struct wlr_vk_render_buffer *buffer = wl_container_of(addon, buffer, addon); destroy_render_buffer(buffer); } static struct wlr_addon_interface render_buffer_addon_impl = { .name = "wlr_vk_render_buffer", .destroy = handle_render_buffer_destroy, }; static bool setup_blend_image(struct wlr_vk_renderer *renderer, struct wlr_vk_render_buffer *buffer, int32_t width, int32_t height) { VkResult res; VkDevice dev = renderer->dev->dev; // Set up an extra 16F buffer on which to do linear blending, // and afterwards to render onto the target VkImageCreateInfo img_info = { .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, .imageType = VK_IMAGE_TYPE_2D, .format = VK_FORMAT_R16G16B16A16_SFLOAT, .mipLevels = 1, .arrayLayers = 1, .samples = VK_SAMPLE_COUNT_1_BIT, .sharingMode = VK_SHARING_MODE_EXCLUSIVE, .tiling = VK_IMAGE_TILING_OPTIMAL, .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, .extent = (VkExtent3D) { width, height, 1 }, .usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT, }; res = vkCreateImage(dev, &img_info, NULL, &buffer->blend_image); if (res != VK_SUCCESS) { wlr_vk_error("vkCreateImage failed", res); goto error; } VkMemoryRequirements mem_reqs; vkGetImageMemoryRequirements(dev, buffer->blend_image, &mem_reqs); int mem_type_index = vulkan_find_mem_type(renderer->dev, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, mem_reqs.memoryTypeBits); if (mem_type_index == -1) { wlr_log(WLR_ERROR, "failed to find suitable vulkan memory type"); goto error; } VkMemoryAllocateInfo mem_info = { .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, .allocationSize = mem_reqs.size, .memoryTypeIndex = mem_type_index, }; res = vkAllocateMemory(dev, &mem_info, NULL, &buffer->blend_memory); if (res != VK_SUCCESS) { wlr_vk_error("vkAllocatorMemory failed", res); goto error; } res = vkBindImageMemory(dev, buffer->blend_image, buffer->blend_memory, 0); if (res != VK_SUCCESS) { wlr_vk_error("vkBindMemory failed", res); goto error; } VkImageViewCreateInfo blend_view_info = { .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, .image = buffer->blend_image, .viewType = VK_IMAGE_VIEW_TYPE_2D, .format = img_info.format, .components.r = VK_COMPONENT_SWIZZLE_IDENTITY, .components.g = VK_COMPONENT_SWIZZLE_IDENTITY, .components.b = VK_COMPONENT_SWIZZLE_IDENTITY, .components.a = VK_COMPONENT_SWIZZLE_IDENTITY, .subresourceRange = (VkImageSubresourceRange) { .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .baseMipLevel = 0, .levelCount = 1, .baseArrayLayer = 0, .layerCount = 1, }, }; res = vkCreateImageView(dev, &blend_view_info, NULL, &buffer->blend_image_view); if (res != VK_SUCCESS) { wlr_vk_error("vkCreateImageView failed", res); goto error; } buffer->blend_attachment_pool = vulkan_alloc_blend_ds(renderer, &buffer->blend_descriptor_set); if (!buffer->blend_attachment_pool) { wlr_log(WLR_ERROR, "failed to allocate descriptor"); goto error; } VkDescriptorImageInfo ds_attach_info = { .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, .imageView = buffer->blend_image_view, .sampler = VK_NULL_HANDLE, }; VkWriteDescriptorSet ds_write = { .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, .descriptorCount = 1, .descriptorType = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, .dstSet = buffer->blend_descriptor_set, .dstBinding = 0, .pImageInfo = &ds_attach_info, }; vkUpdateDescriptorSets(dev, 1, &ds_write, 0, NULL); return true; error: // cleaning up blend_attachment_pool, blend_descriptor_set, blend_image, // blend_memory, and blend_image_view is the caller's responsibility, // since it will need to do this anyway if framebuffer setup fails return false; } static struct wlr_vk_render_buffer *create_render_buffer( struct wlr_vk_renderer *renderer, struct wlr_buffer *wlr_buffer) { VkResult res; VkDevice dev = renderer->dev->dev; struct wlr_vk_render_buffer *buffer = calloc(1, sizeof(*buffer)); if (buffer == NULL) { wlr_log_errno(WLR_ERROR, "Allocation failed"); return NULL; } buffer->wlr_buffer = wlr_buffer; buffer->renderer = renderer; struct wlr_dmabuf_attributes dmabuf = {0}; if (!wlr_buffer_get_dmabuf(wlr_buffer, &dmabuf)) { goto error; } wlr_log(WLR_DEBUG, "vulkan create_render_buffer: %.4s, %dx%d", (const char*) &dmabuf.format, dmabuf.width, dmabuf.height); buffer->image = vulkan_import_dmabuf(renderer, &dmabuf, buffer->memories, &buffer->mem_count, true); if (!buffer->image) { goto error; } const struct wlr_vk_format_props *fmt = vulkan_format_props_from_drm( renderer->dev, dmabuf.format); if (fmt == NULL) { wlr_log(WLR_ERROR, "Unsupported pixel format %"PRIx32 " (%.4s)", dmabuf.format, (const char*) &dmabuf.format); goto error; } VkImageViewCreateInfo view_info = { .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, .image = buffer->image, .viewType = VK_IMAGE_VIEW_TYPE_2D, .format = fmt->format.vk, .components.r = VK_COMPONENT_SWIZZLE_IDENTITY, .components.g = VK_COMPONENT_SWIZZLE_IDENTITY, .components.b = VK_COMPONENT_SWIZZLE_IDENTITY, .components.a = VK_COMPONENT_SWIZZLE_IDENTITY, .subresourceRange = (VkImageSubresourceRange) { .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .baseMipLevel = 0, .levelCount = 1, .baseArrayLayer = 0, .layerCount = 1, }, }; res = vkCreateImageView(dev, &view_info, NULL, &buffer->image_view); if (res != VK_SUCCESS) { wlr_vk_error("vkCreateImageView failed", res); goto error; } bool has_blending_buffer = !fmt->format.is_srgb; buffer->render_setup = find_or_create_render_setup( renderer, &fmt->format, has_blending_buffer); if (!buffer->render_setup) { goto error; } VkImageView attachments[2] = {0}; uint32_t attachment_count = 0; if (has_blending_buffer) { if (!setup_blend_image(renderer, buffer, dmabuf.width, dmabuf.height)) { goto error; } attachments[attachment_count++] = buffer->blend_image_view; } attachments[attachment_count++] = buffer->image_view; VkFramebufferCreateInfo fb_info = { .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, .attachmentCount = attachment_count, .pAttachments = attachments, .flags = 0u, .width = dmabuf.width, .height = dmabuf.height, .layers = 1u, .renderPass = buffer->render_setup->render_pass, }; res = vkCreateFramebuffer(dev, &fb_info, NULL, &buffer->framebuffer); if (res != VK_SUCCESS) { wlr_vk_error("vkCreateFramebuffer", res); goto error; } wlr_addon_init(&buffer->addon, &wlr_buffer->addons, renderer, &render_buffer_addon_impl); wl_list_insert(&renderer->render_buffers, &buffer->link); return buffer; error: if (buffer->blend_attachment_pool) { vulkan_free_ds(buffer->renderer, buffer->blend_attachment_pool, buffer->blend_descriptor_set); } vkDestroyImage(dev, buffer->blend_image, NULL); vkFreeMemory(dev, buffer->blend_memory, NULL); vkDestroyImageView(dev, buffer->blend_image_view, NULL); vkDestroyFramebuffer(dev, buffer->framebuffer, NULL); vkDestroyImageView(dev, buffer->image_view, NULL); vkDestroyImage(dev, buffer->image, NULL); for (size_t i = 0u; i < buffer->mem_count; ++i) { vkFreeMemory(dev, buffer->memories[i], NULL); } wlr_dmabuf_attributes_finish(&dmabuf); free(buffer); return NULL; } static struct wlr_vk_render_buffer *get_render_buffer( struct wlr_vk_renderer *renderer, struct wlr_buffer *wlr_buffer) { struct wlr_addon *addon = wlr_addon_find(&wlr_buffer->addons, renderer, &render_buffer_addon_impl); if (addon == NULL) { return NULL; } struct wlr_vk_render_buffer *buffer = wl_container_of(addon, buffer, addon); return buffer; } static bool vulkan_bind_buffer(struct wlr_renderer *wlr_renderer, struct wlr_buffer *wlr_buffer) { struct wlr_vk_renderer *renderer = vulkan_get_renderer(wlr_renderer); if (renderer->current_render_buffer) { wlr_buffer_unlock(renderer->current_render_buffer->wlr_buffer); renderer->current_render_buffer = NULL; } if (!wlr_buffer) { return true; } struct wlr_vk_render_buffer *buffer = get_render_buffer(renderer, wlr_buffer); if (!buffer) { buffer = create_render_buffer(renderer, wlr_buffer); if (!buffer) { return false; } } wlr_buffer_lock(wlr_buffer); renderer->current_render_buffer = buffer; return true; } static bool vulkan_begin(struct wlr_renderer *wlr_renderer, uint32_t width, uint32_t height) { struct wlr_vk_renderer *renderer = vulkan_get_renderer(wlr_renderer); assert(renderer->current_render_buffer); struct wlr_vk_command_buffer *cb = vulkan_acquire_command_buffer(renderer); if (cb == NULL) { return false; } assert(renderer->current_command_buffer == NULL); renderer->current_command_buffer = cb; VkCommandBufferBeginInfo begin_info = { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, }; VkResult res = vkBeginCommandBuffer(cb->vk, &begin_info); if (res != VK_SUCCESS) { wlr_vk_error("vkBeginCommandBuffer", res); return false; } // begin render pass VkFramebuffer fb = renderer->current_render_buffer->framebuffer; VkRect2D rect = {{0, 0}, {width, height}}; renderer->scissor = rect; VkRenderPassBeginInfo rp_info = { .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, .renderArea = rect, .renderPass = renderer->current_render_buffer->render_setup->render_pass, .framebuffer = fb, .clearValueCount = 0, }; vkCmdBeginRenderPass(cb->vk, &rp_info, VK_SUBPASS_CONTENTS_INLINE); VkViewport vp = {0.f, 0.f, (float) width, (float) height, 0.f, 1.f}; vkCmdSetViewport(cb->vk, 0, 1, &vp); vkCmdSetScissor(cb->vk, 0, 1, &rect); // Refresh projection matrix. // matrix_projection() assumes a GL coordinate system so we need // to pass WL_OUTPUT_TRANSFORM_FLIPPED_180 to adjust it for vulkan. matrix_projection(renderer->projection, width, height, WL_OUTPUT_TRANSFORM_FLIPPED_180); renderer->render_width = width; renderer->render_height = height; renderer->bound_pipe = VK_NULL_HANDLE; return true; } bool vulkan_sync_foreign_texture(struct wlr_vk_texture *texture) { struct wlr_vk_renderer *renderer = texture->renderer; VkResult res; struct wlr_dmabuf_attributes dmabuf = {0}; if (!wlr_buffer_get_dmabuf(texture->buffer, &dmabuf)) { wlr_log(WLR_ERROR, "Failed to get texture DMA-BUF"); return false; } if (!renderer->dev->implicit_sync_interop) { // We have no choice but to block here sadly for (int i = 0; i < dmabuf.n_planes; i++) { struct pollfd pollfd = { .fd = dmabuf.fd[i], .events = POLLIN, }; int timeout_ms = 1000; int ret = poll(&pollfd, 1, timeout_ms); if (ret < 0) { wlr_log_errno(WLR_ERROR, "Failed to wait for DMA-BUF fence"); return false; } else if (ret == 0) { wlr_log(WLR_ERROR, "Timed out while waiting for DMA-BUF fence"); return false; } } return true; } for (int i = 0; i < dmabuf.n_planes; i++) { int sync_file_fd = dmabuf_export_sync_file(dmabuf.fd[i], DMA_BUF_SYNC_READ); if (sync_file_fd < 0) { wlr_log(WLR_ERROR, "Failed to extract DMA-BUF fence"); return false; } if (texture->foreign_semaphores[i] == VK_NULL_HANDLE) { VkSemaphoreCreateInfo semaphore_info = { .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, }; res = vkCreateSemaphore(renderer->dev->dev, &semaphore_info, NULL, &texture->foreign_semaphores[i]); if (res != VK_SUCCESS) { close(sync_file_fd); wlr_vk_error("vkCreateSemaphore", res); return false; } } VkImportSemaphoreFdInfoKHR import_info = { .sType = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR, .handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT, .flags = VK_SEMAPHORE_IMPORT_TEMPORARY_BIT, .semaphore = texture->foreign_semaphores[i], .fd = sync_file_fd, }; res = renderer->dev->api.vkImportSemaphoreFdKHR(renderer->dev->dev, &import_info); if (res != VK_SUCCESS) { close(sync_file_fd); wlr_vk_error("vkImportSemaphoreFdKHR", res); return false; } } return true; } bool vulkan_sync_render_buffer(struct wlr_vk_renderer *renderer, struct wlr_vk_render_buffer *render_buffer, struct wlr_vk_command_buffer *cb) { VkResult res; if (!renderer->dev->implicit_sync_interop) { // We have no choice but to block here sadly return vulkan_wait_command_buffer(cb, renderer); } struct wlr_dmabuf_attributes dmabuf = {0}; if (!wlr_buffer_get_dmabuf(render_buffer->wlr_buffer, &dmabuf)) { wlr_log(WLR_ERROR, "wlr_buffer_get_dmabuf failed"); return false; } // Note: vkGetSemaphoreFdKHR implicitly resets the semaphore const VkSemaphoreGetFdInfoKHR get_fence_fd_info = { .sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR, .semaphore = cb->binary_semaphore, .handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT, }; int sync_file_fd = -1; res = renderer->dev->api.vkGetSemaphoreFdKHR(renderer->dev->dev, &get_fence_fd_info, &sync_file_fd); if (res != VK_SUCCESS) { wlr_vk_error("vkGetSemaphoreFdKHR", res); return false; } for (int i = 0; i < dmabuf.n_planes; i++) { if (!dmabuf_import_sync_file(dmabuf.fd[i], DMA_BUF_SYNC_WRITE, sync_file_fd)) { close(sync_file_fd); return false; } } close(sync_file_fd); return true; } static void vulkan_end(struct wlr_renderer *wlr_renderer) { struct wlr_vk_renderer *renderer = vulkan_get_renderer(wlr_renderer); assert(renderer->current_render_buffer); struct wlr_vk_command_buffer *render_cb = renderer->current_command_buffer; assert(render_cb != NULL); renderer->current_command_buffer = NULL; if (vulkan_record_stage_cb(renderer) == VK_NULL_HANDLE) { return; } struct wlr_vk_command_buffer *stage_cb = renderer->stage.cb; assert(stage_cb != NULL); renderer->stage.cb = NULL; struct wlr_vk_render_buffer *current_rb = renderer->current_render_buffer; if (current_rb->blend_image) { // Apply output shader to map blend image to actual output image vkCmdNextSubpass(render_cb->vk, VK_SUBPASS_CONTENTS_INLINE); VkPipeline pipe = current_rb->render_setup->output_pipe; if (pipe != renderer->bound_pipe) { vkCmdBindPipeline(render_cb->vk, VK_PIPELINE_BIND_POINT_GRAPHICS, pipe); renderer->bound_pipe = pipe; } float final_matrix[9] = { renderer->render_width, 0.f, -1.f, 0.f, renderer->render_height, -1.f, 0.f, 0.f, 0.f, }; struct wlr_vk_vert_pcr_data vert_pcr_data; mat3_to_mat4(final_matrix, vert_pcr_data.mat4); vert_pcr_data.uv_off[0] = 0.f; vert_pcr_data.uv_off[1] = 0.f; vert_pcr_data.uv_size[0] = 1.f; vert_pcr_data.uv_size[1] = 1.f; vkCmdPushConstants(render_cb->vk, renderer->output_pipe_layout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(vert_pcr_data), &vert_pcr_data); vkCmdBindDescriptorSets(render_cb->vk, VK_PIPELINE_BIND_POINT_GRAPHICS, renderer->output_pipe_layout, 0, 1, ¤t_rb->blend_descriptor_set, 0, NULL); vkCmdDraw(render_cb->vk, 4, 1, 0, 0); } vkCmdEndRenderPass(render_cb->vk); renderer->render_width = 0u; renderer->render_height = 0u; renderer->bound_pipe = VK_NULL_HANDLE; // insert acquire and release barriers for dmabuf-images unsigned barrier_count = wl_list_length(&renderer->foreign_textures) + 1; VkImageMemoryBarrier *acquire_barriers = calloc(barrier_count, sizeof(*acquire_barriers)); VkImageMemoryBarrier *release_barriers = calloc(barrier_count, sizeof(*release_barriers)); VkSemaphoreSubmitInfoKHR *render_wait = calloc(barrier_count * WLR_DMABUF_MAX_PLANES, sizeof(*render_wait)); if (acquire_barriers == NULL || release_barriers == NULL || render_wait == NULL) { wlr_log_errno(WLR_ERROR, "Allocation failed"); free(acquire_barriers); free(release_barriers); free(render_wait); return; } struct wlr_vk_texture *texture, *tmp_tex; unsigned idx = 0; uint32_t render_wait_len = 0; wl_list_for_each_safe(texture, tmp_tex, &renderer->foreign_textures, foreign_link) { VkImageLayout src_layout = VK_IMAGE_LAYOUT_GENERAL; if (!texture->transitioned) { src_layout = VK_IMAGE_LAYOUT_UNDEFINED; texture->transitioned = true; } acquire_barriers[idx] = (VkImageMemoryBarrier){ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_FOREIGN_EXT, .dstQueueFamilyIndex = renderer->dev->queue_family, .image = texture->image, .oldLayout = src_layout, .newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, .srcAccessMask = 0, // ignored anyways .dstAccessMask = VK_ACCESS_SHADER_READ_BIT, .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .subresourceRange.layerCount = 1, .subresourceRange.levelCount = 1, }; release_barriers[idx] = (VkImageMemoryBarrier){ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .srcQueueFamilyIndex = renderer->dev->queue_family, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_FOREIGN_EXT, .image = texture->image, .oldLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, .newLayout = VK_IMAGE_LAYOUT_GENERAL, .srcAccessMask = VK_ACCESS_SHADER_READ_BIT, .dstAccessMask = 0, // ignored anyways .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .subresourceRange.layerCount = 1, .subresourceRange.levelCount = 1, }; ++idx; if (!vulkan_sync_foreign_texture(texture)) { wlr_log(WLR_ERROR, "Failed to wait for foreign texture DMA-BUF fence"); } else { for (size_t i = 0; i < WLR_DMABUF_MAX_PLANES; i++) { if (texture->foreign_semaphores[i] != VK_NULL_HANDLE) { assert(render_wait_len < barrier_count * WLR_DMABUF_MAX_PLANES); render_wait[render_wait_len++] = (VkSemaphoreSubmitInfoKHR){ .sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO_KHR, .semaphore = texture->foreign_semaphores[i], .stageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT_KHR, }; } } } wl_list_remove(&texture->foreign_link); texture->owned = false; } // also add acquire/release barriers for the current render buffer VkImageLayout src_layout = VK_IMAGE_LAYOUT_GENERAL; if (!current_rb->transitioned) { src_layout = VK_IMAGE_LAYOUT_PREINITIALIZED; current_rb->transitioned = true; } // acquire render buffer before rendering acquire_barriers[idx] = (VkImageMemoryBarrier){ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_FOREIGN_EXT, .dstQueueFamilyIndex = renderer->dev->queue_family, .image = renderer->current_render_buffer->image, .oldLayout = src_layout, .newLayout = VK_IMAGE_LAYOUT_GENERAL, .srcAccessMask = 0, // ignored anyways .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .subresourceRange.layerCount = 1, .subresourceRange.levelCount = 1, }; // release render buffer after rendering release_barriers[idx] = (VkImageMemoryBarrier){ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .srcQueueFamilyIndex = renderer->dev->queue_family, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_FOREIGN_EXT, .image = renderer->current_render_buffer->image, .oldLayout = VK_IMAGE_LAYOUT_GENERAL, .newLayout = VK_IMAGE_LAYOUT_GENERAL, .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, .dstAccessMask = 0, // ignored anyways .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .subresourceRange.layerCount = 1, .subresourceRange.levelCount = 1, }; ++idx; if (current_rb->blend_image) { // The render pass changes the blend image layout from // color attachment to read only, so on each frame, before // the render pass starts, we change it back VkImageLayout blend_src_layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; if (!current_rb->blend_transitioned) { blend_src_layout = VK_IMAGE_LAYOUT_UNDEFINED; current_rb->blend_transitioned = true; } VkImageMemoryBarrier blend_acq_barrier = { .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .image = current_rb->blend_image, .oldLayout = blend_src_layout, .newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, .srcAccessMask = VK_ACCESS_SHADER_READ_BIT, .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, .subresourceRange = { .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .layerCount = 1, .levelCount = 1, } }; vkCmdPipelineBarrier(stage_cb->vk, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, 0, NULL, 0, NULL, 1, &blend_acq_barrier); } vkCmdPipelineBarrier(stage_cb->vk, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, 0, NULL, 0, NULL, barrier_count, acquire_barriers); vkCmdPipelineBarrier(render_cb->vk, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, NULL, 0, NULL, barrier_count, release_barriers); free(acquire_barriers); free(release_barriers); // No semaphores needed here. // We don't need a semaphore from the stage/transfer submission // to the render submissions since they are on the same queue // and we have a renderpass dependency for that. uint64_t stage_timeline_point = vulkan_end_command_buffer(stage_cb, renderer); if (stage_timeline_point == 0) { return; } VkCommandBufferSubmitInfoKHR stage_cb_info = { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO_KHR, .commandBuffer = stage_cb->vk, }; VkSemaphoreSubmitInfoKHR stage_signal = { .sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO_KHR, .semaphore = renderer->timeline_semaphore, .value = stage_timeline_point, }; VkSubmitInfo2KHR stage_submit = { .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO_2_KHR, .commandBufferInfoCount = 1, .pCommandBufferInfos = &stage_cb_info, .signalSemaphoreInfoCount = 1, .pSignalSemaphoreInfos = &stage_signal, }; VkSemaphoreSubmitInfoKHR stage_wait; if (renderer->stage.last_timeline_point > 0) { stage_wait = (VkSemaphoreSubmitInfoKHR){ .sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO_KHR, .semaphore = renderer->timeline_semaphore, .value = renderer->stage.last_timeline_point, .stageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT_KHR, }; stage_submit.waitSemaphoreInfoCount = 1; stage_submit.pWaitSemaphoreInfos = &stage_wait; } renderer->stage.last_timeline_point = stage_timeline_point; uint64_t render_timeline_point = vulkan_end_command_buffer(render_cb, renderer); if (render_timeline_point == 0) { return; } size_t render_signal_len = 1; VkSemaphoreSubmitInfoKHR render_signal[2] = {0}; render_signal[0] = (VkSemaphoreSubmitInfoKHR){ .sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO_KHR, .semaphore = renderer->timeline_semaphore, .value = render_timeline_point, }; if (renderer->dev->implicit_sync_interop) { if (render_cb->binary_semaphore == VK_NULL_HANDLE) { VkExportSemaphoreCreateInfo export_info = { .sType = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO, .handleTypes = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT, }; VkSemaphoreCreateInfo semaphore_info = { .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, .pNext = &export_info, }; VkResult res = vkCreateSemaphore(renderer->dev->dev, &semaphore_info, NULL, &render_cb->binary_semaphore); if (res != VK_SUCCESS) { wlr_vk_error("vkCreateSemaphore", res); return; } } render_signal[render_signal_len++] = (VkSemaphoreSubmitInfoKHR){ .sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO_KHR, .semaphore = render_cb->binary_semaphore, }; } VkCommandBufferSubmitInfoKHR render_cb_info = { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO_KHR, .commandBuffer = render_cb->vk, }; VkSubmitInfo2KHR render_submit = { .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO_2_KHR, .waitSemaphoreInfoCount = render_wait_len, .pWaitSemaphoreInfos = render_wait, .commandBufferInfoCount = 1, .pCommandBufferInfos = &render_cb_info, .signalSemaphoreInfoCount = render_signal_len, .pSignalSemaphoreInfos = render_signal, }; VkSubmitInfo2KHR submit_infos[] = { stage_submit, render_submit }; VkResult res = renderer->dev->api.vkQueueSubmit2KHR(renderer->dev->queue, 2, submit_infos, VK_NULL_HANDLE); if (res == VK_ERROR_DEVICE_LOST) { wlr_log(WLR_ERROR, "vkQueueSubmit failed with VK_ERROR_DEVICE_LOST"); wl_signal_emit_mutable(&wlr_renderer->events.lost, NULL); return; } else if (res != VK_SUCCESS) { wlr_vk_error("vkQueueSubmit", res); return; } free(render_wait); struct wlr_vk_shared_buffer *stage_buf, *stage_buf_tmp; wl_list_for_each_safe(stage_buf, stage_buf_tmp, &renderer->stage.buffers, link) { if (stage_buf->allocs.size == 0) { continue; } wl_list_remove(&stage_buf->link); wl_list_insert(&stage_cb->stage_buffers, &stage_buf->link); } if (!vulkan_sync_render_buffer(renderer, renderer->current_render_buffer, render_cb)) { return; } } static bool vulkan_render_subtexture_with_matrix(struct wlr_renderer *wlr_renderer, struct wlr_texture *wlr_texture, const struct wlr_fbox *box, const float matrix[static 9], float alpha) { struct wlr_vk_renderer *renderer = vulkan_get_renderer(wlr_renderer); VkCommandBuffer cb = renderer->current_command_buffer->vk; struct wlr_vk_texture *texture = vulkan_get_texture(wlr_texture); assert(texture->renderer == renderer); if (texture->dmabuf_imported && !texture->owned) { // Store this texture in the list of textures that need to be // acquired before rendering and released after rendering. // We don't do it here immediately since barriers inside // a renderpass are suboptimal (would require additional renderpass // dependency and potentially multiple barriers) and it's // better to issue one barrier for all used textures anyways. texture->owned = true; assert(texture->foreign_link.prev == NULL); assert(texture->foreign_link.next == NULL); wl_list_insert(&renderer->foreign_textures, &texture->foreign_link); } struct wlr_vk_pipeline *pipe = setup_get_or_create_pipeline( renderer->current_render_buffer->render_setup, &(struct wlr_vk_pipeline_key) { .layout = { .ycbcr_format = texture->format->is_ycbcr ? texture->format : NULL, }, .texture_transform = texture->transform, }); if (!pipe) { return false; } struct wlr_vk_texture_view *view = vulkan_texture_get_or_create_view(texture, pipe->layout); if (!view) { return false; } if (pipe->vk != renderer->bound_pipe) { vkCmdBindPipeline(cb, VK_PIPELINE_BIND_POINT_GRAPHICS, pipe->vk); renderer->bound_pipe = pipe->vk; } vkCmdBindDescriptorSets(cb, VK_PIPELINE_BIND_POINT_GRAPHICS, pipe->layout->vk, 0, 1, &view->ds, 0, NULL); float final_matrix[9]; wlr_matrix_multiply(final_matrix, renderer->projection, matrix); struct wlr_vk_vert_pcr_data vert_pcr_data; mat3_to_mat4(final_matrix, vert_pcr_data.mat4); vert_pcr_data.uv_off[0] = box->x / wlr_texture->width; vert_pcr_data.uv_off[1] = box->y / wlr_texture->height; vert_pcr_data.uv_size[0] = box->width / wlr_texture->width; vert_pcr_data.uv_size[1] = box->height / wlr_texture->height; vkCmdPushConstants(cb, pipe->layout->vk, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(vert_pcr_data), &vert_pcr_data); vkCmdPushConstants(cb, pipe->layout->vk, VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(vert_pcr_data), sizeof(float), &alpha); vkCmdDraw(cb, 4, 1, 0, 0); texture->last_used_cb = renderer->current_command_buffer; return true; } static void vulkan_clear(struct wlr_renderer *wlr_renderer, const float color[static 4]) { struct wlr_vk_renderer *renderer = vulkan_get_renderer(wlr_renderer); VkCommandBuffer cb = renderer->current_command_buffer->vk; if (renderer->scissor.extent.width == 0 || renderer->scissor.extent.height == 0) { return; } VkClearAttachment att = { .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .colorAttachment = 0u, // Input color values are given in srgb space, vulkan expects // them in linear space. We explicitly import argb8 render buffers // as srgb, vulkan will convert the input values we give here to // srgb first. // But in other parts of wlroots we just always assume // srgb so that's why we have to convert here. .clearValue.color.float32 = { color_to_linear(color[0]), color_to_linear(color[1]), color_to_linear(color[2]), color[3], // no conversion for alpha }, }; VkClearRect rect = { .rect = renderer->scissor, .layerCount = 1, }; vkCmdClearAttachments(cb, 1, &att, 1, &rect); } static void vulkan_scissor(struct wlr_renderer *wlr_renderer, struct wlr_box *box) { struct wlr_vk_renderer *renderer = vulkan_get_renderer(wlr_renderer); VkCommandBuffer cb = renderer->current_command_buffer->vk; uint32_t w = renderer->render_width; uint32_t h = renderer->render_height; struct wlr_box dst = {0, 0, w, h}; if (box && !wlr_box_intersection(&dst, box, &dst)) { dst = (struct wlr_box) {0, 0, 0, 0}; // empty } VkRect2D rect = (VkRect2D) {{dst.x, dst.y}, {dst.width, dst.height}}; renderer->scissor = rect; vkCmdSetScissor(cb, 0, 1, &rect); } static const uint32_t *vulkan_get_shm_texture_formats( struct wlr_renderer *wlr_renderer, size_t *len) { struct wlr_vk_renderer *renderer = vulkan_get_renderer(wlr_renderer); *len = renderer->dev->shm_format_count; return renderer->dev->shm_formats; } static void vulkan_render_quad_with_matrix(struct wlr_renderer *wlr_renderer, const float color[static 4], const float matrix[static 9]) { struct wlr_vk_renderer *renderer = vulkan_get_renderer(wlr_renderer); VkCommandBuffer cb = renderer->current_command_buffer->vk; struct wlr_vk_pipeline *pipe = setup_get_or_create_pipeline( renderer->current_render_buffer->render_setup, &(struct wlr_vk_pipeline_key) { .source = WLR_VK_SHADER_SOURCE_SINGLE_COLOR, .layout = { .ycbcr_format = NULL, }, }); if (!pipe) { return; } if (pipe->vk != renderer->bound_pipe) { vkCmdBindPipeline(cb, VK_PIPELINE_BIND_POINT_GRAPHICS, pipe->vk); renderer->bound_pipe = pipe->vk; } float final_matrix[9]; wlr_matrix_multiply(final_matrix, renderer->projection, matrix); struct wlr_vk_vert_pcr_data vert_pcr_data; mat3_to_mat4(final_matrix, vert_pcr_data.mat4); vert_pcr_data.uv_off[0] = 0.f; vert_pcr_data.uv_off[1] = 0.f; vert_pcr_data.uv_size[0] = 1.f; vert_pcr_data.uv_size[1] = 1.f; // Input color values are given in srgb space, shader expects // them in linear space. The shader does all computation in linear // space and expects in inputs in linear space since it outputs // colors in linear space as well (and vulkan then automatically // does the conversion for out SRGB render targets). // But in other parts of wlroots we just always assume // srgb so that's why we have to convert here. float linear_color[4]; linear_color[0] = color_to_linear(color[0]); linear_color[1] = color_to_linear(color[1]); linear_color[2] = color_to_linear(color[2]); linear_color[3] = color[3]; // no conversion for alpha vkCmdPushConstants(cb, pipe->layout->vk, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(vert_pcr_data), &vert_pcr_data); vkCmdPushConstants(cb, pipe->layout->vk, VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(vert_pcr_data), sizeof(float) * 4, linear_color); vkCmdDraw(cb, 4, 1, 0, 0); } static const struct wlr_drm_format_set *vulkan_get_dmabuf_texture_formats( struct wlr_renderer *wlr_renderer) { struct wlr_vk_renderer *renderer = vulkan_get_renderer(wlr_renderer); return &renderer->dev->dmabuf_texture_formats; } static const struct wlr_drm_format_set *vulkan_get_render_formats( struct wlr_renderer *wlr_renderer) { struct wlr_vk_renderer *renderer = vulkan_get_renderer(wlr_renderer); return &renderer->dev->dmabuf_render_formats; } static uint32_t vulkan_preferred_read_format( struct wlr_renderer *wlr_renderer) { struct wlr_vk_renderer *renderer = vulkan_get_renderer(wlr_renderer); struct wlr_dmabuf_attributes dmabuf = {0}; if (!wlr_buffer_get_dmabuf(renderer->current_render_buffer->wlr_buffer, &dmabuf)) { wlr_log(WLR_ERROR, "vulkan_preferred_read_format: Failed to get dmabuf of current render buffer"); return DRM_FORMAT_INVALID; } return dmabuf.format; } static void vulkan_destroy(struct wlr_renderer *wlr_renderer) { struct wlr_vk_renderer *renderer = vulkan_get_renderer(wlr_renderer); struct wlr_vk_device *dev = renderer->dev; if (!dev) { free(renderer); return; } assert(!renderer->current_render_buffer); VkResult res = vkDeviceWaitIdle(renderer->dev->dev); if (res != VK_SUCCESS) { wlr_vk_error("vkDeviceWaitIdle", res); } for (size_t i = 0; i < VULKAN_COMMAND_BUFFERS_CAP; i++) { struct wlr_vk_command_buffer *cb = &renderer->command_buffers[i]; if (cb->vk == VK_NULL_HANDLE) { continue; } release_command_buffer_resources(cb, renderer); if (cb->binary_semaphore != VK_NULL_HANDLE) { vkDestroySemaphore(renderer->dev->dev, cb->binary_semaphore, NULL); } } // stage.cb automatically freed with command pool struct wlr_vk_shared_buffer *buf, *tmp_buf; wl_list_for_each_safe(buf, tmp_buf, &renderer->stage.buffers, link) { shared_buffer_destroy(renderer, buf); } struct wlr_vk_texture *tex, *tex_tmp; wl_list_for_each_safe(tex, tex_tmp, &renderer->textures, link) { vulkan_texture_destroy(tex); } struct wlr_vk_render_buffer *render_buffer, *render_buffer_tmp; wl_list_for_each_safe(render_buffer, render_buffer_tmp, &renderer->render_buffers, link) { destroy_render_buffer(render_buffer); } struct wlr_vk_render_format_setup *setup, *tmp_setup; wl_list_for_each_safe(setup, tmp_setup, &renderer->render_format_setups, link) { destroy_render_format_setup(renderer, setup); } struct wlr_vk_descriptor_pool *pool, *tmp_pool; wl_list_for_each_safe(pool, tmp_pool, &renderer->descriptor_pools, link) { vkDestroyDescriptorPool(dev->dev, pool->pool, NULL); free(pool); } wl_list_for_each_safe(pool, tmp_pool, &renderer->output_descriptor_pools, link) { vkDestroyDescriptorPool(dev->dev, pool->pool, NULL); free(pool); } vkDestroyShaderModule(dev->dev, renderer->vert_module, NULL); vkDestroyShaderModule(dev->dev, renderer->tex_frag_module, NULL); vkDestroyShaderModule(dev->dev, renderer->quad_frag_module, NULL); vkDestroyShaderModule(dev->dev, renderer->output_module, NULL); struct wlr_vk_pipeline_layout *pipeline_layout, *pipeline_layout_tmp; wl_list_for_each_safe(pipeline_layout, pipeline_layout_tmp, &renderer->pipeline_layouts, link) { vkDestroyPipelineLayout(dev->dev, pipeline_layout->vk, NULL); vkDestroyDescriptorSetLayout(dev->dev, pipeline_layout->ds, NULL); vkDestroySampler(dev->dev, pipeline_layout->sampler, NULL); vkDestroySamplerYcbcrConversion(dev->dev, pipeline_layout->ycbcr.conversion, NULL); } vkDestroySemaphore(dev->dev, renderer->timeline_semaphore, NULL); vkDestroyPipelineLayout(dev->dev, renderer->output_pipe_layout, NULL); vkDestroyDescriptorSetLayout(dev->dev, renderer->output_ds_layout, NULL); vkDestroyCommandPool(dev->dev, renderer->command_pool, NULL); if (renderer->read_pixels_cache.initialized) { vkFreeMemory(dev->dev, renderer->read_pixels_cache.dst_img_memory, NULL); vkDestroyImage(dev->dev, renderer->read_pixels_cache.dst_image, NULL); } struct wlr_vk_instance *ini = dev->instance; vulkan_device_destroy(dev); vulkan_instance_destroy(ini); free(renderer); } static bool vulkan_read_pixels(struct wlr_renderer *wlr_renderer, uint32_t drm_format, uint32_t stride, uint32_t width, uint32_t height, uint32_t src_x, uint32_t src_y, uint32_t dst_x, uint32_t dst_y, void *data) { struct wlr_vk_renderer *vk_renderer = vulkan_get_renderer(wlr_renderer); VkDevice dev = vk_renderer->dev->dev; VkImage src_image = vk_renderer->current_render_buffer->image; const struct wlr_pixel_format_info *pixel_format_info = drm_get_pixel_format_info(drm_format); if (!pixel_format_info) { wlr_log(WLR_ERROR, "vulkan_read_pixels: could not find pixel format info " "for DRM format 0x%08x", drm_format); return false; } else if (pixel_format_info_pixels_per_block(pixel_format_info) != 1) { wlr_log(WLR_ERROR, "vulkan_read_pixels: block formats are not supported"); return false; } const struct wlr_vk_format *wlr_vk_format = vulkan_get_format_from_drm(drm_format); if (!wlr_vk_format) { wlr_log(WLR_ERROR, "vulkan_read_pixels: no vulkan format " "matching drm format 0x%08x available", drm_format); return false; } VkFormat dst_format = wlr_vk_format->vk; VkFormat src_format = vk_renderer->current_render_buffer->render_setup->render_format->vk; VkFormatProperties dst_format_props = {0}, src_format_props = {0}; vkGetPhysicalDeviceFormatProperties(vk_renderer->dev->phdev, dst_format, &dst_format_props); vkGetPhysicalDeviceFormatProperties(vk_renderer->dev->phdev, src_format, &src_format_props); bool blit_supported = src_format_props.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT && dst_format_props.linearTilingFeatures & VK_FORMAT_FEATURE_BLIT_DST_BIT; if (!blit_supported && src_format != dst_format) { wlr_log(WLR_ERROR, "vulkan_read_pixels: blit unsupported and no manual " "conversion available from src to dst format."); return false; } VkResult res; VkImage dst_image; VkDeviceMemory dst_img_memory; bool use_cached = vk_renderer->read_pixels_cache.initialized && vk_renderer->read_pixels_cache.drm_format == drm_format && vk_renderer->read_pixels_cache.width == width && vk_renderer->read_pixels_cache.height == height; if (use_cached) { dst_image = vk_renderer->read_pixels_cache.dst_image; dst_img_memory = vk_renderer->read_pixels_cache.dst_img_memory; } else { VkImageCreateInfo image_create_info = { .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, .imageType = VK_IMAGE_TYPE_2D, .format = dst_format, .extent.width = width, .extent.height = height, .extent.depth = 1, .arrayLayers = 1, .mipLevels = 1, .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, .samples = VK_SAMPLE_COUNT_1_BIT, .tiling = VK_IMAGE_TILING_LINEAR, .usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT }; res = vkCreateImage(dev, &image_create_info, NULL, &dst_image); if (res != VK_SUCCESS) { wlr_vk_error("vkCreateImage", res); return false; } VkMemoryRequirements mem_reqs; vkGetImageMemoryRequirements(dev, dst_image, &mem_reqs); int mem_type = vulkan_find_mem_type(vk_renderer->dev, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT, mem_reqs.memoryTypeBits); if (mem_type < 0) { wlr_log(WLR_ERROR, "vulkan_read_pixels: could not find adequate memory type"); goto destroy_image; } VkMemoryAllocateInfo mem_alloc_info = { .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, }; mem_alloc_info.allocationSize = mem_reqs.size; mem_alloc_info.memoryTypeIndex = mem_type; res = vkAllocateMemory(dev, &mem_alloc_info, NULL, &dst_img_memory); if (res != VK_SUCCESS) { wlr_vk_error("vkAllocateMemory", res); goto destroy_image; } res = vkBindImageMemory(dev, dst_image, dst_img_memory, 0); if (res != VK_SUCCESS) { wlr_vk_error("vkBindImageMemory", res); goto free_memory; } if (vk_renderer->read_pixels_cache.initialized) { vkFreeMemory(dev, vk_renderer->read_pixels_cache.dst_img_memory, NULL); vkDestroyImage(dev, vk_renderer->read_pixels_cache.dst_image, NULL); } vk_renderer->read_pixels_cache.initialized = true; vk_renderer->read_pixels_cache.drm_format = drm_format; vk_renderer->read_pixels_cache.dst_image = dst_image; vk_renderer->read_pixels_cache.dst_img_memory = dst_img_memory; vk_renderer->read_pixels_cache.width = width; vk_renderer->read_pixels_cache.height = height; } VkCommandBuffer cb = vulkan_record_stage_cb(vk_renderer); if (cb == VK_NULL_HANDLE) { return false; } vulkan_change_layout(cb, dst_image, VK_IMAGE_LAYOUT_UNDEFINED, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT); vulkan_change_layout(cb, src_image, VK_IMAGE_LAYOUT_GENERAL, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_MEMORY_READ_BIT, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_READ_BIT); if (blit_supported) { VkImageBlit image_blit_region = { .srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .srcSubresource.layerCount = 1, .srcOffsets[0] = { .x = src_x, .y = src_y, .z = 0, }, .srcOffsets[1] = { .x = src_x + width, .y = src_y + height, .z = 1, }, .dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .dstSubresource.layerCount = 1, .dstOffsets[1] = { .x = width, .y = height, .z = 1, } }; vkCmdBlitImage(cb, src_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dst_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &image_blit_region, VK_FILTER_NEAREST); } else { wlr_log(WLR_DEBUG, "vulkan_read_pixels: blit unsupported, falling back to vkCmdCopyImage."); VkImageCopy image_region = { .srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .srcSubresource.layerCount = 1, .srcOffset = { .x = src_x, .y = src_y, }, .dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .dstSubresource.layerCount = 1, .extent = { .width = width, .height = height, .depth = 1, } }; vkCmdCopyImage(cb, src_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dst_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &image_region); } vulkan_change_layout(cb, dst_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_GENERAL, VK_PIPELINE_STAGE_TRANSFER_BIT, 0); vulkan_change_layout(cb, src_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_READ_BIT, VK_IMAGE_LAYOUT_GENERAL, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_MEMORY_READ_BIT); if (!vulkan_submit_stage_wait(vk_renderer)) { return false; } VkImageSubresource img_sub_res = { .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .arrayLayer = 0, .mipLevel = 0 }; VkSubresourceLayout img_sub_layout; vkGetImageSubresourceLayout(dev, dst_image, &img_sub_res, &img_sub_layout); void *v; res = vkMapMemory(dev, dst_img_memory, 0, VK_WHOLE_SIZE, 0, &v); if (res != VK_SUCCESS) { wlr_vk_error("vkMapMemory", res); return false; } const char *d = (const char *)v + img_sub_layout.offset; unsigned char *p = (unsigned char *)data + dst_y * stride; uint32_t bytes_per_pixel = pixel_format_info->bytes_per_block; uint32_t pack_stride = img_sub_layout.rowPitch; if (pack_stride == stride && dst_x == 0) { memcpy(p, d, height * stride); } else { for (size_t i = 0; i < height; ++i) { memcpy(p + i * stride + dst_x * bytes_per_pixel, d + i * pack_stride, width * bytes_per_pixel); } } vkUnmapMemory(dev, dst_img_memory); // Don't need to free anything else, since memory and image are cached return true; free_memory: vkFreeMemory(dev, dst_img_memory, NULL); destroy_image: vkDestroyImage(dev, dst_image, NULL); return false; } static int vulkan_get_drm_fd(struct wlr_renderer *wlr_renderer) { struct wlr_vk_renderer *renderer = vulkan_get_renderer(wlr_renderer); return renderer->dev->drm_fd; } static uint32_t vulkan_get_render_buffer_caps(struct wlr_renderer *wlr_renderer) { return WLR_BUFFER_CAP_DMABUF; } static struct wlr_render_pass *vulkan_begin_buffer_pass(struct wlr_renderer *wlr_renderer, struct wlr_buffer *buffer, const struct wlr_buffer_pass_options *options) { struct wlr_vk_renderer *renderer = vulkan_get_renderer(wlr_renderer); struct wlr_vk_render_buffer *render_buffer = get_render_buffer(renderer, buffer); if (!render_buffer) { render_buffer = create_render_buffer(renderer, buffer); if (!render_buffer) { return NULL; } } struct wlr_vk_render_pass *render_pass = vulkan_begin_render_pass(renderer, render_buffer); if (render_pass == NULL) { return NULL; } return &render_pass->base; } static const struct wlr_renderer_impl renderer_impl = { .bind_buffer = vulkan_bind_buffer, .begin = vulkan_begin, .end = vulkan_end, .clear = vulkan_clear, .scissor = vulkan_scissor, .render_subtexture_with_matrix = vulkan_render_subtexture_with_matrix, .render_quad_with_matrix = vulkan_render_quad_with_matrix, .get_shm_texture_formats = vulkan_get_shm_texture_formats, .get_dmabuf_texture_formats = vulkan_get_dmabuf_texture_formats, .get_render_formats = vulkan_get_render_formats, .preferred_read_format = vulkan_preferred_read_format, .read_pixels = vulkan_read_pixels, .destroy = vulkan_destroy, .get_drm_fd = vulkan_get_drm_fd, .get_render_buffer_caps = vulkan_get_render_buffer_caps, .texture_from_buffer = vulkan_texture_from_buffer, .begin_buffer_pass = vulkan_begin_buffer_pass, }; // Initializes the VkDescriptorSetLayout and VkPipelineLayout needed // for the texture rendering pipeline using the given VkSampler. static bool init_tex_layouts(struct wlr_vk_renderer *renderer, VkSampler tex_sampler, VkDescriptorSetLayout *out_ds_layout, VkPipelineLayout *out_pipe_layout) { VkResult res; VkDevice dev = renderer->dev->dev; VkDescriptorSetLayoutBinding ds_binding = { .binding = 0, .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, .descriptorCount = 1, .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, .pImmutableSamplers = &tex_sampler, }; VkDescriptorSetLayoutCreateInfo ds_info = { .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, .bindingCount = 1, .pBindings = &ds_binding, }; res = vkCreateDescriptorSetLayout(dev, &ds_info, NULL, out_ds_layout); if (res != VK_SUCCESS) { wlr_vk_error("vkCreateDescriptorSetLayout", res); return false; } VkPushConstantRange pc_ranges[2] = { { .size = sizeof(struct wlr_vk_vert_pcr_data), .stageFlags = VK_SHADER_STAGE_VERTEX_BIT, }, { .offset = pc_ranges[0].size, .size = sizeof(float) * 4, // alpha or color .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, }, }; VkPipelineLayoutCreateInfo pl_info = { .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, .setLayoutCount = 1, .pSetLayouts = out_ds_layout, .pushConstantRangeCount = 2, .pPushConstantRanges = pc_ranges, }; res = vkCreatePipelineLayout(dev, &pl_info, NULL, out_pipe_layout); if (res != VK_SUCCESS) { wlr_vk_error("vkCreatePipelineLayout", res); return false; } return true; } static bool init_blend_to_output_layouts(struct wlr_vk_renderer *renderer, VkDescriptorSetLayout *out_ds_layout, VkPipelineLayout *out_pipe_layout) { VkResult res; VkDevice dev = renderer->dev->dev; VkDescriptorSetLayoutBinding ds_binding = { .binding = 0, .descriptorType = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, .descriptorCount = 1, .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, .pImmutableSamplers = NULL, }; VkDescriptorSetLayoutCreateInfo ds_info = { .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, .bindingCount = 1, .pBindings = &ds_binding, }; res = vkCreateDescriptorSetLayout(dev, &ds_info, NULL, out_ds_layout); if (res != VK_SUCCESS) { wlr_vk_error("vkCreateDescriptorSetLayout", res); return false; } // pipeline layout -- standard vertex uniforms, no shader uniforms VkPushConstantRange pc_ranges[1] = { { .size = sizeof(struct wlr_vk_vert_pcr_data), .stageFlags = VK_SHADER_STAGE_VERTEX_BIT, }, }; VkPipelineLayoutCreateInfo pl_info = { .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, .setLayoutCount = 1, .pSetLayouts = out_ds_layout, .pushConstantRangeCount = 1, .pPushConstantRanges = pc_ranges, }; res = vkCreatePipelineLayout(dev, &pl_info, NULL, out_pipe_layout); if (res != VK_SUCCESS) { wlr_vk_error("vkCreatePipelineLayout", res); return false; } return true; } static bool pipeline_layout_key_equals( const struct wlr_vk_pipeline_layout_key *a, const struct wlr_vk_pipeline_layout_key *b) { assert(!a->ycbcr_format || a->ycbcr_format->is_ycbcr); assert(!b->ycbcr_format || b->ycbcr_format->is_ycbcr); if (a->filter_mode != b->filter_mode) { return false; } if (a->ycbcr_format != b->ycbcr_format) { return false; } return true; } static bool pipeline_key_equals(const struct wlr_vk_pipeline_key *a, const struct wlr_vk_pipeline_key *b) { if (!pipeline_layout_key_equals(&a->layout, &b->layout)) { return false; } if (a->blend_mode != b->blend_mode) { return false; } if (a->source != b->source) { return false; } if (a->source == WLR_VK_SHADER_SOURCE_TEXTURE && a->texture_transform != b->texture_transform) { return false; } return true; } // Initializes the pipeline for rendering textures and using the given // VkRenderPass and VkPipelineLayout. struct wlr_vk_pipeline *setup_get_or_create_pipeline( struct wlr_vk_render_format_setup *setup, const struct wlr_vk_pipeline_key *key) { struct wlr_vk_pipeline *pipeline; wl_list_for_each(pipeline, &setup->pipelines, link) { if (pipeline_key_equals(&pipeline->key, key)) { return pipeline; } } struct wlr_vk_renderer *renderer = setup->renderer; struct wlr_vk_pipeline_layout *pipeline_layout = get_or_create_pipeline_layout( renderer, &key->layout); if (!pipeline_layout) { return NULL; } pipeline = calloc(1, sizeof(*pipeline)); if (!pipeline) { return NULL; } pipeline->setup = setup; pipeline->key = *key; pipeline->layout = pipeline_layout; VkResult res; VkDevice dev = renderer->dev->dev; uint32_t color_transform_type = key->texture_transform; VkSpecializationMapEntry spec_entry = { .constantID = 0, .offset = 0, .size = sizeof(uint32_t), }; VkSpecializationInfo specialization = { .mapEntryCount = 1, .pMapEntries = &spec_entry, .dataSize = sizeof(uint32_t), .pData = &color_transform_type, }; VkPipelineShaderStageCreateInfo stages[2]; stages[0] = (VkPipelineShaderStageCreateInfo) { .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, .stage = VK_SHADER_STAGE_VERTEX_BIT, .module = renderer->vert_module, .pName = "main", }; switch (key->source) { case WLR_VK_SHADER_SOURCE_SINGLE_COLOR: stages[1] = (VkPipelineShaderStageCreateInfo) { .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, .stage = VK_SHADER_STAGE_FRAGMENT_BIT, .module = renderer->quad_frag_module, .pName = "main", }; break; case WLR_VK_SHADER_SOURCE_TEXTURE: stages[1] = (VkPipelineShaderStageCreateInfo) { .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, .stage = VK_SHADER_STAGE_FRAGMENT_BIT, .module = renderer->tex_frag_module, .pName = "main", .pSpecializationInfo = &specialization, }; break; } VkPipelineInputAssemblyStateCreateInfo assembly = { .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN, }; VkPipelineRasterizationStateCreateInfo rasterization = { .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, .polygonMode = VK_POLYGON_MODE_FILL, .cullMode = VK_CULL_MODE_NONE, .frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE, .lineWidth = 1.f, }; VkPipelineColorBlendAttachmentState blend_attachment = { .blendEnable = key->blend_mode == WLR_RENDER_BLEND_MODE_PREMULTIPLIED, // we generally work with pre-multiplied alpha .srcColorBlendFactor = VK_BLEND_FACTOR_ONE, .dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA, .colorBlendOp = VK_BLEND_OP_ADD, .srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE, .dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA, .alphaBlendOp = VK_BLEND_OP_ADD, .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, }; VkPipelineColorBlendStateCreateInfo blend = { .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, .attachmentCount = 1, .pAttachments = &blend_attachment, }; VkPipelineMultisampleStateCreateInfo multisample = { .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, .rasterizationSamples = VK_SAMPLE_COUNT_1_BIT, }; VkPipelineViewportStateCreateInfo viewport = { .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, .viewportCount = 1, .scissorCount = 1, }; VkDynamicState dynStates[2] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR, }; VkPipelineDynamicStateCreateInfo dynamic = { .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, .pDynamicStates = dynStates, .dynamicStateCount = 2, }; VkPipelineVertexInputStateCreateInfo vertex = { .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, }; VkGraphicsPipelineCreateInfo pinfo = { .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, .layout = pipeline_layout->vk, .renderPass = setup->render_pass, .subpass = 0, .stageCount = 2, .pStages = stages, .pInputAssemblyState = &assembly, .pRasterizationState = &rasterization, .pColorBlendState = &blend, .pMultisampleState = &multisample, .pViewportState = &viewport, .pDynamicState = &dynamic, .pVertexInputState = &vertex, }; VkPipelineCache cache = VK_NULL_HANDLE; res = vkCreateGraphicsPipelines(dev, cache, 1, &pinfo, NULL, &pipeline->vk); if (res != VK_SUCCESS) { wlr_vk_error("failed to create vulkan pipelines:", res); free(pipeline); return NULL; } wl_list_insert(&setup->pipelines, &pipeline->link); return pipeline; } static bool init_blend_to_output_pipeline(struct wlr_vk_renderer *renderer, VkRenderPass rp, VkPipelineLayout pipe_layout, VkPipeline *pipe) { VkResult res; VkDevice dev = renderer->dev->dev; VkPipelineShaderStageCreateInfo tex_stages[2] = { { .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, .stage = VK_SHADER_STAGE_VERTEX_BIT, .module = renderer->vert_module, .pName = "main", }, { .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, .stage = VK_SHADER_STAGE_FRAGMENT_BIT, .module = renderer->output_module, .pName = "main", }, }; VkPipelineInputAssemblyStateCreateInfo assembly = { .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN, }; VkPipelineRasterizationStateCreateInfo rasterization = { .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, .polygonMode = VK_POLYGON_MODE_FILL, .cullMode = VK_CULL_MODE_NONE, .frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE, .lineWidth = 1.f, }; VkPipelineColorBlendAttachmentState blend_attachment = { .blendEnable = false, .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, }; VkPipelineColorBlendStateCreateInfo blend = { .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, .attachmentCount = 1, .pAttachments = &blend_attachment, }; VkPipelineMultisampleStateCreateInfo multisample = { .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, .rasterizationSamples = VK_SAMPLE_COUNT_1_BIT, }; VkPipelineViewportStateCreateInfo viewport = { .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, .viewportCount = 1, .scissorCount = 1, }; VkDynamicState dynStates[2] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR, }; VkPipelineDynamicStateCreateInfo dynamic = { .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, .pDynamicStates = dynStates, .dynamicStateCount = 2, }; VkPipelineVertexInputStateCreateInfo vertex = { .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, }; VkGraphicsPipelineCreateInfo pinfo = { .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, .pNext = NULL, .layout = pipe_layout, .renderPass = rp, .subpass = 1, // second subpass! .stageCount = 2, .pStages = tex_stages, .pInputAssemblyState = &assembly, .pRasterizationState = &rasterization, .pColorBlendState = &blend, .pMultisampleState = &multisample, .pViewportState = &viewport, .pDynamicState = &dynamic, .pVertexInputState = &vertex, }; VkPipelineCache cache = VK_NULL_HANDLE; res = vkCreateGraphicsPipelines(dev, cache, 1, &pinfo, NULL, pipe); if (res != VK_SUCCESS) { wlr_vk_error("failed to create vulkan pipelines:", res); return false; } return true; } struct wlr_vk_pipeline_layout *get_or_create_pipeline_layout( struct wlr_vk_renderer *renderer, const struct wlr_vk_pipeline_layout_key *key) { struct wlr_vk_pipeline_layout *pipeline_layout; wl_list_for_each(pipeline_layout, &renderer->pipeline_layouts, link) { if (pipeline_layout_key_equals(&pipeline_layout->key, key)) { return pipeline_layout; } } pipeline_layout = calloc(1, sizeof(*pipeline_layout)); if (!pipeline_layout) { return NULL; } pipeline_layout->key = *key; VkResult res; VkFilter filter = VK_FILTER_LINEAR; switch (key->filter_mode) { case WLR_SCALE_FILTER_BILINEAR: filter = VK_FILTER_LINEAR; break; case WLR_SCALE_FILTER_NEAREST: filter = VK_FILTER_NEAREST; break; } VkSamplerYcbcrConversionInfo conversion_info; VkSamplerCreateInfo sampler_create_info = { .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, .magFilter = filter, .minFilter = filter, .mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST, .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, .addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, .minLod = 0.f, .maxLod = 0.25f, }; if (key->ycbcr_format) { VkSamplerYcbcrConversionCreateInfo conversion_create_info = { .sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO, .format = key->ycbcr_format->vk, .ycbcrModel = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_601, .ycbcrRange = VK_SAMPLER_YCBCR_RANGE_ITU_NARROW, .xChromaOffset = VK_CHROMA_LOCATION_MIDPOINT, .yChromaOffset = VK_CHROMA_LOCATION_MIDPOINT, .chromaFilter = VK_FILTER_LINEAR, }; res = vkCreateSamplerYcbcrConversion(renderer->dev->dev, &conversion_create_info, NULL, &pipeline_layout->ycbcr.conversion); if (res != VK_SUCCESS) { wlr_vk_error("vkCreateSamplerYcbcrConversion", res); free(pipeline_layout); return NULL; } conversion_info = (VkSamplerYcbcrConversionInfo){ .sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO, .conversion = pipeline_layout->ycbcr.conversion, }; sampler_create_info.pNext = &conversion_info; } res = vkCreateSampler(renderer->dev->dev, &sampler_create_info, NULL, &pipeline_layout->sampler); if (res != VK_SUCCESS) { wlr_vk_error("vkCreateSampler", res); free(pipeline_layout); return false; } if (!init_tex_layouts(renderer, pipeline_layout->sampler, &pipeline_layout->ds, &pipeline_layout->vk)) { free(pipeline_layout); return false; } wl_list_insert(&renderer->pipeline_layouts, &pipeline_layout->link); return pipeline_layout; } // Creates static render data, such as sampler, layouts and shader modules // for the given renderer. // Cleanup is done by destroying the renderer. static bool init_static_render_data(struct wlr_vk_renderer *renderer) { VkResult res; VkDevice dev = renderer->dev->dev; if (!init_blend_to_output_layouts(renderer, &renderer->output_ds_layout, &renderer->output_pipe_layout)) { return false; } // load vert module and tex frag module since they are needed to // initialize the tex pipeline VkShaderModuleCreateInfo sinfo = { .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, .codeSize = sizeof(common_vert_data), .pCode = common_vert_data, }; res = vkCreateShaderModule(dev, &sinfo, NULL, &renderer->vert_module); if (res != VK_SUCCESS) { wlr_vk_error("Failed to create vertex shader module", res); return false; } sinfo = (VkShaderModuleCreateInfo){ .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, .codeSize = sizeof(texture_frag_data), .pCode = texture_frag_data, }; res = vkCreateShaderModule(dev, &sinfo, NULL, &renderer->tex_frag_module); if (res != VK_SUCCESS) { wlr_vk_error("Failed to create tex fragment shader module", res); return false; } sinfo = (VkShaderModuleCreateInfo){ .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, .codeSize = sizeof(quad_frag_data), .pCode = quad_frag_data, }; res = vkCreateShaderModule(dev, &sinfo, NULL, &renderer->quad_frag_module); if (res != VK_SUCCESS) { wlr_vk_error("Failed to create quad fragment shader module", res); return false; } sinfo = (VkShaderModuleCreateInfo){ .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, .codeSize = sizeof(output_frag_data), .pCode = output_frag_data, }; res = vkCreateShaderModule(dev, &sinfo, NULL, &renderer->output_module); if (res != VK_SUCCESS) { wlr_vk_error("Failed to create blend->output fragment shader module", res); return false; } return true; } static struct wlr_vk_render_format_setup *find_or_create_render_setup( struct wlr_vk_renderer *renderer, const struct wlr_vk_format *format, bool has_blending_buffer) { struct wlr_vk_render_format_setup *setup; wl_list_for_each(setup, &renderer->render_format_setups, link) { if (setup->render_format == format) { return setup; } } setup = calloc(1u, sizeof(*setup)); if (!setup) { wlr_log(WLR_ERROR, "Allocation failed"); return NULL; } setup->render_format = format; setup->renderer = renderer; wl_list_init(&setup->pipelines); VkDevice dev = renderer->dev->dev; VkResult res; if (has_blending_buffer) { VkAttachmentDescription attachments[2] = { { .format = VK_FORMAT_R16G16B16A16_SFLOAT, .samples = VK_SAMPLE_COUNT_1_BIT, .loadOp = VK_ATTACHMENT_LOAD_OP_LOAD, .storeOp = VK_ATTACHMENT_STORE_OP_STORE, .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE, .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, .initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, .finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, }, { .format = format->vk, .samples = VK_SAMPLE_COUNT_1_BIT, .loadOp = VK_ATTACHMENT_LOAD_OP_LOAD, .storeOp = VK_ATTACHMENT_STORE_OP_STORE, .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE, .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, .initialLayout = VK_IMAGE_LAYOUT_GENERAL, .finalLayout = VK_IMAGE_LAYOUT_GENERAL, } }; VkAttachmentReference blend_write_ref = { .attachment = 0u, .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, }; VkAttachmentReference blend_read_ref = { .attachment = 0u, .layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, }; VkAttachmentReference color_ref = { .attachment = 1u, .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, }; VkSubpassDescription subpasses[2] = { { .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, .colorAttachmentCount = 1, .pColorAttachments = &blend_write_ref, }, { .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, .inputAttachmentCount = 1, .pInputAttachments = &blend_read_ref, .colorAttachmentCount = 1, .pColorAttachments = &color_ref, } }; VkSubpassDependency deps[3] = { { .srcSubpass = VK_SUBPASS_EXTERNAL, .srcStageMask = VK_PIPELINE_STAGE_HOST_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, .srcAccessMask = VK_ACCESS_HOST_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, .dstSubpass = 0, .dstStageMask = VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, .dstAccessMask = VK_ACCESS_UNIFORM_READ_BIT | VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | VK_ACCESS_INDIRECT_COMMAND_READ_BIT | VK_ACCESS_SHADER_READ_BIT, }, { .srcSubpass = 0, .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, .dstSubpass = 1, .dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, .dstAccessMask = VK_ACCESS_SHADER_READ_BIT, .dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT, }, { .srcSubpass = 1, .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, .dstSubpass = VK_SUBPASS_EXTERNAL, .dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_HOST_BIT | VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_MEMORY_READ_BIT, }, }; VkRenderPassCreateInfo rp_info = { .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, .pNext = NULL, .flags = 0, .attachmentCount = 2u, .pAttachments = attachments, .subpassCount = 2u, .pSubpasses = subpasses, .dependencyCount = 3u, .pDependencies = deps, }; res = vkCreateRenderPass(dev, &rp_info, NULL, &setup->render_pass); if (res != VK_SUCCESS) { wlr_vk_error("Failed to create 2-step render pass", res); goto error; } // this is only well defined if render pass has a 2nd subpass if (!init_blend_to_output_pipeline( renderer, setup->render_pass, renderer->output_pipe_layout, &setup->output_pipe)) { goto error; } } else { VkAttachmentDescription attachment = { .format = format->vk, .samples = VK_SAMPLE_COUNT_1_BIT, .loadOp = VK_ATTACHMENT_LOAD_OP_LOAD, .storeOp = VK_ATTACHMENT_STORE_OP_STORE, .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE, .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, .initialLayout = VK_IMAGE_LAYOUT_GENERAL, .finalLayout = VK_IMAGE_LAYOUT_GENERAL, }; VkAttachmentReference color_ref = { .attachment = 0u, .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, }; VkSubpassDescription subpass = { .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, .colorAttachmentCount = 1, .pColorAttachments = &color_ref, }; VkSubpassDependency deps[2] = { { .srcSubpass = VK_SUBPASS_EXTERNAL, .srcStageMask = VK_PIPELINE_STAGE_HOST_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, .srcAccessMask = VK_ACCESS_HOST_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, .dstSubpass = 0, .dstStageMask = VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, .dstAccessMask = VK_ACCESS_UNIFORM_READ_BIT | VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | VK_ACCESS_INDIRECT_COMMAND_READ_BIT | VK_ACCESS_SHADER_READ_BIT, }, { .srcSubpass = 0, .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, .dstSubpass = VK_SUBPASS_EXTERNAL, .dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_HOST_BIT | VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_MEMORY_READ_BIT, }, }; VkRenderPassCreateInfo rp_info = { .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, .attachmentCount = 1, .pAttachments = &attachment, .subpassCount = 1, .pSubpasses = &subpass, .dependencyCount = 2u, .pDependencies = deps, }; res = vkCreateRenderPass(dev, &rp_info, NULL, &setup->render_pass); if (res != VK_SUCCESS) { wlr_vk_error("Failed to create render pass", res); goto error; } } if (!setup_get_or_create_pipeline(setup, &(struct wlr_vk_pipeline_key){ .source = WLR_VK_SHADER_SOURCE_SINGLE_COLOR, .layout = { .ycbcr_format = NULL }, })) { goto error; } if (!setup_get_or_create_pipeline(setup, &(struct wlr_vk_pipeline_key){ .source = WLR_VK_SHADER_SOURCE_TEXTURE, .texture_transform = WLR_VK_TEXTURE_TRANSFORM_IDENTITY, .layout = {.ycbcr_format = NULL }, })) { goto error; } if (!setup_get_or_create_pipeline(setup, &(struct wlr_vk_pipeline_key){ .source = WLR_VK_SHADER_SOURCE_TEXTURE, .texture_transform = WLR_VK_TEXTURE_TRANSFORM_SRGB, .layout = {.ycbcr_format = NULL }, })) { goto error; } for (size_t i = 0; i < renderer->dev->format_prop_count; i++) { const struct wlr_vk_format *format = &renderer->dev->format_props[i].format; const struct wlr_vk_pipeline_layout_key layout = { .ycbcr_format = format, }; if (format->is_ycbcr) { if (!setup_get_or_create_pipeline(setup, &(struct wlr_vk_pipeline_key){ .texture_transform = WLR_VK_TEXTURE_TRANSFORM_SRGB, .layout = layout })) { goto error; } } } wl_list_insert(&renderer->render_format_setups, &setup->link); return setup; error: destroy_render_format_setup(renderer, setup); return NULL; } struct wlr_renderer *vulkan_renderer_create_for_device(struct wlr_vk_device *dev) { struct wlr_vk_renderer *renderer; VkResult res; if (!(renderer = calloc(1, sizeof(*renderer)))) { wlr_log_errno(WLR_ERROR, "failed to allocate wlr_vk_renderer"); return NULL; } renderer->dev = dev; wlr_renderer_init(&renderer->wlr_renderer, &renderer_impl); wl_list_init(&renderer->stage.buffers); wl_list_init(&renderer->foreign_textures); wl_list_init(&renderer->textures); wl_list_init(&renderer->descriptor_pools); wl_list_init(&renderer->output_descriptor_pools); wl_list_init(&renderer->render_format_setups); wl_list_init(&renderer->render_buffers); wl_list_init(&renderer->pipeline_layouts); if (!init_static_render_data(renderer)) { goto error; } VkCommandPoolCreateInfo cpool_info = { .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, .flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, .queueFamilyIndex = dev->queue_family, }; res = vkCreateCommandPool(dev->dev, &cpool_info, NULL, &renderer->command_pool); if (res != VK_SUCCESS) { wlr_vk_error("vkCreateCommandPool", res); goto error; } VkSemaphoreTypeCreateInfoKHR semaphore_type_info = { .sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO_KHR, .semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE_KHR, .initialValue = 0, }; VkSemaphoreCreateInfo semaphore_info = { .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, .pNext = &semaphore_type_info, }; res = vkCreateSemaphore(dev->dev, &semaphore_info, NULL, &renderer->timeline_semaphore); if (res != VK_SUCCESS) { wlr_vk_error("vkCreateSemaphore", res); goto error; } return &renderer->wlr_renderer; error: vulkan_destroy(&renderer->wlr_renderer); return NULL; } struct wlr_renderer *wlr_vk_renderer_create_with_drm_fd(int drm_fd) { wlr_log(WLR_INFO, "The vulkan renderer is only experimental and " "not expected to be ready for daily use"); wlr_log(WLR_INFO, "Run with VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation " "to enable the validation layer"); struct wlr_vk_instance *ini = vulkan_instance_create(default_debug); if (!ini) { wlr_log(WLR_ERROR, "creating vulkan instance for renderer failed"); return NULL; } VkPhysicalDevice phdev = vulkan_find_drm_phdev(ini, drm_fd); if (!phdev) { // We rather fail here than doing some guesswork wlr_log(WLR_ERROR, "Could not match drm and vulkan device"); return NULL; } struct wlr_vk_device *dev = vulkan_device_create(ini, phdev); if (!dev) { wlr_log(WLR_ERROR, "Failed to create vulkan device"); vulkan_instance_destroy(ini); return NULL; } // Do not use the drm_fd that was passed in: we should prefer the render // node even if a primary node was provided dev->drm_fd = vulkan_open_phdev_drm_fd(phdev); if (dev->drm_fd < 0) { vulkan_device_destroy(dev); vulkan_instance_destroy(ini); return NULL; } return vulkan_renderer_create_for_device(dev); } VkInstance wlr_vk_renderer_get_instance(struct wlr_renderer *renderer) { struct wlr_vk_renderer *vk_renderer = vulkan_get_renderer(renderer); return vk_renderer->dev->instance->instance; } VkPhysicalDevice wlr_vk_renderer_get_physical_device(struct wlr_renderer *renderer) { struct wlr_vk_renderer *vk_renderer = vulkan_get_renderer(renderer); return vk_renderer->dev->phdev; } VkDevice wlr_vk_renderer_get_device(struct wlr_renderer *renderer) { struct wlr_vk_renderer *vk_renderer = vulkan_get_renderer(renderer); return vk_renderer->dev->dev; } uint32_t wlr_vk_renderer_get_queue_family(struct wlr_renderer *renderer) { struct wlr_vk_renderer *vk_renderer = vulkan_get_renderer(renderer); return vk_renderer->dev->queue_family; } void wlr_vk_renderer_get_current_image_attribs(struct wlr_renderer *renderer, struct wlr_vk_image_attribs *attribs) { struct wlr_vk_renderer *vk_renderer = vulkan_get_renderer(renderer); attribs->image = vk_renderer->current_render_buffer->image; attribs->format = vk_renderer->current_render_buffer->render_setup->render_format->vk; attribs->layout = VK_IMAGE_LAYOUT_UNDEFINED; } wlroots-0.17.1/render/vulkan/shaders/000077500000000000000000000000001454110342200175155ustar00rootroot00000000000000wlroots-0.17.1/render/vulkan/shaders/common.vert000066400000000000000000000007311454110342200217100ustar00rootroot00000000000000#version 450 // we use a mat4 since it uses the same size as mat3 due to // alignment. Easier to deal with (tighly-packed) mat4 though. layout(push_constant, row_major) uniform UBO { mat4 proj; vec2 uv_offset; vec2 uv_size; } data; layout(location = 0) out vec2 uv; void main() { vec2 pos = vec2(float((gl_VertexIndex + 1) & 2) * 0.5f, float(gl_VertexIndex & 2) * 0.5f); uv = data.uv_offset + pos * data.uv_size; gl_Position = data.proj * vec4(pos, 0.0, 1.0); } wlroots-0.17.1/render/vulkan/shaders/meson.build000066400000000000000000000007631454110342200216650ustar00rootroot00000000000000vulkan_shaders_src = [ 'common.vert', 'texture.frag', 'quad.frag', 'output.frag', ] vulkan_shaders = [] foreach shader : vulkan_shaders_src name = shader.underscorify() + '_data' args = [glslang, '-V', '@INPUT@', '-o', '@OUTPUT@', '--vn', name] if glslang_version.version_compare('>=11.0.0') args += '--quiet' endif header = custom_target( shader + '_spv', output: shader + '.h', input: shader, command: args) vulkan_shaders += [header] endforeach wlr_files += vulkan_shaders wlroots-0.17.1/render/vulkan/shaders/output.frag000066400000000000000000000012161454110342200217160ustar00rootroot00000000000000#version 450 layout (input_attachment_index = 0, binding = 0) uniform subpassInput in_color; layout(location = 0) in vec2 uv; layout(location = 0) out vec4 out_color; float linear_channel_to_srgb(float x) { return max(min(x * 12.92, 0.04045), 1.055 * pow(x, 1. / 2.4) - 0.055); } vec4 linear_color_to_srgb(vec4 color) { if (color.a == 0) { return vec4(0); } color.rgb /= color.a; color.rgb = vec3( linear_channel_to_srgb(color.r), linear_channel_to_srgb(color.g), linear_channel_to_srgb(color.b) ); color.rgb *= color.a; return color; } void main() { vec4 val = subpassLoad(in_color).rgba; out_color = linear_color_to_srgb(val); } wlroots-0.17.1/render/vulkan/shaders/quad.frag000066400000000000000000000002561454110342200213130ustar00rootroot00000000000000#version 450 layout(location = 0) out vec4 out_color; layout(push_constant) uniform UBO { layout(offset = 80) vec4 color; } data; void main() { out_color = data.color; } wlroots-0.17.1/render/vulkan/shaders/texture.frag000066400000000000000000000017451454110342200220650ustar00rootroot00000000000000#version 450 layout(set = 0, binding = 0) uniform sampler2D tex; layout(location = 0) in vec2 uv; layout(location = 0) out vec4 out_color; layout(push_constant) uniform UBO { layout(offset = 80) float alpha; } data; layout (constant_id = 0) const int TEXTURE_TRANSFORM = 0; // Matches enum wlr_vk_texture_transform #define TEXTURE_TRANSFORM_IDENTITY 0 #define TEXTURE_TRANSFORM_SRGB 1 float srgb_channel_to_linear(float x) { return max(x / 12.92, pow((x + 0.055) / 1.055, 2.4)); } vec4 srgb_color_to_linear(vec4 color) { if (color.a == 0) { return vec4(0); } color.rgb /= color.a; color.rgb = vec3( srgb_channel_to_linear(color.r), srgb_channel_to_linear(color.g), srgb_channel_to_linear(color.b) ); color.rgb *= color.a; return color; } void main() { vec4 val = textureLod(tex, uv, 0); if (TEXTURE_TRANSFORM == TEXTURE_TRANSFORM_SRGB) { out_color = srgb_color_to_linear(val); } else { // TEXTURE_TRANSFORM_IDENTITY out_color = val; } out_color *= data.alpha; } wlroots-0.17.1/render/vulkan/texture.c000066400000000000000000000566751454110342200177530ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include #include #include #include #include #include "render/pixel_format.h" #include "render/vulkan.h" static const struct wlr_texture_impl texture_impl; bool wlr_texture_is_vk(struct wlr_texture *wlr_texture) { return wlr_texture->impl == &texture_impl; } struct wlr_vk_texture *vulkan_get_texture(struct wlr_texture *wlr_texture) { assert(wlr_texture_is_vk(wlr_texture)); struct wlr_vk_texture *texture = wl_container_of(wlr_texture, texture, wlr_texture); return texture; } static VkImageAspectFlagBits mem_plane_aspect(unsigned i) { switch (i) { case 0: return VK_IMAGE_ASPECT_MEMORY_PLANE_0_BIT_EXT; case 1: return VK_IMAGE_ASPECT_MEMORY_PLANE_1_BIT_EXT; case 2: return VK_IMAGE_ASPECT_MEMORY_PLANE_2_BIT_EXT; case 3: return VK_IMAGE_ASPECT_MEMORY_PLANE_3_BIT_EXT; default: abort(); // unreachable } } // Will transition the texture to shaderReadOnlyOptimal layout for reading // from fragment shader later on static bool write_pixels(struct wlr_vk_texture *texture, uint32_t stride, const pixman_region32_t *region, const void *vdata, VkImageLayout old_layout, VkPipelineStageFlags src_stage, VkAccessFlags src_access) { VkResult res; struct wlr_vk_renderer *renderer = texture->renderer; VkDevice dev = texture->renderer->dev->dev; const struct wlr_pixel_format_info *format_info = drm_get_pixel_format_info(texture->format->drm); assert(format_info); uint32_t bsize = 0; // deferred upload by transfer; using staging buffer // calculate maximum side needed int rects_len = 0; const pixman_box32_t *rects = pixman_region32_rectangles(region, &rects_len); for (int i = 0; i < rects_len; i++) { pixman_box32_t rect = rects[i]; uint32_t width = rect.x2 - rect.x1; uint32_t height = rect.y2 - rect.y1; // make sure assumptions are met assert((uint32_t)rect.x2 <= texture->wlr_texture.width); assert((uint32_t)rect.y2 <= texture->wlr_texture.height); bsize += height * pixel_format_info_min_stride(format_info, width); } VkBufferImageCopy *copies = calloc((size_t)rects_len, sizeof(*copies)); if (!copies) { wlr_log(WLR_ERROR, "Failed to allocate image copy parameters"); return false; } // get staging buffer struct wlr_vk_buffer_span span = vulkan_get_stage_span(renderer, bsize, format_info->bytes_per_block); if (!span.buffer || span.alloc.size != bsize) { wlr_log(WLR_ERROR, "Failed to retrieve staging buffer"); free(copies); return false; } void *vmap; res = vkMapMemory(dev, span.buffer->memory, span.alloc.start, bsize, 0, &vmap); if (res != VK_SUCCESS) { wlr_vk_error("vkMapMemory", res); free(copies); return false; } char *map = (char *)vmap; // upload data uint32_t buf_off = span.alloc.start + (map - (char *)vmap); for (int i = 0; i < rects_len; i++) { pixman_box32_t rect = rects[i]; uint32_t width = rect.x2 - rect.x1; uint32_t height = rect.y2 - rect.y1; uint32_t src_x = rect.x1; uint32_t src_y = rect.y1; uint32_t packed_stride = (uint32_t)pixel_format_info_min_stride(format_info, width); // write data into staging buffer span const char *pdata = vdata; // data iterator pdata += stride * src_y; pdata += format_info->bytes_per_block * src_x; if (src_x == 0 && width == texture->wlr_texture.width && stride == packed_stride) { memcpy(map, pdata, packed_stride * height); map += packed_stride * height; } else { for (unsigned i = 0u; i < height; ++i) { memcpy(map, pdata, packed_stride); pdata += stride; map += packed_stride; } } copies[i] = (VkBufferImageCopy) { .imageExtent.width = width, .imageExtent.height = height, .imageExtent.depth = 1, .imageOffset.x = src_x, .imageOffset.y = src_y, .imageOffset.z = 0, .bufferOffset = buf_off, .bufferRowLength = width, .bufferImageHeight = height, .imageSubresource.mipLevel = 0, .imageSubresource.baseArrayLayer = 0, .imageSubresource.layerCount = 1, .imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, }; buf_off += height * packed_stride; } assert((uint32_t)(map - (char *)vmap) == bsize); vkUnmapMemory(dev, span.buffer->memory); // record staging cb // will be executed before next frame VkCommandBuffer cb = vulkan_record_stage_cb(renderer); if (cb == VK_NULL_HANDLE) { free(copies); return false; } vulkan_change_layout(cb, texture->image, old_layout, src_stage, src_access, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT); vkCmdCopyBufferToImage(cb, span.buffer->buffer, texture->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, (uint32_t)rects_len, copies); vulkan_change_layout(cb, texture->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_ACCESS_SHADER_READ_BIT); texture->last_used_cb = renderer->stage.cb; free(copies); return true; } static bool vulkan_texture_update_from_buffer(struct wlr_texture *wlr_texture, struct wlr_buffer *buffer, const pixman_region32_t *damage) { struct wlr_vk_texture *texture = vulkan_get_texture(wlr_texture); void *data; uint32_t format; size_t stride; if (!wlr_buffer_begin_data_ptr_access(buffer, WLR_BUFFER_DATA_PTR_ACCESS_READ, &data, &format, &stride)) { return false; } bool ok = true; if (format != texture->format->drm) { ok = false; goto out; } ok = write_pixels(texture, stride, damage, data, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_ACCESS_SHADER_READ_BIT); out: wlr_buffer_end_data_ptr_access(buffer); return ok; } void vulkan_texture_destroy(struct wlr_vk_texture *texture) { if (texture->buffer != NULL) { wlr_addon_finish(&texture->buffer_addon); texture->buffer = NULL; } // when we recorded a command to fill this image _this_ frame, // it has to be executed before the texture can be destroyed. // Add it to the renderer->destroy_textures list, destroying // _after_ the stage command buffer has exectued if (texture->last_used_cb != NULL) { assert(texture->destroy_link.next == NULL); // not already inserted wl_list_insert(&texture->last_used_cb->destroy_textures, &texture->destroy_link); return; } wl_list_remove(&texture->link); VkDevice dev = texture->renderer->dev->dev; struct wlr_vk_texture_view *view, *tmp_view; wl_list_for_each_safe(view, tmp_view, &texture->views, link) { vulkan_free_ds(texture->renderer, view->ds_pool, view->ds); vkDestroyImageView(dev, view->image_view, NULL); free(view); } for (size_t i = 0; i < WLR_DMABUF_MAX_PLANES; i++) { if (texture->foreign_semaphores[i] != VK_NULL_HANDLE) { vkDestroySemaphore(dev, texture->foreign_semaphores[i], NULL); } } vkDestroyImage(dev, texture->image, NULL); for (unsigned i = 0u; i < texture->mem_count; ++i) { vkFreeMemory(dev, texture->memories[i], NULL); } free(texture); } static void vulkan_texture_unref(struct wlr_texture *wlr_texture) { struct wlr_vk_texture *texture = vulkan_get_texture(wlr_texture); if (texture->buffer != NULL) { // Keep the texture around, in case the buffer is re-used later. We're // still listening to the buffer's destroy event. wlr_buffer_unlock(texture->buffer); } else { vulkan_texture_destroy(texture); } } static const struct wlr_texture_impl texture_impl = { .update_from_buffer = vulkan_texture_update_from_buffer, .destroy = vulkan_texture_unref, }; static struct wlr_vk_texture *vulkan_texture_create( struct wlr_vk_renderer *renderer, uint32_t width, uint32_t height) { struct wlr_vk_texture *texture = calloc(1, sizeof(*texture)); if (texture == NULL) { wlr_log_errno(WLR_ERROR, "Allocation failed"); return NULL; } wlr_texture_init(&texture->wlr_texture, &renderer->wlr_renderer, &texture_impl, width, height); texture->renderer = renderer; wl_list_insert(&renderer->textures, &texture->link); wl_list_init(&texture->views); return texture; } struct wlr_vk_texture_view *vulkan_texture_get_or_create_view(struct wlr_vk_texture *texture, const struct wlr_vk_pipeline_layout *pipeline_layout) { struct wlr_vk_texture_view *view; wl_list_for_each(view, &texture->views, link) { if (view->layout == pipeline_layout) { return view; } } view = calloc(1, sizeof(*view)); if (!view) { return NULL; } view->layout = pipeline_layout; VkResult res; VkDevice dev = texture->renderer->dev->dev; VkImageViewCreateInfo view_info = { .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, .viewType = VK_IMAGE_VIEW_TYPE_2D, .format = texture->format->vk, .components.r = VK_COMPONENT_SWIZZLE_IDENTITY, .components.g = VK_COMPONENT_SWIZZLE_IDENTITY, .components.b = VK_COMPONENT_SWIZZLE_IDENTITY, .components.a = texture->has_alpha || texture->format->is_ycbcr ? VK_COMPONENT_SWIZZLE_IDENTITY : VK_COMPONENT_SWIZZLE_ONE, .subresourceRange = (VkImageSubresourceRange){ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .baseMipLevel = 0, .levelCount = 1, .baseArrayLayer = 0, .layerCount = 1, }, .image = texture->image, }; VkSamplerYcbcrConversionInfo ycbcr_conversion_info; if (texture->format->is_ycbcr) { assert(pipeline_layout->ycbcr.conversion != VK_NULL_HANDLE); ycbcr_conversion_info = (VkSamplerYcbcrConversionInfo){ .sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO, .conversion = pipeline_layout->ycbcr.conversion, }; view_info.pNext = &ycbcr_conversion_info; } res = vkCreateImageView(dev, &view_info, NULL, &view->image_view); if (res != VK_SUCCESS) { free(view); wlr_vk_error("vkCreateImageView failed", res); return NULL; } view->ds_pool = vulkan_alloc_texture_ds(texture->renderer, pipeline_layout->ds, &view->ds); if (!view->ds_pool) { free(view); wlr_log(WLR_ERROR, "failed to allocate descriptor"); return NULL; } VkDescriptorImageInfo ds_img_info = { .imageView = view->image_view, .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, }; VkWriteDescriptorSet ds_write = { .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, .descriptorCount = 1, .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, .dstSet = view->ds, .pImageInfo = &ds_img_info, }; vkUpdateDescriptorSets(dev, 1, &ds_write, 0, NULL); wl_list_insert(&texture->views, &view->link); return view; } static void texture_set_format(struct wlr_vk_texture *texture, const struct wlr_vk_format *format) { texture->format = format; texture->transform = !format->is_ycbcr && format->is_srgb ? WLR_VK_TEXTURE_TRANSFORM_IDENTITY : WLR_VK_TEXTURE_TRANSFORM_SRGB; const struct wlr_pixel_format_info *format_info = drm_get_pixel_format_info(format->drm); if (format_info != NULL) { texture->has_alpha = format_info->has_alpha; } else { // We don't have format info for multi-planar formats assert(texture->format->is_ycbcr); } } static struct wlr_texture *vulkan_texture_from_pixels( struct wlr_vk_renderer *renderer, uint32_t drm_fmt, uint32_t stride, uint32_t width, uint32_t height, const void *data) { VkResult res; VkDevice dev = renderer->dev->dev; const struct wlr_vk_format_props *fmt = vulkan_format_props_from_drm(renderer->dev, drm_fmt); if (fmt == NULL || fmt->format.is_ycbcr) { char *format_name = drmGetFormatName(drm_fmt); wlr_log(WLR_ERROR, "Unsupported pixel format %s (0x%08"PRIX32")", format_name, drm_fmt); free(format_name); return NULL; } struct wlr_vk_texture *texture = vulkan_texture_create(renderer, width, height); if (texture == NULL) { return NULL; } texture_set_format(texture, &fmt->format); VkImageCreateInfo img_info = { .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, .imageType = VK_IMAGE_TYPE_2D, .format = texture->format->vk, .mipLevels = 1, .arrayLayers = 1, .samples = VK_SAMPLE_COUNT_1_BIT, .sharingMode = VK_SHARING_MODE_EXCLUSIVE, .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, .extent = (VkExtent3D) { width, height, 1 }, .tiling = VK_IMAGE_TILING_OPTIMAL, .usage = vulkan_shm_tex_usage, }; res = vkCreateImage(dev, &img_info, NULL, &texture->image); if (res != VK_SUCCESS) { wlr_vk_error("vkCreateImage failed", res); goto error; } VkMemoryRequirements mem_reqs; vkGetImageMemoryRequirements(dev, texture->image, &mem_reqs); int mem_type_index = vulkan_find_mem_type(renderer->dev, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, mem_reqs.memoryTypeBits); if (mem_type_index == -1) { wlr_log(WLR_ERROR, "failed to find suitable vulkan memory type"); goto error; } VkMemoryAllocateInfo mem_info = { .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, .allocationSize = mem_reqs.size, .memoryTypeIndex = mem_type_index, }; res = vkAllocateMemory(dev, &mem_info, NULL, &texture->memories[0]); if (res != VK_SUCCESS) { wlr_vk_error("vkAllocatorMemory failed", res); goto error; } texture->mem_count = 1; res = vkBindImageMemory(dev, texture->image, texture->memories[0], 0); if (res != VK_SUCCESS) { wlr_vk_error("vkBindMemory failed", res); goto error; } pixman_region32_t region; pixman_region32_init_rect(®ion, 0, 0, width, height); if (!write_pixels(texture, stride, ®ion, data, VK_IMAGE_LAYOUT_UNDEFINED, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0)) { goto error; } return &texture->wlr_texture; error: vulkan_texture_destroy(texture); return NULL; } static bool is_dmabuf_disjoint(const struct wlr_dmabuf_attributes *attribs) { if (attribs->n_planes == 1) { return false; } struct stat first_stat; if (fstat(attribs->fd[0], &first_stat) != 0) { wlr_log_errno(WLR_ERROR, "fstat failed"); return true; } for (int i = 1; i < attribs->n_planes; i++) { struct stat plane_stat; if (fstat(attribs->fd[i], &plane_stat) != 0) { wlr_log_errno(WLR_ERROR, "fstat failed"); return true; } if (first_stat.st_ino != plane_stat.st_ino) { return true; } } return false; } VkImage vulkan_import_dmabuf(struct wlr_vk_renderer *renderer, const struct wlr_dmabuf_attributes *attribs, VkDeviceMemory mems[static WLR_DMABUF_MAX_PLANES], uint32_t *n_mems, bool for_render) { VkResult res; VkDevice dev = renderer->dev->dev; *n_mems = 0u; struct wlr_vk_format_props *fmt = vulkan_format_props_from_drm(renderer->dev, attribs->format); if (fmt == NULL) { char *format_name = drmGetFormatName(attribs->format); wlr_log(WLR_ERROR, "Unsupported pixel format %s (0x%08"PRIX32")", format_name, attribs->format); free(format_name); return VK_NULL_HANDLE; } uint32_t plane_count = attribs->n_planes; assert(plane_count < WLR_DMABUF_MAX_PLANES); const struct wlr_vk_format_modifier_props *mod = vulkan_format_props_find_modifier(fmt, attribs->modifier, for_render); if (!mod) { char *format_name = drmGetFormatName(attribs->format); char *modifier_name = drmGetFormatModifierName(attribs->modifier); wlr_log(WLR_ERROR, "Format %s (0x%08"PRIX32") can't be used with modifier " "%s (0x%016"PRIX64")", format_name, attribs->format, modifier_name, attribs->modifier); free(format_name); free(modifier_name); return VK_NULL_HANDLE; } if ((uint32_t) attribs->width > mod->max_extent.width || (uint32_t) attribs->height > mod->max_extent.height) { wlr_log(WLR_ERROR, "DMA-BUF is too large to import"); return VK_NULL_HANDLE; } if (mod->props.drmFormatModifierPlaneCount != plane_count) { wlr_log(WLR_ERROR, "Number of planes (%d) does not match format (%d)", plane_count, mod->props.drmFormatModifierPlaneCount); return VK_NULL_HANDLE; } // check if we have to create the image disjoint bool disjoint = is_dmabuf_disjoint(attribs); if (disjoint && !(mod->props.drmFormatModifierTilingFeatures & VK_FORMAT_FEATURE_DISJOINT_BIT)) { wlr_log(WLR_ERROR, "Format/Modifier does not support disjoint images"); return VK_NULL_HANDLE; } VkExternalMemoryHandleTypeFlagBits htype = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT; VkImageCreateInfo img_info = { .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, .imageType = VK_IMAGE_TYPE_2D, .format = fmt->format.vk, .mipLevels = 1, .arrayLayers = 1, .samples = VK_SAMPLE_COUNT_1_BIT, .sharingMode = VK_SHARING_MODE_EXCLUSIVE, .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, .extent = (VkExtent3D) { attribs->width, attribs->height, 1 }, .usage = for_render ? vulkan_render_usage : vulkan_dma_tex_usage, }; if (disjoint) { img_info.flags = VK_IMAGE_CREATE_DISJOINT_BIT; } VkExternalMemoryImageCreateInfo eimg = { .sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO, .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT, }; img_info.pNext = &eimg; VkSubresourceLayout plane_layouts[WLR_DMABUF_MAX_PLANES] = {0}; img_info.tiling = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT; for (unsigned i = 0u; i < plane_count; ++i) { plane_layouts[i].offset = attribs->offset[i]; plane_layouts[i].rowPitch = attribs->stride[i]; plane_layouts[i].size = 0; } VkImageDrmFormatModifierExplicitCreateInfoEXT mod_info = { .sType = VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_EXPLICIT_CREATE_INFO_EXT, .drmFormatModifierPlaneCount = plane_count, .drmFormatModifier = mod->props.drmFormatModifier, .pPlaneLayouts = plane_layouts, }; eimg.pNext = &mod_info; VkImage image; res = vkCreateImage(dev, &img_info, NULL, &image); if (res != VK_SUCCESS) { wlr_vk_error("vkCreateImage", res); return VK_NULL_HANDLE; } unsigned mem_count = disjoint ? plane_count : 1u; VkBindImageMemoryInfo bindi[WLR_DMABUF_MAX_PLANES] = {0}; VkBindImagePlaneMemoryInfo planei[WLR_DMABUF_MAX_PLANES] = {0}; for (unsigned i = 0u; i < mem_count; ++i) { VkMemoryFdPropertiesKHR fdp = { .sType = VK_STRUCTURE_TYPE_MEMORY_FD_PROPERTIES_KHR, }; res = renderer->dev->api.vkGetMemoryFdPropertiesKHR(dev, htype, attribs->fd[i], &fdp); if (res != VK_SUCCESS) { wlr_vk_error("getMemoryFdPropertiesKHR", res); goto error_image; } VkImageMemoryRequirementsInfo2 memri = { .image = image, .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2, }; VkImagePlaneMemoryRequirementsInfo planeri; if (disjoint) { planeri = (VkImagePlaneMemoryRequirementsInfo){ .sType = VK_STRUCTURE_TYPE_IMAGE_PLANE_MEMORY_REQUIREMENTS_INFO, .planeAspect = mem_plane_aspect(i), }; memri.pNext = &planeri; } VkMemoryRequirements2 memr = { .sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2, }; vkGetImageMemoryRequirements2(dev, &memri, &memr); int mem = vulkan_find_mem_type(renderer->dev, 0, memr.memoryRequirements.memoryTypeBits & fdp.memoryTypeBits); if (mem < 0) { wlr_log(WLR_ERROR, "no valid memory type index"); goto error_image; } // Since importing transfers ownership of the FD to Vulkan, we have // to duplicate it since this operation does not transfer ownership // of the attribs to this texture. Will be closed by Vulkan on // vkFreeMemory. int dfd = fcntl(attribs->fd[i], F_DUPFD_CLOEXEC, 0); if (dfd < 0) { wlr_log_errno(WLR_ERROR, "fcntl(F_DUPFD_CLOEXEC) failed"); goto error_image; } VkMemoryAllocateInfo memi = { .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, .allocationSize = memr.memoryRequirements.size, .memoryTypeIndex = mem, }; VkImportMemoryFdInfoKHR importi = { .sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR, .fd = dfd, .handleType = htype, }; memi.pNext = &importi; VkMemoryDedicatedAllocateInfo dedi = { .sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO, .image = image, }; importi.pNext = &dedi; res = vkAllocateMemory(dev, &memi, NULL, &mems[i]); if (res != VK_SUCCESS) { close(dfd); wlr_vk_error("vkAllocateMemory failed", res); goto error_image; } ++(*n_mems); // fill bind info bindi[i].image = image; bindi[i].memory = mems[i]; bindi[i].memoryOffset = 0; bindi[i].sType = VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO; if (disjoint) { planei[i].sType = VK_STRUCTURE_TYPE_BIND_IMAGE_PLANE_MEMORY_INFO; planei[i].planeAspect = planeri.planeAspect; bindi[i].pNext = &planei[i]; } } res = vkBindImageMemory2(dev, mem_count, bindi); if (res != VK_SUCCESS) { wlr_vk_error("vkBindMemory failed", res); goto error_image; } return image; error_image: vkDestroyImage(dev, image, NULL); for (size_t i = 0u; i < *n_mems; ++i) { vkFreeMemory(dev, mems[i], NULL); mems[i] = VK_NULL_HANDLE; } return VK_NULL_HANDLE; } static struct wlr_vk_texture *vulkan_texture_from_dmabuf( struct wlr_vk_renderer *renderer, struct wlr_dmabuf_attributes *attribs) { const struct wlr_vk_format_props *fmt = vulkan_format_props_from_drm( renderer->dev, attribs->format); if (fmt == NULL) { char *format_name = drmGetFormatName(attribs->format); wlr_log(WLR_ERROR, "Unsupported pixel format %s (0x%08"PRIX32")", format_name, attribs->format); free(format_name); return NULL; } struct wlr_vk_texture *texture = vulkan_texture_create(renderer, attribs->width, attribs->height); if (texture == NULL) { return NULL; } texture_set_format(texture, &fmt->format); texture->image = vulkan_import_dmabuf(renderer, attribs, texture->memories, &texture->mem_count, false); if (!texture->image) { goto error; } texture->dmabuf_imported = true; return texture; error: vulkan_texture_destroy(texture); return NULL; } static void texture_handle_buffer_destroy(struct wlr_addon *addon) { struct wlr_vk_texture *texture = wl_container_of(addon, texture, buffer_addon); // We might keep the texture around, waiting for pending command buffers to // complete before free'ing descriptor sets. vulkan_texture_destroy(texture); } static const struct wlr_addon_interface buffer_addon_impl = { .name = "wlr_vk_texture", .destroy = texture_handle_buffer_destroy, }; static struct wlr_texture *vulkan_texture_from_dmabuf_buffer( struct wlr_vk_renderer *renderer, struct wlr_buffer *buffer, struct wlr_dmabuf_attributes *dmabuf) { struct wlr_addon *addon = wlr_addon_find(&buffer->addons, renderer, &buffer_addon_impl); if (addon != NULL) { struct wlr_vk_texture *texture = wl_container_of(addon, texture, buffer_addon); wlr_buffer_lock(texture->buffer); return &texture->wlr_texture; } struct wlr_vk_texture *texture = vulkan_texture_from_dmabuf(renderer, dmabuf); if (texture == NULL) { return false; } texture->buffer = wlr_buffer_lock(buffer); wlr_addon_init(&texture->buffer_addon, &buffer->addons, renderer, &buffer_addon_impl); return &texture->wlr_texture; } struct wlr_texture *vulkan_texture_from_buffer(struct wlr_renderer *wlr_renderer, struct wlr_buffer *buffer) { struct wlr_vk_renderer *renderer = vulkan_get_renderer(wlr_renderer); void *data; uint32_t format; size_t stride; struct wlr_dmabuf_attributes dmabuf; if (wlr_buffer_get_dmabuf(buffer, &dmabuf)) { return vulkan_texture_from_dmabuf_buffer(renderer, buffer, &dmabuf); } else if (wlr_buffer_begin_data_ptr_access(buffer, WLR_BUFFER_DATA_PTR_ACCESS_READ, &data, &format, &stride)) { struct wlr_texture *tex = vulkan_texture_from_pixels(renderer, format, stride, buffer->width, buffer->height, data); wlr_buffer_end_data_ptr_access(buffer); return tex; } else { return NULL; } } void wlr_vk_texture_get_image_attribs(struct wlr_texture *texture, struct wlr_vk_image_attribs *attribs) { struct wlr_vk_texture *vk_texture = vulkan_get_texture(texture); attribs->image = vk_texture->image; attribs->format = vk_texture->format->vk; attribs->layout = vk_texture->transitioned ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : VK_IMAGE_LAYOUT_UNDEFINED; } bool wlr_vk_texture_has_alpha(struct wlr_texture *texture) { struct wlr_vk_texture *vk_texture = vulkan_get_texture(texture); return vk_texture->has_alpha; } wlroots-0.17.1/render/vulkan/util.c000066400000000000000000000045131454110342200172100ustar00rootroot00000000000000#include #include #include "render/vulkan.h" int vulkan_find_mem_type(struct wlr_vk_device *dev, VkMemoryPropertyFlags flags, uint32_t req_bits) { VkPhysicalDeviceMemoryProperties props; vkGetPhysicalDeviceMemoryProperties(dev->phdev, &props); for (unsigned i = 0u; i < props.memoryTypeCount; ++i) { if (req_bits & (1 << i)) { if ((props.memoryTypes[i].propertyFlags & flags) == flags) { return i; } } } return -1; } const char *vulkan_strerror(VkResult err) { #define ERR_STR(r) case VK_ ##r: return #r switch (err) { ERR_STR(SUCCESS); ERR_STR(NOT_READY); ERR_STR(TIMEOUT); ERR_STR(EVENT_SET); ERR_STR(EVENT_RESET); ERR_STR(INCOMPLETE); ERR_STR(ERROR_OUT_OF_HOST_MEMORY); ERR_STR(ERROR_OUT_OF_DEVICE_MEMORY); ERR_STR(ERROR_INITIALIZATION_FAILED); ERR_STR(ERROR_DEVICE_LOST); ERR_STR(ERROR_MEMORY_MAP_FAILED); ERR_STR(ERROR_LAYER_NOT_PRESENT); ERR_STR(ERROR_EXTENSION_NOT_PRESENT); ERR_STR(ERROR_FEATURE_NOT_PRESENT); ERR_STR(ERROR_INCOMPATIBLE_DRIVER); ERR_STR(ERROR_TOO_MANY_OBJECTS); ERR_STR(ERROR_FORMAT_NOT_SUPPORTED); ERR_STR(ERROR_FRAGMENTED_POOL); ERR_STR(ERROR_UNKNOWN); ERR_STR(ERROR_OUT_OF_POOL_MEMORY); ERR_STR(ERROR_INVALID_EXTERNAL_HANDLE); ERR_STR(ERROR_FRAGMENTATION); ERR_STR(ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS); ERR_STR(PIPELINE_COMPILE_REQUIRED); ERR_STR(ERROR_SURFACE_LOST_KHR); ERR_STR(ERROR_NATIVE_WINDOW_IN_USE_KHR); ERR_STR(SUBOPTIMAL_KHR); ERR_STR(ERROR_OUT_OF_DATE_KHR); ERR_STR(ERROR_INCOMPATIBLE_DISPLAY_KHR); ERR_STR(ERROR_VALIDATION_FAILED_EXT); ERR_STR(ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT); default: return ""; } #undef ERR_STR } void vulkan_change_layout(VkCommandBuffer cb, VkImage img, VkImageLayout ol, VkPipelineStageFlags srcs, VkAccessFlags srca, VkImageLayout nl, VkPipelineStageFlags dsts, VkAccessFlags dsta) { VkImageMemoryBarrier barrier = { .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .oldLayout = ol, .newLayout = nl, .image = img, .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .subresourceRange.layerCount = 1, .subresourceRange.levelCount = 1, .srcAccessMask = srca, .dstAccessMask = dsta, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, }; vkCmdPipelineBarrier(cb, srcs, dsts, 0, 0, NULL, 0, NULL, 1, &barrier); } wlroots-0.17.1/render/vulkan/vulkan.c000066400000000000000000000513421454110342200175350ustar00rootroot00000000000000#if !defined(__FreeBSD__) #define _POSIX_C_SOURCE 200809L #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include "render/dmabuf.h" #include "render/vulkan.h" #if defined(__linux__) #include #endif static bool check_extension(const VkExtensionProperties *avail, uint32_t avail_len, const char *name) { for (size_t i = 0; i < avail_len; i++) { if (strcmp(avail[i].extensionName, name) == 0) { return true; } } return false; } static VkBool32 debug_callback(VkDebugUtilsMessageSeverityFlagBitsEXT severity, VkDebugUtilsMessageTypeFlagsEXT type, const VkDebugUtilsMessengerCallbackDataEXT *debug_data, void *data) { // we ignore some of the non-helpful warnings static const char *const ignored[] = { // notifies us that shader output is not consumed since // we use the shared vertex buffer with uv output "UNASSIGNED-CoreValidation-Shader-OutputNotConsumed", }; if (debug_data->pMessageIdName) { for (unsigned i = 0; i < sizeof(ignored) / sizeof(ignored[0]); ++i) { if (strcmp(debug_data->pMessageIdName, ignored[i]) == 0) { return false; } } } enum wlr_log_importance importance; switch (severity) { case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: importance = WLR_ERROR; break; default: case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: importance = WLR_INFO; break; } wlr_log(importance, "%s (%s)", debug_data->pMessage, debug_data->pMessageIdName); if (debug_data->queueLabelCount > 0) { const char *name = debug_data->pQueueLabels[0].pLabelName; if (name) { wlr_log(importance, " last label '%s'", name); } } for (unsigned i = 0; i < debug_data->objectCount; ++i) { if (debug_data->pObjects[i].pObjectName) { wlr_log(importance, " involving '%s'", debug_data->pMessage); } } return false; } struct wlr_vk_instance *vulkan_instance_create(bool debug) { // we require vulkan 1.1 PFN_vkEnumerateInstanceVersion pfEnumInstanceVersion = (PFN_vkEnumerateInstanceVersion) vkGetInstanceProcAddr(VK_NULL_HANDLE, "vkEnumerateInstanceVersion"); if (!pfEnumInstanceVersion) { wlr_log(WLR_ERROR, "wlroots requires vulkan 1.1 which is not available"); return NULL; } uint32_t ini_version; if (pfEnumInstanceVersion(&ini_version) != VK_SUCCESS || ini_version < VK_API_VERSION_1_1) { wlr_log(WLR_ERROR, "wlroots requires vulkan 1.1 which is not available"); return NULL; } uint32_t avail_extc = 0; VkResult res; res = vkEnumerateInstanceExtensionProperties(NULL, &avail_extc, NULL); if ((res != VK_SUCCESS) || (avail_extc == 0)) { wlr_vk_error("Could not enumerate instance extensions (1)", res); return NULL; } VkExtensionProperties avail_ext_props[avail_extc + 1]; res = vkEnumerateInstanceExtensionProperties(NULL, &avail_extc, avail_ext_props); if (res != VK_SUCCESS) { wlr_vk_error("Could not enumerate instance extensions (2)", res); return NULL; } for (size_t j = 0; j < avail_extc; ++j) { wlr_log(WLR_DEBUG, "Vulkan instance extension %s v%"PRIu32, avail_ext_props[j].extensionName, avail_ext_props[j].specVersion); } struct wlr_vk_instance *ini = calloc(1, sizeof(*ini)); if (!ini) { wlr_log_errno(WLR_ERROR, "allocation failed"); return NULL; } size_t extensions_len = 0; const char *extensions[1] = {0}; bool debug_utils_found = false; if (debug && check_extension(avail_ext_props, avail_extc, VK_EXT_DEBUG_UTILS_EXTENSION_NAME)) { debug_utils_found = true; extensions[extensions_len++] = VK_EXT_DEBUG_UTILS_EXTENSION_NAME; } assert(extensions_len <= sizeof(extensions) / sizeof(extensions[0])); VkApplicationInfo application_info = { .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, .pEngineName = "wlroots", .engineVersion = WLR_VERSION_NUM, .apiVersion = VK_API_VERSION_1_1, }; VkInstanceCreateInfo instance_info = { .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, .pApplicationInfo = &application_info, .enabledExtensionCount = extensions_len, .ppEnabledExtensionNames = extensions, .enabledLayerCount = 0, .ppEnabledLayerNames = NULL, }; VkDebugUtilsMessageSeverityFlagsEXT severity = // VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; VkDebugUtilsMessageTypeFlagsEXT types = // VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; VkDebugUtilsMessengerCreateInfoEXT debug_info = { .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT, .messageSeverity = severity, .messageType = types, .pfnUserCallback = &debug_callback, .pUserData = ini, }; if (debug_utils_found) { // already adding the debug utils messenger extension to // instance creation gives us additional information during // instance creation and destruction, can be useful for debugging // layers/extensions not being found. instance_info.pNext = &debug_info; } res = vkCreateInstance(&instance_info, NULL, &ini->instance); if (res != VK_SUCCESS) { wlr_vk_error("Could not create instance", res); goto error; } if (debug_utils_found) { ini->api.createDebugUtilsMessengerEXT = (PFN_vkCreateDebugUtilsMessengerEXT) vkGetInstanceProcAddr( ini->instance, "vkCreateDebugUtilsMessengerEXT"); ini->api.destroyDebugUtilsMessengerEXT = (PFN_vkDestroyDebugUtilsMessengerEXT) vkGetInstanceProcAddr( ini->instance, "vkDestroyDebugUtilsMessengerEXT"); if (ini->api.createDebugUtilsMessengerEXT) { ini->api.createDebugUtilsMessengerEXT(ini->instance, &debug_info, NULL, &ini->messenger); } else { wlr_log(WLR_ERROR, "vkCreateDebugUtilsMessengerEXT not found"); } } return ini; error: vulkan_instance_destroy(ini); return NULL; } void vulkan_instance_destroy(struct wlr_vk_instance *ini) { if (!ini) { return; } if (ini->messenger && ini->api.destroyDebugUtilsMessengerEXT) { ini->api.destroyDebugUtilsMessengerEXT(ini->instance, ini->messenger, NULL); } if (ini->instance) { vkDestroyInstance(ini->instance, NULL); } free(ini); } static void log_phdev(const VkPhysicalDeviceProperties *props) { uint32_t vv_major = VK_VERSION_MAJOR(props->apiVersion); uint32_t vv_minor = VK_VERSION_MINOR(props->apiVersion); uint32_t vv_patch = VK_VERSION_PATCH(props->apiVersion); uint32_t dv_major = VK_VERSION_MAJOR(props->driverVersion); uint32_t dv_minor = VK_VERSION_MINOR(props->driverVersion); uint32_t dv_patch = VK_VERSION_PATCH(props->driverVersion); const char *dev_type = "unknown"; switch (props->deviceType) { case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: dev_type = "integrated"; break; case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: dev_type = "discrete"; break; case VK_PHYSICAL_DEVICE_TYPE_CPU: dev_type = "cpu"; break; case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU: dev_type = "vgpu"; break; default: break; } wlr_log(WLR_INFO, "Vulkan device: '%s'", props->deviceName); wlr_log(WLR_INFO, " Device type: '%s'", dev_type); wlr_log(WLR_INFO, " Supported API version: %u.%u.%u", vv_major, vv_minor, vv_patch); wlr_log(WLR_INFO, " Driver version: %u.%u.%u", dv_major, dv_minor, dv_patch); } VkPhysicalDevice vulkan_find_drm_phdev(struct wlr_vk_instance *ini, int drm_fd) { VkResult res; uint32_t num_phdevs; res = vkEnumeratePhysicalDevices(ini->instance, &num_phdevs, NULL); if (res != VK_SUCCESS) { wlr_vk_error("Could not retrieve physical devices", res); return VK_NULL_HANDLE; } VkPhysicalDevice phdevs[1 + num_phdevs]; res = vkEnumeratePhysicalDevices(ini->instance, &num_phdevs, phdevs); if (res != VK_SUCCESS) { wlr_vk_error("Could not retrieve physical devices", res); return VK_NULL_HANDLE; } struct stat drm_stat = {0}; if (fstat(drm_fd, &drm_stat) != 0) { wlr_log_errno(WLR_ERROR, "fstat failed"); return VK_NULL_HANDLE; } for (uint32_t i = 0; i < num_phdevs; ++i) { VkPhysicalDevice phdev = phdevs[i]; // check whether device supports vulkan 1.1, needed for // vkGetPhysicalDeviceProperties2 VkPhysicalDeviceProperties phdev_props; vkGetPhysicalDeviceProperties(phdev, &phdev_props); log_phdev(&phdev_props); if (phdev_props.apiVersion < VK_API_VERSION_1_1) { // NOTE: we could additionaly check whether the // VkPhysicalDeviceProperties2KHR extension is supported but // implementations not supporting 1.1 are unlikely in future continue; } // check for extensions uint32_t avail_extc = 0; res = vkEnumerateDeviceExtensionProperties(phdev, NULL, &avail_extc, NULL); if ((res != VK_SUCCESS) || (avail_extc == 0)) { wlr_vk_error(" Could not enumerate device extensions", res); continue; } VkExtensionProperties avail_ext_props[avail_extc + 1]; res = vkEnumerateDeviceExtensionProperties(phdev, NULL, &avail_extc, avail_ext_props); if (res != VK_SUCCESS) { wlr_vk_error(" Could not enumerate device extensions", res); continue; } bool has_drm_props = check_extension(avail_ext_props, avail_extc, VK_EXT_PHYSICAL_DEVICE_DRM_EXTENSION_NAME); bool has_driver_props = check_extension(avail_ext_props, avail_extc, VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME); VkPhysicalDeviceProperties2 props = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, }; VkPhysicalDeviceDrmPropertiesEXT drm_props = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRM_PROPERTIES_EXT, }; if (has_drm_props) { drm_props.pNext = props.pNext; props.pNext = &drm_props; } VkPhysicalDeviceDriverPropertiesKHR driver_props = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES, }; if (has_driver_props) { driver_props.pNext = props.pNext; props.pNext = &driver_props; } vkGetPhysicalDeviceProperties2(phdev, &props); if (has_driver_props) { wlr_log(WLR_INFO, " Driver name: %s (%s)", driver_props.driverName, driver_props.driverInfo); } if (!has_drm_props) { wlr_log(WLR_DEBUG, " Ignoring physical device \"%s\": " "VK_EXT_physical_device_drm not supported", phdev_props.deviceName); continue; } dev_t primary_devid = makedev(drm_props.primaryMajor, drm_props.primaryMinor); dev_t render_devid = makedev(drm_props.renderMajor, drm_props.renderMinor); if (primary_devid == drm_stat.st_rdev || render_devid == drm_stat.st_rdev) { wlr_log(WLR_INFO, "Found matching Vulkan physical device: %s", phdev_props.deviceName); return phdev; } } return VK_NULL_HANDLE; } int vulkan_open_phdev_drm_fd(VkPhysicalDevice phdev) { // vulkan_find_drm_phdev() already checks that VK_EXT_physical_device_drm // is supported VkPhysicalDeviceDrmPropertiesEXT drm_props = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRM_PROPERTIES_EXT, }; VkPhysicalDeviceProperties2 props = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, .pNext = &drm_props, }; vkGetPhysicalDeviceProperties2(phdev, &props); dev_t devid; if (drm_props.hasRender) { devid = makedev(drm_props.renderMajor, drm_props.renderMinor); } else if (drm_props.hasPrimary) { devid = makedev(drm_props.primaryMajor, drm_props.primaryMinor); } else { wlr_log(WLR_ERROR, "Physical device is missing both render and primary nodes"); return -1; } drmDevice *device = NULL; if (drmGetDeviceFromDevId(devid, 0, &device) != 0) { wlr_log_errno(WLR_ERROR, "drmGetDeviceFromDevId failed"); return -1; } const char *name = NULL; if (device->available_nodes & (1 << DRM_NODE_RENDER)) { name = device->nodes[DRM_NODE_RENDER]; } else { assert(device->available_nodes & (1 << DRM_NODE_PRIMARY)); name = device->nodes[DRM_NODE_PRIMARY]; wlr_log(WLR_DEBUG, "DRM device %s has no render node, " "falling back to primary node", name); } int drm_fd = open(name, O_RDWR | O_NONBLOCK | O_CLOEXEC); if (drm_fd < 0) { wlr_log_errno(WLR_ERROR, "Failed to open DRM node %s", name); } drmFreeDevice(&device); return drm_fd; } static void load_device_proc(struct wlr_vk_device *dev, const char *name, void *proc_ptr) { void *proc = (void *)vkGetDeviceProcAddr(dev->dev, name); if (proc == NULL) { abort(); } *(void **)proc_ptr = proc; } struct wlr_vk_device *vulkan_device_create(struct wlr_vk_instance *ini, VkPhysicalDevice phdev) { VkResult res; uint32_t avail_extc = 0; res = vkEnumerateDeviceExtensionProperties(phdev, NULL, &avail_extc, NULL); if (res != VK_SUCCESS || avail_extc == 0) { wlr_vk_error("Could not enumerate device extensions (1)", res); return NULL; } VkExtensionProperties avail_ext_props[avail_extc + 1]; res = vkEnumerateDeviceExtensionProperties(phdev, NULL, &avail_extc, avail_ext_props); if (res != VK_SUCCESS) { wlr_vk_error("Could not enumerate device extensions (2)", res); return NULL; } for (size_t j = 0; j < avail_extc; ++j) { wlr_log(WLR_DEBUG, "Vulkan device extension %s v%"PRIu32, avail_ext_props[j].extensionName, avail_ext_props[j].specVersion); } struct wlr_vk_device *dev = calloc(1, sizeof(*dev)); if (!dev) { wlr_log_errno(WLR_ERROR, "allocation failed"); return NULL; } dev->phdev = phdev; dev->instance = ini; dev->drm_fd = -1; // For dmabuf import we require at least the external_memory_fd, // external_memory_dma_buf, queue_family_foreign and // image_drm_format_modifier extensions. // The size is set to a large number to allow for other conditional // extensions before the device is created const char *extensions[32] = {0}; size_t extensions_len = 0; extensions[extensions_len++] = VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME; extensions[extensions_len++] = VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME; extensions[extensions_len++] = VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME; // or vulkan 1.2 extensions[extensions_len++] = VK_EXT_EXTERNAL_MEMORY_DMA_BUF_EXTENSION_NAME; extensions[extensions_len++] = VK_EXT_QUEUE_FAMILY_FOREIGN_EXTENSION_NAME; extensions[extensions_len++] = VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME; extensions[extensions_len++] = VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME; // or vulkan 1.2 extensions[extensions_len++] = VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME; // or vulkan 1.3 for (size_t i = 0; i < extensions_len; i++) { if (!check_extension(avail_ext_props, avail_extc, extensions[i])) { wlr_log(WLR_ERROR, "vulkan: required device extension %s not found", extensions[i]); goto error; } } { uint32_t qfam_count; vkGetPhysicalDeviceQueueFamilyProperties(phdev, &qfam_count, NULL); assert(qfam_count > 0); VkQueueFamilyProperties queue_props[qfam_count]; vkGetPhysicalDeviceQueueFamilyProperties(phdev, &qfam_count, queue_props); bool graphics_found = false; for (unsigned i = 0u; i < qfam_count; ++i) { graphics_found = queue_props[i].queueFlags & VK_QUEUE_GRAPHICS_BIT; if (graphics_found) { dev->queue_family = i; break; } } assert(graphics_found); } const VkPhysicalDeviceExternalSemaphoreInfo ext_semaphore_info = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO, .handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT, }; VkExternalSemaphoreProperties ext_semaphore_props = { .sType = VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES, }; vkGetPhysicalDeviceExternalSemaphoreProperties(phdev, &ext_semaphore_info, &ext_semaphore_props); bool exportable_semaphore = ext_semaphore_props.externalSemaphoreFeatures & VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT; bool importable_semaphore = ext_semaphore_props.externalSemaphoreFeatures & VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT; if (!exportable_semaphore) { wlr_log(WLR_DEBUG, "VkSemaphore is not exportable to a sync_file"); } if (!importable_semaphore) { wlr_log(WLR_DEBUG, "VkSemaphore is not importable from a sync_file"); } bool dmabuf_sync_file_import_export = dmabuf_check_sync_file_import_export(); if (!dmabuf_sync_file_import_export) { wlr_log(WLR_DEBUG, "DMA-BUF sync_file import/export not supported"); } dev->implicit_sync_interop = exportable_semaphore && importable_semaphore && dmabuf_sync_file_import_export; if (dev->implicit_sync_interop) { wlr_log(WLR_DEBUG, "Implicit sync interop supported"); } else { wlr_log(WLR_INFO, "Implicit sync interop not supported, " "falling back to blocking"); } VkPhysicalDeviceSamplerYcbcrConversionFeatures phdev_sampler_ycbcr_features = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES, }; VkPhysicalDeviceFeatures2 phdev_features = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2, .pNext = &phdev_sampler_ycbcr_features, }; vkGetPhysicalDeviceFeatures2(phdev, &phdev_features); dev->sampler_ycbcr_conversion = phdev_sampler_ycbcr_features.samplerYcbcrConversion; wlr_log(WLR_DEBUG, "Sampler YCbCr conversion %s", dev->sampler_ycbcr_conversion ? "supported" : "not supported"); const float prio = 1.f; VkDeviceQueueCreateInfo qinfo = { .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, .queueFamilyIndex = dev->queue_family, .queueCount = 1, .pQueuePriorities = &prio, }; VkDeviceQueueGlobalPriorityCreateInfoKHR global_priority; bool has_global_priority = check_extension(avail_ext_props, avail_extc, VK_KHR_GLOBAL_PRIORITY_EXTENSION_NAME); if (has_global_priority) { // If global priorities are supported, request a high-priority context global_priority = (VkDeviceQueueGlobalPriorityCreateInfoKHR){ .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_GLOBAL_PRIORITY_CREATE_INFO_KHR, .globalPriority = VK_QUEUE_GLOBAL_PRIORITY_HIGH_KHR, }; qinfo.pNext = &global_priority; extensions[extensions_len++] = VK_KHR_GLOBAL_PRIORITY_EXTENSION_NAME; wlr_log(WLR_DEBUG, "Requesting a high-priority device queue"); } else { wlr_log(WLR_DEBUG, "Global priorities are not supported, " "falling back to regular queue priority"); } VkPhysicalDeviceSamplerYcbcrConversionFeatures sampler_ycbcr_features = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES, .samplerYcbcrConversion = dev->sampler_ycbcr_conversion, }; VkPhysicalDeviceSynchronization2FeaturesKHR sync2_features = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SYNCHRONIZATION_2_FEATURES_KHR, .pNext = &sampler_ycbcr_features, .synchronization2 = VK_TRUE, }; VkPhysicalDeviceTimelineSemaphoreFeaturesKHR timeline_features = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES_KHR, .pNext = &sync2_features, .timelineSemaphore = VK_TRUE, }; VkDeviceCreateInfo dev_info = { .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, .pNext = &timeline_features, .queueCreateInfoCount = 1u, .pQueueCreateInfos = &qinfo, .enabledExtensionCount = extensions_len, .ppEnabledExtensionNames = extensions, }; assert(extensions_len < sizeof(extensions) / sizeof(extensions[0])); res = vkCreateDevice(phdev, &dev_info, NULL, &dev->dev); if (has_global_priority && (res == VK_ERROR_NOT_PERMITTED_EXT || res == VK_ERROR_INITIALIZATION_FAILED)) { // Try to recover from the driver denying a global priority queue wlr_log(WLR_DEBUG, "Failed to obtain a high-priority device queue, " "falling back to regular queue priority"); qinfo.pNext = NULL; res = vkCreateDevice(phdev, &dev_info, NULL, &dev->dev); } if (res != VK_SUCCESS) { wlr_vk_error("Failed to create vulkan device", res); goto error; } vkGetDeviceQueue(dev->dev, dev->queue_family, 0, &dev->queue); load_device_proc(dev, "vkGetMemoryFdPropertiesKHR", &dev->api.vkGetMemoryFdPropertiesKHR); load_device_proc(dev, "vkWaitSemaphoresKHR", &dev->api.vkWaitSemaphoresKHR); load_device_proc(dev, "vkGetSemaphoreCounterValueKHR", &dev->api.vkGetSemaphoreCounterValueKHR); load_device_proc(dev, "vkGetSemaphoreFdKHR", &dev->api.vkGetSemaphoreFdKHR); load_device_proc(dev, "vkImportSemaphoreFdKHR", &dev->api.vkImportSemaphoreFdKHR); load_device_proc(dev, "vkQueueSubmit2KHR", &dev->api.vkQueueSubmit2KHR); size_t max_fmts; const struct wlr_vk_format *fmts = vulkan_get_format_list(&max_fmts); dev->shm_formats = calloc(max_fmts, sizeof(*dev->shm_formats)); dev->format_props = calloc(max_fmts, sizeof(*dev->format_props)); if (!dev->shm_formats || !dev->format_props) { wlr_log_errno(WLR_ERROR, "allocation failed"); goto error; } wlr_log(WLR_DEBUG, "Supported Vulkan formats:"); for (unsigned i = 0u; i < max_fmts; ++i) { vulkan_format_props_query(dev, &fmts[i]); } return dev; error: vulkan_device_destroy(dev); return NULL; } void vulkan_device_destroy(struct wlr_vk_device *dev) { if (!dev) { return; } if (dev->dev) { vkDestroyDevice(dev->dev, NULL); } if (dev->drm_fd > 0) { close(dev->drm_fd); } wlr_drm_format_set_finish(&dev->dmabuf_render_formats); wlr_drm_format_set_finish(&dev->dmabuf_texture_formats); for (unsigned i = 0u; i < dev->format_prop_count; ++i) { vulkan_format_props_finish(&dev->format_props[i]); } free(dev->shm_formats); free(dev->format_props); free(dev); } wlroots-0.17.1/render/wlr_renderer.c000066400000000000000000000300261454110342200174230ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if WLR_HAS_GLES2_RENDERER #include #include #endif #if WLR_HAS_VULKAN_RENDERER #include #endif // WLR_HAS_VULKAN_RENDERER #include "backend/backend.h" #include "render/pass.h" #include "render/pixel_format.h" #include "render/wlr_renderer.h" #include "util/env.h" void wlr_renderer_init(struct wlr_renderer *renderer, const struct wlr_renderer_impl *impl) { if (!impl->begin_buffer_pass) { assert(impl->begin); assert(impl->clear); assert(impl->scissor); assert(impl->render_subtexture_with_matrix); assert(impl->render_quad_with_matrix); } assert(impl->get_shm_texture_formats); assert(impl->get_render_buffer_caps); *renderer = (struct wlr_renderer){ .impl = impl, }; wl_signal_init(&renderer->events.destroy); wl_signal_init(&renderer->events.lost); } void wlr_renderer_destroy(struct wlr_renderer *r) { if (!r) { return; } assert(!r->rendering); wl_signal_emit_mutable(&r->events.destroy, r); if (r->impl && r->impl->destroy) { r->impl->destroy(r); } else { free(r); } } bool renderer_bind_buffer(struct wlr_renderer *r, struct wlr_buffer *buffer) { assert(!r->rendering); if (!r->impl->bind_buffer) { return false; } return r->impl->bind_buffer(r, buffer); } bool wlr_renderer_begin(struct wlr_renderer *r, uint32_t width, uint32_t height) { assert(!r->rendering); if (!r->impl->begin(r, width, height)) { return false; } r->rendering = true; return true; } bool wlr_renderer_begin_with_buffer(struct wlr_renderer *r, struct wlr_buffer *buffer) { if (!renderer_bind_buffer(r, buffer)) { return false; } if (!wlr_renderer_begin(r, buffer->width, buffer->height)) { renderer_bind_buffer(r, NULL); return false; } r->rendering_with_buffer = true; return true; } void wlr_renderer_end(struct wlr_renderer *r) { assert(r->rendering); if (r->impl->end) { r->impl->end(r); } r->rendering = false; if (r->rendering_with_buffer) { renderer_bind_buffer(r, NULL); r->rendering_with_buffer = false; } } void wlr_renderer_clear(struct wlr_renderer *r, const float color[static 4]) { assert(r->rendering); r->impl->clear(r, color); } void wlr_renderer_scissor(struct wlr_renderer *r, struct wlr_box *box) { assert(r->rendering); r->impl->scissor(r, box); } bool wlr_render_texture(struct wlr_renderer *r, struct wlr_texture *texture, const float projection[static 9], int x, int y, float alpha) { struct wlr_box box = { .x = x, .y = y, .width = texture->width, .height = texture->height, }; float matrix[9]; wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL, 0, projection); return wlr_render_texture_with_matrix(r, texture, matrix, alpha); } bool wlr_render_texture_with_matrix(struct wlr_renderer *r, struct wlr_texture *texture, const float matrix[static 9], float alpha) { struct wlr_fbox box = { .x = 0, .y = 0, .width = texture->width, .height = texture->height, }; return wlr_render_subtexture_with_matrix(r, texture, &box, matrix, alpha); } bool wlr_render_subtexture_with_matrix(struct wlr_renderer *r, struct wlr_texture *texture, const struct wlr_fbox *box, const float matrix[static 9], float alpha) { assert(r->rendering); assert(texture->renderer == r); return r->impl->render_subtexture_with_matrix(r, texture, box, matrix, alpha); } void wlr_render_rect(struct wlr_renderer *r, const struct wlr_box *box, const float color[static 4], const float projection[static 9]) { if (box->width == 0 || box->height == 0) { return; } assert(box->width > 0 && box->height > 0); float matrix[9]; wlr_matrix_project_box(matrix, box, WL_OUTPUT_TRANSFORM_NORMAL, 0, projection); wlr_render_quad_with_matrix(r, color, matrix); } void wlr_render_quad_with_matrix(struct wlr_renderer *r, const float color[static 4], const float matrix[static 9]) { assert(r->rendering); r->impl->render_quad_with_matrix(r, color, matrix); } const uint32_t *wlr_renderer_get_shm_texture_formats(struct wlr_renderer *r, size_t *len) { return r->impl->get_shm_texture_formats(r, len); } const struct wlr_drm_format_set *wlr_renderer_get_dmabuf_texture_formats( struct wlr_renderer *r) { if (!r->impl->get_dmabuf_texture_formats) { return NULL; } return r->impl->get_dmabuf_texture_formats(r); } const struct wlr_drm_format_set *wlr_renderer_get_render_formats( struct wlr_renderer *r) { if (!r->impl->get_render_formats) { return NULL; } return r->impl->get_render_formats(r); } uint32_t renderer_get_render_buffer_caps(struct wlr_renderer *r) { return r->impl->get_render_buffer_caps(r); } bool wlr_renderer_read_pixels(struct wlr_renderer *r, uint32_t fmt, uint32_t stride, uint32_t width, uint32_t height, uint32_t src_x, uint32_t src_y, uint32_t dst_x, uint32_t dst_y, void *data) { if (!r->impl->read_pixels) { return false; } return r->impl->read_pixels(r, fmt, stride, width, height, src_x, src_y, dst_x, dst_y, data); } bool wlr_renderer_init_wl_shm(struct wlr_renderer *r, struct wl_display *wl_display) { return wlr_shm_create_with_renderer(wl_display, 1, r) != NULL; } bool wlr_renderer_init_wl_display(struct wlr_renderer *r, struct wl_display *wl_display) { if (!wlr_renderer_init_wl_shm(r, wl_display)) { return false; } if (wlr_renderer_get_dmabuf_texture_formats(r) != NULL && wlr_renderer_get_drm_fd(r) >= 0) { if (wlr_drm_create(wl_display, r) == NULL) { return false; } if (wlr_linux_dmabuf_v1_create_with_renderer(wl_display, 4, r) == NULL) { return false; } } return true; } static int open_drm_render_node(void) { uint32_t flags = 0; int devices_len = drmGetDevices2(flags, NULL, 0); if (devices_len < 0) { wlr_log(WLR_ERROR, "drmGetDevices2 failed: %s", strerror(-devices_len)); return -1; } drmDevice **devices = calloc(devices_len, sizeof(*devices)); if (devices == NULL) { wlr_log_errno(WLR_ERROR, "Allocation failed"); return -1; } devices_len = drmGetDevices2(flags, devices, devices_len); if (devices_len < 0) { free(devices); wlr_log(WLR_ERROR, "drmGetDevices2 failed: %s", strerror(-devices_len)); return -1; } int fd = -1; for (int i = 0; i < devices_len; i++) { drmDevice *dev = devices[i]; if (dev->available_nodes & (1 << DRM_NODE_RENDER)) { const char *name = dev->nodes[DRM_NODE_RENDER]; wlr_log(WLR_DEBUG, "Opening DRM render node '%s'", name); fd = open(name, O_RDWR | O_CLOEXEC); if (fd < 0) { wlr_log_errno(WLR_ERROR, "Failed to open '%s'", name); goto out; } break; } } if (fd < 0) { wlr_log(WLR_ERROR, "Failed to find any DRM render node"); } out: for (int i = 0; i < devices_len; i++) { drmFreeDevice(&devices[i]); } free(devices); return fd; } static bool open_preferred_drm_fd(struct wlr_backend *backend, int *drm_fd_ptr, bool *own_drm_fd) { if (*drm_fd_ptr >= 0) { return true; } // Allow the user to override the render node const char *render_name = getenv("WLR_RENDER_DRM_DEVICE"); if (render_name != NULL) { wlr_log(WLR_INFO, "Opening DRM render node '%s' from WLR_RENDER_DRM_DEVICE", render_name); int drm_fd = open(render_name, O_RDWR | O_CLOEXEC); if (drm_fd < 0) { wlr_log_errno(WLR_ERROR, "Failed to open '%s'", render_name); return false; } if (drmGetNodeTypeFromFd(drm_fd) != DRM_NODE_RENDER) { wlr_log(WLR_ERROR, "'%s' is not a DRM render node", render_name); close(drm_fd); return false; } *drm_fd_ptr = drm_fd; *own_drm_fd = true; return true; } // Prefer the backend's DRM node, if any int backend_drm_fd = wlr_backend_get_drm_fd(backend); if (backend_drm_fd >= 0) { *drm_fd_ptr = backend_drm_fd; *own_drm_fd = false; return true; } // If the backend hasn't picked a DRM FD, but accepts DMA-BUFs, pick an // arbitrary render node uint32_t backend_caps = backend_get_buffer_caps(backend); if (backend_caps & WLR_BUFFER_CAP_DMABUF) { int drm_fd = open_drm_render_node(); if (drm_fd < 0) { return false; } *drm_fd_ptr = drm_fd; *own_drm_fd = true; return true; } return false; } static void log_creation_failure(bool is_auto, const char *msg) { wlr_log(is_auto ? WLR_DEBUG : WLR_ERROR, "%s%s", msg, is_auto ? ". Skipping!" : ""); } static bool has_render_node(struct wlr_backend *backend) { if (!backend) { return false; } int backend_drm_fd = wlr_backend_get_drm_fd(backend); if (backend_drm_fd < 0) { return false; } char *render_node = drmGetRenderDeviceNameFromFd(backend_drm_fd); bool has_render_node = render_node != NULL; free(render_node); return has_render_node; } static struct wlr_renderer *renderer_autocreate(struct wlr_backend *backend, int drm_fd) { const char *renderer_options[] = { "auto", "gles2", "vulkan", "pixman", NULL }; const char *renderer_name = renderer_options[env_parse_switch("WLR_RENDERER", renderer_options)]; bool is_auto = strcmp(renderer_name, "auto") == 0; struct wlr_renderer *renderer = NULL; bool own_drm_fd = false; if ((is_auto && WLR_HAS_GLES2_RENDERER) || strcmp(renderer_name, "gles2") == 0) { if (!open_preferred_drm_fd(backend, &drm_fd, &own_drm_fd)) { log_creation_failure(is_auto, "Cannot create GLES2 renderer: no DRM FD available"); } else { #if WLR_HAS_GLES2_RENDERER renderer = wlr_gles2_renderer_create_with_drm_fd(drm_fd); #else wlr_log(WLR_ERROR, "Cannot create GLES renderer: disabled at compile-time"); #endif if (renderer) { goto out; } else { log_creation_failure(is_auto, "Failed to create a GLES2 renderer"); } } } if (strcmp(renderer_name, "vulkan") == 0) { if (!open_preferred_drm_fd(backend, &drm_fd, &own_drm_fd)) { log_creation_failure(is_auto, "Cannot create Vulkan renderer: no DRM FD available"); } else { #if WLR_HAS_VULKAN_RENDERER renderer = wlr_vk_renderer_create_with_drm_fd(drm_fd); #else wlr_log(WLR_ERROR, "Cannot create Vulkan renderer: disabled at compile-time"); #endif if (renderer) { goto out; } else { log_creation_failure(is_auto, "Failed to create a Vulkan renderer"); } } } if ((is_auto && !has_render_node(backend)) || strcmp(renderer_name, "pixman") == 0) { renderer = wlr_pixman_renderer_create(); if (renderer) { goto out; } else { log_creation_failure(is_auto, "Failed to create a pixman renderer"); } } out: if (renderer == NULL) { wlr_log(WLR_ERROR, "Could not initialize renderer"); } if (own_drm_fd && drm_fd >= 0) { close(drm_fd); } return renderer; } struct wlr_renderer *renderer_autocreate_with_drm_fd(int drm_fd) { assert(drm_fd >= 0); return renderer_autocreate(NULL, drm_fd); } struct wlr_renderer *wlr_renderer_autocreate(struct wlr_backend *backend) { return renderer_autocreate(backend, -1); } int wlr_renderer_get_drm_fd(struct wlr_renderer *r) { if (!r->impl->get_drm_fd) { return -1; } return r->impl->get_drm_fd(r); } struct wlr_render_pass *wlr_renderer_begin_buffer_pass(struct wlr_renderer *renderer, struct wlr_buffer *buffer, const struct wlr_buffer_pass_options *options) { if (!renderer->impl->begin_buffer_pass) { return begin_legacy_buffer_render_pass(renderer, buffer); } struct wlr_buffer_pass_options default_options = {0}; if (!options) { options = &default_options; } return renderer->impl->begin_buffer_pass(renderer, buffer, options); } struct wlr_render_timer *wlr_render_timer_create(struct wlr_renderer *renderer) { if (!renderer->impl->render_timer_create) { return NULL; } return renderer->impl->render_timer_create(renderer); } int wlr_render_timer_get_duration_ns(struct wlr_render_timer *timer) { if (!timer->impl->get_duration_ns) { return -1; } return timer->impl->get_duration_ns(timer); } void wlr_render_timer_destroy(struct wlr_render_timer *timer) { if (!timer->impl->destroy) { return; } timer->impl->destroy(timer); } wlroots-0.17.1/render/wlr_texture.c000066400000000000000000000050321454110342200173140ustar00rootroot00000000000000#include #include #include #include #include #include #include "types/wlr_buffer.h" void wlr_texture_init(struct wlr_texture *texture, struct wlr_renderer *renderer, const struct wlr_texture_impl *impl, uint32_t width, uint32_t height) { assert(renderer); *texture = (struct wlr_texture){ .renderer = renderer, .impl = impl, .width = width, .height = height, }; } void wlr_texture_destroy(struct wlr_texture *texture) { if (texture && texture->impl && texture->impl->destroy) { texture->impl->destroy(texture); } else { free(texture); } } struct wlr_texture *wlr_texture_from_pixels(struct wlr_renderer *renderer, uint32_t fmt, uint32_t stride, uint32_t width, uint32_t height, const void *data) { assert(width > 0); assert(height > 0); assert(stride > 0); assert(data); struct wlr_readonly_data_buffer *buffer = readonly_data_buffer_create(fmt, stride, width, height, data); if (buffer == NULL) { return NULL; } struct wlr_texture *texture = wlr_texture_from_buffer(renderer, &buffer->base); // By this point, the renderer should have locked the buffer if it still // needs to access it in the future. readonly_data_buffer_drop(buffer); return texture; } struct wlr_texture *wlr_texture_from_dmabuf(struct wlr_renderer *renderer, struct wlr_dmabuf_attributes *attribs) { struct wlr_dmabuf_buffer *buffer = dmabuf_buffer_create(attribs); if (buffer == NULL) { return NULL; } struct wlr_texture *texture = wlr_texture_from_buffer(renderer, &buffer->base); // By this point, the renderer should have locked the buffer if it still // needs to access it in the future. dmabuf_buffer_drop(buffer); return texture; } struct wlr_texture *wlr_texture_from_buffer(struct wlr_renderer *renderer, struct wlr_buffer *buffer) { if (!renderer->impl->texture_from_buffer) { return NULL; } return renderer->impl->texture_from_buffer(renderer, buffer); } bool wlr_texture_update_from_buffer(struct wlr_texture *texture, struct wlr_buffer *buffer, const pixman_region32_t *damage) { if (!texture->impl->update_from_buffer) { return false; } if (texture->width != (uint32_t)buffer->width || texture->height != (uint32_t)buffer->height) { return false; } const pixman_box32_t *extents = pixman_region32_extents(damage); if (extents->x1 < 0 || extents->y1 < 0 || extents->x2 > buffer->width || extents->y2 > buffer->height) { return false; } return texture->impl->update_from_buffer(texture, buffer, damage); } wlroots-0.17.1/tinywl/000077500000000000000000000000001454110342200146335ustar00rootroot00000000000000wlroots-0.17.1/tinywl/.gitignore000066400000000000000000000000411454110342200166160ustar00rootroot00000000000000tinywl *-protocol.c *-protocol.h wlroots-0.17.1/tinywl/LICENSE000066400000000000000000000157371454110342200156550ustar00rootroot00000000000000This work is licensed under CC0, which effectively puts it in the public domain. --- Creative Commons Legal Code CC0 1.0 Universal CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER. Statement of Purpose The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an "owner") of an original work of authorship and/or a database (each, a "Work"). Certain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a commons of creative, cultural and scientific works ("Commons") that the public can reliably and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse and redistribute as freely as possible in any form whatsoever and for any purposes, including without limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a free culture and the further production of creative, cultural and scientific works, or to gain reputation or greater distribution for their Work in part through the use and efforts of others. For these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights. 1. Copyright and Related Rights. A Work made available under CC0 may be protected by copyright and related or neighboring rights ("Copyright and Related Rights"). Copyright and Related Rights include, but are not limited to, the following: i. the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work; ii. moral rights retained by the original author(s) and/or performer(s); iii. publicity and privacy rights pertaining to a person's image or likeness depicted in a Work; iv. rights protecting against unfair competition in regards to a Work, subject to the limitations in paragraph 4(a), below; v. rights protecting the extraction, dissemination, use and reuse of data in a Work; vi. database rights (such as those arising under Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, and under any national implementation thereof, including any amended or successor version of such directive); and vii. other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof. 2. Waiver. To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer's Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer's heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer's express Statement of Purpose. 3. Public License Fallback. Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer's express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer's Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "License"). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer's express Statement of Purpose. 4. Limitations and Disclaimers. a. No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document. b. Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law. c. Affirmer disclaims responsibility for clearing rights of other persons that may apply to the Work or any use thereof, including without limitation any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any necessary consents, permissions or other rights required for any use of the Work. d. Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work. wlroots-0.17.1/tinywl/Makefile000066400000000000000000000015741454110342200163020ustar00rootroot00000000000000WAYLAND_PROTOCOLS=$(shell pkg-config --variable=pkgdatadir wayland-protocols) WAYLAND_SCANNER=$(shell pkg-config --variable=wayland_scanner wayland-scanner) LIBS=\ $(shell pkg-config --cflags --libs "wlroots >= 0.17.0") \ $(shell pkg-config --cflags --libs wayland-server) \ $(shell pkg-config --cflags --libs xkbcommon) # wayland-scanner is a tool which generates C headers and rigging for Wayland # protocols, which are specified in XML. wlroots requires you to rig these up # to your build system yourself and provide them in the include path. xdg-shell-protocol.h: $(WAYLAND_SCANNER) server-header \ $(WAYLAND_PROTOCOLS)/stable/xdg-shell/xdg-shell.xml $@ tinywl: tinywl.c xdg-shell-protocol.h $(CC) $(CFLAGS) \ -g -Werror -I. \ -DWLR_USE_UNSTABLE \ -o $@ $< \ $(LIBS) clean: rm -f tinywl xdg-shell-protocol.h xdg-shell-protocol.c .DEFAULT_GOAL=tinywl .PHONY: clean wlroots-0.17.1/tinywl/README.md000066400000000000000000000032131454110342200161110ustar00rootroot00000000000000# TinyWL This is the "minimum viable product" Wayland compositor based on wlroots. It aims to implement a Wayland compositor in the fewest lines of code possible, while still supporting a reasonable set of features. Reading this code is the best starting point for anyone looking to build their own Wayland compositor based on wlroots. ## Building TinyWL TinyWL is disconnected from the main wlroots build system, in order to make it easier to understand the build requirements for your own Wayland compositors. Simply install the dependencies: - wlroots - wayland-protocols And run `make`. ## Running TinyWL You can run TinyWL with `./tinywl`. In an existing Wayland or X11 session, tinywl will open a Wayland or X11 window respectively to act as a virtual display. You can then open Wayland windows by setting `WAYLAND_DISPLAY` to the value shown in the logs. You can also run `./tinywl` from a TTY. In either case, you will likely want to specify `-s [cmd]` to run a command at startup, such as a terminal emulator. This will be necessary to start any new programs from within the compositor, as TinyWL does not support any custom keybindings. TinyWL supports the following keybindings: - `Alt+Escape`: Terminate the compositor - `Alt+F1`: Cycle between windows ## Limitations Notable omissions from TinyWL: - HiDPI support - Any kind of configuration, e.g. output layout - Any protocol other than xdg-shell (e.g. layer-shell, for panels/taskbars/etc; or Xwayland, for proxied X11 windows) - Optional protocols, e.g. screen capture, primary selection, virtual keyboard, etc. Most of these are plug-and-play with wlroots, but they're omitted for brevity. wlroots-0.17.1/tinywl/meson.build000066400000000000000000000001461454110342200167760ustar00rootroot00000000000000executable( 'tinywl', ['tinywl.c', protocols_client_header['xdg-shell']], dependencies: wlroots, ) wlroots-0.17.1/tinywl/tinywl.c000066400000000000000000001164431454110342200163360ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200112L #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* For brevity's sake, struct members are annotated where they are used. */ enum tinywl_cursor_mode { TINYWL_CURSOR_PASSTHROUGH, TINYWL_CURSOR_MOVE, TINYWL_CURSOR_RESIZE, }; struct tinywl_server { struct wl_display *wl_display; struct wlr_backend *backend; struct wlr_renderer *renderer; struct wlr_allocator *allocator; struct wlr_scene *scene; struct wlr_scene_output_layout *scene_layout; struct wlr_xdg_shell *xdg_shell; struct wl_listener new_xdg_surface; struct wl_list toplevels; struct wlr_cursor *cursor; struct wlr_xcursor_manager *cursor_mgr; struct wl_listener cursor_motion; struct wl_listener cursor_motion_absolute; struct wl_listener cursor_button; struct wl_listener cursor_axis; struct wl_listener cursor_frame; struct wlr_seat *seat; struct wl_listener new_input; struct wl_listener request_cursor; struct wl_listener request_set_selection; struct wl_list keyboards; enum tinywl_cursor_mode cursor_mode; struct tinywl_toplevel *grabbed_toplevel; double grab_x, grab_y; struct wlr_box grab_geobox; uint32_t resize_edges; struct wlr_output_layout *output_layout; struct wl_list outputs; struct wl_listener new_output; }; struct tinywl_output { struct wl_list link; struct tinywl_server *server; struct wlr_output *wlr_output; struct wl_listener frame; struct wl_listener request_state; struct wl_listener destroy; }; struct tinywl_toplevel { struct wl_list link; struct tinywl_server *server; struct wlr_xdg_toplevel *xdg_toplevel; struct wlr_scene_tree *scene_tree; struct wl_listener map; struct wl_listener unmap; struct wl_listener destroy; struct wl_listener request_move; struct wl_listener request_resize; struct wl_listener request_maximize; struct wl_listener request_fullscreen; }; struct tinywl_keyboard { struct wl_list link; struct tinywl_server *server; struct wlr_keyboard *wlr_keyboard; struct wl_listener modifiers; struct wl_listener key; struct wl_listener destroy; }; static void focus_toplevel(struct tinywl_toplevel *toplevel, struct wlr_surface *surface) { /* Note: this function only deals with keyboard focus. */ if (toplevel == NULL) { return; } struct tinywl_server *server = toplevel->server; struct wlr_seat *seat = server->seat; struct wlr_surface *prev_surface = seat->keyboard_state.focused_surface; if (prev_surface == surface) { /* Don't re-focus an already focused surface. */ return; } if (prev_surface) { /* * Deactivate the previously focused surface. This lets the client know * it no longer has focus and the client will repaint accordingly, e.g. * stop displaying a caret. */ struct wlr_xdg_toplevel *prev_toplevel = wlr_xdg_toplevel_try_from_wlr_surface(prev_surface); if (prev_toplevel != NULL) { wlr_xdg_toplevel_set_activated(prev_toplevel, false); } } struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat); /* Move the toplevel to the front */ wlr_scene_node_raise_to_top(&toplevel->scene_tree->node); wl_list_remove(&toplevel->link); wl_list_insert(&server->toplevels, &toplevel->link); /* Activate the new surface */ wlr_xdg_toplevel_set_activated(toplevel->xdg_toplevel, true); /* * Tell the seat to have the keyboard enter this surface. wlroots will keep * track of this and automatically send key events to the appropriate * clients without additional work on your part. */ if (keyboard != NULL) { wlr_seat_keyboard_notify_enter(seat, toplevel->xdg_toplevel->base->surface, keyboard->keycodes, keyboard->num_keycodes, &keyboard->modifiers); } } static void keyboard_handle_modifiers( struct wl_listener *listener, void *data) { /* This event is raised when a modifier key, such as shift or alt, is * pressed. We simply communicate this to the client. */ struct tinywl_keyboard *keyboard = wl_container_of(listener, keyboard, modifiers); /* * A seat can only have one keyboard, but this is a limitation of the * Wayland protocol - not wlroots. We assign all connected keyboards to the * same seat. You can swap out the underlying wlr_keyboard like this and * wlr_seat handles this transparently. */ wlr_seat_set_keyboard(keyboard->server->seat, keyboard->wlr_keyboard); /* Send modifiers to the client. */ wlr_seat_keyboard_notify_modifiers(keyboard->server->seat, &keyboard->wlr_keyboard->modifiers); } static bool handle_keybinding(struct tinywl_server *server, xkb_keysym_t sym) { /* * Here we handle compositor keybindings. This is when the compositor is * processing keys, rather than passing them on to the client for its own * processing. * * This function assumes Alt is held down. */ switch (sym) { case XKB_KEY_Escape: wl_display_terminate(server->wl_display); break; case XKB_KEY_F1: /* Cycle to the next toplevel */ if (wl_list_length(&server->toplevels) < 2) { break; } struct tinywl_toplevel *next_toplevel = wl_container_of(server->toplevels.prev, next_toplevel, link); focus_toplevel(next_toplevel, next_toplevel->xdg_toplevel->base->surface); break; default: return false; } return true; } static void keyboard_handle_key( struct wl_listener *listener, void *data) { /* This event is raised when a key is pressed or released. */ struct tinywl_keyboard *keyboard = wl_container_of(listener, keyboard, key); struct tinywl_server *server = keyboard->server; struct wlr_keyboard_key_event *event = data; struct wlr_seat *seat = server->seat; /* Translate libinput keycode -> xkbcommon */ uint32_t keycode = event->keycode + 8; /* Get a list of keysyms based on the keymap for this keyboard */ const xkb_keysym_t *syms; int nsyms = xkb_state_key_get_syms( keyboard->wlr_keyboard->xkb_state, keycode, &syms); bool handled = false; uint32_t modifiers = wlr_keyboard_get_modifiers(keyboard->wlr_keyboard); if ((modifiers & WLR_MODIFIER_ALT) && event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { /* If alt is held down and this button was _pressed_, we attempt to * process it as a compositor keybinding. */ for (int i = 0; i < nsyms; i++) { handled = handle_keybinding(server, syms[i]); } } if (!handled) { /* Otherwise, we pass it along to the client. */ wlr_seat_set_keyboard(seat, keyboard->wlr_keyboard); wlr_seat_keyboard_notify_key(seat, event->time_msec, event->keycode, event->state); } } static void keyboard_handle_destroy(struct wl_listener *listener, void *data) { /* This event is raised by the keyboard base wlr_input_device to signal * the destruction of the wlr_keyboard. It will no longer receive events * and should be destroyed. */ struct tinywl_keyboard *keyboard = wl_container_of(listener, keyboard, destroy); wl_list_remove(&keyboard->modifiers.link); wl_list_remove(&keyboard->key.link); wl_list_remove(&keyboard->destroy.link); wl_list_remove(&keyboard->link); free(keyboard); } static void server_new_keyboard(struct tinywl_server *server, struct wlr_input_device *device) { struct wlr_keyboard *wlr_keyboard = wlr_keyboard_from_input_device(device); struct tinywl_keyboard *keyboard = calloc(1, sizeof(*keyboard)); keyboard->server = server; keyboard->wlr_keyboard = wlr_keyboard; /* We need to prepare an XKB keymap and assign it to the keyboard. This * assumes the defaults (e.g. layout = "us"). */ struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); struct xkb_keymap *keymap = xkb_keymap_new_from_names(context, NULL, XKB_KEYMAP_COMPILE_NO_FLAGS); wlr_keyboard_set_keymap(wlr_keyboard, keymap); xkb_keymap_unref(keymap); xkb_context_unref(context); wlr_keyboard_set_repeat_info(wlr_keyboard, 25, 600); /* Here we set up listeners for keyboard events. */ keyboard->modifiers.notify = keyboard_handle_modifiers; wl_signal_add(&wlr_keyboard->events.modifiers, &keyboard->modifiers); keyboard->key.notify = keyboard_handle_key; wl_signal_add(&wlr_keyboard->events.key, &keyboard->key); keyboard->destroy.notify = keyboard_handle_destroy; wl_signal_add(&device->events.destroy, &keyboard->destroy); wlr_seat_set_keyboard(server->seat, keyboard->wlr_keyboard); /* And add the keyboard to our list of keyboards */ wl_list_insert(&server->keyboards, &keyboard->link); } static void server_new_pointer(struct tinywl_server *server, struct wlr_input_device *device) { /* We don't do anything special with pointers. All of our pointer handling * is proxied through wlr_cursor. On another compositor, you might take this * opportunity to do libinput configuration on the device to set * acceleration, etc. */ wlr_cursor_attach_input_device(server->cursor, device); } static void server_new_input(struct wl_listener *listener, void *data) { /* This event is raised by the backend when a new input device becomes * available. */ struct tinywl_server *server = wl_container_of(listener, server, new_input); struct wlr_input_device *device = data; switch (device->type) { case WLR_INPUT_DEVICE_KEYBOARD: server_new_keyboard(server, device); break; case WLR_INPUT_DEVICE_POINTER: server_new_pointer(server, device); break; default: break; } /* We need to let the wlr_seat know what our capabilities are, which is * communiciated to the client. In TinyWL we always have a cursor, even if * there are no pointer devices, so we always include that capability. */ uint32_t caps = WL_SEAT_CAPABILITY_POINTER; if (!wl_list_empty(&server->keyboards)) { caps |= WL_SEAT_CAPABILITY_KEYBOARD; } wlr_seat_set_capabilities(server->seat, caps); } static void seat_request_cursor(struct wl_listener *listener, void *data) { struct tinywl_server *server = wl_container_of( listener, server, request_cursor); /* This event is raised by the seat when a client provides a cursor image */ struct wlr_seat_pointer_request_set_cursor_event *event = data; struct wlr_seat_client *focused_client = server->seat->pointer_state.focused_client; /* This can be sent by any client, so we check to make sure this one is * actually has pointer focus first. */ if (focused_client == event->seat_client) { /* Once we've vetted the client, we can tell the cursor to use the * provided surface as the cursor image. It will set the hardware cursor * on the output that it's currently on and continue to do so as the * cursor moves between outputs. */ wlr_cursor_set_surface(server->cursor, event->surface, event->hotspot_x, event->hotspot_y); } } static void seat_request_set_selection(struct wl_listener *listener, void *data) { /* This event is raised by the seat when a client wants to set the selection, * usually when the user copies something. wlroots allows compositors to * ignore such requests if they so choose, but in tinywl we always honor */ struct tinywl_server *server = wl_container_of( listener, server, request_set_selection); struct wlr_seat_request_set_selection_event *event = data; wlr_seat_set_selection(server->seat, event->source, event->serial); } static struct tinywl_toplevel *desktop_toplevel_at( struct tinywl_server *server, double lx, double ly, struct wlr_surface **surface, double *sx, double *sy) { /* This returns the topmost node in the scene at the given layout coords. * We only care about surface nodes as we are specifically looking for a * surface in the surface tree of a tinywl_toplevel. */ struct wlr_scene_node *node = wlr_scene_node_at( &server->scene->tree.node, lx, ly, sx, sy); if (node == NULL || node->type != WLR_SCENE_NODE_BUFFER) { return NULL; } struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); struct wlr_scene_surface *scene_surface = wlr_scene_surface_try_from_buffer(scene_buffer); if (!scene_surface) { return NULL; } *surface = scene_surface->surface; /* Find the node corresponding to the tinywl_toplevel at the root of this * surface tree, it is the only one for which we set the data field. */ struct wlr_scene_tree *tree = node->parent; while (tree != NULL && tree->node.data == NULL) { tree = tree->node.parent; } return tree->node.data; } static void reset_cursor_mode(struct tinywl_server *server) { /* Reset the cursor mode to passthrough. */ server->cursor_mode = TINYWL_CURSOR_PASSTHROUGH; server->grabbed_toplevel = NULL; } static void process_cursor_move(struct tinywl_server *server, uint32_t time) { /* Move the grabbed toplevel to the new position. */ struct tinywl_toplevel *toplevel = server->grabbed_toplevel; wlr_scene_node_set_position(&toplevel->scene_tree->node, server->cursor->x - server->grab_x, server->cursor->y - server->grab_y); } static void process_cursor_resize(struct tinywl_server *server, uint32_t time) { /* * Resizing the grabbed toplevel can be a little bit complicated, because we * could be resizing from any corner or edge. This not only resizes the * toplevel on one or two axes, but can also move the toplevel if you resize * from the top or left edges (or top-left corner). * * Note that some shortcuts are taken here. In a more fleshed-out * compositor, you'd wait for the client to prepare a buffer at the new * size, then commit any movement that was prepared. */ struct tinywl_toplevel *toplevel = server->grabbed_toplevel; double border_x = server->cursor->x - server->grab_x; double border_y = server->cursor->y - server->grab_y; int new_left = server->grab_geobox.x; int new_right = server->grab_geobox.x + server->grab_geobox.width; int new_top = server->grab_geobox.y; int new_bottom = server->grab_geobox.y + server->grab_geobox.height; if (server->resize_edges & WLR_EDGE_TOP) { new_top = border_y; if (new_top >= new_bottom) { new_top = new_bottom - 1; } } else if (server->resize_edges & WLR_EDGE_BOTTOM) { new_bottom = border_y; if (new_bottom <= new_top) { new_bottom = new_top + 1; } } if (server->resize_edges & WLR_EDGE_LEFT) { new_left = border_x; if (new_left >= new_right) { new_left = new_right - 1; } } else if (server->resize_edges & WLR_EDGE_RIGHT) { new_right = border_x; if (new_right <= new_left) { new_right = new_left + 1; } } struct wlr_box geo_box; wlr_xdg_surface_get_geometry(toplevel->xdg_toplevel->base, &geo_box); wlr_scene_node_set_position(&toplevel->scene_tree->node, new_left - geo_box.x, new_top - geo_box.y); int new_width = new_right - new_left; int new_height = new_bottom - new_top; wlr_xdg_toplevel_set_size(toplevel->xdg_toplevel, new_width, new_height); } static void process_cursor_motion(struct tinywl_server *server, uint32_t time) { /* If the mode is non-passthrough, delegate to those functions. */ if (server->cursor_mode == TINYWL_CURSOR_MOVE) { process_cursor_move(server, time); return; } else if (server->cursor_mode == TINYWL_CURSOR_RESIZE) { process_cursor_resize(server, time); return; } /* Otherwise, find the toplevel under the pointer and send the event along. */ double sx, sy; struct wlr_seat *seat = server->seat; struct wlr_surface *surface = NULL; struct tinywl_toplevel *toplevel = desktop_toplevel_at(server, server->cursor->x, server->cursor->y, &surface, &sx, &sy); if (!toplevel) { /* If there's no toplevel under the cursor, set the cursor image to a * default. This is what makes the cursor image appear when you move it * around the screen, not over any toplevels. */ wlr_cursor_set_xcursor(server->cursor, server->cursor_mgr, "default"); } if (surface) { /* * Send pointer enter and motion events. * * The enter event gives the surface "pointer focus", which is distinct * from keyboard focus. You get pointer focus by moving the pointer over * a window. * * Note that wlroots will avoid sending duplicate enter/motion events if * the surface has already has pointer focus or if the client is already * aware of the coordinates passed. */ wlr_seat_pointer_notify_enter(seat, surface, sx, sy); wlr_seat_pointer_notify_motion(seat, time, sx, sy); } else { /* Clear pointer focus so future button events and such are not sent to * the last client to have the cursor over it. */ wlr_seat_pointer_clear_focus(seat); } } static void server_cursor_motion(struct wl_listener *listener, void *data) { /* This event is forwarded by the cursor when a pointer emits a _relative_ * pointer motion event (i.e. a delta) */ struct tinywl_server *server = wl_container_of(listener, server, cursor_motion); struct wlr_pointer_motion_event *event = data; /* The cursor doesn't move unless we tell it to. The cursor automatically * handles constraining the motion to the output layout, as well as any * special configuration applied for the specific input device which * generated the event. You can pass NULL for the device if you want to move * the cursor around without any input. */ wlr_cursor_move(server->cursor, &event->pointer->base, event->delta_x, event->delta_y); process_cursor_motion(server, event->time_msec); } static void server_cursor_motion_absolute( struct wl_listener *listener, void *data) { /* This event is forwarded by the cursor when a pointer emits an _absolute_ * motion event, from 0..1 on each axis. This happens, for example, when * wlroots is running under a Wayland window rather than KMS+DRM, and you * move the mouse over the window. You could enter the window from any edge, * so we have to warp the mouse there. There is also some hardware which * emits these events. */ struct tinywl_server *server = wl_container_of(listener, server, cursor_motion_absolute); struct wlr_pointer_motion_absolute_event *event = data; wlr_cursor_warp_absolute(server->cursor, &event->pointer->base, event->x, event->y); process_cursor_motion(server, event->time_msec); } static void server_cursor_button(struct wl_listener *listener, void *data) { /* This event is forwarded by the cursor when a pointer emits a button * event. */ struct tinywl_server *server = wl_container_of(listener, server, cursor_button); struct wlr_pointer_button_event *event = data; /* Notify the client with pointer focus that a button press has occurred */ wlr_seat_pointer_notify_button(server->seat, event->time_msec, event->button, event->state); double sx, sy; struct wlr_surface *surface = NULL; struct tinywl_toplevel *toplevel = desktop_toplevel_at(server, server->cursor->x, server->cursor->y, &surface, &sx, &sy); if (event->state == WLR_BUTTON_RELEASED) { /* If you released any buttons, we exit interactive move/resize mode. */ reset_cursor_mode(server); } else { /* Focus that client if the button was _pressed_ */ focus_toplevel(toplevel, surface); } } static void server_cursor_axis(struct wl_listener *listener, void *data) { /* This event is forwarded by the cursor when a pointer emits an axis event, * for example when you move the scroll wheel. */ struct tinywl_server *server = wl_container_of(listener, server, cursor_axis); struct wlr_pointer_axis_event *event = data; /* Notify the client with pointer focus of the axis event. */ wlr_seat_pointer_notify_axis(server->seat, event->time_msec, event->orientation, event->delta, event->delta_discrete, event->source); } static void server_cursor_frame(struct wl_listener *listener, void *data) { /* This event is forwarded by the cursor when a pointer emits an frame * event. Frame events are sent after regular pointer events to group * multiple events together. For instance, two axis events may happen at the * same time, in which case a frame event won't be sent in between. */ struct tinywl_server *server = wl_container_of(listener, server, cursor_frame); /* Notify the client with pointer focus of the frame event. */ wlr_seat_pointer_notify_frame(server->seat); } static void output_frame(struct wl_listener *listener, void *data) { /* This function is called every time an output is ready to display a frame, * generally at the output's refresh rate (e.g. 60Hz). */ struct tinywl_output *output = wl_container_of(listener, output, frame); struct wlr_scene *scene = output->server->scene; struct wlr_scene_output *scene_output = wlr_scene_get_scene_output( scene, output->wlr_output); /* Render the scene if needed and commit the output */ wlr_scene_output_commit(scene_output, NULL); struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); wlr_scene_output_send_frame_done(scene_output, &now); } static void output_request_state(struct wl_listener *listener, void *data) { /* This function is called when the backend requests a new state for * the output. For example, Wayland and X11 backends request a new mode * when the output window is resized. */ struct tinywl_output *output = wl_container_of(listener, output, request_state); const struct wlr_output_event_request_state *event = data; wlr_output_commit_state(output->wlr_output, event->state); } static void output_destroy(struct wl_listener *listener, void *data) { struct tinywl_output *output = wl_container_of(listener, output, destroy); wl_list_remove(&output->frame.link); wl_list_remove(&output->request_state.link); wl_list_remove(&output->destroy.link); wl_list_remove(&output->link); free(output); } static void server_new_output(struct wl_listener *listener, void *data) { /* This event is raised by the backend when a new output (aka a display or * monitor) becomes available. */ struct tinywl_server *server = wl_container_of(listener, server, new_output); struct wlr_output *wlr_output = data; /* Configures the output created by the backend to use our allocator * and our renderer. Must be done once, before commiting the output */ wlr_output_init_render(wlr_output, server->allocator, server->renderer); /* The output may be disabled, switch it on. */ struct wlr_output_state state; wlr_output_state_init(&state); wlr_output_state_set_enabled(&state, true); /* Some backends don't have modes. DRM+KMS does, and we need to set a mode * before we can use the output. The mode is a tuple of (width, height, * refresh rate), and each monitor supports only a specific set of modes. We * just pick the monitor's preferred mode, a more sophisticated compositor * would let the user configure it. */ struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output); if (mode != NULL) { wlr_output_state_set_mode(&state, mode); } /* Atomically applies the new output state. */ wlr_output_commit_state(wlr_output, &state); wlr_output_state_finish(&state); /* Allocates and configures our state for this output */ struct tinywl_output *output = calloc(1, sizeof(*output)); output->wlr_output = wlr_output; output->server = server; /* Sets up a listener for the frame event. */ output->frame.notify = output_frame; wl_signal_add(&wlr_output->events.frame, &output->frame); /* Sets up a listener for the state request event. */ output->request_state.notify = output_request_state; wl_signal_add(&wlr_output->events.request_state, &output->request_state); /* Sets up a listener for the destroy event. */ output->destroy.notify = output_destroy; wl_signal_add(&wlr_output->events.destroy, &output->destroy); wl_list_insert(&server->outputs, &output->link); /* Adds this to the output layout. The add_auto function arranges outputs * from left-to-right in the order they appear. A more sophisticated * compositor would let the user configure the arrangement of outputs in the * layout. * * The output layout utility automatically adds a wl_output global to the * display, which Wayland clients can see to find out information about the * output (such as DPI, scale factor, manufacturer, etc). */ struct wlr_output_layout_output *l_output = wlr_output_layout_add_auto(server->output_layout, wlr_output); struct wlr_scene_output *scene_output = wlr_scene_output_create(server->scene, wlr_output); wlr_scene_output_layout_add_output(server->scene_layout, l_output, scene_output); } static void xdg_toplevel_map(struct wl_listener *listener, void *data) { /* Called when the surface is mapped, or ready to display on-screen. */ struct tinywl_toplevel *toplevel = wl_container_of(listener, toplevel, map); wl_list_insert(&toplevel->server->toplevels, &toplevel->link); focus_toplevel(toplevel, toplevel->xdg_toplevel->base->surface); } static void xdg_toplevel_unmap(struct wl_listener *listener, void *data) { /* Called when the surface is unmapped, and should no longer be shown. */ struct tinywl_toplevel *toplevel = wl_container_of(listener, toplevel, unmap); /* Reset the cursor mode if the grabbed toplevel was unmapped. */ if (toplevel == toplevel->server->grabbed_toplevel) { reset_cursor_mode(toplevel->server); } wl_list_remove(&toplevel->link); } static void xdg_toplevel_destroy(struct wl_listener *listener, void *data) { /* Called when the xdg_toplevel is destroyed. */ struct tinywl_toplevel *toplevel = wl_container_of(listener, toplevel, destroy); wl_list_remove(&toplevel->map.link); wl_list_remove(&toplevel->unmap.link); wl_list_remove(&toplevel->destroy.link); wl_list_remove(&toplevel->request_move.link); wl_list_remove(&toplevel->request_resize.link); wl_list_remove(&toplevel->request_maximize.link); wl_list_remove(&toplevel->request_fullscreen.link); free(toplevel); } static void begin_interactive(struct tinywl_toplevel *toplevel, enum tinywl_cursor_mode mode, uint32_t edges) { /* This function sets up an interactive move or resize operation, where the * compositor stops propegating pointer events to clients and instead * consumes them itself, to move or resize windows. */ struct tinywl_server *server = toplevel->server; struct wlr_surface *focused_surface = server->seat->pointer_state.focused_surface; if (toplevel->xdg_toplevel->base->surface != wlr_surface_get_root_surface(focused_surface)) { /* Deny move/resize requests from unfocused clients. */ return; } server->grabbed_toplevel = toplevel; server->cursor_mode = mode; if (mode == TINYWL_CURSOR_MOVE) { server->grab_x = server->cursor->x - toplevel->scene_tree->node.x; server->grab_y = server->cursor->y - toplevel->scene_tree->node.y; } else { struct wlr_box geo_box; wlr_xdg_surface_get_geometry(toplevel->xdg_toplevel->base, &geo_box); double border_x = (toplevel->scene_tree->node.x + geo_box.x) + ((edges & WLR_EDGE_RIGHT) ? geo_box.width : 0); double border_y = (toplevel->scene_tree->node.y + geo_box.y) + ((edges & WLR_EDGE_BOTTOM) ? geo_box.height : 0); server->grab_x = server->cursor->x - border_x; server->grab_y = server->cursor->y - border_y; server->grab_geobox = geo_box; server->grab_geobox.x += toplevel->scene_tree->node.x; server->grab_geobox.y += toplevel->scene_tree->node.y; server->resize_edges = edges; } } static void xdg_toplevel_request_move( struct wl_listener *listener, void *data) { /* This event is raised when a client would like to begin an interactive * move, typically because the user clicked on their client-side * decorations. Note that a more sophisticated compositor should check the * provided serial against a list of button press serials sent to this * client, to prevent the client from requesting this whenever they want. */ struct tinywl_toplevel *toplevel = wl_container_of(listener, toplevel, request_move); begin_interactive(toplevel, TINYWL_CURSOR_MOVE, 0); } static void xdg_toplevel_request_resize( struct wl_listener *listener, void *data) { /* This event is raised when a client would like to begin an interactive * resize, typically because the user clicked on their client-side * decorations. Note that a more sophisticated compositor should check the * provided serial against a list of button press serials sent to this * client, to prevent the client from requesting this whenever they want. */ struct wlr_xdg_toplevel_resize_event *event = data; struct tinywl_toplevel *toplevel = wl_container_of(listener, toplevel, request_resize); begin_interactive(toplevel, TINYWL_CURSOR_RESIZE, event->edges); } static void xdg_toplevel_request_maximize( struct wl_listener *listener, void *data) { /* This event is raised when a client would like to maximize itself, * typically because the user clicked on the maximize button on * client-side decorations. tinywl doesn't support maximization, but * to conform to xdg-shell protocol we still must send a configure. * wlr_xdg_surface_schedule_configure() is used to send an empty reply. */ struct tinywl_toplevel *toplevel = wl_container_of(listener, toplevel, request_maximize); wlr_xdg_surface_schedule_configure(toplevel->xdg_toplevel->base); } static void xdg_toplevel_request_fullscreen( struct wl_listener *listener, void *data) { /* Just as with request_maximize, we must send a configure here. */ struct tinywl_toplevel *toplevel = wl_container_of(listener, toplevel, request_fullscreen); wlr_xdg_surface_schedule_configure(toplevel->xdg_toplevel->base); } static void server_new_xdg_surface(struct wl_listener *listener, void *data) { /* This event is raised when wlr_xdg_shell receives a new xdg surface from a * client, either a toplevel (application window) or popup. */ struct tinywl_server *server = wl_container_of(listener, server, new_xdg_surface); struct wlr_xdg_surface *xdg_surface = data; /* We must add xdg popups to the scene graph so they get rendered. The * wlroots scene graph provides a helper for this, but to use it we must * provide the proper parent scene node of the xdg popup. To enable this, * we always set the user data field of xdg_surfaces to the corresponding * scene node. */ if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { struct wlr_xdg_surface *parent = wlr_xdg_surface_try_from_wlr_surface(xdg_surface->popup->parent); assert(parent != NULL); struct wlr_scene_tree *parent_tree = parent->data; xdg_surface->data = wlr_scene_xdg_surface_create( parent_tree, xdg_surface); return; } assert(xdg_surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL); /* Allocate a tinywl_toplevel for this surface */ struct tinywl_toplevel *toplevel = calloc(1, sizeof(*toplevel)); toplevel->server = server; toplevel->xdg_toplevel = xdg_surface->toplevel; toplevel->scene_tree = wlr_scene_xdg_surface_create( &toplevel->server->scene->tree, toplevel->xdg_toplevel->base); toplevel->scene_tree->node.data = toplevel; xdg_surface->data = toplevel->scene_tree; /* Listen to the various events it can emit */ toplevel->map.notify = xdg_toplevel_map; wl_signal_add(&xdg_surface->surface->events.map, &toplevel->map); toplevel->unmap.notify = xdg_toplevel_unmap; wl_signal_add(&xdg_surface->surface->events.unmap, &toplevel->unmap); toplevel->destroy.notify = xdg_toplevel_destroy; wl_signal_add(&xdg_surface->events.destroy, &toplevel->destroy); /* cotd */ struct wlr_xdg_toplevel *xdg_toplevel = xdg_surface->toplevel; toplevel->request_move.notify = xdg_toplevel_request_move; wl_signal_add(&xdg_toplevel->events.request_move, &toplevel->request_move); toplevel->request_resize.notify = xdg_toplevel_request_resize; wl_signal_add(&xdg_toplevel->events.request_resize, &toplevel->request_resize); toplevel->request_maximize.notify = xdg_toplevel_request_maximize; wl_signal_add(&xdg_toplevel->events.request_maximize, &toplevel->request_maximize); toplevel->request_fullscreen.notify = xdg_toplevel_request_fullscreen; wl_signal_add(&xdg_toplevel->events.request_fullscreen, &toplevel->request_fullscreen); } int main(int argc, char *argv[]) { wlr_log_init(WLR_DEBUG, NULL); char *startup_cmd = NULL; int c; while ((c = getopt(argc, argv, "s:h")) != -1) { switch (c) { case 's': startup_cmd = optarg; break; default: printf("Usage: %s [-s startup command]\n", argv[0]); return 0; } } if (optind < argc) { printf("Usage: %s [-s startup command]\n", argv[0]); return 0; } struct tinywl_server server = {0}; /* The Wayland display is managed by libwayland. It handles accepting * clients from the Unix socket, manging Wayland globals, and so on. */ server.wl_display = wl_display_create(); /* The backend is a wlroots feature which abstracts the underlying input and * output hardware. The autocreate option will choose the most suitable * backend based on the current environment, such as opening an X11 window * if an X11 server is running. */ server.backend = wlr_backend_autocreate(server.wl_display, NULL); if (server.backend == NULL) { wlr_log(WLR_ERROR, "failed to create wlr_backend"); return 1; } /* Autocreates a renderer, either Pixman, GLES2 or Vulkan for us. The user * can also specify a renderer using the WLR_RENDERER env var. * The renderer is responsible for defining the various pixel formats it * supports for shared memory, this configures that for clients. */ server.renderer = wlr_renderer_autocreate(server.backend); if (server.renderer == NULL) { wlr_log(WLR_ERROR, "failed to create wlr_renderer"); return 1; } wlr_renderer_init_wl_display(server.renderer, server.wl_display); /* Autocreates an allocator for us. * The allocator is the bridge between the renderer and the backend. It * handles the buffer creation, allowing wlroots to render onto the * screen */ server.allocator = wlr_allocator_autocreate(server.backend, server.renderer); if (server.allocator == NULL) { wlr_log(WLR_ERROR, "failed to create wlr_allocator"); return 1; } /* This creates some hands-off wlroots interfaces. The compositor is * necessary for clients to allocate surfaces, the subcompositor allows to * assign the role of subsurfaces to surfaces and the data device manager * handles the clipboard. Each of these wlroots interfaces has room for you * to dig your fingers in and play with their behavior if you want. Note that * the clients cannot set the selection directly without compositor approval, * see the handling of the request_set_selection event below.*/ wlr_compositor_create(server.wl_display, 5, server.renderer); wlr_subcompositor_create(server.wl_display); wlr_data_device_manager_create(server.wl_display); /* Creates an output layout, which a wlroots utility for working with an * arrangement of screens in a physical layout. */ server.output_layout = wlr_output_layout_create(); /* Configure a listener to be notified when new outputs are available on the * backend. */ wl_list_init(&server.outputs); server.new_output.notify = server_new_output; wl_signal_add(&server.backend->events.new_output, &server.new_output); /* Create a scene graph. This is a wlroots abstraction that handles all * rendering and damage tracking. All the compositor author needs to do * is add things that should be rendered to the scene graph at the proper * positions and then call wlr_scene_output_commit() to render a frame if * necessary. */ server.scene = wlr_scene_create(); server.scene_layout = wlr_scene_attach_output_layout(server.scene, server.output_layout); /* Set up xdg-shell version 3. The xdg-shell is a Wayland protocol which is * used for application windows. For more detail on shells, refer to * https://drewdevault.com/2018/07/29/Wayland-shells.html. */ wl_list_init(&server.toplevels); server.xdg_shell = wlr_xdg_shell_create(server.wl_display, 3); server.new_xdg_surface.notify = server_new_xdg_surface; wl_signal_add(&server.xdg_shell->events.new_surface, &server.new_xdg_surface); /* * Creates a cursor, which is a wlroots utility for tracking the cursor * image shown on screen. */ server.cursor = wlr_cursor_create(); wlr_cursor_attach_output_layout(server.cursor, server.output_layout); /* Creates an xcursor manager, another wlroots utility which loads up * Xcursor themes to source cursor images from and makes sure that cursor * images are available at all scale factors on the screen (necessary for * HiDPI support). */ server.cursor_mgr = wlr_xcursor_manager_create(NULL, 24); /* * wlr_cursor *only* displays an image on screen. It does not move around * when the pointer moves. However, we can attach input devices to it, and * it will generate aggregate events for all of them. In these events, we * can choose how we want to process them, forwarding them to clients and * moving the cursor around. More detail on this process is described in * https://drewdevault.com/2018/07/17/Input-handling-in-wlroots.html. * * And more comments are sprinkled throughout the notify functions above. */ server.cursor_mode = TINYWL_CURSOR_PASSTHROUGH; server.cursor_motion.notify = server_cursor_motion; wl_signal_add(&server.cursor->events.motion, &server.cursor_motion); server.cursor_motion_absolute.notify = server_cursor_motion_absolute; wl_signal_add(&server.cursor->events.motion_absolute, &server.cursor_motion_absolute); server.cursor_button.notify = server_cursor_button; wl_signal_add(&server.cursor->events.button, &server.cursor_button); server.cursor_axis.notify = server_cursor_axis; wl_signal_add(&server.cursor->events.axis, &server.cursor_axis); server.cursor_frame.notify = server_cursor_frame; wl_signal_add(&server.cursor->events.frame, &server.cursor_frame); /* * Configures a seat, which is a single "seat" at which a user sits and * operates the computer. This conceptually includes up to one keyboard, * pointer, touch, and drawing tablet device. We also rig up a listener to * let us know when new input devices are available on the backend. */ wl_list_init(&server.keyboards); server.new_input.notify = server_new_input; wl_signal_add(&server.backend->events.new_input, &server.new_input); server.seat = wlr_seat_create(server.wl_display, "seat0"); server.request_cursor.notify = seat_request_cursor; wl_signal_add(&server.seat->events.request_set_cursor, &server.request_cursor); server.request_set_selection.notify = seat_request_set_selection; wl_signal_add(&server.seat->events.request_set_selection, &server.request_set_selection); /* Add a Unix socket to the Wayland display. */ const char *socket = wl_display_add_socket_auto(server.wl_display); if (!socket) { wlr_backend_destroy(server.backend); return 1; } /* Start the backend. This will enumerate outputs and inputs, become the DRM * master, etc */ if (!wlr_backend_start(server.backend)) { wlr_backend_destroy(server.backend); wl_display_destroy(server.wl_display); return 1; } /* Set the WAYLAND_DISPLAY environment variable to our socket and run the * startup command if requested. */ setenv("WAYLAND_DISPLAY", socket, true); if (startup_cmd) { if (fork() == 0) { execl("/bin/sh", "/bin/sh", "-c", startup_cmd, (void *)NULL); } } /* Run the Wayland event loop. This does not return until you exit the * compositor. Starting the backend rigged up all of the necessary event * loop configuration to listen to libinput events, DRM events, generate * frame events at the refresh rate, and so on. */ wlr_log(WLR_INFO, "Running Wayland compositor on WAYLAND_DISPLAY=%s", socket); wl_display_run(server.wl_display); /* Once wl_display_run returns, we destroy all clients then shut down the * server. */ wl_display_destroy_clients(server.wl_display); wlr_scene_node_destroy(&server.scene->tree.node); wlr_xcursor_manager_destroy(server.cursor_mgr); wlr_output_layout_destroy(server.output_layout); wl_display_destroy(server.wl_display); return 0; } wlroots-0.17.1/types/000077500000000000000000000000001454110342200144515ustar00rootroot00000000000000wlroots-0.17.1/types/buffer/000077500000000000000000000000001454110342200157225ustar00rootroot00000000000000wlroots-0.17.1/types/buffer/buffer.c000066400000000000000000000060541454110342200173440ustar00rootroot00000000000000#include #include #include #include "render/pixel_format.h" #include "types/wlr_buffer.h" void wlr_buffer_init(struct wlr_buffer *buffer, const struct wlr_buffer_impl *impl, int width, int height) { assert(impl->destroy); if (impl->begin_data_ptr_access || impl->end_data_ptr_access) { assert(impl->begin_data_ptr_access && impl->end_data_ptr_access); } *buffer = (struct wlr_buffer){ .impl = impl, .width = width, .height = height, }; wl_signal_init(&buffer->events.destroy); wl_signal_init(&buffer->events.release); wlr_addon_set_init(&buffer->addons); } static void buffer_consider_destroy(struct wlr_buffer *buffer) { if (!buffer->dropped || buffer->n_locks > 0) { return; } assert(!buffer->accessing_data_ptr); wl_signal_emit_mutable(&buffer->events.destroy, NULL); wlr_addon_set_finish(&buffer->addons); buffer->impl->destroy(buffer); } void wlr_buffer_drop(struct wlr_buffer *buffer) { if (buffer == NULL) { return; } assert(!buffer->dropped); buffer->dropped = true; buffer_consider_destroy(buffer); } struct wlr_buffer *wlr_buffer_lock(struct wlr_buffer *buffer) { buffer->n_locks++; return buffer; } void wlr_buffer_unlock(struct wlr_buffer *buffer) { if (buffer == NULL) { return; } assert(buffer->n_locks > 0); buffer->n_locks--; if (buffer->n_locks == 0) { wl_signal_emit_mutable(&buffer->events.release, NULL); } buffer_consider_destroy(buffer); } bool wlr_buffer_get_dmabuf(struct wlr_buffer *buffer, struct wlr_dmabuf_attributes *attribs) { if (!buffer->impl->get_dmabuf) { return false; } return buffer->impl->get_dmabuf(buffer, attribs); } bool wlr_buffer_begin_data_ptr_access(struct wlr_buffer *buffer, uint32_t flags, void **data, uint32_t *format, size_t *stride) { assert(!buffer->accessing_data_ptr); if (!buffer->impl->begin_data_ptr_access) { return false; } if (!buffer->impl->begin_data_ptr_access(buffer, flags, data, format, stride)) { return false; } buffer->accessing_data_ptr = true; return true; } void wlr_buffer_end_data_ptr_access(struct wlr_buffer *buffer) { assert(buffer->accessing_data_ptr); buffer->impl->end_data_ptr_access(buffer); buffer->accessing_data_ptr = false; } bool wlr_buffer_get_shm(struct wlr_buffer *buffer, struct wlr_shm_attributes *attribs) { if (!buffer->impl->get_shm) { return false; } return buffer->impl->get_shm(buffer, attribs); } bool buffer_is_opaque(struct wlr_buffer *buffer) { void *data; uint32_t format; size_t stride; struct wlr_dmabuf_attributes dmabuf; struct wlr_shm_attributes shm; if (wlr_buffer_get_dmabuf(buffer, &dmabuf)) { format = dmabuf.format; } else if (wlr_buffer_get_shm(buffer, &shm)) { format = shm.format; } else if (wlr_buffer_begin_data_ptr_access(buffer, WLR_BUFFER_DATA_PTR_ACCESS_READ, &data, &format, &stride)) { wlr_buffer_end_data_ptr_access(buffer); } else { return false; } const struct wlr_pixel_format_info *format_info = drm_get_pixel_format_info(format); if (format_info == NULL) { return false; } return !format_info->has_alpha; } wlroots-0.17.1/types/buffer/client.c000066400000000000000000000057361454110342200173570ustar00rootroot00000000000000#include #include #include #include #include #include "types/wlr_buffer.h" static const struct wlr_buffer_impl client_buffer_impl; struct wlr_client_buffer *wlr_client_buffer_get(struct wlr_buffer *wlr_buffer) { if (wlr_buffer->impl != &client_buffer_impl) { return NULL; } struct wlr_client_buffer *buffer = wl_container_of(wlr_buffer, buffer, base); return buffer; } static struct wlr_client_buffer *client_buffer_from_buffer( struct wlr_buffer *buffer) { struct wlr_client_buffer *client_buffer = wlr_client_buffer_get(buffer); assert(client_buffer != NULL); return client_buffer; } static void client_buffer_destroy(struct wlr_buffer *buffer) { struct wlr_client_buffer *client_buffer = client_buffer_from_buffer(buffer); wl_list_remove(&client_buffer->source_destroy.link); wlr_texture_destroy(client_buffer->texture); free(client_buffer); } static bool client_buffer_get_dmabuf(struct wlr_buffer *buffer, struct wlr_dmabuf_attributes *attribs) { struct wlr_client_buffer *client_buffer = client_buffer_from_buffer(buffer); if (client_buffer->source == NULL) { return false; } return wlr_buffer_get_dmabuf(client_buffer->source, attribs); } static const struct wlr_buffer_impl client_buffer_impl = { .destroy = client_buffer_destroy, .get_dmabuf = client_buffer_get_dmabuf, }; static void client_buffer_handle_source_destroy(struct wl_listener *listener, void *data) { struct wlr_client_buffer *client_buffer = wl_container_of(listener, client_buffer, source_destroy); wl_list_remove(&client_buffer->source_destroy.link); wl_list_init(&client_buffer->source_destroy.link); client_buffer->source = NULL; } struct wlr_client_buffer *wlr_client_buffer_create(struct wlr_buffer *buffer, struct wlr_renderer *renderer) { struct wlr_texture *texture = wlr_texture_from_buffer(renderer, buffer); if (texture == NULL) { wlr_log(WLR_ERROR, "Failed to create texture"); return NULL; } struct wlr_client_buffer *client_buffer = calloc(1, sizeof(*client_buffer)); if (client_buffer == NULL) { wlr_texture_destroy(texture); return NULL; } wlr_buffer_init(&client_buffer->base, &client_buffer_impl, texture->width, texture->height); client_buffer->source = buffer; client_buffer->texture = texture; wl_signal_add(&buffer->events.destroy, &client_buffer->source_destroy); client_buffer->source_destroy.notify = client_buffer_handle_source_destroy; // Ensure the buffer will be released before being destroyed wlr_buffer_lock(&client_buffer->base); wlr_buffer_drop(&client_buffer->base); return client_buffer; } bool wlr_client_buffer_apply_damage(struct wlr_client_buffer *client_buffer, struct wlr_buffer *next, const pixman_region32_t *damage) { if (client_buffer->base.n_locks - client_buffer->n_ignore_locks > 1) { // Someone else still has a reference to the buffer return false; } return wlr_texture_update_from_buffer(client_buffer->texture, next, damage); } wlroots-0.17.1/types/buffer/dmabuf.c000066400000000000000000000035531454110342200173320ustar00rootroot00000000000000#include #include #include #include #include "types/wlr_buffer.h" static const struct wlr_buffer_impl dmabuf_buffer_impl; static struct wlr_dmabuf_buffer *dmabuf_buffer_from_buffer( struct wlr_buffer *wlr_buffer) { assert(wlr_buffer->impl == &dmabuf_buffer_impl); struct wlr_dmabuf_buffer *buffer = wl_container_of(wlr_buffer, buffer, base); return buffer; } static void dmabuf_buffer_destroy(struct wlr_buffer *wlr_buffer) { struct wlr_dmabuf_buffer *buffer = dmabuf_buffer_from_buffer(wlr_buffer); if (buffer->saved) { wlr_dmabuf_attributes_finish(&buffer->dmabuf); } free(buffer); } static bool dmabuf_buffer_get_dmabuf(struct wlr_buffer *wlr_buffer, struct wlr_dmabuf_attributes *dmabuf) { struct wlr_dmabuf_buffer *buffer = dmabuf_buffer_from_buffer(wlr_buffer); if (buffer->dmabuf.n_planes == 0) { return false; } *dmabuf = buffer->dmabuf; return true; } static const struct wlr_buffer_impl dmabuf_buffer_impl = { .destroy = dmabuf_buffer_destroy, .get_dmabuf = dmabuf_buffer_get_dmabuf, }; struct wlr_dmabuf_buffer *dmabuf_buffer_create( struct wlr_dmabuf_attributes *dmabuf) { struct wlr_dmabuf_buffer *buffer = calloc(1, sizeof(*buffer)); if (buffer == NULL) { return NULL; } wlr_buffer_init(&buffer->base, &dmabuf_buffer_impl, dmabuf->width, dmabuf->height); buffer->dmabuf = *dmabuf; return buffer; } bool dmabuf_buffer_drop(struct wlr_dmabuf_buffer *buffer) { bool ok = true; if (buffer->base.n_locks > 0) { struct wlr_dmabuf_attributes saved_dmabuf = {0}; if (!wlr_dmabuf_attributes_copy(&saved_dmabuf, &buffer->dmabuf)) { wlr_log(WLR_ERROR, "Failed to save DMA-BUF"); ok = false; buffer->dmabuf = (struct wlr_dmabuf_attributes){0}; } else { buffer->dmabuf = saved_dmabuf; buffer->saved = true; } } wlr_buffer_drop(&buffer->base); return ok; } wlroots-0.17.1/types/buffer/readonly_data.c000066400000000000000000000052031454110342200206740ustar00rootroot00000000000000#include #include #include #include #include #include "types/wlr_buffer.h" static const struct wlr_buffer_impl readonly_data_buffer_impl; static struct wlr_readonly_data_buffer *readonly_data_buffer_from_buffer( struct wlr_buffer *wlr_buffer) { assert(wlr_buffer->impl == &readonly_data_buffer_impl); struct wlr_readonly_data_buffer *buffer = wl_container_of(wlr_buffer, buffer, base); return buffer; } static void readonly_data_buffer_destroy(struct wlr_buffer *wlr_buffer) { struct wlr_readonly_data_buffer *buffer = readonly_data_buffer_from_buffer(wlr_buffer); free(buffer->saved_data); free(buffer); } static bool readonly_data_buffer_begin_data_ptr_access(struct wlr_buffer *wlr_buffer, uint32_t flags, void **data, uint32_t *format, size_t *stride) { struct wlr_readonly_data_buffer *buffer = readonly_data_buffer_from_buffer(wlr_buffer); if (buffer->data == NULL) { return false; } if (flags & WLR_BUFFER_DATA_PTR_ACCESS_WRITE) { return false; } *data = (void *)buffer->data; *format = buffer->format; *stride = buffer->stride; return true; } static void readonly_data_buffer_end_data_ptr_access(struct wlr_buffer *wlr_buffer) { // This space is intentionally left blank } static const struct wlr_buffer_impl readonly_data_buffer_impl = { .destroy = readonly_data_buffer_destroy, .begin_data_ptr_access = readonly_data_buffer_begin_data_ptr_access, .end_data_ptr_access = readonly_data_buffer_end_data_ptr_access, }; struct wlr_readonly_data_buffer *readonly_data_buffer_create(uint32_t format, size_t stride, uint32_t width, uint32_t height, const void *data) { struct wlr_readonly_data_buffer *buffer = calloc(1, sizeof(*buffer)); if (buffer == NULL) { return NULL; } wlr_buffer_init(&buffer->base, &readonly_data_buffer_impl, width, height); buffer->data = data; buffer->format = format; buffer->stride = stride; return buffer; } bool readonly_data_buffer_drop(struct wlr_readonly_data_buffer *buffer) { bool ok = true; if (buffer->base.n_locks > 0) { size_t size = buffer->stride * buffer->base.height; buffer->saved_data = malloc(size); if (buffer->saved_data == NULL) { wlr_log_errno(WLR_ERROR, "Allocation failed"); ok = false; buffer->data = NULL; // We can't destroy the buffer, or we risk use-after-free in the // consumers. We can't allow accesses to buffer->data anymore, so // set it to NULL and make subsequent begin_data_ptr_access() // calls fail. } else { memcpy(buffer->saved_data, buffer->data, size); buffer->data = buffer->saved_data; } } wlr_buffer_drop(&buffer->base); return ok; } wlroots-0.17.1/types/buffer/resource.c000066400000000000000000000032101454110342200177110ustar00rootroot00000000000000#include #include #include #include #include #include "types/wlr_buffer.h" /* struct wlr_buffer_resource_interface */ static struct wl_array buffer_resource_interfaces = {0}; void wlr_buffer_register_resource_interface( const struct wlr_buffer_resource_interface *iface) { assert(iface); assert(iface->is_instance); assert(iface->from_resource); const struct wlr_buffer_resource_interface **iface_ptr; wl_array_for_each(iface_ptr, &buffer_resource_interfaces) { if (*iface_ptr == iface) { wlr_log(WLR_DEBUG, "wlr_resource_buffer_interface %s has already" "been registered", iface->name); return; } } iface_ptr = wl_array_add(&buffer_resource_interfaces, sizeof(iface)); *iface_ptr = iface; } static const struct wlr_buffer_resource_interface *get_buffer_resource_iface( struct wl_resource *resource) { struct wlr_buffer_resource_interface **iface_ptr; wl_array_for_each(iface_ptr, &buffer_resource_interfaces) { if ((*iface_ptr)->is_instance(resource)) { return *iface_ptr; } } return NULL; } struct wlr_buffer *wlr_buffer_try_from_resource(struct wl_resource *resource) { if (strcmp(wl_resource_get_class(resource), wl_buffer_interface.name) != 0) { return NULL; } const struct wlr_buffer_resource_interface *iface = get_buffer_resource_iface(resource); if (!iface) { wlr_log(WLR_ERROR, "Unknown buffer type"); return NULL; } struct wlr_buffer *buffer = iface->from_resource(resource); if (!buffer) { wlr_log(WLR_ERROR, "Failed to create %s buffer", iface->name); return NULL; } return wlr_buffer_lock(buffer); } wlroots-0.17.1/types/data_device/000077500000000000000000000000001454110342200167015ustar00rootroot00000000000000wlroots-0.17.1/types/data_device/wlr_data_device.c000066400000000000000000000227611454110342200221710ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include "types/wlr_data_device.h" #define DATA_DEVICE_MANAGER_VERSION 3 static const struct wl_data_device_interface data_device_impl; struct wlr_seat_client *seat_client_from_data_device_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &wl_data_device_interface, &data_device_impl)); return wl_resource_get_user_data(resource); } static void data_device_set_selection(struct wl_client *client, struct wl_resource *device_resource, struct wl_resource *source_resource, uint32_t serial) { struct wlr_seat_client *seat_client = seat_client_from_data_device_resource(device_resource); if (seat_client == NULL) { return; } struct wlr_client_data_source *source = NULL; if (source_resource != NULL) { source = client_data_source_from_resource(source_resource); } if (source != NULL) { source->finalized = true; } struct wlr_data_source *wlr_source = source != NULL ? &source->source : NULL; wlr_seat_request_set_selection(seat_client->seat, seat_client, wlr_source, serial); } static void data_device_start_drag(struct wl_client *client, struct wl_resource *device_resource, struct wl_resource *source_resource, struct wl_resource *origin_resource, struct wl_resource *icon_resource, uint32_t serial) { struct wlr_seat_client *seat_client = seat_client_from_data_device_resource(device_resource); if (seat_client == NULL) { return; } struct wlr_surface *origin = wlr_surface_from_resource(origin_resource); struct wlr_client_data_source *source = NULL; if (source_resource != NULL) { source = client_data_source_from_resource(source_resource); } struct wlr_surface *icon = NULL; if (icon_resource) { icon = wlr_surface_from_resource(icon_resource); if (!wlr_surface_set_role(icon, &drag_icon_surface_role, icon_resource, WL_DATA_DEVICE_ERROR_ROLE)) { return; } } struct wlr_data_source *wlr_source = source != NULL ? &source->source : NULL; struct wlr_drag *drag = wlr_drag_create(seat_client, wlr_source, icon); if (drag == NULL) { wl_resource_post_no_memory(device_resource); return; } if (source != NULL) { source->finalized = true; } wlr_seat_request_start_drag(seat_client->seat, drag, origin, serial); } static void data_device_release(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static const struct wl_data_device_interface data_device_impl = { .start_drag = data_device_start_drag, .set_selection = data_device_set_selection, .release = data_device_release, }; static void data_device_handle_resource_destroy(struct wl_resource *resource) { wl_list_remove(wl_resource_get_link(resource)); wl_list_init(wl_resource_get_link(resource)); } static void device_resource_send_selection(struct wl_resource *device_resource) { struct wlr_seat_client *seat_client = seat_client_from_data_device_resource(device_resource); assert(seat_client != NULL); struct wlr_data_source *source = seat_client->seat->selection_source; if (source != NULL) { struct wlr_data_offer *offer = data_offer_create(device_resource, source, WLR_DATA_OFFER_SELECTION); if (offer == NULL) { wl_client_post_no_memory(seat_client->client); return; } wl_data_device_send_selection(device_resource, offer->resource); } else { wl_data_device_send_selection(device_resource, NULL); } } void seat_client_send_selection(struct wlr_seat_client *seat_client) { struct wlr_data_source *source = seat_client->seat->selection_source; if (source != NULL) { source->accepted = false; } // Make all current offers inert struct wlr_data_offer *offer, *tmp; wl_list_for_each_safe(offer, tmp, &seat_client->seat->selection_offers, link) { data_offer_destroy(offer); } struct wl_resource *device_resource; wl_resource_for_each(device_resource, &seat_client->data_devices) { device_resource_send_selection(device_resource); } } void wlr_seat_request_set_selection(struct wlr_seat *seat, struct wlr_seat_client *client, struct wlr_data_source *source, uint32_t serial) { if (client && !wlr_seat_client_validate_event_serial(client, serial)) { wlr_log(WLR_DEBUG, "Rejecting set_selection request, " "serial %"PRIu32" was never given to client", serial); return; } if (seat->selection_source && serial - seat->selection_serial > UINT32_MAX / 2) { wlr_log(WLR_DEBUG, "Rejecting set_selection request, serial indicates superseded " "(%"PRIu32" < %"PRIu32")", serial, seat->selection_serial); return; } struct wlr_seat_request_set_selection_event event = { .source = source, .serial = serial, }; wl_signal_emit_mutable(&seat->events.request_set_selection, &event); } static void seat_handle_selection_source_destroy( struct wl_listener *listener, void *data) { struct wlr_seat *seat = wl_container_of(listener, seat, selection_source_destroy); wl_list_remove(&seat->selection_source_destroy.link); seat->selection_source = NULL; struct wlr_seat_client *focused_client = seat->keyboard_state.focused_client; if (focused_client != NULL) { seat_client_send_selection(focused_client); } wl_signal_emit_mutable(&seat->events.set_selection, seat); } void wlr_seat_set_selection(struct wlr_seat *seat, struct wlr_data_source *source, uint32_t serial) { if (seat->selection_source == source) { seat->selection_serial = serial; return; } if (seat->selection_source) { wl_list_remove(&seat->selection_source_destroy.link); wlr_data_source_destroy(seat->selection_source); seat->selection_source = NULL; } seat->selection_source = source; seat->selection_serial = serial; if (source) { seat->selection_source_destroy.notify = seat_handle_selection_source_destroy; wl_signal_add(&source->events.destroy, &seat->selection_source_destroy); } struct wlr_seat_client *focused_client = seat->keyboard_state.focused_client; if (focused_client) { seat_client_send_selection(focused_client); } wl_signal_emit_mutable(&seat->events.set_selection, seat); } static const struct wl_data_device_manager_interface data_device_manager_impl; static struct wlr_data_device_manager *data_device_manager_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &wl_data_device_manager_interface, &data_device_manager_impl)); return wl_resource_get_user_data(resource); } static void data_device_manager_get_data_device(struct wl_client *client, struct wl_resource *manager_resource, uint32_t id, struct wl_resource *seat_resource) { struct wlr_seat_client *seat_client = wlr_seat_client_from_resource(seat_resource); uint32_t version = wl_resource_get_version(manager_resource); struct wl_resource *resource = wl_resource_create(client, &wl_data_device_interface, version, id); if (resource == NULL) { wl_resource_post_no_memory(manager_resource); return; } wl_resource_set_implementation(resource, &data_device_impl, seat_client, data_device_handle_resource_destroy); if (seat_client == NULL) { wl_list_init(wl_resource_get_link(resource)); return; } wl_list_insert(&seat_client->data_devices, wl_resource_get_link(resource)); struct wlr_seat_client *focused_client = seat_client->seat->keyboard_state.focused_client; if (focused_client == seat_client) { device_resource_send_selection(resource); } } static void data_device_manager_create_data_source(struct wl_client *client, struct wl_resource *manager_resource, uint32_t id) { struct wlr_data_device_manager *manager = data_device_manager_from_resource(manager_resource); client_data_source_create(client, wl_resource_get_version(manager_resource), id, &manager->data_sources); } static const struct wl_data_device_manager_interface data_device_manager_impl = { .create_data_source = data_device_manager_create_data_source, .get_data_device = data_device_manager_get_data_device, }; static void data_device_manager_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id) { struct wlr_data_device_manager *manager = data; struct wl_resource *resource = wl_resource_create(client, &wl_data_device_manager_interface, version, id); if (resource == NULL) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(resource, &data_device_manager_impl, manager, NULL); } static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_data_device_manager *manager = wl_container_of(listener, manager, display_destroy); wl_signal_emit_mutable(&manager->events.destroy, manager); wl_list_remove(&manager->display_destroy.link); wl_global_destroy(manager->global); free(manager); } struct wlr_data_device_manager *wlr_data_device_manager_create( struct wl_display *display) { struct wlr_data_device_manager *manager = calloc(1, sizeof(*manager)); if (manager == NULL) { wlr_log(WLR_ERROR, "could not create data device manager"); return NULL; } wl_list_init(&manager->data_sources); wl_signal_init(&manager->events.destroy); manager->global = wl_global_create(display, &wl_data_device_manager_interface, DATA_DEVICE_MANAGER_VERSION, manager, data_device_manager_bind); if (!manager->global) { wlr_log(WLR_ERROR, "could not create data device manager wl_global"); free(manager); return NULL; } manager->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &manager->display_destroy); return manager; } wlroots-0.17.1/types/data_device/wlr_data_offer.c000066400000000000000000000177641454110342200220420ustar00rootroot00000000000000#define _XOPEN_SOURCE 700 #include #include #include #include #include #include #include #include #include "types/wlr_data_device.h" static const struct wl_data_offer_interface data_offer_impl; static struct wlr_data_offer *data_offer_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &wl_data_offer_interface, &data_offer_impl)); return wl_resource_get_user_data(resource); } static uint32_t data_offer_choose_action(struct wlr_data_offer *offer) { uint32_t offer_actions, preferred_action = 0; if (wl_resource_get_version(offer->resource) >= WL_DATA_OFFER_ACTION_SINCE_VERSION) { offer_actions = offer->actions; preferred_action = offer->preferred_action; } else { offer_actions = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY; } uint32_t source_actions; if (offer->source->actions >= 0) { source_actions = offer->source->actions; } else { source_actions = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY; } uint32_t available_actions = offer_actions & source_actions; if (!available_actions) { return WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE; } if (offer->source->compositor_action & available_actions) { return offer->source->compositor_action; } // If the dest side has a preferred DnD action, use it if ((preferred_action & available_actions) != 0) { return preferred_action; } // Use the first found action, in bit order return 1 << (ffs(available_actions) - 1); } void data_offer_update_action(struct wlr_data_offer *offer) { assert(offer->type == WLR_DATA_OFFER_DRAG); uint32_t action = data_offer_choose_action(offer); if (offer->source->current_dnd_action == action) { return; } offer->source->current_dnd_action = action; if (offer->in_ask) { return; } wlr_data_source_dnd_action(offer->source, action); if (wl_resource_get_version(offer->resource) >= WL_DATA_OFFER_ACTION_SINCE_VERSION) { wl_data_offer_send_action(offer->resource, action); } } static void data_offer_handle_accept(struct wl_client *client, struct wl_resource *resource, uint32_t serial, const char *mime_type) { struct wlr_data_offer *offer = data_offer_from_resource(resource); if (offer == NULL) { return; } if (offer->type != WLR_DATA_OFFER_DRAG) { wlr_log(WLR_DEBUG, "Ignoring wl_data_offer.accept request on a " "non-drag-and-drop offer"); return; } wlr_data_source_accept(offer->source, serial, mime_type); } static void data_offer_handle_receive(struct wl_client *client, struct wl_resource *resource, const char *mime_type, int32_t fd) { struct wlr_data_offer *offer = data_offer_from_resource(resource); if (offer == NULL) { close(fd); return; } wlr_data_source_send(offer->source, mime_type, fd); } static void data_offer_source_dnd_finish(struct wlr_data_offer *offer) { struct wlr_data_source *source = offer->source; if (source->actions < 0) { return; } if (offer->in_ask) { wlr_data_source_dnd_action(source, source->current_dnd_action); } wlr_data_source_dnd_finish(source); } static void data_offer_handle_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static void data_offer_handle_finish(struct wl_client *client, struct wl_resource *resource) { struct wlr_data_offer *offer = data_offer_from_resource(resource); if (offer == NULL) { return; } // TODO: also fail while we have a drag-and-drop grab if (offer->type != WLR_DATA_OFFER_DRAG) { wl_resource_post_error(offer->resource, WL_DATA_OFFER_ERROR_INVALID_FINISH, "Offer is not drag-and-drop"); return; } if (!offer->source->accepted) { wl_resource_post_error(offer->resource, WL_DATA_OFFER_ERROR_INVALID_FINISH, "Premature finish request"); return; } enum wl_data_device_manager_dnd_action action = offer->source->current_dnd_action; if (action == WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE || action == WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK) { wl_resource_post_error(offer->resource, WL_DATA_OFFER_ERROR_INVALID_FINISH, "Offer finished with an invalid action"); return; } data_offer_source_dnd_finish(offer); data_offer_destroy(offer); } static void data_offer_handle_set_actions(struct wl_client *client, struct wl_resource *resource, uint32_t actions, uint32_t preferred_action) { struct wlr_data_offer *offer = data_offer_from_resource(resource); if (offer == NULL) { return; } if (actions & ~DATA_DEVICE_ALL_ACTIONS) { wl_resource_post_error(offer->resource, WL_DATA_OFFER_ERROR_INVALID_ACTION_MASK, "invalid action mask %x", actions); return; } if (preferred_action && (!(preferred_action & actions) || __builtin_popcount(preferred_action) > 1)) { wl_resource_post_error(offer->resource, WL_DATA_OFFER_ERROR_INVALID_ACTION, "invalid action %x", preferred_action); return; } if (offer->type != WLR_DATA_OFFER_DRAG) { wl_resource_post_error(offer->resource, WL_DATA_OFFER_ERROR_INVALID_OFFER, "set_action can only be sent to drag-and-drop offers"); return; } offer->actions = actions; offer->preferred_action = preferred_action; data_offer_update_action(offer); } void data_offer_destroy(struct wlr_data_offer *offer) { if (offer == NULL) { return; } wl_list_remove(&offer->source_destroy.link); wl_list_remove(&offer->link); if (offer->type == WLR_DATA_OFFER_DRAG && offer->source) { // If the drag destination has version < 3, wl_data_offer.finish // won't be called, so do this here as a safety net, because // we still want the version >= 3 drag source to be happy. if (wl_resource_get_version(offer->resource) < WL_DATA_OFFER_ACTION_SINCE_VERSION) { data_offer_source_dnd_finish(offer); } else if (offer->source->impl->dnd_finish) { wlr_data_source_destroy(offer->source); } } // Make the resource inert wl_resource_set_user_data(offer->resource, NULL); free(offer); } static const struct wl_data_offer_interface data_offer_impl = { .accept = data_offer_handle_accept, .receive = data_offer_handle_receive, .destroy = data_offer_handle_destroy, .finish = data_offer_handle_finish, .set_actions = data_offer_handle_set_actions, }; static void data_offer_handle_resource_destroy(struct wl_resource *resource) { struct wlr_data_offer *offer = data_offer_from_resource(resource); data_offer_destroy(offer); } static void data_offer_handle_source_destroy(struct wl_listener *listener, void *data) { struct wlr_data_offer *offer = wl_container_of(listener, offer, source_destroy); // Prevent data_offer_destroy from destroying the source again offer->source = NULL; data_offer_destroy(offer); } struct wlr_data_offer *data_offer_create(struct wl_resource *device_resource, struct wlr_data_source *source, enum wlr_data_offer_type type) { struct wlr_seat_client *seat_client = seat_client_from_data_device_resource(device_resource); assert(seat_client != NULL); assert(source != NULL); // a NULL source means no selection struct wlr_data_offer *offer = calloc(1, sizeof(*offer)); if (offer == NULL) { return NULL; } offer->source = source; offer->type = type; struct wl_client *client = wl_resource_get_client(device_resource); uint32_t version = wl_resource_get_version(device_resource); offer->resource = wl_resource_create(client, &wl_data_offer_interface, version, 0); if (offer->resource == NULL) { free(offer); return NULL; } wl_resource_set_implementation(offer->resource, &data_offer_impl, offer, data_offer_handle_resource_destroy); switch (type) { case WLR_DATA_OFFER_SELECTION: wl_list_insert(&seat_client->seat->selection_offers, &offer->link); break; case WLR_DATA_OFFER_DRAG: wl_list_insert(&seat_client->seat->drag_offers, &offer->link); break; } offer->source_destroy.notify = data_offer_handle_source_destroy; wl_signal_add(&source->events.destroy, &offer->source_destroy); wl_data_device_send_data_offer(device_resource, offer->resource); char **p; wl_array_for_each(p, &source->mime_types) { wl_data_offer_send_offer(offer->resource, *p); } return offer; } wlroots-0.17.1/types/data_device/wlr_data_source.c000066400000000000000000000177161454110342200222360ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include #include #include "types/wlr_data_device.h" void wlr_data_source_init(struct wlr_data_source *source, const struct wlr_data_source_impl *impl) { assert(impl->send); *source = (struct wlr_data_source){ .impl = impl, .actions = -1, }; wl_array_init(&source->mime_types); wl_signal_init(&source->events.destroy); } void wlr_data_source_send(struct wlr_data_source *source, const char *mime_type, int32_t fd) { source->impl->send(source, mime_type, fd); } void wlr_data_source_accept(struct wlr_data_source *source, uint32_t serial, const char *mime_type) { source->accepted = (mime_type != NULL); if (source->impl->accept) { source->impl->accept(source, serial, mime_type); } } void wlr_data_source_destroy(struct wlr_data_source *source) { if (source == NULL) { return; } wl_signal_emit_mutable(&source->events.destroy, source); char **p; wl_array_for_each(p, &source->mime_types) { free(*p); } wl_array_release(&source->mime_types); if (source->impl->destroy) { source->impl->destroy(source); } else { free(source); } } void wlr_data_source_dnd_drop(struct wlr_data_source *source) { if (source->impl->dnd_drop) { source->impl->dnd_drop(source); } } void wlr_data_source_dnd_finish(struct wlr_data_source *source) { if (source->impl->dnd_finish) { source->impl->dnd_finish(source); } } void wlr_data_source_dnd_action(struct wlr_data_source *source, enum wl_data_device_manager_dnd_action action) { source->current_dnd_action = action; if (source->impl->dnd_action) { source->impl->dnd_action(source, action); } } static const struct wl_data_source_interface data_source_impl; struct wlr_client_data_source *client_data_source_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &wl_data_source_interface, &data_source_impl)); return wl_resource_get_user_data(resource); } static void client_data_source_accept(struct wlr_data_source *wlr_source, uint32_t serial, const char *mime_type); static struct wlr_client_data_source *client_data_source_from_wlr_data_source( struct wlr_data_source *wlr_source) { assert(wlr_source->impl->accept == client_data_source_accept); struct wlr_client_data_source *source = wl_container_of(wlr_source, source, source); return source; } static void client_data_source_accept(struct wlr_data_source *wlr_source, uint32_t serial, const char *mime_type) { struct wlr_client_data_source *source = client_data_source_from_wlr_data_source(wlr_source); wl_data_source_send_target(source->resource, mime_type); } static void client_data_source_send(struct wlr_data_source *wlr_source, const char *mime_type, int32_t fd) { struct wlr_client_data_source *source = client_data_source_from_wlr_data_source(wlr_source); wl_data_source_send_send(source->resource, mime_type, fd); close(fd); } static void client_data_source_destroy(struct wlr_data_source *wlr_source) { struct wlr_client_data_source *source = client_data_source_from_wlr_data_source(wlr_source); wl_data_source_send_cancelled(source->resource); wl_resource_set_user_data(source->resource, NULL); free(source); } static void client_data_source_dnd_drop(struct wlr_data_source *wlr_source) { struct wlr_client_data_source *source = client_data_source_from_wlr_data_source(wlr_source); assert(wl_resource_get_version(source->resource) >= WL_DATA_SOURCE_DND_DROP_PERFORMED_SINCE_VERSION); wl_data_source_send_dnd_drop_performed(source->resource); } static void client_data_source_dnd_finish(struct wlr_data_source *wlr_source) { struct wlr_client_data_source *source = client_data_source_from_wlr_data_source(wlr_source); assert(wl_resource_get_version(source->resource) >= WL_DATA_SOURCE_DND_FINISHED_SINCE_VERSION); wl_data_source_send_dnd_finished(source->resource); } static void client_data_source_dnd_action(struct wlr_data_source *wlr_source, enum wl_data_device_manager_dnd_action action) { struct wlr_client_data_source *source = client_data_source_from_wlr_data_source(wlr_source); assert(wl_resource_get_version(source->resource) >= WL_DATA_SOURCE_ACTION_SINCE_VERSION); wl_data_source_send_action(source->resource, action); } static void data_source_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static void data_source_set_actions(struct wl_client *client, struct wl_resource *resource, uint32_t dnd_actions) { struct wlr_client_data_source *source = client_data_source_from_resource(resource); if (source == NULL) { return; } if (source->source.actions >= 0) { wl_resource_post_error(source->resource, WL_DATA_SOURCE_ERROR_INVALID_ACTION_MASK, "cannot set actions more than once"); return; } if (dnd_actions & ~DATA_DEVICE_ALL_ACTIONS) { wl_resource_post_error(source->resource, WL_DATA_SOURCE_ERROR_INVALID_ACTION_MASK, "invalid action mask %x", dnd_actions); return; } if (source->finalized) { wl_resource_post_error(source->resource, WL_DATA_SOURCE_ERROR_INVALID_ACTION_MASK, "invalid action change after wl_data_device.start_drag"); return; } source->source.actions = dnd_actions; } static void data_source_offer(struct wl_client *client, struct wl_resource *resource, const char *mime_type) { struct wlr_client_data_source *source = client_data_source_from_resource(resource); if (source == NULL) { return; } if (source->finalized) { wlr_log(WLR_DEBUG, "Offering additional MIME type after " "wl_data_device.set_selection"); } const char **mime_type_ptr; wl_array_for_each(mime_type_ptr, &source->source.mime_types) { if (strcmp(*mime_type_ptr, mime_type) == 0) { wlr_log(WLR_DEBUG, "Ignoring duplicate MIME type offer %s", mime_type); return; } } char *dup_mime_type = strdup(mime_type); if (dup_mime_type == NULL) { wl_resource_post_no_memory(resource); return; } char **p = wl_array_add(&source->source.mime_types, sizeof(*p)); if (p == NULL) { free(dup_mime_type); wl_resource_post_no_memory(resource); return; } *p = dup_mime_type; } static const struct wl_data_source_interface data_source_impl = { .destroy = data_source_destroy, .offer = data_source_offer, .set_actions = data_source_set_actions, }; static void data_source_handle_resource_destroy(struct wl_resource *resource) { struct wlr_client_data_source *source = client_data_source_from_resource(resource); if (source != NULL) { wlr_data_source_destroy(&source->source); } wl_list_remove(wl_resource_get_link(resource)); } struct wlr_client_data_source *client_data_source_create( struct wl_client *client, uint32_t version, uint32_t id, struct wl_list *resource_list) { struct wlr_client_data_source *source = calloc(1, sizeof(*source)); if (source == NULL) { return NULL; } source->resource = wl_resource_create(client, &wl_data_source_interface, version, id); if (source->resource == NULL) { wl_resource_post_no_memory(source->resource); free(source); return NULL; } wl_resource_set_implementation(source->resource, &data_source_impl, source, data_source_handle_resource_destroy); wl_list_insert(resource_list, wl_resource_get_link(source->resource)); source->impl.accept = client_data_source_accept; source->impl.send = client_data_source_send; source->impl.destroy = client_data_source_destroy; if (wl_resource_get_version(source->resource) >= WL_DATA_SOURCE_DND_DROP_PERFORMED_SINCE_VERSION) { source->impl.dnd_drop = client_data_source_dnd_drop; } if (wl_resource_get_version(source->resource) >= WL_DATA_SOURCE_DND_FINISHED_SINCE_VERSION) { source->impl.dnd_finish = client_data_source_dnd_finish; } if (wl_resource_get_version(source->resource) >= WL_DATA_SOURCE_ACTION_SINCE_VERSION) { source->impl.dnd_action = client_data_source_dnd_action; } wlr_data_source_init(&source->source, &source->impl); return source; } wlroots-0.17.1/types/data_device/wlr_drag.c000066400000000000000000000346271454110342200206620ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include "types/wlr_data_device.h" static void drag_handle_seat_client_destroy(struct wl_listener *listener, void *data) { struct wlr_drag *drag = wl_container_of(listener, drag, seat_client_destroy); drag->focus_client = NULL; wl_list_remove(&drag->seat_client_destroy.link); } static void drag_set_focus(struct wlr_drag *drag, struct wlr_surface *surface, double sx, double sy) { if (drag->focus == surface) { return; } if (drag->focus_client) { wl_list_remove(&drag->seat_client_destroy.link); // If we're switching focus to another client, we want to destroy all // offers without destroying the source. If the drag operation ends, we // want to keep the offer around for the data transfer. struct wlr_data_offer *offer, *tmp; wl_list_for_each_safe(offer, tmp, &drag->focus_client->seat->drag_offers, link) { struct wl_client *client = wl_resource_get_client(offer->resource); if (!drag->dropped && offer->source == drag->source && client == drag->focus_client->client) { offer->source = NULL; data_offer_destroy(offer); } } struct wl_resource *resource; wl_resource_for_each(resource, &drag->focus_client->data_devices) { wl_data_device_send_leave(resource); } drag->focus_client = NULL; drag->focus = NULL; } if (!surface) { goto out; } if (!drag->source && drag->seat_client && wl_resource_get_client(surface->resource) != drag->seat_client->client) { goto out; } struct wlr_seat_client *focus_client = wlr_seat_client_for_wl_client( drag->seat, wl_resource_get_client(surface->resource)); if (!focus_client) { goto out; } if (drag->source != NULL) { drag->source->accepted = false; uint32_t serial = wl_display_next_serial(drag->seat->display); struct wl_resource *device_resource; wl_resource_for_each(device_resource, &focus_client->data_devices) { struct wlr_data_offer *offer = data_offer_create(device_resource, drag->source, WLR_DATA_OFFER_DRAG); if (offer == NULL) { wl_resource_post_no_memory(device_resource); return; } data_offer_update_action(offer); if (wl_resource_get_version(offer->resource) >= WL_DATA_OFFER_SOURCE_ACTIONS_SINCE_VERSION) { wl_data_offer_send_source_actions(offer->resource, drag->source->actions); } wl_data_device_send_enter(device_resource, serial, surface->resource, wl_fixed_from_double(sx), wl_fixed_from_double(sy), offer->resource); } } drag->focus = surface; drag->focus_client = focus_client; drag->seat_client_destroy.notify = drag_handle_seat_client_destroy; wl_signal_add(&focus_client->events.destroy, &drag->seat_client_destroy); out: wl_signal_emit_mutable(&drag->events.focus, drag); } static void drag_icon_destroy(struct wlr_drag_icon *icon) { icon->drag->icon = NULL; wl_list_remove(&icon->surface_destroy.link); wl_signal_emit_mutable(&icon->events.destroy, icon); free(icon); } static void drag_destroy(struct wlr_drag *drag) { if (drag->cancelling) { return; } drag->cancelling = true; if (drag->started) { wlr_seat_keyboard_end_grab(drag->seat); switch (drag->grab_type) { case WLR_DRAG_GRAB_KEYBOARD: break; case WLR_DRAG_GRAB_KEYBOARD_POINTER: wlr_seat_pointer_end_grab(drag->seat); break; case WLR_DRAG_GRAB_KEYBOARD_TOUCH: wlr_seat_touch_end_grab(drag->seat); break; } } if (drag->started) { drag_set_focus(drag, NULL, 0, 0); assert(drag->seat->drag == drag); drag->seat->drag = NULL; } // We issue destroy after ending the grab to allow focus changes. // Furthermore, we wait until after clearing the drag focus in order // to ensure that the wl_data_device.leave is sent before emitting the // signal. This allows e.g. wl_pointer.enter to be sent in the destroy // signal handler. wl_signal_emit_mutable(&drag->events.destroy, drag); if (drag->source) { wl_list_remove(&drag->source_destroy.link); } if (drag->icon != NULL) { drag_icon_destroy(drag->icon); } free(drag); } static void drag_handle_pointer_enter(struct wlr_seat_pointer_grab *grab, struct wlr_surface *surface, double sx, double sy) { struct wlr_drag *drag = grab->data; drag_set_focus(drag, surface, sx, sy); } static void drag_handle_pointer_clear_focus(struct wlr_seat_pointer_grab *grab) { struct wlr_drag *drag = grab->data; drag_set_focus(drag, NULL, 0, 0); } static void drag_handle_pointer_motion(struct wlr_seat_pointer_grab *grab, uint32_t time, double sx, double sy) { struct wlr_drag *drag = grab->data; if (drag->focus != NULL && drag->focus_client != NULL) { struct wl_resource *resource; wl_resource_for_each(resource, &drag->focus_client->data_devices) { wl_data_device_send_motion(resource, time, wl_fixed_from_double(sx), wl_fixed_from_double(sy)); } struct wlr_drag_motion_event event = { .drag = drag, .time = time, .sx = sx, .sy = sy, }; wl_signal_emit_mutable(&drag->events.motion, &event); } } static void drag_drop(struct wlr_drag *drag, uint32_t time) { assert(drag->focus_client); drag->dropped = true; struct wl_resource *resource; wl_resource_for_each(resource, &drag->focus_client->data_devices) { wl_data_device_send_drop(resource); } if (drag->source) { wlr_data_source_dnd_drop(drag->source); } struct wlr_drag_drop_event event = { .drag = drag, .time = time, }; wl_signal_emit_mutable(&drag->events.drop, &event); } static uint32_t drag_handle_pointer_button(struct wlr_seat_pointer_grab *grab, uint32_t time, uint32_t button, uint32_t state) { struct wlr_drag *drag = grab->data; if (drag->source && grab->seat->pointer_state.grab_button == button && state == WL_POINTER_BUTTON_STATE_RELEASED) { if (drag->focus_client && drag->source->current_dnd_action && drag->source->accepted) { drag_drop(drag, time); } else if (drag->source->impl->dnd_finish) { // This will end the grab and free `drag` wlr_data_source_destroy(drag->source); return 0; } } if (grab->seat->pointer_state.button_count == 0 && state == WL_POINTER_BUTTON_STATE_RELEASED) { drag_destroy(drag); } return 0; } static void drag_handle_pointer_axis(struct wlr_seat_pointer_grab *grab, uint32_t time, enum wlr_axis_orientation orientation, double value, int32_t value_discrete, enum wlr_axis_source source) { // This space is intentionally left blank } static void drag_handle_pointer_cancel(struct wlr_seat_pointer_grab *grab) { struct wlr_drag *drag = grab->data; drag_destroy(drag); } static const struct wlr_pointer_grab_interface data_device_pointer_drag_interface = { .enter = drag_handle_pointer_enter, .clear_focus = drag_handle_pointer_clear_focus, .motion = drag_handle_pointer_motion, .button = drag_handle_pointer_button, .axis = drag_handle_pointer_axis, .cancel = drag_handle_pointer_cancel, }; static uint32_t drag_handle_touch_down(struct wlr_seat_touch_grab *grab, uint32_t time, struct wlr_touch_point *point) { // eat the event return 0; } static void drag_handle_touch_up(struct wlr_seat_touch_grab *grab, uint32_t time, struct wlr_touch_point *point) { struct wlr_drag *drag = grab->data; if (drag->grab_touch_id != point->touch_id) { return; } if (drag->focus_client) { drag_drop(drag, time); } drag_destroy(drag); } static void drag_handle_touch_motion(struct wlr_seat_touch_grab *grab, uint32_t time, struct wlr_touch_point *point) { struct wlr_drag *drag = grab->data; if (drag->focus && drag->focus_client) { struct wl_resource *resource; wl_resource_for_each(resource, &drag->focus_client->data_devices) { wl_data_device_send_motion(resource, time, wl_fixed_from_double(point->sx), wl_fixed_from_double(point->sy)); } } } static void drag_handle_touch_enter(struct wlr_seat_touch_grab *grab, uint32_t time, struct wlr_touch_point *point) { struct wlr_drag *drag = grab->data; drag_set_focus(drag, point->focus_surface, point->sx, point->sy); } static void drag_handle_touch_cancel(struct wlr_seat_touch_grab *grab) { struct wlr_drag *drag = grab->data; drag_destroy(drag); } static const struct wlr_touch_grab_interface data_device_touch_drag_interface = { .down = drag_handle_touch_down, .up = drag_handle_touch_up, .motion = drag_handle_touch_motion, .enter = drag_handle_touch_enter, .cancel = drag_handle_touch_cancel, }; static void drag_handle_keyboard_enter(struct wlr_seat_keyboard_grab *grab, struct wlr_surface *surface, const uint32_t keycodes[], size_t num_keycodes, const struct wlr_keyboard_modifiers *modifiers) { // nothing has keyboard focus during drags } static void drag_handle_keyboard_clear_focus(struct wlr_seat_keyboard_grab *grab) { // nothing has keyboard focus during drags } static void drag_handle_keyboard_key(struct wlr_seat_keyboard_grab *grab, uint32_t time, uint32_t key, uint32_t state) { // no keyboard input during drags } static void drag_handle_keyboard_modifiers(struct wlr_seat_keyboard_grab *grab, const struct wlr_keyboard_modifiers *modifiers) { //struct wlr_keyboard *keyboard = grab->seat->keyboard_state.keyboard; // TODO change the dnd action based on what modifier is pressed on the // keyboard } static void drag_handle_keyboard_cancel(struct wlr_seat_keyboard_grab *grab) { struct wlr_drag *drag = grab->data; drag_destroy(drag); } static const struct wlr_keyboard_grab_interface data_device_keyboard_drag_interface = { .enter = drag_handle_keyboard_enter, .clear_focus = drag_handle_keyboard_clear_focus, .key = drag_handle_keyboard_key, .modifiers = drag_handle_keyboard_modifiers, .cancel = drag_handle_keyboard_cancel, }; static void drag_handle_icon_destroy(struct wl_listener *listener, void *data) { struct wlr_drag *drag = wl_container_of(listener, drag, icon_destroy); drag->icon = NULL; } static void drag_handle_drag_source_destroy(struct wl_listener *listener, void *data) { struct wlr_drag *drag = wl_container_of(listener, drag, source_destroy); drag_destroy(drag); } static void drag_icon_surface_role_commit(struct wlr_surface *surface) { assert(surface->role == &drag_icon_surface_role); pixman_region32_clear(&surface->input_region); if (wlr_surface_has_buffer(surface)) { wlr_surface_map(surface); } } const struct wlr_surface_role drag_icon_surface_role = { .name = "wl_data_device-icon", .no_object = true, .commit = drag_icon_surface_role_commit, }; static void drag_icon_handle_surface_destroy(struct wl_listener *listener, void *data) { struct wlr_drag_icon *icon = wl_container_of(listener, icon, surface_destroy); drag_icon_destroy(icon); } static struct wlr_drag_icon *drag_icon_create(struct wlr_drag *drag, struct wlr_surface *surface) { struct wlr_drag_icon *icon = calloc(1, sizeof(*icon)); if (!icon) { return NULL; } icon->drag = drag; icon->surface = surface; wl_signal_init(&icon->events.destroy); icon->surface_destroy.notify = drag_icon_handle_surface_destroy; wl_signal_add(&surface->events.destroy, &icon->surface_destroy); drag_icon_surface_role_commit(surface); return icon; } struct wlr_drag *wlr_drag_create(struct wlr_seat_client *seat_client, struct wlr_data_source *source, struct wlr_surface *icon_surface) { struct wlr_drag *drag = calloc(1, sizeof(*drag)); if (drag == NULL) { return NULL; } wl_signal_init(&drag->events.focus); wl_signal_init(&drag->events.motion); wl_signal_init(&drag->events.drop); wl_signal_init(&drag->events.destroy); drag->seat = seat_client->seat; drag->seat_client = seat_client; if (icon_surface) { struct wlr_drag_icon *icon = drag_icon_create(drag, icon_surface); if (icon == NULL) { free(drag); return NULL; } drag->icon = icon; drag->icon_destroy.notify = drag_handle_icon_destroy; wl_signal_add(&icon->events.destroy, &drag->icon_destroy); } drag->source = source; if (source != NULL) { drag->source_destroy.notify = drag_handle_drag_source_destroy; wl_signal_add(&source->events.destroy, &drag->source_destroy); } drag->pointer_grab.data = drag; drag->pointer_grab.interface = &data_device_pointer_drag_interface; drag->touch_grab.data = drag; drag->touch_grab.interface = &data_device_touch_drag_interface; drag->keyboard_grab.data = drag; drag->keyboard_grab.interface = &data_device_keyboard_drag_interface; return drag; } void wlr_seat_request_start_drag(struct wlr_seat *seat, struct wlr_drag *drag, struct wlr_surface *origin, uint32_t serial) { assert(drag->seat == seat); if (seat->drag != NULL) { wlr_log(WLR_DEBUG, "Rejecting start_drag request, " "another drag-and-drop operation is already in progress"); return; } struct wlr_seat_request_start_drag_event event = { .drag = drag, .origin = origin, .serial = serial, }; wl_signal_emit_mutable(&seat->events.request_start_drag, &event); } static void seat_handle_drag_source_destroy(struct wl_listener *listener, void *data) { struct wlr_seat *seat = wl_container_of(listener, seat, drag_source_destroy); wl_list_remove(&seat->drag_source_destroy.link); seat->drag_source = NULL; } void wlr_seat_start_drag(struct wlr_seat *seat, struct wlr_drag *drag, uint32_t serial) { assert(drag->seat == seat); assert(!drag->started); drag->started = true; wlr_seat_keyboard_start_grab(seat, &drag->keyboard_grab); seat->drag = drag; seat->drag_serial = serial; // We need to destroy the previous source, because listeners only expect one // active drag source at a time. wlr_data_source_destroy(seat->drag_source); seat->drag_source = drag->source; if (drag->source != NULL) { seat->drag_source_destroy.notify = seat_handle_drag_source_destroy; wl_signal_add(&drag->source->events.destroy, &seat->drag_source_destroy); } wl_signal_emit_mutable(&seat->events.start_drag, drag); } void wlr_seat_start_pointer_drag(struct wlr_seat *seat, struct wlr_drag *drag, uint32_t serial) { drag->grab_type = WLR_DRAG_GRAB_KEYBOARD_POINTER; wlr_seat_pointer_clear_focus(seat); wlr_seat_pointer_start_grab(seat, &drag->pointer_grab); wlr_seat_start_drag(seat, drag, serial); } void wlr_seat_start_touch_drag(struct wlr_seat *seat, struct wlr_drag *drag, uint32_t serial, struct wlr_touch_point *point) { drag->grab_type = WLR_DRAG_GRAB_KEYBOARD_TOUCH; drag->grab_touch_id = seat->touch_state.grab_id; drag->touch_id = point->touch_id; wlr_seat_touch_start_grab(seat, &drag->touch_grab); drag_set_focus(drag, point->surface, point->sx, point->sy); wlr_seat_start_drag(seat, drag, serial); } wlroots-0.17.1/types/meson.build000066400000000000000000000047361454110342200166250ustar00rootroot00000000000000wlr_files += files( 'data_device/wlr_data_device.c', 'data_device/wlr_data_offer.c', 'data_device/wlr_data_source.c', 'data_device/wlr_drag.c', 'output/cursor.c', 'output/output.c', 'output/render.c', 'output/state.c', 'output/swapchain.c', 'output/transform.c', 'scene/drag_icon.c', 'scene/subsurface_tree.c', 'scene/surface.c', 'scene/wlr_scene.c', 'scene/output_layout.c', 'scene/xdg_shell.c', 'scene/layer_shell_v1.c', 'seat/wlr_seat_keyboard.c', 'seat/wlr_seat_pointer.c', 'seat/wlr_seat_touch.c', 'seat/wlr_seat.c', 'tablet_v2/wlr_tablet_v2_pad.c', 'tablet_v2/wlr_tablet_v2_tablet.c', 'tablet_v2/wlr_tablet_v2_tool.c', 'tablet_v2/wlr_tablet_v2.c', 'xdg_shell/wlr_xdg_popup.c', 'xdg_shell/wlr_xdg_positioner.c', 'xdg_shell/wlr_xdg_shell.c', 'xdg_shell/wlr_xdg_surface.c', 'xdg_shell/wlr_xdg_toplevel.c', 'buffer/buffer.c', 'buffer/client.c', 'buffer/dmabuf.c', 'buffer/readonly_data.c', 'buffer/resource.c', 'wlr_compositor.c', 'wlr_content_type_v1.c', 'wlr_cursor_shape_v1.c', 'wlr_cursor.c', 'wlr_damage_ring.c', 'wlr_data_control_v1.c', 'wlr_drm.c', 'wlr_export_dmabuf_v1.c', 'wlr_foreign_toplevel_management_v1.c', 'wlr_fullscreen_shell_v1.c', 'wlr_gamma_control_v1.c', 'wlr_idle_inhibit_v1.c', 'wlr_idle_notify_v1.c', 'wlr_input_device.c', 'wlr_input_inhibitor.c', 'wlr_input_method_v2.c', 'wlr_keyboard.c', 'wlr_keyboard_group.c', 'wlr_keyboard_shortcuts_inhibit_v1.c', 'wlr_layer_shell_v1.c', 'wlr_linux_dmabuf_v1.c', 'wlr_matrix.c', 'wlr_output_layer.c', 'wlr_output_layout.c', 'wlr_output_management_v1.c', 'wlr_output_power_management_v1.c', 'wlr_pointer_constraints_v1.c', 'wlr_pointer_gestures_v1.c', 'wlr_pointer.c', 'wlr_presentation_time.c', 'wlr_primary_selection_v1.c', 'wlr_primary_selection.c', 'wlr_region.c', 'wlr_relative_pointer_v1.c', 'wlr_screencopy_v1.c', 'wlr_security_context_v1.c', 'wlr_server_decoration.c', 'wlr_session_lock_v1.c', 'wlr_shm.c', 'wlr_single_pixel_buffer_v1.c', 'wlr_subcompositor.c', 'wlr_fractional_scale_v1.c', 'wlr_switch.c', 'wlr_tablet_pad.c', 'wlr_tablet_tool.c', 'wlr_text_input_v3.c', 'wlr_touch.c', 'wlr_viewporter.c', 'wlr_virtual_keyboard_v1.c', 'wlr_virtual_pointer_v1.c', 'wlr_xcursor_manager.c', 'wlr_xdg_activation_v1.c', 'wlr_xdg_decoration_v1.c', 'wlr_xdg_foreign_v1.c', 'wlr_xdg_foreign_v2.c', 'wlr_xdg_foreign_registry.c', 'wlr_xdg_output_v1.c', 'wlr_tearing_control_v1.c', ) if features.get('drm-backend') wlr_files += files( 'wlr_drm_lease_v1.c', ) endif wlroots-0.17.1/types/output/000077500000000000000000000000001454110342200160115ustar00rootroot00000000000000wlroots-0.17.1/types/output/cursor.c000066400000000000000000000352521454110342200175010ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include "render/allocator/allocator.h" #include "types/wlr_buffer.h" #include "types/wlr_output.h" static bool output_set_hardware_cursor(struct wlr_output *output, struct wlr_buffer *buffer, int hotspot_x, int hotspot_y) { if (!output->impl->set_cursor) { return false; } if (!output->impl->set_cursor(output, buffer, hotspot_x, hotspot_y)) { return false; } wlr_buffer_unlock(output->cursor_front_buffer); output->cursor_front_buffer = NULL; if (buffer != NULL) { output->cursor_front_buffer = wlr_buffer_lock(buffer); } return true; } static void output_cursor_damage_whole(struct wlr_output_cursor *cursor); void wlr_output_lock_software_cursors(struct wlr_output *output, bool lock) { if (lock) { ++output->software_cursor_locks; } else { assert(output->software_cursor_locks > 0); --output->software_cursor_locks; } wlr_log(WLR_DEBUG, "%s hardware cursors on output '%s' (locks: %d)", lock ? "Disabling" : "Enabling", output->name, output->software_cursor_locks); if (output->software_cursor_locks > 0 && output->hardware_cursor != NULL) { output_set_hardware_cursor(output, NULL, 0, 0); output_cursor_damage_whole(output->hardware_cursor); output->hardware_cursor = NULL; } // If it's possible to use hardware cursors again, don't switch immediately // since a recorder is likely to lock software cursors for the next frame // again. } static void output_scissor(struct wlr_output *output, pixman_box32_t *rect) { struct wlr_renderer *renderer = output->renderer; assert(renderer); struct wlr_box box = { .x = rect->x1, .y = rect->y1, .width = rect->x2 - rect->x1, .height = rect->y2 - rect->y1, }; int ow, oh; wlr_output_transformed_resolution(output, &ow, &oh); enum wl_output_transform transform = wlr_output_transform_invert(output->transform); wlr_box_transform(&box, &box, transform, ow, oh); wlr_renderer_scissor(renderer, &box); } /** * Returns the cursor box, scaled for its output. */ static void output_cursor_get_box(struct wlr_output_cursor *cursor, struct wlr_box *box) { box->x = cursor->x - cursor->hotspot_x; box->y = cursor->y - cursor->hotspot_y; box->width = cursor->width; box->height = cursor->height; } static void output_cursor_render(struct wlr_output_cursor *cursor, pixman_region32_t *damage) { struct wlr_renderer *renderer = cursor->output->renderer; assert(renderer); struct wlr_texture *texture = cursor->texture; if (texture == NULL) { return; } struct wlr_box box; output_cursor_get_box(cursor, &box); pixman_region32_t surface_damage; pixman_region32_init(&surface_damage); pixman_region32_union_rect(&surface_damage, &surface_damage, box.x, box.y, box.width, box.height); pixman_region32_intersect(&surface_damage, &surface_damage, damage); if (!pixman_region32_not_empty(&surface_damage)) { goto surface_damage_finish; } float matrix[9]; wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL, 0, cursor->output->transform_matrix); int nrects; pixman_box32_t *rects = pixman_region32_rectangles(&surface_damage, &nrects); for (int i = 0; i < nrects; ++i) { output_scissor(cursor->output, &rects[i]); wlr_render_texture_with_matrix(renderer, texture, matrix, 1.0f); } wlr_renderer_scissor(renderer, NULL); surface_damage_finish: pixman_region32_fini(&surface_damage); } void wlr_output_render_software_cursors(struct wlr_output *output, const pixman_region32_t *damage) { int width, height; wlr_output_transformed_resolution(output, &width, &height); pixman_region32_t render_damage; pixman_region32_init(&render_damage); pixman_region32_union_rect(&render_damage, &render_damage, 0, 0, width, height); if (damage != NULL) { // Damage tracking supported pixman_region32_intersect(&render_damage, &render_damage, damage); } if (pixman_region32_not_empty(&render_damage)) { struct wlr_output_cursor *cursor; wl_list_for_each(cursor, &output->cursors, link) { if (!cursor->enabled || !cursor->visible || output->hardware_cursor == cursor) { continue; } output_cursor_render(cursor, &render_damage); } } pixman_region32_fini(&render_damage); } void wlr_output_add_software_cursors_to_render_pass(struct wlr_output *output, struct wlr_render_pass *render_pass, const pixman_region32_t *damage) { int width, height; wlr_output_transformed_resolution(output, &width, &height); pixman_region32_t render_damage; pixman_region32_init_rect(&render_damage, 0, 0, width, height); if (damage != NULL) { pixman_region32_intersect(&render_damage, &render_damage, damage); } struct wlr_output_cursor *cursor; wl_list_for_each(cursor, &output->cursors, link) { if (!cursor->enabled || !cursor->visible || output->hardware_cursor == cursor) { continue; } struct wlr_texture *texture = cursor->texture; if (texture == NULL) { continue; } struct wlr_box box; output_cursor_get_box(cursor, &box); pixman_region32_t cursor_damage; pixman_region32_init_rect(&cursor_damage, box.x, box.y, box.width, box.height); pixman_region32_intersect(&cursor_damage, &cursor_damage, &render_damage); if (!pixman_region32_not_empty(&cursor_damage)) { pixman_region32_fini(&cursor_damage); continue; } enum wl_output_transform transform = wlr_output_transform_invert(output->transform); wlr_box_transform(&box, &box, transform, width, height); wlr_region_transform(&cursor_damage, &cursor_damage, transform, width, height); wlr_render_pass_add_texture(render_pass, &(struct wlr_render_texture_options) { .texture = texture, .src_box = cursor->src_box, .dst_box = box, .clip = &cursor_damage, .transform = output->transform, }); pixman_region32_fini(&cursor_damage); } pixman_region32_fini(&render_damage); } static void output_cursor_damage_whole(struct wlr_output_cursor *cursor) { struct wlr_box box; output_cursor_get_box(cursor, &box); pixman_region32_t damage; pixman_region32_init_rect(&damage, box.x, box.y, box.width, box.height); struct wlr_output_event_damage event = { .output = cursor->output, .damage = &damage, }; wl_signal_emit_mutable(&cursor->output->events.damage, &event); pixman_region32_fini(&damage); } static void output_cursor_reset(struct wlr_output_cursor *cursor) { if (cursor->output->hardware_cursor != cursor) { output_cursor_damage_whole(cursor); } } static void output_cursor_update_visible(struct wlr_output_cursor *cursor) { struct wlr_box output_box; output_box.x = output_box.y = 0; wlr_output_transformed_resolution(cursor->output, &output_box.width, &output_box.height); struct wlr_box cursor_box; output_cursor_get_box(cursor, &cursor_box); struct wlr_box intersection; cursor->visible = wlr_box_intersection(&intersection, &output_box, &cursor_box); } static bool output_pick_cursor_format(struct wlr_output *output, struct wlr_drm_format *format) { struct wlr_allocator *allocator = output->allocator; assert(allocator != NULL); const struct wlr_drm_format_set *display_formats = NULL; if (output->impl->get_cursor_formats) { display_formats = output->impl->get_cursor_formats(output, allocator->buffer_caps); if (display_formats == NULL) { wlr_log(WLR_DEBUG, "Failed to get cursor display formats"); return false; } } return output_pick_format(output, display_formats, format, DRM_FORMAT_ARGB8888); } static struct wlr_buffer *render_cursor_buffer(struct wlr_output_cursor *cursor) { struct wlr_output *output = cursor->output; struct wlr_texture *texture = cursor->texture; if (texture == NULL) { return NULL; } struct wlr_allocator *allocator = output->allocator; struct wlr_renderer *renderer = output->renderer; assert(allocator != NULL && renderer != NULL); int width = cursor->width; int height = cursor->height; if (output->impl->get_cursor_size) { // Apply hardware limitations on buffer size output->impl->get_cursor_size(cursor->output, &width, &height); if ((int)texture->width > width || (int)texture->height > height) { wlr_log(WLR_DEBUG, "Cursor texture too large (%dx%d), " "exceeds hardware limitations (%dx%d)", texture->width, texture->height, width, height); return NULL; } } if (output->cursor_swapchain == NULL || output->cursor_swapchain->width != width || output->cursor_swapchain->height != height) { struct wlr_drm_format format = {0}; if (!output_pick_cursor_format(output, &format)) { wlr_log(WLR_DEBUG, "Failed to pick cursor format"); return NULL; } wlr_swapchain_destroy(output->cursor_swapchain); output->cursor_swapchain = wlr_swapchain_create(allocator, width, height, &format); wlr_drm_format_finish(&format); if (output->cursor_swapchain == NULL) { wlr_log(WLR_ERROR, "Failed to create cursor swapchain"); return NULL; } } struct wlr_buffer *buffer = wlr_swapchain_acquire(output->cursor_swapchain, NULL); if (buffer == NULL) { return NULL; } struct wlr_box dst_box = { .width = cursor->width, .height = cursor->height, }; wlr_box_transform(&dst_box, &dst_box, wlr_output_transform_invert(output->transform), buffer->width, buffer->height); struct wlr_render_pass *pass = wlr_renderer_begin_buffer_pass(renderer, buffer, NULL); if (pass == NULL) { wlr_buffer_unlock(buffer); return NULL; } enum wl_output_transform transform = wlr_output_transform_invert(cursor->transform); transform = wlr_output_transform_compose(transform, output->transform); wlr_render_pass_add_rect(pass, &(struct wlr_render_rect_options){ .box = { .width = buffer->width, .height = buffer->height }, .blend_mode = WLR_RENDER_BLEND_MODE_NONE, }); wlr_render_pass_add_texture(pass, &(struct wlr_render_texture_options){ .texture = texture, .src_box = cursor->src_box, .dst_box = dst_box, .transform = transform, }); if (!wlr_render_pass_submit(pass)) { wlr_buffer_unlock(buffer); return NULL; } return buffer; } static bool output_cursor_attempt_hardware(struct wlr_output_cursor *cursor) { struct wlr_output *output = cursor->output; if (!output->impl->set_cursor || output->software_cursor_locks > 0) { return false; } struct wlr_output_cursor *hwcur = output->hardware_cursor; if (hwcur != NULL && hwcur != cursor) { return false; } struct wlr_texture *texture = cursor->texture; // If the cursor was hidden or was a software cursor, the hardware // cursor position is outdated output->impl->move_cursor(cursor->output, (int)cursor->x, (int)cursor->y); struct wlr_buffer *buffer = NULL; if (texture != NULL) { buffer = render_cursor_buffer(cursor); if (buffer == NULL) { wlr_log(WLR_DEBUG, "Failed to render cursor buffer"); return false; } } struct wlr_box hotspot = { .x = cursor->hotspot_x, .y = cursor->hotspot_y, }; wlr_box_transform(&hotspot, &hotspot, wlr_output_transform_invert(output->transform), buffer ? buffer->width : 0, buffer ? buffer->height : 0); bool ok = output_set_hardware_cursor(output, buffer, hotspot.x, hotspot.y); wlr_buffer_unlock(buffer); if (ok) { output->hardware_cursor = cursor; } return ok; } bool wlr_output_cursor_set_buffer(struct wlr_output_cursor *cursor, struct wlr_buffer *buffer, int32_t hotspot_x, int32_t hotspot_y) { struct wlr_renderer *renderer = cursor->output->renderer; assert(renderer != NULL); struct wlr_texture *texture = NULL; struct wlr_fbox src_box = {0}; int dst_width = 0, dst_height = 0; if (buffer != NULL) { texture = wlr_texture_from_buffer(renderer, buffer); if (texture == NULL) { return false; } src_box = (struct wlr_fbox){ .width = texture->width, .height = texture->height, }; dst_width = texture->width / cursor->output->scale; dst_height = texture->height / cursor->output->scale; } hotspot_x /= cursor->output->scale; hotspot_y /= cursor->output->scale; return output_cursor_set_texture(cursor, texture, true, &src_box, dst_width, dst_height, WL_OUTPUT_TRANSFORM_NORMAL, hotspot_x, hotspot_y); } bool output_cursor_set_texture(struct wlr_output_cursor *cursor, struct wlr_texture *texture, bool own_texture, const struct wlr_fbox *src_box, int dst_width, int dst_height, enum wl_output_transform transform, int32_t hotspot_x, int32_t hotspot_y) { struct wlr_output *output = cursor->output; output_cursor_reset(cursor); cursor->enabled = texture != NULL; if (texture != NULL) { cursor->width = (int)roundf(dst_width * output->scale); cursor->height = (int)roundf(dst_height * output->scale); cursor->src_box = *src_box; cursor->transform = transform; } else { cursor->width = 0; cursor->height = 0; } cursor->hotspot_x = (int)roundf(hotspot_x * output->scale); cursor->hotspot_y = (int)roundf(hotspot_y * output->scale); output_cursor_update_visible(cursor); if (cursor->own_texture) { wlr_texture_destroy(cursor->texture); } cursor->texture = texture; cursor->own_texture = own_texture; if (output_cursor_attempt_hardware(cursor)) { return true; } wlr_log(WLR_DEBUG, "Falling back to software cursor on output '%s'", cursor->output->name); output_cursor_damage_whole(cursor); return true; } bool wlr_output_cursor_move(struct wlr_output_cursor *cursor, double x, double y) { // Scale coordinates for the output x *= cursor->output->scale; y *= cursor->output->scale; if (cursor->x == x && cursor->y == y) { return true; } if (cursor->output->hardware_cursor != cursor) { output_cursor_damage_whole(cursor); } cursor->x = x; cursor->y = y; bool was_visible = cursor->visible; output_cursor_update_visible(cursor); if (!was_visible && !cursor->visible) { // Cursor is still hidden, do nothing return true; } if (cursor->output->hardware_cursor != cursor) { output_cursor_damage_whole(cursor); return true; } assert(cursor->output->impl->move_cursor); return cursor->output->impl->move_cursor(cursor->output, (int)x, (int)y); } struct wlr_output_cursor *wlr_output_cursor_create(struct wlr_output *output) { struct wlr_output_cursor *cursor = calloc(1, sizeof(*cursor)); if (cursor == NULL) { return NULL; } cursor->output = output; wl_list_insert(&output->cursors, &cursor->link); cursor->visible = true; // default position is at (0, 0) return cursor; } void wlr_output_cursor_destroy(struct wlr_output_cursor *cursor) { if (cursor == NULL) { return; } output_cursor_reset(cursor); if (cursor->output->hardware_cursor == cursor) { // If this cursor was the hardware cursor, disable it output_set_hardware_cursor(cursor->output, NULL, 0, 0); cursor->output->hardware_cursor = NULL; } if (cursor->own_texture) { wlr_texture_destroy(cursor->texture); } wl_list_remove(&cursor->link); free(cursor); } wlroots-0.17.1/types/output/output.c000066400000000000000000000730621454110342200175250ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include #include #include #include "render/allocator/allocator.h" #include "types/wlr_output.h" #include "util/env.h" #include "util/global.h" #define OUTPUT_VERSION 4 static void send_geometry(struct wl_resource *resource) { struct wlr_output *output = wlr_output_from_resource(resource); const char *make = output->make; if (make == NULL) { make = "Unknown"; } const char *model = output->model; if (model == NULL) { model = "Unknown"; } wl_output_send_geometry(resource, 0, 0, output->phys_width, output->phys_height, output->subpixel, make, model, output->transform); } static void send_current_mode(struct wl_resource *resource) { struct wlr_output *output = wlr_output_from_resource(resource); if (output->current_mode != NULL) { struct wlr_output_mode *mode = output->current_mode; wl_output_send_mode(resource, WL_OUTPUT_MODE_CURRENT, mode->width, mode->height, mode->refresh); } else { // Output has no mode wl_output_send_mode(resource, WL_OUTPUT_MODE_CURRENT, output->width, output->height, output->refresh); } } static void send_scale(struct wl_resource *resource) { struct wlr_output *output = wlr_output_from_resource(resource); uint32_t version = wl_resource_get_version(resource); if (version >= WL_OUTPUT_SCALE_SINCE_VERSION) { wl_output_send_scale(resource, (uint32_t)ceil(output->scale)); } } static void send_name(struct wl_resource *resource) { struct wlr_output *output = wlr_output_from_resource(resource); uint32_t version = wl_resource_get_version(resource); if (version >= WL_OUTPUT_NAME_SINCE_VERSION) { wl_output_send_name(resource, output->name); } } static void send_description(struct wl_resource *resource) { struct wlr_output *output = wlr_output_from_resource(resource); uint32_t version = wl_resource_get_version(resource); if (output->description != NULL && version >= WL_OUTPUT_DESCRIPTION_SINCE_VERSION) { wl_output_send_description(resource, output->description); } } static void send_done(struct wl_resource *resource) { uint32_t version = wl_resource_get_version(resource); if (version >= WL_OUTPUT_DONE_SINCE_VERSION) { wl_output_send_done(resource); } } static void output_handle_resource_destroy(struct wl_resource *resource) { wl_list_remove(wl_resource_get_link(resource)); } static void output_handle_release(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static const struct wl_output_interface output_impl = { .release = output_handle_release, }; static void output_bind(struct wl_client *wl_client, void *data, uint32_t version, uint32_t id) { // `output` can be NULL if the output global is being destroyed struct wlr_output *output = data; struct wl_resource *resource = wl_resource_create(wl_client, &wl_output_interface, version, id); if (resource == NULL) { wl_client_post_no_memory(wl_client); return; } wl_resource_set_implementation(resource, &output_impl, output, output_handle_resource_destroy); if (output == NULL) { wl_list_init(wl_resource_get_link(resource)); return; } wl_list_insert(&output->resources, wl_resource_get_link(resource)); send_geometry(resource); send_current_mode(resource); send_scale(resource); send_name(resource); send_description(resource); send_done(resource); struct wlr_output_event_bind evt = { .output = output, .resource = resource, }; wl_signal_emit_mutable(&output->events.bind, &evt); } void wlr_output_create_global(struct wlr_output *output) { if (output->global != NULL) { return; } output->global = wl_global_create(output->display, &wl_output_interface, OUTPUT_VERSION, output, output_bind); if (output->global == NULL) { wlr_log(WLR_ERROR, "Failed to allocate wl_output global"); } } void wlr_output_destroy_global(struct wlr_output *output) { if (output->global == NULL) { return; } // Make all output resources inert struct wl_resource *resource, *tmp; wl_resource_for_each_safe(resource, tmp, &output->resources) { wl_resource_set_user_data(resource, NULL); wl_list_remove(wl_resource_get_link(resource)); wl_list_init(wl_resource_get_link(resource)); } wlr_global_destroy_safe(output->global); output->global = NULL; } static void schedule_done_handle_idle_timer(void *data) { struct wlr_output *output = data; output->idle_done = NULL; struct wl_resource *resource; wl_resource_for_each(resource, &output->resources) { send_done(resource); } } void wlr_output_schedule_done(struct wlr_output *output) { if (output->idle_done != NULL) { return; // Already scheduled } struct wl_event_loop *ev = wl_display_get_event_loop(output->display); output->idle_done = wl_event_loop_add_idle(ev, schedule_done_handle_idle_timer, output); } struct wlr_output *wlr_output_from_resource(struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &wl_output_interface, &output_impl)); return wl_resource_get_user_data(resource); } static void output_update_matrix(struct wlr_output *output) { wlr_matrix_identity(output->transform_matrix); if (output->transform != WL_OUTPUT_TRANSFORM_NORMAL) { int tr_width, tr_height; wlr_output_transformed_resolution(output, &tr_width, &tr_height); wlr_matrix_translate(output->transform_matrix, output->width / 2.0, output->height / 2.0); wlr_matrix_transform(output->transform_matrix, output->transform); wlr_matrix_translate(output->transform_matrix, - tr_width / 2.0, - tr_height / 2.0); } } void wlr_output_enable(struct wlr_output *output, bool enable) { wlr_output_state_set_enabled(&output->pending, enable); } void wlr_output_set_mode(struct wlr_output *output, struct wlr_output_mode *mode) { wlr_output_state_set_mode(&output->pending, mode); } void wlr_output_set_custom_mode(struct wlr_output *output, int32_t width, int32_t height, int32_t refresh) { // If there is a fixed mode which matches what the user wants, use that struct wlr_output_mode *mode; wl_list_for_each(mode, &output->modes, link) { if (mode->width == width && mode->height == height && mode->refresh == refresh) { wlr_output_set_mode(output, mode); return; } } wlr_output_state_set_custom_mode(&output->pending, width, height, refresh); } void wlr_output_set_transform(struct wlr_output *output, enum wl_output_transform transform) { wlr_output_state_set_transform(&output->pending, transform); } void wlr_output_set_scale(struct wlr_output *output, float scale) { wlr_output_state_set_scale(&output->pending, scale); } void wlr_output_enable_adaptive_sync(struct wlr_output *output, bool enabled) { wlr_output_state_set_adaptive_sync_enabled(&output->pending, enabled); } void wlr_output_set_render_format(struct wlr_output *output, uint32_t format) { wlr_output_state_set_render_format(&output->pending, format); } void wlr_output_set_subpixel(struct wlr_output *output, enum wl_output_subpixel subpixel) { wlr_output_state_set_subpixel(&output->pending, subpixel); } void wlr_output_set_name(struct wlr_output *output, const char *name) { assert(output->global == NULL); free(output->name); output->name = strdup(name); } void wlr_output_set_description(struct wlr_output *output, const char *desc) { if (output->description != NULL && desc != NULL && strcmp(output->description, desc) == 0) { return; } free(output->description); if (desc != NULL) { output->description = strdup(desc); } else { output->description = NULL; } struct wl_resource *resource; wl_resource_for_each(resource, &output->resources) { send_description(resource); } wlr_output_schedule_done(output); wl_signal_emit_mutable(&output->events.description, output); } static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_output *output = wl_container_of(listener, output, display_destroy); wlr_output_destroy_global(output); } static void output_state_move(struct wlr_output_state *dst, struct wlr_output_state *src) { *dst = *src; wlr_output_state_init(src); } static void output_apply_state(struct wlr_output *output, const struct wlr_output_state *state) { if (state->committed & WLR_OUTPUT_STATE_RENDER_FORMAT) { output->render_format = state->render_format; } if (state->committed & WLR_OUTPUT_STATE_SUBPIXEL) { output->subpixel = state->subpixel; } if (state->committed & WLR_OUTPUT_STATE_ENABLED) { output->enabled = state->enabled; } bool scale_updated = state->committed & WLR_OUTPUT_STATE_SCALE; if (scale_updated) { output->scale = state->scale; } if (state->committed & WLR_OUTPUT_STATE_TRANSFORM) { output->transform = state->transform; output_update_matrix(output); } bool geometry_updated = state->committed & (WLR_OUTPUT_STATE_MODE | WLR_OUTPUT_STATE_TRANSFORM | WLR_OUTPUT_STATE_SUBPIXEL); // Destroy the swapchains when an output is disabled if ((state->committed & WLR_OUTPUT_STATE_ENABLED) && !state->enabled) { wlr_swapchain_destroy(output->swapchain); output->swapchain = NULL; wlr_swapchain_destroy(output->cursor_swapchain); output->cursor_swapchain = NULL; } if (state->committed & WLR_OUTPUT_STATE_LAYERS) { for (size_t i = 0; i < state->layers_len; i++) { struct wlr_output_layer_state *layer_state = &state->layers[i]; struct wlr_output_layer *layer = layer_state->layer; // Commit layer ordering wl_list_remove(&layer->link); wl_list_insert(output->layers.prev, &layer->link); // Commit layer state layer->src_box = layer_state->src_box; layer->dst_box = layer_state->dst_box; } } if ((state->committed & WLR_OUTPUT_STATE_BUFFER) && output->swapchain != NULL) { wlr_swapchain_set_buffer_submitted(output->swapchain, state->buffer); } bool mode_updated = false; if (state->committed & WLR_OUTPUT_STATE_MODE) { int width = 0, height = 0, refresh = 0; switch (state->mode_type) { case WLR_OUTPUT_STATE_MODE_FIXED:; struct wlr_output_mode *mode = state->mode; output->current_mode = mode; if (mode != NULL) { width = mode->width; height = mode->height; refresh = mode->refresh; } break; case WLR_OUTPUT_STATE_MODE_CUSTOM: output->current_mode = NULL; width = state->custom_mode.width; height = state->custom_mode.height; refresh = state->custom_mode.refresh; break; } if (output->width != width || output->height != height || output->refresh != refresh) { output->width = width; output->height = height; output_update_matrix(output); output->refresh = refresh; if (output->swapchain != NULL && (output->swapchain->width != output->width || output->swapchain->height != output->height)) { wlr_swapchain_destroy(output->swapchain); output->swapchain = NULL; } mode_updated = true; } } if (geometry_updated || scale_updated || mode_updated) { struct wl_resource *resource; wl_resource_for_each(resource, &output->resources) { if (mode_updated) { send_current_mode(resource); } if (geometry_updated) { send_geometry(resource); } if (scale_updated) { send_scale(resource); } } wlr_output_schedule_done(output); } } void wlr_output_init(struct wlr_output *output, struct wlr_backend *backend, const struct wlr_output_impl *impl, struct wl_display *display, const struct wlr_output_state *state) { assert(impl->commit); if (impl->set_cursor || impl->move_cursor) { assert(impl->set_cursor && impl->move_cursor); } *output = (struct wlr_output){ .backend = backend, .impl = impl, .display = display, .render_format = DRM_FORMAT_XRGB8888, .transform = WL_OUTPUT_TRANSFORM_NORMAL, .scale = 1, .commit_seq = 0, }; wl_list_init(&output->modes); wl_list_init(&output->cursors); wl_list_init(&output->layers); wl_list_init(&output->resources); wl_signal_init(&output->events.frame); wl_signal_init(&output->events.damage); wl_signal_init(&output->events.needs_frame); wl_signal_init(&output->events.precommit); wl_signal_init(&output->events.commit); wl_signal_init(&output->events.present); wl_signal_init(&output->events.bind); wl_signal_init(&output->events.description); wl_signal_init(&output->events.request_state); wl_signal_init(&output->events.destroy); wlr_output_state_init(&output->pending); output->software_cursor_locks = env_parse_bool("WLR_NO_HARDWARE_CURSORS"); if (output->software_cursor_locks) { wlr_log(WLR_DEBUG, "WLR_NO_HARDWARE_CURSORS set, forcing software cursors"); } wlr_addon_set_init(&output->addons); output->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &output->display_destroy); if (state) { output_apply_state(output, state); } } void wlr_output_destroy(struct wlr_output *output) { if (!output) { return; } wl_signal_emit_mutable(&output->events.destroy, output); wl_list_remove(&output->display_destroy.link); wlr_output_destroy_global(output); output_clear_back_buffer(output); wlr_addon_set_finish(&output->addons); // The backend is responsible for free-ing the list of modes struct wlr_output_cursor *cursor, *tmp_cursor; wl_list_for_each_safe(cursor, tmp_cursor, &output->cursors, link) { wlr_output_cursor_destroy(cursor); } struct wlr_output_layer *layer, *tmp_layer; wl_list_for_each_safe(layer, tmp_layer, &output->layers, link) { wlr_output_layer_destroy(layer); } wlr_swapchain_destroy(output->cursor_swapchain); wlr_buffer_unlock(output->cursor_front_buffer); wlr_swapchain_destroy(output->swapchain); if (output->idle_frame != NULL) { wl_event_source_remove(output->idle_frame); } if (output->idle_done != NULL) { wl_event_source_remove(output->idle_done); } free(output->name); free(output->description); free(output->make); free(output->model); free(output->serial); wlr_output_state_finish(&output->pending); if (output->impl && output->impl->destroy) { output->impl->destroy(output); } else { free(output); } } void wlr_output_transformed_resolution(struct wlr_output *output, int *width, int *height) { if (output->transform % 2 == 0) { *width = output->width; *height = output->height; } else { *width = output->height; *height = output->width; } } void wlr_output_effective_resolution(struct wlr_output *output, int *width, int *height) { wlr_output_transformed_resolution(output, width, height); *width /= output->scale; *height /= output->scale; } struct wlr_output_mode *wlr_output_preferred_mode(struct wlr_output *output) { if (wl_list_empty(&output->modes)) { return NULL; } struct wlr_output_mode *mode; wl_list_for_each(mode, &output->modes, link) { if (mode->preferred) { return mode; } } // No preferred mode, choose the first one return wl_container_of(output->modes.next, mode, link); } static void output_state_clear_buffer(struct wlr_output_state *state) { if (!(state->committed & WLR_OUTPUT_STATE_BUFFER)) { return; } wlr_buffer_unlock(state->buffer); state->buffer = NULL; state->committed &= ~WLR_OUTPUT_STATE_BUFFER; } void wlr_output_set_damage(struct wlr_output *output, const pixman_region32_t *damage) { pixman_region32_intersect_rect(&output->pending.damage, damage, 0, 0, output->width, output->height); output->pending.committed |= WLR_OUTPUT_STATE_DAMAGE; } void wlr_output_set_layers(struct wlr_output *output, struct wlr_output_layer_state *layers, size_t layers_len) { wlr_output_state_set_layers(&output->pending, layers, layers_len); } static void output_state_clear_gamma_lut(struct wlr_output_state *state) { free(state->gamma_lut); state->gamma_lut = NULL; state->committed &= ~WLR_OUTPUT_STATE_GAMMA_LUT; } static void output_state_clear(struct wlr_output_state *state) { output_state_clear_buffer(state); output_state_clear_gamma_lut(state); pixman_region32_clear(&state->damage); state->committed = 0; } void output_pending_resolution(struct wlr_output *output, const struct wlr_output_state *state, int *width, int *height) { if (state->committed & WLR_OUTPUT_STATE_MODE) { switch (state->mode_type) { case WLR_OUTPUT_STATE_MODE_FIXED: *width = state->mode->width; *height = state->mode->height; return; case WLR_OUTPUT_STATE_MODE_CUSTOM: *width = state->custom_mode.width; *height = state->custom_mode.height; return; } abort(); } else { *width = output->width; *height = output->height; } } bool output_pending_enabled(struct wlr_output *output, const struct wlr_output_state *state) { if (state->committed & WLR_OUTPUT_STATE_ENABLED) { return state->enabled; } return output->enabled; } /** * Compare a struct wlr_output_state with the current state of a struct * wlr_output. * * Returns a bitfield of the unchanged fields. * * Some fields are not checked: damage always changes in-between frames, the * gamma LUT is too expensive to check, the contents of the buffer might have * changed, etc. */ static uint32_t output_compare_state(struct wlr_output *output, const struct wlr_output_state *state) { uint32_t fields = 0; if (state->committed & WLR_OUTPUT_STATE_MODE) { bool unchanged = false; switch (state->mode_type) { case WLR_OUTPUT_STATE_MODE_FIXED: unchanged = output->current_mode == state->mode; break; case WLR_OUTPUT_STATE_MODE_CUSTOM: unchanged = output->width == state->custom_mode.width && output->height == state->custom_mode.height && output->refresh == state->custom_mode.refresh; break; } if (unchanged) { fields |= WLR_OUTPUT_STATE_MODE; } } if ((state->committed & WLR_OUTPUT_STATE_ENABLED) && output->enabled == state->enabled) { fields |= WLR_OUTPUT_STATE_ENABLED; } if ((state->committed & WLR_OUTPUT_STATE_SCALE) && output->scale == state->scale) { fields |= WLR_OUTPUT_STATE_SCALE; } if ((state->committed & WLR_OUTPUT_STATE_TRANSFORM) && output->transform == state->transform) { fields |= WLR_OUTPUT_STATE_TRANSFORM; } if (state->committed & WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED) { bool enabled = output->adaptive_sync_status != WLR_OUTPUT_ADAPTIVE_SYNC_DISABLED; if (enabled == state->adaptive_sync_enabled) { fields |= WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED; } } if ((state->committed & WLR_OUTPUT_STATE_RENDER_FORMAT) && output->render_format == state->render_format) { fields |= WLR_OUTPUT_STATE_RENDER_FORMAT; } if ((state->committed & WLR_OUTPUT_STATE_SUBPIXEL) && output->subpixel == state->subpixel) { fields |= WLR_OUTPUT_STATE_SUBPIXEL; } return fields; } static bool output_basic_test(struct wlr_output *output, const struct wlr_output_state *state) { if (state->committed & WLR_OUTPUT_STATE_BUFFER) { // If the size doesn't match, reject buffer (scaling is not // supported) int pending_width, pending_height; output_pending_resolution(output, state, &pending_width, &pending_height); if (state->buffer->width != pending_width || state->buffer->height != pending_height) { wlr_log(WLR_DEBUG, "Primary buffer size mismatch"); return false; } } else if (state->tearing_page_flip) { wlr_log(WLR_ERROR, "Trying to commit a tearing page flip without a buffer?"); return false; } if (state->committed & WLR_OUTPUT_STATE_RENDER_FORMAT) { struct wlr_allocator *allocator = output->allocator; assert(allocator != NULL); const struct wlr_drm_format_set *display_formats = wlr_output_get_primary_formats(output, allocator->buffer_caps); struct wlr_drm_format format = {0}; if (!output_pick_format(output, display_formats, &format, state->render_format)) { wlr_log(WLR_ERROR, "Failed to pick primary buffer format for output"); return false; } wlr_drm_format_finish(&format); } bool enabled = output->enabled; if (state->committed & WLR_OUTPUT_STATE_ENABLED) { enabled = state->enabled; } if (enabled && (state->committed & (WLR_OUTPUT_STATE_ENABLED | WLR_OUTPUT_STATE_MODE))) { int pending_width, pending_height; output_pending_resolution(output, state, &pending_width, &pending_height); if (pending_width == 0 || pending_height == 0) { wlr_log(WLR_DEBUG, "Tried to enable an output with a zero mode"); return false; } } if (!enabled && state->committed & WLR_OUTPUT_STATE_BUFFER) { wlr_log(WLR_DEBUG, "Tried to commit a buffer on a disabled output"); return false; } if (!enabled && state->committed & WLR_OUTPUT_STATE_MODE) { wlr_log(WLR_DEBUG, "Tried to modeset a disabled output"); return false; } if (!enabled && state->committed & WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED) { wlr_log(WLR_DEBUG, "Tried to enable adaptive sync on a disabled output"); return false; } if (!enabled && state->committed & WLR_OUTPUT_STATE_RENDER_FORMAT) { wlr_log(WLR_DEBUG, "Tried to set format for a disabled output"); return false; } if (!enabled && state->committed & WLR_OUTPUT_STATE_GAMMA_LUT) { wlr_log(WLR_DEBUG, "Tried to set the gamma lut on a disabled output"); return false; } if (!enabled && state->committed & WLR_OUTPUT_STATE_SUBPIXEL) { wlr_log(WLR_DEBUG, "Tried to set the subpixel layout on a disabled output"); return false; } if (state->committed & WLR_OUTPUT_STATE_LAYERS) { if (state->layers_len != (size_t)wl_list_length(&output->layers)) { wlr_log(WLR_DEBUG, "All output layers must be specified in wlr_output_state.layers"); return false; } for (size_t i = 0; i < state->layers_len; i++) { state->layers[i].accepted = false; } } return true; } bool wlr_output_test_state(struct wlr_output *output, const struct wlr_output_state *state) { uint32_t unchanged = output_compare_state(output, state); // Create a shallow copy of the state with only the fields which have been // changed and potentially a new buffer. struct wlr_output_state copy = *state; copy.committed &= ~unchanged; if (!output_basic_test(output, ©)) { return false; } if (!output->impl->test) { return true; } bool new_back_buffer = false; if (!output_ensure_buffer(output, ©, &new_back_buffer)) { return false; } bool success = output->impl->test(output, ©); if (new_back_buffer) { wlr_buffer_unlock(copy.buffer); } return success; } bool wlr_output_test(struct wlr_output *output) { struct wlr_output_state state = output->pending; if (output->back_buffer != NULL) { assert((state.committed & WLR_OUTPUT_STATE_BUFFER) == 0); state.committed |= WLR_OUTPUT_STATE_BUFFER; state.buffer = output->back_buffer; } return wlr_output_test_state(output, &state); } bool wlr_output_commit_state(struct wlr_output *output, const struct wlr_output_state *state) { uint32_t unchanged = output_compare_state(output, state); // Create a shallow copy of the state with only the fields which have been // changed and potentially a new buffer. struct wlr_output_state pending = *state; pending.committed &= ~unchanged; if (!output_basic_test(output, &pending)) { wlr_log(WLR_ERROR, "Basic output test failed for %s", output->name); return false; } bool new_back_buffer = false; if (!output_ensure_buffer(output, &pending, &new_back_buffer)) { return false; } if ((pending.committed & WLR_OUTPUT_STATE_BUFFER) && output->idle_frame != NULL) { wl_event_source_remove(output->idle_frame); output->idle_frame = NULL; } struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); struct wlr_output_event_precommit pre_event = { .output = output, .when = &now, .state = &pending, }; wl_signal_emit_mutable(&output->events.precommit, &pre_event); if (!output->impl->commit(output, &pending)) { if (new_back_buffer) { wlr_buffer_unlock(pending.buffer); } return false; } output->commit_seq++; if (output_pending_enabled(output, state)) { output->frame_pending = true; output->needs_frame = false; } output_apply_state(output, &pending); struct wlr_output_event_commit event = { .output = output, .when = &now, .state = &pending, }; wl_signal_emit_mutable(&output->events.commit, &event); if (new_back_buffer) { wlr_buffer_unlock(pending.buffer); } return true; } bool wlr_output_commit(struct wlr_output *output) { // Make sure the pending state is cleared before the output is committed struct wlr_output_state state = {0}; output_state_move(&state, &output->pending); // output_clear_back_buffer detaches the buffer from the renderer. This is // important to do before calling impl->commit(), because this marks an // implicit rendering synchronization point. The backend needs it to avoid // displaying a buffer when asynchronous GPU work isn't finished. if (output->back_buffer != NULL) { wlr_output_state_set_buffer(&state, output->back_buffer); output_clear_back_buffer(output); } bool ok = wlr_output_commit_state(output, &state); wlr_output_state_finish(&state); return ok; } void wlr_output_rollback(struct wlr_output *output) { output_clear_back_buffer(output); output_state_clear(&output->pending); } void wlr_output_attach_buffer(struct wlr_output *output, struct wlr_buffer *buffer) { wlr_output_state_set_buffer(&output->pending, buffer); } void wlr_output_send_frame(struct wlr_output *output) { output->frame_pending = false; if (output->enabled) { wl_signal_emit_mutable(&output->events.frame, output); } } static void schedule_frame_handle_idle_timer(void *data) { struct wlr_output *output = data; output->idle_frame = NULL; if (!output->frame_pending) { wlr_output_send_frame(output); } } void wlr_output_schedule_frame(struct wlr_output *output) { // Make sure the compositor commits a new frame. This is necessary to make // clients which ask for frame callbacks without submitting a new buffer // work. wlr_output_update_needs_frame(output); if (output->frame_pending || output->idle_frame != NULL) { return; } // We're using an idle timer here in case a buffer swap happens right after // this function is called struct wl_event_loop *ev = wl_display_get_event_loop(output->display); output->idle_frame = wl_event_loop_add_idle(ev, schedule_frame_handle_idle_timer, output); } void wlr_output_send_present(struct wlr_output *output, struct wlr_output_event_present *event) { assert(event); event->output = output; struct timespec now; if (event->presented && event->when == NULL) { if (clock_gettime(CLOCK_MONOTONIC, &now) != 0) { wlr_log_errno(WLR_ERROR, "failed to send output present event: " "failed to read clock"); return; } event->when = &now; } wl_signal_emit_mutable(&output->events.present, event); } struct deferred_present_event { struct wlr_output *output; struct wl_event_source *idle_source; struct wlr_output_event_present event; struct wl_listener output_destroy; }; static void deferred_present_event_destroy(struct deferred_present_event *deferred) { wl_list_remove(&deferred->output_destroy.link); free(deferred); } static void deferred_present_event_handle_idle(void *data) { struct deferred_present_event *deferred = data; wlr_output_send_present(deferred->output, &deferred->event); deferred_present_event_destroy(deferred); } static void deferred_present_event_handle_output_destroy(struct wl_listener *listener, void *data) { struct deferred_present_event *deferred = wl_container_of(listener, deferred, output_destroy); wl_event_source_remove(deferred->idle_source); deferred_present_event_destroy(deferred); } void output_defer_present(struct wlr_output *output, struct wlr_output_event_present event) { struct deferred_present_event *deferred = calloc(1, sizeof(*deferred)); if (!deferred) { return; } *deferred = (struct deferred_present_event){ .output = output, .event = event, }; deferred->output_destroy.notify = deferred_present_event_handle_output_destroy; wl_signal_add(&output->events.destroy, &deferred->output_destroy); struct wl_event_loop *ev = wl_display_get_event_loop(output->display); deferred->idle_source = wl_event_loop_add_idle(ev, deferred_present_event_handle_idle, deferred); } void wlr_output_send_request_state(struct wlr_output *output, const struct wlr_output_state *state) { uint32_t unchanged = output_compare_state(output, state); struct wlr_output_state copy = *state; copy.committed &= ~unchanged; if (copy.committed == 0) { return; } struct wlr_output_event_request_state event = { .output = output, .state = ©, }; wl_signal_emit_mutable(&output->events.request_state, &event); } void wlr_output_set_gamma(struct wlr_output *output, size_t size, const uint16_t *r, const uint16_t *g, const uint16_t *b) { wlr_output_state_set_gamma_lut(&output->pending, size, r, g, b); } size_t wlr_output_get_gamma_size(struct wlr_output *output) { if (!output->impl->get_gamma_size) { return 0; } return output->impl->get_gamma_size(output); } void wlr_output_update_needs_frame(struct wlr_output *output) { if (output->needs_frame) { return; } output->needs_frame = true; wl_signal_emit_mutable(&output->events.needs_frame, output); } const struct wlr_drm_format_set *wlr_output_get_primary_formats( struct wlr_output *output, uint32_t buffer_caps) { if (!output->impl->get_primary_formats) { return NULL; } const struct wlr_drm_format_set *formats = output->impl->get_primary_formats(output, buffer_caps); if (formats == NULL) { wlr_log(WLR_ERROR, "Failed to get primary display formats"); static const struct wlr_drm_format_set empty_format_set = {0}; return &empty_format_set; } return formats; } bool wlr_output_is_direct_scanout_allowed(struct wlr_output *output) { if (output->attach_render_locks > 0) { wlr_log(WLR_DEBUG, "Direct scan-out disabled by lock"); return false; } // If the output has at least one software cursor, reject direct scan-out struct wlr_output_cursor *cursor; wl_list_for_each(cursor, &output->cursors, link) { if (cursor->enabled && cursor->visible && cursor != output->hardware_cursor) { wlr_log(WLR_DEBUG, "Direct scan-out disabled by software cursor"); return false; } } return true; } wlroots-0.17.1/types/output/render.c000066400000000000000000000176001454110342200174400ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include "backend/backend.h" #include "render/allocator/allocator.h" #include "render/drm_format_set.h" #include "render/wlr_renderer.h" #include "render/pixel_format.h" #include "types/wlr_output.h" bool wlr_output_init_render(struct wlr_output *output, struct wlr_allocator *allocator, struct wlr_renderer *renderer) { assert(allocator != NULL && renderer != NULL); assert(output->back_buffer == NULL); uint32_t backend_caps = backend_get_buffer_caps(output->backend); uint32_t renderer_caps = renderer_get_render_buffer_caps(renderer); if (!(backend_caps & allocator->buffer_caps)) { wlr_log(WLR_ERROR, "output backend and allocator buffer capabilities " "don't match"); return false; } else if (!(renderer_caps & allocator->buffer_caps)) { wlr_log(WLR_ERROR, "renderer and allocator buffer capabilities " "don't match"); return false; } wlr_swapchain_destroy(output->swapchain); output->swapchain = NULL; wlr_swapchain_destroy(output->cursor_swapchain); output->cursor_swapchain = NULL; output->allocator = allocator; output->renderer = renderer; return true; } void output_clear_back_buffer(struct wlr_output *output) { if (output->back_buffer == NULL) { return; } struct wlr_renderer *renderer = output->renderer; assert(renderer != NULL); renderer_bind_buffer(renderer, NULL); wlr_buffer_unlock(output->back_buffer); output->back_buffer = NULL; } bool wlr_output_attach_render(struct wlr_output *output, int *buffer_age) { assert(output->back_buffer == NULL); if (!wlr_output_configure_primary_swapchain(output, &output->pending, &output->swapchain)) { return false; } struct wlr_renderer *renderer = output->renderer; assert(renderer != NULL); struct wlr_buffer *buffer = wlr_swapchain_acquire(output->swapchain, buffer_age); if (buffer == NULL) { return false; } if (!renderer_bind_buffer(renderer, buffer)) { wlr_buffer_unlock(buffer); return false; } output->back_buffer = buffer; return true; } static struct wlr_buffer *output_acquire_empty_buffer(struct wlr_output *output, const struct wlr_output_state *state) { assert(!(state->committed & WLR_OUTPUT_STATE_BUFFER)); // wlr_output_configure_primary_swapchain() function will call // wlr_output_test_state(), which can call us again. This is dangerous: we // risk infinite recursion. However, a buffer will always be supplied in // wlr_output_test_state(), which will prevent us from being called. if (!wlr_output_configure_primary_swapchain(output, state, &output->swapchain)) { return false; } struct wlr_buffer *buffer = wlr_swapchain_acquire(output->swapchain, NULL); if (buffer == NULL) { return false; } struct wlr_render_pass *pass = wlr_renderer_begin_buffer_pass(output->renderer, buffer, NULL); if (pass == NULL) { wlr_buffer_unlock(buffer); return NULL; } wlr_render_pass_add_rect(pass, &(struct wlr_render_rect_options){ .color = { 0, 0, 0, 0 }, .blend_mode = WLR_RENDER_BLEND_MODE_NONE, }); if (!wlr_render_pass_submit(pass)) { wlr_buffer_unlock(buffer); return NULL; } return buffer; } // This function may attach a new, empty buffer if necessary. // If so, the new_back_buffer out parameter will be set to true. bool output_ensure_buffer(struct wlr_output *output, struct wlr_output_state *state, bool *new_buffer) { assert(*new_buffer == false); // If we already have a buffer, we don't need to allocate a new one if (state->committed & WLR_OUTPUT_STATE_BUFFER) { return true; } // If the compositor hasn't called wlr_output_init_render(), they will use // their own logic to attach buffers if (output->renderer == NULL) { return true; } bool enabled = output->enabled; if (state->committed & WLR_OUTPUT_STATE_ENABLED) { enabled = state->enabled; } // If we're lighting up an output or changing its mode, make sure to // provide a new buffer bool needs_new_buffer = false; if ((state->committed & WLR_OUTPUT_STATE_ENABLED) && state->enabled) { needs_new_buffer = true; } if (state->committed & WLR_OUTPUT_STATE_MODE) { needs_new_buffer = true; } if (state->committed & WLR_OUTPUT_STATE_RENDER_FORMAT) { needs_new_buffer = true; } if (state->allow_reconfiguration && output->commit_seq == 0 && enabled) { // On first commit, require a new buffer if the compositor called a // mode-setting function, even if the mode won't change. This makes it // so the swapchain is created now. needs_new_buffer = true; } if (!needs_new_buffer) { return true; } wlr_log(WLR_DEBUG, "Attaching empty buffer to output for modeset"); struct wlr_buffer *buffer = output_acquire_empty_buffer(output, state); if (buffer == NULL) { return false; } *new_buffer = true; wlr_output_state_set_buffer(state, buffer); wlr_buffer_unlock(buffer); return true; } void wlr_output_lock_attach_render(struct wlr_output *output, bool lock) { if (lock) { ++output->attach_render_locks; } else { assert(output->attach_render_locks > 0); --output->attach_render_locks; } wlr_log(WLR_DEBUG, "%s direct scan-out on output '%s' (locks: %d)", lock ? "Disabling" : "Enabling", output->name, output->attach_render_locks); } bool output_pick_format(struct wlr_output *output, const struct wlr_drm_format_set *display_formats, struct wlr_drm_format *format, uint32_t fmt) { struct wlr_renderer *renderer = output->renderer; struct wlr_allocator *allocator = output->allocator; assert(renderer != NULL && allocator != NULL); const struct wlr_drm_format_set *render_formats = wlr_renderer_get_render_formats(renderer); if (render_formats == NULL) { wlr_log(WLR_ERROR, "Failed to get render formats"); return false; } const struct wlr_drm_format *render_format = wlr_drm_format_set_get(render_formats, fmt); if (render_format == NULL) { wlr_log(WLR_DEBUG, "Renderer doesn't support format 0x%"PRIX32, fmt); return false; } if (display_formats != NULL) { const struct wlr_drm_format *display_format = wlr_drm_format_set_get(display_formats, fmt); if (display_format == NULL) { wlr_log(WLR_DEBUG, "Output doesn't support format 0x%"PRIX32, fmt); return false; } if (!wlr_drm_format_intersect(format, display_format, render_format)) { wlr_log(WLR_DEBUG, "Failed to intersect display and render " "modifiers for format 0x%"PRIX32 " on output %s", fmt, output->name); return false; } } else { // The output can display any format if (!wlr_drm_format_copy(format, render_format)) { return false; } } if (format->len == 0) { wlr_drm_format_finish(format); wlr_log(WLR_DEBUG, "Failed to pick output format"); return false; } return true; } uint32_t wlr_output_preferred_read_format(struct wlr_output *output) { struct wlr_renderer *renderer = output->renderer; assert(renderer != NULL); if (!renderer->impl->preferred_read_format || !renderer->impl->read_pixels) { return DRM_FORMAT_INVALID; } if (!wlr_output_attach_render(output, NULL)) { return false; } uint32_t fmt = renderer->impl->preferred_read_format(renderer); output_clear_back_buffer(output); return fmt; } struct wlr_render_pass *wlr_output_begin_render_pass(struct wlr_output *output, struct wlr_output_state *state, int *buffer_age, struct wlr_render_timer *timer) { if (!wlr_output_configure_primary_swapchain(output, state, &output->swapchain)) { return NULL; } struct wlr_buffer *buffer = wlr_swapchain_acquire(output->swapchain, buffer_age); if (buffer == NULL) { return NULL; } struct wlr_renderer *renderer = output->renderer; assert(renderer != NULL); struct wlr_render_pass *pass = wlr_renderer_begin_buffer_pass(renderer, buffer, &(struct wlr_buffer_pass_options){ .timer = timer, }); if (pass == NULL) { return NULL; } wlr_output_state_set_buffer(state, buffer); wlr_buffer_unlock(buffer); return pass; } wlroots-0.17.1/types/output/state.c000066400000000000000000000111671454110342200173030ustar00rootroot00000000000000#include #include #include #include "types/wlr_output.h" void wlr_output_state_init(struct wlr_output_state *state) { *state = (struct wlr_output_state){0}; pixman_region32_init(&state->damage); } void wlr_output_state_finish(struct wlr_output_state *state) { wlr_buffer_unlock(state->buffer); // struct wlr_buffer is ref'counted, so the pointer may remain valid after // wlr_buffer_unlock(). Reset the field to NULL to ensure nobody mistakenly // reads it after output_state_finish(). state->buffer = NULL; pixman_region32_fini(&state->damage); free(state->gamma_lut); } void wlr_output_state_set_enabled(struct wlr_output_state *state, bool enabled) { state->committed |= WLR_OUTPUT_STATE_ENABLED; state->enabled = enabled; state->allow_reconfiguration = true; } void wlr_output_state_set_mode(struct wlr_output_state *state, struct wlr_output_mode *mode) { state->committed |= WLR_OUTPUT_STATE_MODE; state->mode_type = WLR_OUTPUT_STATE_MODE_FIXED; state->mode = mode; state->allow_reconfiguration = true; } void wlr_output_state_set_custom_mode(struct wlr_output_state *state, int32_t width, int32_t height, int32_t refresh) { state->committed |= WLR_OUTPUT_STATE_MODE; state->mode_type = WLR_OUTPUT_STATE_MODE_CUSTOM; state->custom_mode.width = width; state->custom_mode.height = height; state->custom_mode.refresh = refresh; state->allow_reconfiguration = true; } void wlr_output_state_set_scale(struct wlr_output_state *state, float scale) { state->committed |= WLR_OUTPUT_STATE_SCALE; state->scale = scale; } void wlr_output_state_set_transform(struct wlr_output_state *state, enum wl_output_transform transform) { state->committed |= WLR_OUTPUT_STATE_TRANSFORM; state->transform = transform; } void wlr_output_state_set_adaptive_sync_enabled(struct wlr_output_state *state, bool enabled) { state->committed |= WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED; state->adaptive_sync_enabled = enabled; } void wlr_output_state_set_render_format(struct wlr_output_state *state, uint32_t format) { state->committed |= WLR_OUTPUT_STATE_RENDER_FORMAT; state->render_format = format; } void wlr_output_state_set_subpixel(struct wlr_output_state *state, enum wl_output_subpixel subpixel) { state->committed |= WLR_OUTPUT_STATE_SUBPIXEL; state->subpixel = subpixel; } void wlr_output_state_set_buffer(struct wlr_output_state *state, struct wlr_buffer *buffer) { state->committed |= WLR_OUTPUT_STATE_BUFFER; wlr_buffer_unlock(state->buffer); state->buffer = wlr_buffer_lock(buffer); } void wlr_output_state_set_damage(struct wlr_output_state *state, const pixman_region32_t *damage) { state->committed |= WLR_OUTPUT_STATE_DAMAGE; pixman_region32_copy(&state->damage, damage); } bool wlr_output_state_set_gamma_lut(struct wlr_output_state *state, size_t ramp_size, const uint16_t *r, const uint16_t *g, const uint16_t *b) { uint16_t *gamma_lut = NULL; if (ramp_size > 0) { gamma_lut = realloc(state->gamma_lut, 3 * ramp_size * sizeof(uint16_t)); if (gamma_lut == NULL) { wlr_log_errno(WLR_ERROR, "Allocation failed"); return false; } memcpy(gamma_lut, r, ramp_size * sizeof(uint16_t)); memcpy(gamma_lut + ramp_size, g, ramp_size * sizeof(uint16_t)); memcpy(gamma_lut + 2 * ramp_size, b, ramp_size * sizeof(uint16_t)); } else { free(state->gamma_lut); } state->committed |= WLR_OUTPUT_STATE_GAMMA_LUT; state->gamma_lut_size = ramp_size; state->gamma_lut = gamma_lut; return true; } void wlr_output_state_set_layers(struct wlr_output_state *state, struct wlr_output_layer_state *layers, size_t layers_len) { state->committed |= WLR_OUTPUT_STATE_LAYERS; state->layers = layers; state->layers_len = layers_len; } bool wlr_output_state_copy(struct wlr_output_state *dst, const struct wlr_output_state *src) { struct wlr_output_state copy = *src; copy.committed &= ~(WLR_OUTPUT_STATE_BUFFER | WLR_OUTPUT_STATE_DAMAGE | WLR_OUTPUT_STATE_GAMMA_LUT); copy.buffer = NULL; pixman_region32_init(©.damage); copy.gamma_lut = NULL; copy.gamma_lut_size = 0; if (src->committed & WLR_OUTPUT_STATE_BUFFER) { wlr_output_state_set_buffer(©, src->buffer); } if (src->committed & WLR_OUTPUT_STATE_DAMAGE) { wlr_output_state_set_damage(©, &src->damage); } if (src->committed & WLR_OUTPUT_STATE_GAMMA_LUT) { const uint16_t *r = src->gamma_lut; const uint16_t *g = src->gamma_lut + src->gamma_lut_size; const uint16_t *b = src->gamma_lut + 2 * src->gamma_lut_size; if (!wlr_output_state_set_gamma_lut(©, src->gamma_lut_size, r, g, b)) { goto err; } } wlr_output_state_finish(dst); *dst = copy; return true; err: wlr_output_state_finish(©); return false; } wlroots-0.17.1/types/output/swapchain.c000066400000000000000000000075541454110342200201450ustar00rootroot00000000000000#include #include #include #include #include #include #include #include "render/drm_format_set.h" #include "types/wlr_output.h" static struct wlr_swapchain *create_swapchain(struct wlr_output *output, int width, int height, uint32_t render_format, bool allow_modifiers) { struct wlr_allocator *allocator = output->allocator; assert(output->allocator != NULL); const struct wlr_drm_format_set *display_formats = wlr_output_get_primary_formats(output, allocator->buffer_caps); struct wlr_drm_format format = {0}; if (!output_pick_format(output, display_formats, &format, render_format)) { wlr_log(WLR_ERROR, "Failed to pick primary buffer format for output '%s'", output->name); return NULL; } char *format_name = drmGetFormatName(format.format); wlr_log(WLR_DEBUG, "Choosing primary buffer format %s (0x%08"PRIX32") for output '%s'", format_name ? format_name : "", format.format, output->name); free(format_name); if (!allow_modifiers && (format.len != 1 || format.modifiers[0] != DRM_FORMAT_MOD_LINEAR)) { if (!wlr_drm_format_has(&format, DRM_FORMAT_MOD_INVALID)) { wlr_log(WLR_DEBUG, "Implicit modifiers not supported"); wlr_drm_format_finish(&format); return NULL; } format.len = 0; if (!wlr_drm_format_add(&format, DRM_FORMAT_MOD_INVALID)) { wlr_log(WLR_DEBUG, "Failed to add implicit modifier to format"); wlr_drm_format_finish(&format); return NULL; } } struct wlr_swapchain *swapchain = wlr_swapchain_create(allocator, width, height, &format); wlr_drm_format_finish(&format); return swapchain; } static bool test_swapchain(struct wlr_output *output, struct wlr_swapchain *swapchain, const struct wlr_output_state *state) { struct wlr_buffer *buffer = wlr_swapchain_acquire(swapchain, NULL); if (buffer == NULL) { return false; } struct wlr_output_state copy = *state; copy.committed |= WLR_OUTPUT_STATE_BUFFER; copy.buffer = buffer; bool ok = wlr_output_test_state(output, ©); wlr_buffer_unlock(buffer); return ok; } bool wlr_output_configure_primary_swapchain(struct wlr_output *output, const struct wlr_output_state *state, struct wlr_swapchain **swapchain_ptr) { struct wlr_output_state empty_state; if (state == NULL) { wlr_output_state_init(&empty_state); state = &empty_state; } int width, height; output_pending_resolution(output, state, &width, &height); uint32_t format = output->render_format; if (state->committed & WLR_OUTPUT_STATE_RENDER_FORMAT) { format = state->render_format; } // Re-use the existing swapchain if possible struct wlr_swapchain *old_swapchain = *swapchain_ptr; if (old_swapchain != NULL && old_swapchain->width == width && old_swapchain->height == height && old_swapchain->format.format == format) { return true; } struct wlr_swapchain *swapchain = create_swapchain(output, width, height, format, true); if (swapchain == NULL) { wlr_log(WLR_ERROR, "Failed to create swapchain for output '%s'", output->name); return false; } wlr_log(WLR_DEBUG, "Testing swapchain for output '%s'", output->name); if (!test_swapchain(output, swapchain, state)) { wlr_log(WLR_DEBUG, "Output test failed on '%s', retrying without modifiers", output->name); wlr_swapchain_destroy(swapchain); swapchain = create_swapchain(output, width, height, format, false); if (swapchain == NULL) { wlr_log(WLR_ERROR, "Failed to create modifier-less swapchain for output '%s'", output->name); return false; } wlr_log(WLR_DEBUG, "Testing modifier-less swapchain for output '%s'", output->name); if (!test_swapchain(output, swapchain, state)) { wlr_log(WLR_ERROR, "Swapchain for output '%s' failed test", output->name); wlr_swapchain_destroy(swapchain); return false; } } wlr_swapchain_destroy(*swapchain_ptr); *swapchain_ptr = swapchain; return true; } wlroots-0.17.1/types/output/transform.c000066400000000000000000000015121454110342200201670ustar00rootroot00000000000000#include enum wl_output_transform wlr_output_transform_invert( enum wl_output_transform tr) { if ((tr & WL_OUTPUT_TRANSFORM_90) && !(tr & WL_OUTPUT_TRANSFORM_FLIPPED)) { tr ^= WL_OUTPUT_TRANSFORM_180; } return tr; } enum wl_output_transform wlr_output_transform_compose( enum wl_output_transform tr_a, enum wl_output_transform tr_b) { uint32_t flipped = (tr_a ^ tr_b) & WL_OUTPUT_TRANSFORM_FLIPPED; uint32_t rotation_mask = WL_OUTPUT_TRANSFORM_90 | WL_OUTPUT_TRANSFORM_180; uint32_t rotated; if (tr_b & WL_OUTPUT_TRANSFORM_FLIPPED) { // When a rotation of k degrees is followed by a flip, the // equivalent transform is a flip followed by a rotation of // -k degrees. rotated = (tr_b - tr_a) & rotation_mask; } else { rotated = (tr_a + tr_b) & rotation_mask; } return flipped | rotated; } wlroots-0.17.1/types/scene/000077500000000000000000000000001454110342200155465ustar00rootroot00000000000000wlroots-0.17.1/types/scene/drag_icon.c000066400000000000000000000064551454110342200176510ustar00rootroot00000000000000#include #include "wlr/types/wlr_compositor.h" #include #include struct wlr_scene_drag_icon { struct wlr_scene_tree *tree; struct wlr_scene_tree *surface_tree; struct wlr_drag_icon *drag_icon; struct wl_listener tree_destroy; struct wl_listener drag_icon_surface_commit; struct wl_listener drag_icon_surface_map; struct wl_listener drag_icon_surface_unmap; struct wl_listener drag_icon_destroy; }; static void drag_icon_handle_surface_commit(struct wl_listener *listener, void *data) { struct wlr_scene_drag_icon *icon = wl_container_of(listener, icon, drag_icon_surface_commit); struct wlr_surface *surface = icon->drag_icon->surface; struct wlr_scene_node *node = &icon->surface_tree->node; wlr_scene_node_set_position(node, node->x + surface->current.dx, node->y + surface->current.dy); } static void drag_icon_handle_surface_map(struct wl_listener *listener, void *data) { struct wlr_scene_drag_icon *icon = wl_container_of(listener, icon, drag_icon_surface_map); wlr_scene_node_set_enabled(&icon->tree->node, true); } static void drag_icon_handle_surface_unmap(struct wl_listener *listener, void *data) { struct wlr_scene_drag_icon *icon = wl_container_of(listener, icon, drag_icon_surface_unmap); wlr_scene_node_set_enabled(&icon->tree->node, false); } static void drag_icon_handle_tree_destroy(struct wl_listener *listener, void *data) { struct wlr_scene_drag_icon *icon = wl_container_of(listener, icon, tree_destroy); wl_list_remove(&icon->tree_destroy.link); wl_list_remove(&icon->drag_icon_surface_commit.link); wl_list_remove(&icon->drag_icon_surface_map.link); wl_list_remove(&icon->drag_icon_surface_unmap.link); wl_list_remove(&icon->drag_icon_destroy.link); free(icon); } static void drag_icon_handle_destroy(struct wl_listener *listener, void *data) { struct wlr_scene_drag_icon *icon = wl_container_of(listener, icon, drag_icon_destroy); wlr_scene_node_destroy(&icon->tree->node); } struct wlr_scene_tree *wlr_scene_drag_icon_create( struct wlr_scene_tree *parent, struct wlr_drag_icon *drag_icon) { struct wlr_scene_drag_icon *icon = calloc(1, sizeof(*icon)); if (!icon) { return NULL; } icon->drag_icon = drag_icon; icon->tree = wlr_scene_tree_create(parent); if (!icon->tree) { free(icon); return NULL; } icon->surface_tree = wlr_scene_subsurface_tree_create( icon->tree, drag_icon->surface); if (!icon->surface_tree) { wlr_scene_node_destroy(&icon->tree->node); free(icon); return NULL; } wlr_scene_node_set_enabled(&icon->tree->node, drag_icon->surface->mapped); icon->tree_destroy.notify = drag_icon_handle_tree_destroy; wl_signal_add(&icon->tree->node.events.destroy, &icon->tree_destroy); icon->drag_icon_surface_commit.notify = drag_icon_handle_surface_commit; wl_signal_add(&drag_icon->surface->events.commit, &icon->drag_icon_surface_commit); icon->drag_icon_surface_map.notify = drag_icon_handle_surface_map; wl_signal_add(&drag_icon->surface->events.map, &icon->drag_icon_surface_map); icon->drag_icon_surface_unmap.notify = drag_icon_handle_surface_unmap; wl_signal_add(&drag_icon->surface->events.unmap, &icon->drag_icon_surface_unmap); icon->drag_icon_destroy.notify = drag_icon_handle_destroy; wl_signal_add(&drag_icon->events.destroy, &icon->drag_icon_destroy); return icon->tree; } wlroots-0.17.1/types/scene/layer_shell_v1.c000066400000000000000000000153161454110342200206310ustar00rootroot00000000000000#include #include #include static void scene_layer_surface_handle_tree_destroy( struct wl_listener *listener, void *data) { struct wlr_scene_layer_surface_v1 *scene_layer_surface = wl_container_of(listener, scene_layer_surface, tree_destroy); // tree and surface_node will be cleaned up by scene_node_finish wl_list_remove(&scene_layer_surface->tree_destroy.link); wl_list_remove(&scene_layer_surface->layer_surface_destroy.link); wl_list_remove(&scene_layer_surface->layer_surface_map.link); wl_list_remove(&scene_layer_surface->layer_surface_unmap.link); free(scene_layer_surface); } static void scene_layer_surface_handle_layer_surface_destroy( struct wl_listener *listener, void *data) { struct wlr_scene_layer_surface_v1 *scene_layer_surface = wl_container_of(listener, scene_layer_surface, layer_surface_destroy); wlr_scene_node_destroy(&scene_layer_surface->tree->node); } static void scene_layer_surface_handle_layer_surface_map( struct wl_listener *listener, void *data) { struct wlr_scene_layer_surface_v1 *scene_layer_surface = wl_container_of(listener, scene_layer_surface, layer_surface_map); wlr_scene_node_set_enabled(&scene_layer_surface->tree->node, true); } static void scene_layer_surface_handle_layer_surface_unmap( struct wl_listener *listener, void *data) { struct wlr_scene_layer_surface_v1 *scene_layer_surface = wl_container_of(listener, scene_layer_surface, layer_surface_unmap); wlr_scene_node_set_enabled(&scene_layer_surface->tree->node, false); } static void layer_surface_exclusive_zone( struct wlr_layer_surface_v1_state *state, struct wlr_box *usable_area) { switch (state->anchor) { case ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP: case (ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT): // Anchor top usable_area->y += state->exclusive_zone + state->margin.top; usable_area->height -= state->exclusive_zone + state->margin.top; break; case ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM: case (ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT): // Anchor bottom usable_area->height -= state->exclusive_zone + state->margin.bottom; break; case ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT: case (ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT): // Anchor left usable_area->x += state->exclusive_zone + state->margin.left; usable_area->width -= state->exclusive_zone + state->margin.left; break; case ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT: case (ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT): // Anchor right usable_area->width -= state->exclusive_zone + state->margin.right; break; } } void wlr_scene_layer_surface_v1_configure( struct wlr_scene_layer_surface_v1 *scene_layer_surface, const struct wlr_box *full_area, struct wlr_box *usable_area) { struct wlr_layer_surface_v1 *layer_surface = scene_layer_surface->layer_surface; struct wlr_layer_surface_v1_state *state = &layer_surface->current; // If the exclusive zone is set to -1, the layer surface will use the // full area of the output, otherwise it is constrained to the // remaining usable area. struct wlr_box bounds; if (state->exclusive_zone == -1) { bounds = *full_area; } else { bounds = *usable_area; } struct wlr_box box = { .width = state->desired_width, .height = state->desired_height, }; // Horizontal positioning if (box.width == 0) { box.x = bounds.x + state->margin.left; box.width = bounds.width - (state->margin.left + state->margin.right); } else if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT && state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT) { box.x = bounds.x + bounds.width/2 -box.width/2; } else if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT) { box.x = bounds.x + state->margin.left; } else if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT) { box.x = bounds.x + bounds.width - box.width - state->margin.right; } else { box.x = bounds.x + bounds.width/2 - box.width/2; } // Vertical positioning if (box.height == 0) { box.y = bounds.y + state->margin.top; box.height = bounds.height - (state->margin.top + state->margin.bottom); } else if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP && state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM) { box.y = bounds.y + bounds.height/2 - box.height/2; } else if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP) { box.y = bounds.y + state->margin.top; } else if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM) { box.y = bounds.y + bounds.height - box.height - state->margin.bottom; } else { box.y = bounds.y + bounds.height/2 - box.height/2; } wlr_scene_node_set_position(&scene_layer_surface->tree->node, box.x, box.y); wlr_layer_surface_v1_configure(layer_surface, box.width, box.height); if (layer_surface->surface->mapped && state->exclusive_zone > 0) { layer_surface_exclusive_zone(state, usable_area); } } struct wlr_scene_layer_surface_v1 *wlr_scene_layer_surface_v1_create( struct wlr_scene_tree *parent, struct wlr_layer_surface_v1 *layer_surface) { struct wlr_scene_layer_surface_v1 *scene_layer_surface = calloc(1, sizeof(*scene_layer_surface)); if (scene_layer_surface == NULL) { return NULL; } scene_layer_surface->layer_surface = layer_surface; scene_layer_surface->tree = wlr_scene_tree_create(parent); if (scene_layer_surface->tree == NULL) { free(scene_layer_surface); return NULL; } struct wlr_scene_tree *surface_tree = wlr_scene_subsurface_tree_create( scene_layer_surface->tree, layer_surface->surface); if (surface_tree == NULL) { wlr_scene_node_destroy(&scene_layer_surface->tree->node); free(scene_layer_surface); return NULL; } scene_layer_surface->tree_destroy.notify = scene_layer_surface_handle_tree_destroy; wl_signal_add(&scene_layer_surface->tree->node.events.destroy, &scene_layer_surface->tree_destroy); scene_layer_surface->layer_surface_destroy.notify = scene_layer_surface_handle_layer_surface_destroy; wl_signal_add(&layer_surface->events.destroy, &scene_layer_surface->layer_surface_destroy); scene_layer_surface->layer_surface_map.notify = scene_layer_surface_handle_layer_surface_map; wl_signal_add(&layer_surface->surface->events.map, &scene_layer_surface->layer_surface_map); scene_layer_surface->layer_surface_unmap.notify = scene_layer_surface_handle_layer_surface_unmap; wl_signal_add(&layer_surface->surface->events.unmap, &scene_layer_surface->layer_surface_unmap); wlr_scene_node_set_enabled(&scene_layer_surface->tree->node, layer_surface->surface->mapped); return scene_layer_surface; } wlroots-0.17.1/types/scene/output_layout.c000066400000000000000000000103441454110342200206510ustar00rootroot00000000000000#include #include #include #include struct wlr_scene_output_layout { struct wlr_output_layout *layout; struct wlr_scene *scene; struct wl_list outputs; // wlr_scene_output_layout_output.link struct wl_listener layout_change; struct wl_listener layout_destroy; struct wl_listener scene_destroy; }; struct wlr_scene_output_layout_output { struct wlr_output_layout_output *layout_output; struct wlr_scene_output *scene_output; struct wl_list link; // wlr_scene_output_layout.outputs struct wl_listener layout_output_destroy; struct wl_listener scene_output_destroy; }; static void scene_output_layout_output_destroy( struct wlr_scene_output_layout_output *solo) { wl_list_remove(&solo->layout_output_destroy.link); wl_list_remove(&solo->scene_output_destroy.link); wl_list_remove(&solo->link); free(solo); } static void scene_output_layout_output_handle_layout_output_destroy( struct wl_listener *listener, void *data) { struct wlr_scene_output_layout_output *solo = wl_container_of(listener, solo, layout_output_destroy); scene_output_layout_output_destroy(solo); } static void scene_output_layout_output_handle_scene_output_destroy( struct wl_listener *listener, void *data) { struct wlr_scene_output_layout_output *solo = wl_container_of(listener, solo, scene_output_destroy); scene_output_layout_output_destroy(solo); } static void scene_output_layout_destroy(struct wlr_scene_output_layout *sol) { struct wlr_scene_output_layout_output *solo, *tmp; wl_list_for_each_safe(solo, tmp, &sol->outputs, link) { scene_output_layout_output_destroy(solo); } wl_list_remove(&sol->layout_change.link); wl_list_remove(&sol->layout_destroy.link); wl_list_remove(&sol->scene_destroy.link); free(sol); } static void scene_output_layout_handle_layout_change( struct wl_listener *listener, void *data) { struct wlr_scene_output_layout *sol = wl_container_of(listener, sol, layout_change); struct wlr_scene_output_layout_output *solo; wl_list_for_each(solo, &sol->outputs, link) { wlr_scene_output_set_position(solo->scene_output, solo->layout_output->x, solo->layout_output->y); } } void wlr_scene_output_layout_add_output(struct wlr_scene_output_layout *sol, struct wlr_output_layout_output *lo, struct wlr_scene_output *so) { assert(lo->output == so->output); struct wlr_scene_output_layout_output *solo; wl_list_for_each(solo, &sol->outputs, link) { assert(solo->scene_output != so); } solo = calloc(1, sizeof(*solo)); if (solo == NULL) { return; } solo->scene_output = so; solo->layout_output = lo; solo->layout_output_destroy.notify = scene_output_layout_output_handle_layout_output_destroy; wl_signal_add(&lo->events.destroy, &solo->layout_output_destroy); solo->scene_output_destroy.notify = scene_output_layout_output_handle_scene_output_destroy; wl_signal_add(&solo->scene_output->events.destroy, &solo->scene_output_destroy); wl_list_insert(&sol->outputs, &solo->link); wlr_scene_output_set_position(solo->scene_output, lo->x, lo->y); } static void scene_output_layout_handle_layout_destroy( struct wl_listener *listener, void *data) { struct wlr_scene_output_layout *sol = wl_container_of(listener, sol, layout_destroy); scene_output_layout_destroy(sol); } static void scene_output_layout_handle_scene_destroy( struct wl_listener *listener, void *data) { struct wlr_scene_output_layout *sol = wl_container_of(listener, sol, scene_destroy); scene_output_layout_destroy(sol); } struct wlr_scene_output_layout *wlr_scene_attach_output_layout(struct wlr_scene *scene, struct wlr_output_layout *output_layout) { struct wlr_scene_output_layout *sol = calloc(1, sizeof(*sol)); if (sol == NULL) { return NULL; } sol->scene = scene; sol->layout = output_layout; wl_list_init(&sol->outputs); sol->layout_destroy.notify = scene_output_layout_handle_layout_destroy; wl_signal_add(&output_layout->events.destroy, &sol->layout_destroy); sol->layout_change.notify = scene_output_layout_handle_layout_change; wl_signal_add(&output_layout->events.change, &sol->layout_change); sol->scene_destroy.notify = scene_output_layout_handle_scene_destroy; wl_signal_add(&scene->tree.node.events.destroy, &sol->scene_destroy); return sol; } wlroots-0.17.1/types/scene/subsurface_tree.c000066400000000000000000000272431454110342200211030ustar00rootroot00000000000000#include #include #include #include #include #include #include "types/wlr_scene.h" /** * A tree for a surface and all of its child sub-surfaces. * * `tree` contains `scene_surface` and one node per sub-surface. */ struct wlr_scene_subsurface_tree { struct wlr_scene_tree *tree; struct wlr_surface *surface; struct wlr_scene_surface *scene_surface; struct wl_listener surface_destroy; struct wl_listener surface_commit; struct wl_listener surface_map; struct wl_listener surface_unmap; struct wl_listener surface_new_subsurface; struct wlr_scene_subsurface_tree *parent; // NULL for the top-level surface struct wlr_addon scene_addon; struct wlr_box clip; // Only valid if the surface is a sub-surface struct wlr_addon surface_addon; struct wl_listener subsurface_destroy; }; static void subsurface_tree_addon_destroy(struct wlr_addon *addon) { struct wlr_scene_subsurface_tree *subsurface_tree = wl_container_of(addon, subsurface_tree, scene_addon); // tree and scene_surface will be cleaned up by scene_node_finish if (subsurface_tree->parent) { wlr_addon_finish(&subsurface_tree->surface_addon); wl_list_remove(&subsurface_tree->subsurface_destroy.link); } wlr_addon_finish(&subsurface_tree->scene_addon); wl_list_remove(&subsurface_tree->surface_destroy.link); wl_list_remove(&subsurface_tree->surface_commit.link); wl_list_remove(&subsurface_tree->surface_map.link); wl_list_remove(&subsurface_tree->surface_unmap.link); wl_list_remove(&subsurface_tree->surface_new_subsurface.link); free(subsurface_tree); } static const struct wlr_addon_interface subsurface_tree_surface_addon_impl; static struct wlr_scene_subsurface_tree *subsurface_tree_from_subsurface( struct wlr_scene_subsurface_tree *parent, struct wlr_subsurface *subsurface) { struct wlr_addon *addon = wlr_addon_find(&subsurface->surface->addons, parent, &subsurface_tree_surface_addon_impl); assert(addon != NULL); struct wlr_scene_subsurface_tree *subsurface_tree = wl_container_of(addon, subsurface_tree, surface_addon); return subsurface_tree; } static bool subsurface_tree_reconfigure_clip( struct wlr_scene_subsurface_tree *subsurface_tree) { if (subsurface_tree->parent) { subsurface_tree->clip = (struct wlr_box){ .x = subsurface_tree->parent->clip.x - subsurface_tree->tree->node.x, .y = subsurface_tree->parent->clip.y - subsurface_tree->tree->node.y, .width = subsurface_tree->parent->clip.width, .height = subsurface_tree->parent->clip.height, }; } if (wlr_box_empty(&subsurface_tree->clip)) { scene_surface_set_clip(subsurface_tree->scene_surface, NULL); wlr_scene_node_set_enabled(&subsurface_tree->scene_surface->buffer->node, true); wlr_scene_node_set_position(&subsurface_tree->scene_surface->buffer->node, 0, 0); return false; } else { struct wlr_box clip = subsurface_tree->clip; struct wlr_box surface_box = { .width = subsurface_tree->surface->current.width, .height = subsurface_tree->surface->current.height, }; bool intersects = wlr_box_intersection(&clip, &clip, &surface_box); wlr_scene_node_set_enabled(&subsurface_tree->scene_surface->buffer->node, intersects); if (intersects) { wlr_scene_node_set_position(&subsurface_tree->scene_surface->buffer->node, clip.x, clip.y); scene_surface_set_clip(subsurface_tree->scene_surface, &clip); } return true; } } static void subsurface_tree_reconfigure( struct wlr_scene_subsurface_tree *subsurface_tree) { bool has_clip = subsurface_tree_reconfigure_clip(subsurface_tree); struct wlr_surface *surface = subsurface_tree->surface; struct wlr_scene_node *prev = NULL; struct wlr_subsurface *subsurface; wl_list_for_each(subsurface, &surface->current.subsurfaces_below, current.link) { struct wlr_scene_subsurface_tree *child = subsurface_tree_from_subsurface(subsurface_tree, subsurface); if (prev != NULL) { wlr_scene_node_place_above(&child->tree->node, prev); } prev = &child->tree->node; wlr_scene_node_set_position(&child->tree->node, subsurface->current.x, subsurface->current.y); if (has_clip) { subsurface_tree_reconfigure_clip(child); } } if (prev != NULL) { wlr_scene_node_place_above(&subsurface_tree->scene_surface->buffer->node, prev); } prev = &subsurface_tree->scene_surface->buffer->node; wl_list_for_each(subsurface, &surface->current.subsurfaces_above, current.link) { struct wlr_scene_subsurface_tree *child = subsurface_tree_from_subsurface(subsurface_tree, subsurface); wlr_scene_node_place_above(&child->tree->node, prev); prev = &child->tree->node; wlr_scene_node_set_position(&child->tree->node, subsurface->current.x, subsurface->current.y); if (has_clip) { subsurface_tree_reconfigure_clip(child); } } } static void subsurface_tree_handle_surface_destroy(struct wl_listener *listener, void *data) { struct wlr_scene_subsurface_tree *subsurface_tree = wl_container_of(listener, subsurface_tree, surface_destroy); wlr_scene_node_destroy(&subsurface_tree->tree->node); } static void subsurface_tree_handle_surface_commit(struct wl_listener *listener, void *data) { struct wlr_scene_subsurface_tree *subsurface_tree = wl_container_of(listener, subsurface_tree, surface_commit); // TODO: only do this on subsurface order or position change subsurface_tree_reconfigure(subsurface_tree); } static void subsurface_tree_handle_subsurface_destroy(struct wl_listener *listener, void *data) { struct wlr_scene_subsurface_tree *subsurface_tree = wl_container_of(listener, subsurface_tree, subsurface_destroy); wlr_scene_node_destroy(&subsurface_tree->tree->node); } static void subsurface_tree_handle_surface_map(struct wl_listener *listener, void *data) { struct wlr_scene_subsurface_tree *subsurface_tree = wl_container_of(listener, subsurface_tree, surface_map); wlr_scene_node_set_enabled(&subsurface_tree->tree->node, true); } static void subsurface_tree_handle_surface_unmap(struct wl_listener *listener, void *data) { struct wlr_scene_subsurface_tree *subsurface_tree = wl_container_of(listener, subsurface_tree, surface_unmap); wlr_scene_node_set_enabled(&subsurface_tree->tree->node, false); } static void subsurface_tree_surface_addon_destroy(struct wlr_addon *addon) { struct wlr_scene_subsurface_tree *subsurface_tree = wl_container_of(addon, subsurface_tree, surface_addon); wlr_scene_node_destroy(&subsurface_tree->tree->node); } static const struct wlr_addon_interface subsurface_tree_surface_addon_impl = { .name = "wlr_scene_subsurface_tree", .destroy = subsurface_tree_surface_addon_destroy, }; static struct wlr_scene_subsurface_tree *scene_surface_tree_create( struct wlr_scene_tree *parent, struct wlr_surface *surface); static bool subsurface_tree_create_subsurface( struct wlr_scene_subsurface_tree *parent, struct wlr_subsurface *subsurface) { struct wlr_scene_subsurface_tree *child = scene_surface_tree_create( parent->tree, subsurface->surface); if (child == NULL) { return false; } child->parent = parent; wlr_addon_init(&child->surface_addon, &subsurface->surface->addons, parent, &subsurface_tree_surface_addon_impl); child->subsurface_destroy.notify = subsurface_tree_handle_subsurface_destroy; wl_signal_add(&subsurface->events.destroy, &child->subsurface_destroy); return true; } static void subsurface_tree_handle_surface_new_subsurface( struct wl_listener *listener, void *data) { struct wlr_scene_subsurface_tree *subsurface_tree = wl_container_of(listener, subsurface_tree, surface_new_subsurface); struct wlr_subsurface *subsurface = data; if (!subsurface_tree_create_subsurface(subsurface_tree, subsurface)) { wl_resource_post_no_memory(subsurface->resource); } } static const struct wlr_addon_interface subsurface_tree_addon_impl = { .name = "wlr_scene_subsurface_tree", .destroy = subsurface_tree_addon_destroy, }; static struct wlr_scene_subsurface_tree *scene_surface_tree_create( struct wlr_scene_tree *parent, struct wlr_surface *surface) { struct wlr_scene_subsurface_tree *subsurface_tree = calloc(1, sizeof(*subsurface_tree)); if (subsurface_tree == NULL) { return NULL; } subsurface_tree->tree = wlr_scene_tree_create(parent); if (subsurface_tree->tree == NULL) { goto error_surface_tree; } subsurface_tree->scene_surface = wlr_scene_surface_create(subsurface_tree->tree, surface); if (subsurface_tree->scene_surface == NULL) { goto error_scene_surface; } subsurface_tree->surface = surface; struct wlr_subsurface *subsurface; wl_list_for_each(subsurface, &surface->current.subsurfaces_below, current.link) { if (!subsurface_tree_create_subsurface(subsurface_tree, subsurface)) { goto error_scene_surface; } } wl_list_for_each(subsurface, &surface->current.subsurfaces_above, current.link) { if (!subsurface_tree_create_subsurface(subsurface_tree, subsurface)) { goto error_scene_surface; } } subsurface_tree_reconfigure(subsurface_tree); wlr_addon_init(&subsurface_tree->scene_addon, &subsurface_tree->tree->node.addons, NULL, &subsurface_tree_addon_impl); subsurface_tree->surface_destroy.notify = subsurface_tree_handle_surface_destroy; wl_signal_add(&surface->events.destroy, &subsurface_tree->surface_destroy); subsurface_tree->surface_commit.notify = subsurface_tree_handle_surface_commit; wl_signal_add(&surface->events.commit, &subsurface_tree->surface_commit); subsurface_tree->surface_map.notify = subsurface_tree_handle_surface_map; wl_signal_add(&surface->events.map, &subsurface_tree->surface_map); subsurface_tree->surface_unmap.notify = subsurface_tree_handle_surface_unmap; wl_signal_add(&surface->events.unmap, &subsurface_tree->surface_unmap); subsurface_tree->surface_new_subsurface.notify = subsurface_tree_handle_surface_new_subsurface; wl_signal_add(&surface->events.new_subsurface, &subsurface_tree->surface_new_subsurface); wlr_scene_node_set_enabled(&subsurface_tree->tree->node, surface->mapped); return subsurface_tree; error_scene_surface: wlr_scene_node_destroy(&subsurface_tree->tree->node); error_surface_tree: free(subsurface_tree); return NULL; } struct wlr_scene_tree *wlr_scene_subsurface_tree_create( struct wlr_scene_tree *parent, struct wlr_surface *surface) { struct wlr_scene_subsurface_tree *subsurface_tree = scene_surface_tree_create(parent, surface); if (subsurface_tree == NULL) { return NULL; } return subsurface_tree->tree; } static struct wlr_scene_subsurface_tree *get_subsurface_tree_from_node( struct wlr_scene_node *node) { struct wlr_addon *addon = wlr_addon_find(&node->addons, NULL, &subsurface_tree_addon_impl); if (!addon) { return NULL; } struct wlr_scene_subsurface_tree *tree = wl_container_of(addon, tree, scene_addon); return tree; } static bool subsurface_tree_set_clip(struct wlr_scene_node *node, struct wlr_box *clip) { if (node->type != WLR_SCENE_NODE_TREE) { return false; } bool discovered_subsurface_tree = false; struct wlr_scene_subsurface_tree *tree = get_subsurface_tree_from_node(node); if (tree) { if (tree->parent == NULL) { if (wlr_box_equal(&tree->clip, clip)) { return true; } if (clip) { tree->clip = *clip; } else { tree->clip = (struct wlr_box){0}; } } discovered_subsurface_tree = true; subsurface_tree_reconfigure_clip(tree); } struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node); struct wlr_scene_node *child; wl_list_for_each(child, &scene_tree->children, link) { discovered_subsurface_tree |= subsurface_tree_set_clip(child, clip); } return discovered_subsurface_tree; } void wlr_scene_subsurface_tree_set_clip(struct wlr_scene_node *node, struct wlr_box *clip) { #ifndef NDEBUG bool found = #endif subsurface_tree_set_clip(node, clip); assert(found); } wlroots-0.17.1/types/scene/surface.c000066400000000000000000000226731454110342200173540ustar00rootroot00000000000000#include #include #include #include #include #include #include "types/wlr_scene.h" static void handle_scene_buffer_outputs_update( struct wl_listener *listener, void *data) { struct wlr_scene_surface *surface = wl_container_of(listener, surface, outputs_update); if (surface->buffer->primary_output == NULL) { return; } double scale = surface->buffer->primary_output->output->scale; wlr_fractional_scale_v1_notify_scale(surface->surface, scale); wlr_surface_set_preferred_buffer_scale(surface->surface, ceil(scale)); } static void handle_scene_buffer_output_enter( struct wl_listener *listener, void *data) { struct wlr_scene_surface *surface = wl_container_of(listener, surface, output_enter); struct wlr_scene_output *output = data; wlr_surface_send_enter(surface->surface, output->output); } static void handle_scene_buffer_output_leave( struct wl_listener *listener, void *data) { struct wlr_scene_surface *surface = wl_container_of(listener, surface, output_leave); struct wlr_scene_output *output = data; wlr_surface_send_leave(surface->surface, output->output); } static void handle_scene_buffer_output_sample( struct wl_listener *listener, void *data) { struct wlr_scene_surface *surface = wl_container_of(listener, surface, output_sample); const struct wlr_scene_output_sample_event *event = data; struct wlr_scene_output *scene_output = event->output; if (surface->buffer->primary_output != scene_output) { return; } struct wlr_scene *root = scene_node_get_root(&surface->buffer->node); if (!root->presentation) { return; } if (event->direct_scanout) { wlr_presentation_surface_scanned_out_on_output( root->presentation, surface->surface, scene_output->output); } else { wlr_presentation_surface_textured_on_output( root->presentation, surface->surface, scene_output->output); } } static void handle_scene_buffer_frame_done( struct wl_listener *listener, void *data) { struct wlr_scene_surface *surface = wl_container_of(listener, surface, frame_done); struct timespec *now = data; wlr_surface_send_frame_done(surface->surface, now); } static void scene_surface_handle_surface_destroy( struct wl_listener *listener, void *data) { struct wlr_scene_surface *surface = wl_container_of(listener, surface, surface_destroy); wlr_scene_node_destroy(&surface->buffer->node); } // This is used for wlr_scene where it unconditionally locks buffers preventing // reuse of the existing texture for shm clients. With the usage pattern of // wlr_scene surface handling, we can mark its locked buffer as safe // for mutation. static void client_buffer_mark_next_can_damage(struct wlr_client_buffer *buffer) { buffer->n_ignore_locks++; } static void scene_buffer_unmark_client_buffer(struct wlr_scene_buffer *scene_buffer) { if (!scene_buffer->buffer) { return; } struct wlr_client_buffer *buffer = wlr_client_buffer_get(scene_buffer->buffer); if (!buffer) { return; } assert(buffer->n_ignore_locks > 0); buffer->n_ignore_locks--; } static int min(int a, int b) { return a < b ? a : b; } static void surface_reconfigure(struct wlr_scene_surface *scene_surface) { struct wlr_scene_buffer *scene_buffer = scene_surface->buffer; struct wlr_surface *surface = scene_surface->surface; struct wlr_surface_state *state = &surface->current; struct wlr_fbox src_box; wlr_surface_get_buffer_source_box(surface, &src_box); pixman_region32_t opaque; pixman_region32_init(&opaque); pixman_region32_copy(&opaque, &surface->opaque_region); int width = state->width; int height = state->height; if (!wlr_box_empty(&scene_surface->clip)) { struct wlr_box *clip = &scene_surface->clip; int buffer_width = state->buffer_width; int buffer_height = state->buffer_height; width = min(clip->width, width - clip->x); height = min(clip->height, height - clip->y); wlr_fbox_transform(&src_box, &src_box, state->transform, buffer_width, buffer_height); if (state->transform & WL_OUTPUT_TRANSFORM_90) { int tmp = buffer_width; buffer_width = buffer_height; buffer_height = tmp; } src_box.x += (double)(clip->x * buffer_width) / state->width; src_box.y += (double)(clip->y * buffer_height) / state->height; src_box.width *= (double)width / state->width; src_box.height *= (double)height / state->height; wlr_fbox_transform(&src_box, &src_box, wlr_output_transform_invert(state->transform), buffer_width, buffer_height); pixman_region32_translate(&opaque, -clip->x, -clip->y); pixman_region32_intersect_rect(&opaque, &opaque, 0, 0, width, height); } if (width <= 0 || height <= 0) { wlr_scene_buffer_set_buffer(scene_buffer, NULL); pixman_region32_fini(&opaque); return; } wlr_scene_buffer_set_opaque_region(scene_buffer, &opaque); wlr_scene_buffer_set_source_box(scene_buffer, &src_box); wlr_scene_buffer_set_dest_size(scene_buffer, width, height); wlr_scene_buffer_set_transform(scene_buffer, state->transform); scene_buffer_unmark_client_buffer(scene_buffer); if (surface->buffer) { client_buffer_mark_next_can_damage(surface->buffer); wlr_scene_buffer_set_buffer_with_damage(scene_buffer, &surface->buffer->base, &surface->buffer_damage); } else { wlr_scene_buffer_set_buffer(scene_buffer, NULL); } pixman_region32_fini(&opaque); } static void handle_scene_surface_surface_commit( struct wl_listener *listener, void *data) { struct wlr_scene_surface *surface = wl_container_of(listener, surface, surface_commit); struct wlr_scene_buffer *scene_buffer = surface->buffer; surface_reconfigure(surface); // If the surface has requested a frame done event, honour that. The // frame_callback_list will be populated in this case. We should only // schedule the frame however if the node is enabled and there is an // output intersecting, otherwise the frame done events would never reach // the surface anyway. int lx, ly; bool enabled = wlr_scene_node_coords(&scene_buffer->node, &lx, &ly); if (!wl_list_empty(&surface->surface->current.frame_callback_list) && surface->buffer->primary_output != NULL && enabled) { wlr_output_schedule_frame(surface->buffer->primary_output->output); } } static bool scene_buffer_point_accepts_input(struct wlr_scene_buffer *scene_buffer, double *sx, double *sy) { struct wlr_scene_surface *scene_surface = wlr_scene_surface_try_from_buffer(scene_buffer); *sx += scene_surface->clip.x; *sy += scene_surface->clip.y; return wlr_surface_point_accepts_input(scene_surface->surface, *sx, *sy); } static void surface_addon_destroy(struct wlr_addon *addon) { struct wlr_scene_surface *surface = wl_container_of(addon, surface, addon); scene_buffer_unmark_client_buffer(surface->buffer); wlr_addon_finish(&surface->addon); wl_list_remove(&surface->outputs_update.link); wl_list_remove(&surface->output_enter.link); wl_list_remove(&surface->output_leave.link); wl_list_remove(&surface->output_sample.link); wl_list_remove(&surface->frame_done.link); wl_list_remove(&surface->surface_destroy.link); wl_list_remove(&surface->surface_commit.link); free(surface); } static const struct wlr_addon_interface surface_addon_impl = { .name = "wlr_scene_surface", .destroy = surface_addon_destroy, }; struct wlr_scene_surface *wlr_scene_surface_try_from_buffer( struct wlr_scene_buffer *scene_buffer) { struct wlr_addon *addon = wlr_addon_find(&scene_buffer->node.addons, scene_buffer, &surface_addon_impl); if (!addon) { return NULL; } struct wlr_scene_surface *surface = wl_container_of(addon, surface, addon); return surface; } struct wlr_scene_surface *wlr_scene_surface_create(struct wlr_scene_tree *parent, struct wlr_surface *wlr_surface) { struct wlr_scene_surface *surface = calloc(1, sizeof(*surface)); if (surface == NULL) { return NULL; } struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_create(parent, NULL); if (!scene_buffer) { free(surface); return NULL; } surface->buffer = scene_buffer; surface->surface = wlr_surface; scene_buffer->point_accepts_input = scene_buffer_point_accepts_input; surface->outputs_update.notify = handle_scene_buffer_outputs_update; wl_signal_add(&scene_buffer->events.outputs_update, &surface->outputs_update); surface->output_enter.notify = handle_scene_buffer_output_enter; wl_signal_add(&scene_buffer->events.output_enter, &surface->output_enter); surface->output_leave.notify = handle_scene_buffer_output_leave; wl_signal_add(&scene_buffer->events.output_leave, &surface->output_leave); surface->output_sample.notify = handle_scene_buffer_output_sample; wl_signal_add(&scene_buffer->events.output_sample, &surface->output_sample); surface->frame_done.notify = handle_scene_buffer_frame_done; wl_signal_add(&scene_buffer->events.frame_done, &surface->frame_done); surface->surface_destroy.notify = scene_surface_handle_surface_destroy; wl_signal_add(&wlr_surface->events.destroy, &surface->surface_destroy); surface->surface_commit.notify = handle_scene_surface_surface_commit; wl_signal_add(&wlr_surface->events.commit, &surface->surface_commit); wlr_addon_init(&surface->addon, &scene_buffer->node.addons, scene_buffer, &surface_addon_impl); surface_reconfigure(surface); return surface; } void scene_surface_set_clip(struct wlr_scene_surface *surface, struct wlr_box *clip) { if (wlr_box_equal(clip, &surface->clip)) { return; } if (clip) { surface->clip = *clip; } else { surface->clip = (struct wlr_box){0}; } surface_reconfigure(surface); } wlroots-0.17.1/types/scene/wlr_scene.c000066400000000000000000001644431454110342200177070ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "types/wlr_buffer.h" #include "types/wlr_output.h" #include "types/wlr_scene.h" #include "util/array.h" #include "util/env.h" #include "util/time.h" #define HIGHLIGHT_DAMAGE_FADEOUT_TIME 250 struct wlr_scene_tree *wlr_scene_tree_from_node(struct wlr_scene_node *node) { assert(node->type == WLR_SCENE_NODE_TREE); struct wlr_scene_tree *tree = wl_container_of(node, tree, node); return tree; } struct wlr_scene_rect *wlr_scene_rect_from_node(struct wlr_scene_node *node) { assert(node->type == WLR_SCENE_NODE_RECT); struct wlr_scene_rect *rect = wl_container_of(node, rect, node); return rect; } struct wlr_scene_buffer *wlr_scene_buffer_from_node( struct wlr_scene_node *node) { assert(node->type == WLR_SCENE_NODE_BUFFER); struct wlr_scene_buffer *buffer = wl_container_of(node, buffer, node); return buffer; } struct wlr_scene *scene_node_get_root(struct wlr_scene_node *node) { struct wlr_scene_tree *tree; if (node->type == WLR_SCENE_NODE_TREE) { tree = wlr_scene_tree_from_node(node); } else { tree = node->parent; } while (tree->node.parent != NULL) { tree = tree->node.parent; } struct wlr_scene *scene = wl_container_of(tree, scene, tree); return scene; } static void scene_node_init(struct wlr_scene_node *node, enum wlr_scene_node_type type, struct wlr_scene_tree *parent) { *node = (struct wlr_scene_node){ .type = type, .parent = parent, .enabled = true, }; wl_list_init(&node->link); wl_signal_init(&node->events.destroy); pixman_region32_init(&node->visible); if (parent != NULL) { wl_list_insert(parent->children.prev, &node->link); } wlr_addon_set_init(&node->addons); } struct highlight_region { pixman_region32_t region; struct timespec when; struct wl_list link; }; void wlr_scene_node_destroy(struct wlr_scene_node *node) { if (node == NULL) { return; } // We want to call the destroy listeners before we do anything else // in case the destroy signal would like to remove children before they // are recursively destroyed. wl_signal_emit_mutable(&node->events.destroy, NULL); wlr_addon_set_finish(&node->addons); wlr_scene_node_set_enabled(node, false); struct wlr_scene *scene = scene_node_get_root(node); if (node->type == WLR_SCENE_NODE_BUFFER) { struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); uint64_t active = scene_buffer->active_outputs; if (active) { struct wlr_scene_output *scene_output; wl_list_for_each(scene_output, &scene->outputs, link) { if (active & (1ull << scene_output->index)) { wl_signal_emit_mutable(&scene_buffer->events.output_leave, scene_output); } } } wlr_texture_destroy(scene_buffer->texture); wlr_buffer_unlock(scene_buffer->buffer); pixman_region32_fini(&scene_buffer->opaque_region); } else if (node->type == WLR_SCENE_NODE_TREE) { struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node); if (scene_tree == &scene->tree) { assert(!node->parent); struct wlr_scene_output *scene_output, *scene_output_tmp; wl_list_for_each_safe(scene_output, scene_output_tmp, &scene->outputs, link) { wlr_scene_output_destroy(scene_output); } wl_list_remove(&scene->presentation_destroy.link); wl_list_remove(&scene->linux_dmabuf_v1_destroy.link); } else { assert(node->parent); } struct wlr_scene_node *child, *child_tmp; wl_list_for_each_safe(child, child_tmp, &scene_tree->children, link) { wlr_scene_node_destroy(child); } } wl_list_remove(&node->link); pixman_region32_fini(&node->visible); free(node); } static void scene_tree_init(struct wlr_scene_tree *tree, struct wlr_scene_tree *parent) { *tree = (struct wlr_scene_tree){0}; scene_node_init(&tree->node, WLR_SCENE_NODE_TREE, parent); wl_list_init(&tree->children); } struct wlr_scene *wlr_scene_create(void) { struct wlr_scene *scene = calloc(1, sizeof(*scene)); if (scene == NULL) { return NULL; } scene_tree_init(&scene->tree, NULL); wl_list_init(&scene->outputs); wl_list_init(&scene->presentation_destroy.link); wl_list_init(&scene->linux_dmabuf_v1_destroy.link); const char *debug_damage_options[] = { "none", "rerender", "highlight", NULL }; scene->debug_damage_option = env_parse_switch("WLR_SCENE_DEBUG_DAMAGE", debug_damage_options); scene->direct_scanout = !env_parse_bool("WLR_SCENE_DISABLE_DIRECT_SCANOUT"); scene->calculate_visibility = !env_parse_bool("WLR_SCENE_DISABLE_VISIBILITY"); return scene; } struct wlr_scene_tree *wlr_scene_tree_create(struct wlr_scene_tree *parent) { assert(parent); struct wlr_scene_tree *tree = calloc(1, sizeof(*tree)); if (tree == NULL) { return NULL; } scene_tree_init(tree, parent); return tree; } static void scene_node_get_size(struct wlr_scene_node *node, int *lx, int *ly); typedef bool (*scene_node_box_iterator_func_t)(struct wlr_scene_node *node, int sx, int sy, void *data); static bool _scene_nodes_in_box(struct wlr_scene_node *node, struct wlr_box *box, scene_node_box_iterator_func_t iterator, void *user_data, int lx, int ly) { if (!node->enabled) { return false; } switch (node->type) { case WLR_SCENE_NODE_TREE:; struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node); struct wlr_scene_node *child; wl_list_for_each_reverse(child, &scene_tree->children, link) { if (_scene_nodes_in_box(child, box, iterator, user_data, lx + child->x, ly + child->y)) { return true; } } break; case WLR_SCENE_NODE_RECT: case WLR_SCENE_NODE_BUFFER:; struct wlr_box node_box = { .x = lx, .y = ly }; scene_node_get_size(node, &node_box.width, &node_box.height); if (wlr_box_intersection(&node_box, &node_box, box) && iterator(node, lx, ly, user_data)) { return true; } break; } return false; } static bool scene_nodes_in_box(struct wlr_scene_node *node, struct wlr_box *box, scene_node_box_iterator_func_t iterator, void *user_data) { int x, y; wlr_scene_node_coords(node, &x, &y); return _scene_nodes_in_box(node, box, iterator, user_data, x, y); } static void scene_node_opaque_region(struct wlr_scene_node *node, int x, int y, pixman_region32_t *opaque) { int width, height; scene_node_get_size(node, &width, &height); if (node->type == WLR_SCENE_NODE_RECT) { struct wlr_scene_rect *scene_rect = wlr_scene_rect_from_node(node); if (scene_rect->color[3] != 1) { return; } } else if (node->type == WLR_SCENE_NODE_BUFFER) { struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); if (!scene_buffer->buffer) { return; } if (scene_buffer->opacity != 1) { return; } if (!buffer_is_opaque(scene_buffer->buffer)) { pixman_region32_copy(opaque, &scene_buffer->opaque_region); pixman_region32_intersect_rect(opaque, opaque, 0, 0, width, height); pixman_region32_translate(opaque, x, y); return; } } pixman_region32_fini(opaque); pixman_region32_init_rect(opaque, x, y, width, height); } struct scene_update_data { pixman_region32_t *visible; pixman_region32_t *update_region; struct wl_list *outputs; bool calculate_visibility; }; static uint32_t region_area(pixman_region32_t *region) { uint32_t area = 0; int nrects; pixman_box32_t *rects = pixman_region32_rectangles(region, &nrects); for (int i = 0; i < nrects; ++i) { area += (rects[i].x2 - rects[i].x1) * (rects[i].y2 - rects[i].y1); } return area; } static void scale_output_damage(pixman_region32_t *damage, float scale) { wlr_region_scale(damage, damage, scale); if (floor(scale) != scale) { wlr_region_expand(damage, damage, 1); } } struct render_data { enum wl_output_transform transform; float scale; struct wlr_box logical; int trans_width, trans_height; struct wlr_scene_output *output; struct wlr_render_pass *render_pass; pixman_region32_t damage; }; static void transform_output_damage(pixman_region32_t *damage, const struct render_data *data) { enum wl_output_transform transform = wlr_output_transform_invert(data->transform); wlr_region_transform(damage, damage, transform, data->trans_width, data->trans_height); } static void transform_output_box(struct wlr_box *box, const struct render_data *data) { enum wl_output_transform transform = wlr_output_transform_invert(data->transform); wlr_box_transform(box, box, transform, data->trans_width, data->trans_height); } static void scene_damage_outputs(struct wlr_scene *scene, pixman_region32_t *damage) { if (!pixman_region32_not_empty(damage)) { return; } struct wlr_scene_output *scene_output; wl_list_for_each(scene_output, &scene->outputs, link) { pixman_region32_t output_damage; pixman_region32_init(&output_damage); pixman_region32_copy(&output_damage, damage); pixman_region32_translate(&output_damage, -scene_output->x, -scene_output->y); scale_output_damage(&output_damage, scene_output->output->scale); if (wlr_damage_ring_add(&scene_output->damage_ring, &output_damage)) { wlr_output_schedule_frame(scene_output->output); } pixman_region32_fini(&output_damage); } } static void update_node_update_outputs(struct wlr_scene_node *node, struct wl_list *outputs, struct wlr_scene_output *ignore, struct wlr_scene_output *force) { if (node->type != WLR_SCENE_NODE_BUFFER) { return; } struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); uint32_t largest_overlap = 0; struct wlr_scene_output *old_primary_output = scene_buffer->primary_output; scene_buffer->primary_output = NULL; size_t count = 0; uint64_t active_outputs = 0; // let's update the outputs in two steps: // - the primary outputs // - the enter/leave signals // This ensures that the enter/leave signals can rely on the primary output // to have a reasonable value. Otherwise, they may get a value that's in // the middle of a calculation. struct wlr_scene_output *scene_output; wl_list_for_each(scene_output, outputs, link) { if (scene_output == ignore) { continue; } if (!scene_output->output->enabled) { continue; } struct wlr_box output_box = { .x = scene_output->x, .y = scene_output->y, }; wlr_output_effective_resolution(scene_output->output, &output_box.width, &output_box.height); pixman_region32_t intersection; pixman_region32_init(&intersection); pixman_region32_intersect_rect(&intersection, &node->visible, output_box.x, output_box.y, output_box.width, output_box.height); if (pixman_region32_not_empty(&intersection)) { uint32_t overlap = region_area(&intersection); if (overlap >= largest_overlap) { largest_overlap = overlap; scene_buffer->primary_output = scene_output; } active_outputs |= 1ull << scene_output->index; count++; } pixman_region32_fini(&intersection); } if (old_primary_output != scene_buffer->primary_output) { scene_buffer->prev_feedback_options = (struct wlr_linux_dmabuf_feedback_v1_init_options){0}; } uint64_t old_active = scene_buffer->active_outputs; scene_buffer->active_outputs = active_outputs; wl_list_for_each(scene_output, outputs, link) { uint64_t mask = 1ull << scene_output->index; bool intersects = active_outputs & mask; bool intersects_before = old_active & mask; if (intersects && !intersects_before) { wl_signal_emit_mutable(&scene_buffer->events.output_enter, scene_output); } else if (!intersects && intersects_before) { wl_signal_emit_mutable(&scene_buffer->events.output_leave, scene_output); } } // if there are active outputs on this node, we should always have a primary // output assert(!scene_buffer->active_outputs || scene_buffer->primary_output); // Skip output update event if nothing was updated if (old_active == active_outputs && (!force || ((1ull << force->index) & ~active_outputs)) && old_primary_output == scene_buffer->primary_output) { return; } struct wlr_scene_output *outputs_array[64]; struct wlr_scene_outputs_update_event event = { .active = outputs_array, .size = count, }; size_t i = 0; wl_list_for_each(scene_output, outputs, link) { if (~active_outputs & (1ull << scene_output->index)) { continue; } assert(i < count); outputs_array[i++] = scene_output; } wl_signal_emit_mutable(&scene_buffer->events.outputs_update, &event); } static bool scene_node_update_iterator(struct wlr_scene_node *node, int lx, int ly, void *_data) { struct scene_update_data *data = _data; struct wlr_box box = { .x = lx, .y = ly }; scene_node_get_size(node, &box.width, &box.height); pixman_region32_subtract(&node->visible, &node->visible, data->update_region); pixman_region32_union(&node->visible, &node->visible, data->visible); pixman_region32_intersect_rect(&node->visible, &node->visible, lx, ly, box.width, box.height); if (data->calculate_visibility) { pixman_region32_t opaque; pixman_region32_init(&opaque); scene_node_opaque_region(node, lx, ly, &opaque); pixman_region32_subtract(data->visible, data->visible, &opaque); pixman_region32_fini(&opaque); } update_node_update_outputs(node, data->outputs, NULL, NULL); return false; } static void scene_node_visibility(struct wlr_scene_node *node, pixman_region32_t *visible) { if (!node->enabled) { return; } if (node->type == WLR_SCENE_NODE_TREE) { struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node); struct wlr_scene_node *child; wl_list_for_each(child, &scene_tree->children, link) { scene_node_visibility(child, visible); } return; } pixman_region32_union(visible, visible, &node->visible); } static void scene_node_bounds(struct wlr_scene_node *node, int x, int y, pixman_region32_t *visible) { if (!node->enabled) { return; } if (node->type == WLR_SCENE_NODE_TREE) { struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node); struct wlr_scene_node *child; wl_list_for_each(child, &scene_tree->children, link) { scene_node_bounds(child, x + child->x, y + child->y, visible); } return; } int width, height; scene_node_get_size(node, &width, &height); pixman_region32_union_rect(visible, visible, x, y, width, height); } static void scene_update_region(struct wlr_scene *scene, pixman_region32_t *update_region) { pixman_region32_t visible; pixman_region32_init(&visible); pixman_region32_copy(&visible, update_region); struct scene_update_data data = { .visible = &visible, .update_region = update_region, .outputs = &scene->outputs, .calculate_visibility = scene->calculate_visibility, }; struct pixman_box32 *region_box = pixman_region32_extents(update_region); struct wlr_box box = { .x = region_box->x1, .y = region_box->y1, .width = region_box->x2 - region_box->x1, .height = region_box->y2 - region_box->y1, }; // update node visibility and output enter/leave events scene_nodes_in_box(&scene->tree.node, &box, scene_node_update_iterator, &data); pixman_region32_fini(&visible); } static void scene_node_update(struct wlr_scene_node *node, pixman_region32_t *damage) { struct wlr_scene *scene = scene_node_get_root(node); int x, y; if (!wlr_scene_node_coords(node, &x, &y)) { if (damage) { scene_update_region(scene, damage); scene_damage_outputs(scene, damage); pixman_region32_fini(damage); } return; } pixman_region32_t visible; if (!damage) { pixman_region32_init(&visible); scene_node_visibility(node, &visible); damage = &visible; } pixman_region32_t update_region; pixman_region32_init(&update_region); pixman_region32_copy(&update_region, damage); scene_node_bounds(node, x, y, &update_region); scene_update_region(scene, &update_region); pixman_region32_fini(&update_region); scene_node_visibility(node, damage); scene_damage_outputs(scene, damage); pixman_region32_fini(damage); } struct wlr_scene_rect *wlr_scene_rect_create(struct wlr_scene_tree *parent, int width, int height, const float color[static 4]) { struct wlr_scene_rect *scene_rect = calloc(1, sizeof(*scene_rect)); if (scene_rect == NULL) { return NULL; } assert(parent); scene_node_init(&scene_rect->node, WLR_SCENE_NODE_RECT, parent); scene_rect->width = width; scene_rect->height = height; memcpy(scene_rect->color, color, sizeof(scene_rect->color)); scene_node_update(&scene_rect->node, NULL); return scene_rect; } void wlr_scene_rect_set_size(struct wlr_scene_rect *rect, int width, int height) { if (rect->width == width && rect->height == height) { return; } rect->width = width; rect->height = height; scene_node_update(&rect->node, NULL); } void wlr_scene_rect_set_color(struct wlr_scene_rect *rect, const float color[static 4]) { if (memcmp(rect->color, color, sizeof(rect->color)) == 0) { return; } memcpy(rect->color, color, sizeof(rect->color)); scene_node_update(&rect->node, NULL); } struct wlr_scene_buffer *wlr_scene_buffer_create(struct wlr_scene_tree *parent, struct wlr_buffer *buffer) { struct wlr_scene_buffer *scene_buffer = calloc(1, sizeof(*scene_buffer)); if (scene_buffer == NULL) { return NULL; } assert(parent); scene_node_init(&scene_buffer->node, WLR_SCENE_NODE_BUFFER, parent); if (buffer) { scene_buffer->buffer = wlr_buffer_lock(buffer); } wl_signal_init(&scene_buffer->events.outputs_update); wl_signal_init(&scene_buffer->events.output_enter); wl_signal_init(&scene_buffer->events.output_leave); wl_signal_init(&scene_buffer->events.output_sample); wl_signal_init(&scene_buffer->events.frame_done); pixman_region32_init(&scene_buffer->opaque_region); scene_buffer->opacity = 1; scene_node_update(&scene_buffer->node, NULL); return scene_buffer; } void wlr_scene_buffer_set_buffer_with_damage(struct wlr_scene_buffer *scene_buffer, struct wlr_buffer *buffer, const pixman_region32_t *damage) { // specifying a region for a NULL buffer doesn't make sense. We need to know // about the buffer to scale the buffer local coordinates down to scene // coordinates. assert(buffer || !damage); bool update = false; wlr_texture_destroy(scene_buffer->texture); scene_buffer->texture = NULL; if (buffer) { // if this node used to not be mapped or its previous displayed // buffer region will be different from what the new buffer would // produce we need to update the node. update = !scene_buffer->buffer || (scene_buffer->dst_width == 0 && scene_buffer->dst_height == 0 && (scene_buffer->buffer->width != buffer->width || scene_buffer->buffer->height != buffer->height)); wlr_buffer_unlock(scene_buffer->buffer); scene_buffer->buffer = wlr_buffer_lock(buffer); } else { wlr_buffer_unlock(scene_buffer->buffer); update = true; scene_buffer->buffer = NULL; } if (update) { scene_node_update(&scene_buffer->node, NULL); // updating the node will already damage the whole node for us. Return // early to not damage again return; } int lx, ly; if (!wlr_scene_node_coords(&scene_buffer->node, &lx, &ly)) { return; } pixman_region32_t fallback_damage; pixman_region32_init_rect(&fallback_damage, 0, 0, buffer->width, buffer->height); if (!damage) { damage = &fallback_damage; } struct wlr_fbox box = scene_buffer->src_box; if (wlr_fbox_empty(&box)) { box.x = 0; box.y = 0; box.width = buffer->width; box.height = buffer->height; } wlr_fbox_transform(&box, &box, scene_buffer->transform, buffer->width, buffer->height); float scale_x, scale_y; if (scene_buffer->dst_width || scene_buffer->dst_height) { scale_x = scene_buffer->dst_width / box.width; scale_y = scene_buffer->dst_height / box.height; } else { scale_x = buffer->width / box.width; scale_y = buffer->height / box.height; } pixman_region32_t trans_damage; pixman_region32_init(&trans_damage); wlr_region_transform(&trans_damage, damage, scene_buffer->transform, buffer->width, buffer->height); pixman_region32_intersect_rect(&trans_damage, &trans_damage, box.x, box.y, box.width, box.height); pixman_region32_translate(&trans_damage, -box.x, -box.y); struct wlr_scene *scene = scene_node_get_root(&scene_buffer->node); struct wlr_scene_output *scene_output; wl_list_for_each(scene_output, &scene->outputs, link) { float output_scale = scene_output->output->scale; float output_scale_x = output_scale * scale_x; float output_scale_y = output_scale * scale_y; pixman_region32_t output_damage; pixman_region32_init(&output_damage); wlr_region_scale_xy(&output_damage, &trans_damage, output_scale_x, output_scale_y); // One output pixel will match (buffer_scale_x)x(buffer_scale_y) buffer pixels. // If the buffer is upscaled on the given axis (output_scale_* > 1.0, // buffer_scale_* < 1.0), its contents will bleed into adjacent // (ceil(output_scale_* / 2)) output pixels because of linear filtering. // Additionally, if the buffer is downscaled (output_scale_* < 1.0, // buffer_scale_* > 1.0), and one output pixel matches a non-integer number of // buffer pixels, its contents will bleed into neighboring output pixels. // Handle both cases by computing buffer_scale_{x,y} and checking if they are // integer numbers; ceilf() is used to ensure that the distance is at least 1. float buffer_scale_x = 1.0f / output_scale_x; float buffer_scale_y = 1.0f / output_scale_y; int dist_x = floor(buffer_scale_x) != buffer_scale_x ? (int)ceilf(output_scale_x / 2.0f) : 0; int dist_y = floor(buffer_scale_y) != buffer_scale_y ? (int)ceilf(output_scale_y / 2.0f) : 0; // TODO: expand with per-axis distances wlr_region_expand(&output_damage, &output_damage, dist_x >= dist_y ? dist_x : dist_y); pixman_region32_t cull_region; pixman_region32_init(&cull_region); pixman_region32_copy(&cull_region, &scene_buffer->node.visible); scale_output_damage(&cull_region, output_scale); pixman_region32_translate(&cull_region, -lx * output_scale, -ly * output_scale); pixman_region32_intersect(&output_damage, &output_damage, &cull_region); pixman_region32_fini(&cull_region); pixman_region32_translate(&output_damage, (int)round((lx - scene_output->x) * output_scale), (int)round((ly - scene_output->y) * output_scale)); if (wlr_damage_ring_add(&scene_output->damage_ring, &output_damage)) { wlr_output_schedule_frame(scene_output->output); } pixman_region32_fini(&output_damage); } pixman_region32_fini(&trans_damage); pixman_region32_fini(&fallback_damage); } void wlr_scene_buffer_set_buffer(struct wlr_scene_buffer *scene_buffer, struct wlr_buffer *buffer) { wlr_scene_buffer_set_buffer_with_damage(scene_buffer, buffer, NULL); } void wlr_scene_buffer_set_opaque_region(struct wlr_scene_buffer *scene_buffer, const pixman_region32_t *region) { if (pixman_region32_equal(&scene_buffer->opaque_region, region)) { return; } pixman_region32_copy(&scene_buffer->opaque_region, region); int x, y; if (!wlr_scene_node_coords(&scene_buffer->node, &x, &y)) { return; } pixman_region32_t update_region; pixman_region32_init(&update_region); scene_node_bounds(&scene_buffer->node, x, y, &update_region); scene_update_region(scene_node_get_root(&scene_buffer->node), &update_region); pixman_region32_fini(&update_region); } void wlr_scene_buffer_set_source_box(struct wlr_scene_buffer *scene_buffer, const struct wlr_fbox *box) { if (wlr_fbox_equal(&scene_buffer->src_box, box)) { return; } if (box != NULL) { scene_buffer->src_box = *box; } else { scene_buffer->src_box = (struct wlr_fbox){0}; } scene_node_update(&scene_buffer->node, NULL); } void wlr_scene_buffer_set_dest_size(struct wlr_scene_buffer *scene_buffer, int width, int height) { if (scene_buffer->dst_width == width && scene_buffer->dst_height == height) { return; } scene_buffer->dst_width = width; scene_buffer->dst_height = height; scene_node_update(&scene_buffer->node, NULL); } void wlr_scene_buffer_set_transform(struct wlr_scene_buffer *scene_buffer, enum wl_output_transform transform) { if (scene_buffer->transform == transform) { return; } scene_buffer->transform = transform; scene_node_update(&scene_buffer->node, NULL); } void wlr_scene_buffer_send_frame_done(struct wlr_scene_buffer *scene_buffer, struct timespec *now) { if (pixman_region32_not_empty(&scene_buffer->node.visible)) { wl_signal_emit_mutable(&scene_buffer->events.frame_done, now); } } void wlr_scene_buffer_set_opacity(struct wlr_scene_buffer *scene_buffer, float opacity) { if (scene_buffer->opacity == opacity) { return; } scene_buffer->opacity = opacity; scene_node_update(&scene_buffer->node, NULL); } void wlr_scene_buffer_set_filter_mode(struct wlr_scene_buffer *scene_buffer, enum wlr_scale_filter_mode filter_mode) { if (scene_buffer->filter_mode == filter_mode) { return; } scene_buffer->filter_mode = filter_mode; scene_node_update(&scene_buffer->node, NULL); } static struct wlr_texture *scene_buffer_get_texture( struct wlr_scene_buffer *scene_buffer, struct wlr_renderer *renderer) { struct wlr_client_buffer *client_buffer = wlr_client_buffer_get(scene_buffer->buffer); if (client_buffer != NULL) { return client_buffer->texture; } if (scene_buffer->texture != NULL) { return scene_buffer->texture; } scene_buffer->texture = wlr_texture_from_buffer(renderer, scene_buffer->buffer); return scene_buffer->texture; } static void scene_node_get_size(struct wlr_scene_node *node, int *width, int *height) { *width = 0; *height = 0; switch (node->type) { case WLR_SCENE_NODE_TREE: return; case WLR_SCENE_NODE_RECT:; struct wlr_scene_rect *scene_rect = wlr_scene_rect_from_node(node); *width = scene_rect->width; *height = scene_rect->height; break; case WLR_SCENE_NODE_BUFFER:; struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); if (scene_buffer->dst_width > 0 && scene_buffer->dst_height > 0) { *width = scene_buffer->dst_width; *height = scene_buffer->dst_height; } else if (scene_buffer->buffer) { if (scene_buffer->transform & WL_OUTPUT_TRANSFORM_90) { *height = scene_buffer->buffer->width; *width = scene_buffer->buffer->height; } else { *width = scene_buffer->buffer->width; *height = scene_buffer->buffer->height; } } break; } } static int scale_length(int length, int offset, float scale) { return round((offset + length) * scale) - round(offset * scale); } static void scale_box(struct wlr_box *box, float scale) { box->width = scale_length(box->width, box->x, scale); box->height = scale_length(box->height, box->y, scale); box->x = round(box->x * scale); box->y = round(box->y * scale); } void wlr_scene_node_set_enabled(struct wlr_scene_node *node, bool enabled) { if (node->enabled == enabled) { return; } int x, y; pixman_region32_t visible; pixman_region32_init(&visible); if (wlr_scene_node_coords(node, &x, &y)) { scene_node_visibility(node, &visible); } node->enabled = enabled; scene_node_update(node, &visible); } void wlr_scene_node_set_position(struct wlr_scene_node *node, int x, int y) { if (node->x == x && node->y == y) { return; } node->x = x; node->y = y; scene_node_update(node, NULL); } void wlr_scene_node_place_above(struct wlr_scene_node *node, struct wlr_scene_node *sibling) { assert(node != sibling); assert(node->parent == sibling->parent); if (node->link.prev == &sibling->link) { return; } wl_list_remove(&node->link); wl_list_insert(&sibling->link, &node->link); scene_node_update(node, NULL); } void wlr_scene_node_place_below(struct wlr_scene_node *node, struct wlr_scene_node *sibling) { assert(node != sibling); assert(node->parent == sibling->parent); if (node->link.next == &sibling->link) { return; } wl_list_remove(&node->link); wl_list_insert(sibling->link.prev, &node->link); scene_node_update(node, NULL); } void wlr_scene_node_raise_to_top(struct wlr_scene_node *node) { struct wlr_scene_node *current_top = wl_container_of( node->parent->children.prev, current_top, link); if (node == current_top) { return; } wlr_scene_node_place_above(node, current_top); } void wlr_scene_node_lower_to_bottom(struct wlr_scene_node *node) { struct wlr_scene_node *current_bottom = wl_container_of( node->parent->children.next, current_bottom, link); if (node == current_bottom) { return; } wlr_scene_node_place_below(node, current_bottom); } void wlr_scene_node_reparent(struct wlr_scene_node *node, struct wlr_scene_tree *new_parent) { assert(new_parent != NULL); if (node->parent == new_parent) { return; } /* Ensure that a node cannot become its own ancestor */ for (struct wlr_scene_tree *ancestor = new_parent; ancestor != NULL; ancestor = ancestor->node.parent) { assert(&ancestor->node != node); } int x, y; pixman_region32_t visible; pixman_region32_init(&visible); if (wlr_scene_node_coords(node, &x, &y)) { scene_node_visibility(node, &visible); } wl_list_remove(&node->link); node->parent = new_parent; wl_list_insert(new_parent->children.prev, &node->link); scene_node_update(node, &visible); } bool wlr_scene_node_coords(struct wlr_scene_node *node, int *lx_ptr, int *ly_ptr) { assert(node); int lx = 0, ly = 0; bool enabled = true; while (true) { lx += node->x; ly += node->y; enabled = enabled && node->enabled; if (node->parent == NULL) { break; } node = &node->parent->node; } *lx_ptr = lx; *ly_ptr = ly; return enabled; } static void scene_node_for_each_scene_buffer(struct wlr_scene_node *node, int lx, int ly, wlr_scene_buffer_iterator_func_t user_iterator, void *user_data) { if (!node->enabled) { return; } lx += node->x; ly += node->y; if (node->type == WLR_SCENE_NODE_BUFFER) { struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); user_iterator(scene_buffer, lx, ly, user_data); } else if (node->type == WLR_SCENE_NODE_TREE) { struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node); struct wlr_scene_node *child; wl_list_for_each(child, &scene_tree->children, link) { scene_node_for_each_scene_buffer(child, lx, ly, user_iterator, user_data); } } } void wlr_scene_node_for_each_buffer(struct wlr_scene_node *node, wlr_scene_buffer_iterator_func_t user_iterator, void *user_data) { scene_node_for_each_scene_buffer(node, 0, 0, user_iterator, user_data); } struct node_at_data { double lx, ly; double rx, ry; struct wlr_scene_node *node; }; static bool scene_node_at_iterator(struct wlr_scene_node *node, int lx, int ly, void *data) { struct node_at_data *at_data = data; double rx = at_data->lx - lx; double ry = at_data->ly - ly; if (node->type == WLR_SCENE_NODE_BUFFER) { struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); if (scene_buffer->point_accepts_input && !scene_buffer->point_accepts_input(scene_buffer, &rx, &ry)) { return false; } } at_data->rx = rx; at_data->ry = ry; at_data->node = node; return true; } struct wlr_scene_node *wlr_scene_node_at(struct wlr_scene_node *node, double lx, double ly, double *nx, double *ny) { struct wlr_box box = { .x = floor(lx), .y = floor(ly), .width = 1, .height = 1 }; struct node_at_data data = { .lx = lx, .ly = ly }; if (scene_nodes_in_box(node, &box, scene_node_at_iterator, &data)) { if (nx) { *nx = data.rx; } if (ny) { *ny = data.ry; } return data.node; } return NULL; } struct render_list_entry { struct wlr_scene_node *node; bool sent_dmabuf_feedback; int x, y; }; static void scene_entry_render(struct render_list_entry *entry, const struct render_data *data) { struct wlr_scene_node *node = entry->node; pixman_region32_t render_region; pixman_region32_init(&render_region); pixman_region32_copy(&render_region, &node->visible); pixman_region32_translate(&render_region, -data->logical.x, -data->logical.y); scale_output_damage(&render_region, data->scale); pixman_region32_intersect(&render_region, &render_region, &data->damage); if (!pixman_region32_not_empty(&render_region)) { pixman_region32_fini(&render_region); return; } struct wlr_box dst_box = { .x = entry->x - data->logical.x, .y = entry->y - data->logical.y, }; scene_node_get_size(node, &dst_box.width, &dst_box.height); scale_box(&dst_box, data->scale); pixman_region32_t opaque; pixman_region32_init(&opaque); scene_node_opaque_region(node, dst_box.x, dst_box.y, &opaque); scale_output_damage(&opaque, data->scale); pixman_region32_subtract(&opaque, &render_region, &opaque); transform_output_box(&dst_box, data); transform_output_damage(&render_region, data); switch (node->type) { case WLR_SCENE_NODE_TREE: assert(false); break; case WLR_SCENE_NODE_RECT:; struct wlr_scene_rect *scene_rect = wlr_scene_rect_from_node(node); wlr_render_pass_add_rect(data->render_pass, &(struct wlr_render_rect_options){ .box = dst_box, .color = { .r = scene_rect->color[0], .g = scene_rect->color[1], .b = scene_rect->color[2], .a = scene_rect->color[3], }, .clip = &render_region, }); break; case WLR_SCENE_NODE_BUFFER:; struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); assert(scene_buffer->buffer); struct wlr_texture *texture = scene_buffer_get_texture(scene_buffer, data->output->output->renderer); if (texture == NULL) { break; } enum wl_output_transform transform = wlr_output_transform_invert(scene_buffer->transform); transform = wlr_output_transform_compose(transform, data->transform); wlr_render_pass_add_texture(data->render_pass, &(struct wlr_render_texture_options) { .texture = texture, .src_box = scene_buffer->src_box, .dst_box = dst_box, .transform = transform, .clip = &render_region, .alpha = &scene_buffer->opacity, .filter_mode = scene_buffer->filter_mode, .blend_mode = pixman_region32_not_empty(&opaque) ? WLR_RENDER_BLEND_MODE_PREMULTIPLIED : WLR_RENDER_BLEND_MODE_NONE, }); struct wlr_scene_output_sample_event sample_event = { .output = data->output, .direct_scanout = false, }; wl_signal_emit_mutable(&scene_buffer->events.output_sample, &sample_event); break; } pixman_region32_fini(&opaque); pixman_region32_fini(&render_region); } static void scene_handle_presentation_destroy(struct wl_listener *listener, void *data) { struct wlr_scene *scene = wl_container_of(listener, scene, presentation_destroy); wl_list_remove(&scene->presentation_destroy.link); wl_list_init(&scene->presentation_destroy.link); scene->presentation = NULL; } void wlr_scene_set_presentation(struct wlr_scene *scene, struct wlr_presentation *presentation) { assert(scene->presentation == NULL); scene->presentation = presentation; scene->presentation_destroy.notify = scene_handle_presentation_destroy; wl_signal_add(&presentation->events.destroy, &scene->presentation_destroy); } static void scene_handle_linux_dmabuf_v1_destroy(struct wl_listener *listener, void *data) { struct wlr_scene *scene = wl_container_of(listener, scene, linux_dmabuf_v1_destroy); wl_list_remove(&scene->linux_dmabuf_v1_destroy.link); wl_list_init(&scene->linux_dmabuf_v1_destroy.link); scene->linux_dmabuf_v1 = NULL; } void wlr_scene_set_linux_dmabuf_v1(struct wlr_scene *scene, struct wlr_linux_dmabuf_v1 *linux_dmabuf_v1) { assert(scene->linux_dmabuf_v1 == NULL); scene->linux_dmabuf_v1 = linux_dmabuf_v1; scene->linux_dmabuf_v1_destroy.notify = scene_handle_linux_dmabuf_v1_destroy; wl_signal_add(&linux_dmabuf_v1->events.destroy, &scene->linux_dmabuf_v1_destroy); } static void scene_output_handle_destroy(struct wlr_addon *addon) { struct wlr_scene_output *scene_output = wl_container_of(addon, scene_output, addon); wlr_scene_output_destroy(scene_output); } static const struct wlr_addon_interface output_addon_impl = { .name = "wlr_scene_output", .destroy = scene_output_handle_destroy, }; static void scene_node_output_update(struct wlr_scene_node *node, struct wl_list *outputs, struct wlr_scene_output *ignore, struct wlr_scene_output *force) { if (node->type == WLR_SCENE_NODE_TREE) { struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node); struct wlr_scene_node *child; wl_list_for_each(child, &scene_tree->children, link) { scene_node_output_update(child, outputs, ignore, force); } return; } update_node_update_outputs(node, outputs, ignore, force); } static void scene_output_update_geometry(struct wlr_scene_output *scene_output, bool force_update) { wlr_damage_ring_add_whole(&scene_output->damage_ring); wlr_output_schedule_frame(scene_output->output); scene_node_output_update(&scene_output->scene->tree.node, &scene_output->scene->outputs, NULL, force_update ? scene_output : NULL); } static void scene_output_handle_commit(struct wl_listener *listener, void *data) { struct wlr_scene_output *scene_output = wl_container_of(listener, scene_output, output_commit); struct wlr_output_event_commit *event = data; const struct wlr_output_state *state = event->state; bool force_update = state->committed & ( WLR_OUTPUT_STATE_TRANSFORM | WLR_OUTPUT_STATE_SCALE | WLR_OUTPUT_STATE_SUBPIXEL); if (force_update || state->committed & (WLR_OUTPUT_STATE_MODE | WLR_OUTPUT_STATE_ENABLED)) { scene_output_update_geometry(scene_output, force_update); } } static void scene_output_handle_damage(struct wl_listener *listener, void *data) { struct wlr_scene_output *scene_output = wl_container_of(listener, scene_output, output_damage); struct wlr_output_event_damage *event = data; if (wlr_damage_ring_add(&scene_output->damage_ring, event->damage)) { wlr_output_schedule_frame(scene_output->output); } } static void scene_output_handle_needs_frame(struct wl_listener *listener, void *data) { struct wlr_scene_output *scene_output = wl_container_of(listener, scene_output, output_needs_frame); wlr_output_schedule_frame(scene_output->output); } struct wlr_scene_output *wlr_scene_output_create(struct wlr_scene *scene, struct wlr_output *output) { struct wlr_scene_output *scene_output = calloc(1, sizeof(*scene_output)); if (scene_output == NULL) { return NULL; } scene_output->output = output; scene_output->scene = scene; wlr_addon_init(&scene_output->addon, &output->addons, scene, &output_addon_impl); wlr_damage_ring_init(&scene_output->damage_ring); wl_list_init(&scene_output->damage_highlight_regions); int prev_output_index = -1; struct wl_list *prev_output_link = &scene->outputs; struct wlr_scene_output *current_output; wl_list_for_each(current_output, &scene->outputs, link) { if (prev_output_index + 1 != current_output->index) { break; } prev_output_index = current_output->index; prev_output_link = ¤t_output->link; } scene_output->index = prev_output_index + 1; assert(scene_output->index < 64); wl_list_insert(prev_output_link, &scene_output->link); wl_signal_init(&scene_output->events.destroy); scene_output->output_commit.notify = scene_output_handle_commit; wl_signal_add(&output->events.commit, &scene_output->output_commit); scene_output->output_damage.notify = scene_output_handle_damage; wl_signal_add(&output->events.damage, &scene_output->output_damage); scene_output->output_needs_frame.notify = scene_output_handle_needs_frame; wl_signal_add(&output->events.needs_frame, &scene_output->output_needs_frame); scene_output_update_geometry(scene_output, false); return scene_output; } static void highlight_region_destroy(struct highlight_region *damage) { wl_list_remove(&damage->link); pixman_region32_fini(&damage->region); free(damage); } void wlr_scene_output_destroy(struct wlr_scene_output *scene_output) { if (scene_output == NULL) { return; } wl_signal_emit_mutable(&scene_output->events.destroy, NULL); scene_node_output_update(&scene_output->scene->tree.node, &scene_output->scene->outputs, scene_output, NULL); struct highlight_region *damage, *tmp_damage; wl_list_for_each_safe(damage, tmp_damage, &scene_output->damage_highlight_regions, link) { highlight_region_destroy(damage); } wlr_addon_finish(&scene_output->addon); wlr_damage_ring_finish(&scene_output->damage_ring); wl_list_remove(&scene_output->link); wl_list_remove(&scene_output->output_commit.link); wl_list_remove(&scene_output->output_damage.link); wl_list_remove(&scene_output->output_needs_frame.link); wl_array_release(&scene_output->render_list); free(scene_output); } struct wlr_scene_output *wlr_scene_get_scene_output(struct wlr_scene *scene, struct wlr_output *output) { struct wlr_addon *addon = wlr_addon_find(&output->addons, scene, &output_addon_impl); if (addon == NULL) { return NULL; } struct wlr_scene_output *scene_output = wl_container_of(addon, scene_output, addon); return scene_output; } void wlr_scene_output_set_position(struct wlr_scene_output *scene_output, int lx, int ly) { if (scene_output->x == lx && scene_output->y == ly) { return; } scene_output->x = lx; scene_output->y = ly; scene_output_update_geometry(scene_output, false); } static bool scene_node_invisible(struct wlr_scene_node *node) { if (node->type == WLR_SCENE_NODE_TREE) { return true; } else if (node->type == WLR_SCENE_NODE_RECT) { struct wlr_scene_rect *rect = wlr_scene_rect_from_node(node); return rect->color[3] == 0.f; } else if (node->type == WLR_SCENE_NODE_BUFFER) { struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node); return buffer->buffer == NULL; } return false; } struct render_list_constructor_data { struct wlr_box box; struct wl_array *render_list; bool calculate_visibility; }; static bool construct_render_list_iterator(struct wlr_scene_node *node, int lx, int ly, void *_data) { struct render_list_constructor_data *data = _data; if (scene_node_invisible(node)) { return false; } // while rendering, the background should always be black. // If we see a black rect, we can ignore rendering everything under the rect // and even the rect itself. if (node->type == WLR_SCENE_NODE_RECT && data->calculate_visibility) { struct wlr_scene_rect *rect = wlr_scene_rect_from_node(node); float *black = (float[4]){ 0.f, 0.f, 0.f, 1.f }; if (memcmp(rect->color, black, sizeof(float) * 4) == 0) { return false; } } pixman_region32_t intersection; pixman_region32_init(&intersection); pixman_region32_intersect_rect(&intersection, &node->visible, data->box.x, data->box.y, data->box.width, data->box.height); if (!pixman_region32_not_empty(&intersection)) { pixman_region32_fini(&intersection); return false; } pixman_region32_fini(&intersection); struct render_list_entry *entry = wl_array_add(data->render_list, sizeof(*entry)); if (!entry) { return false; } *entry = (struct render_list_entry){ .node = node, .x = lx, .y = ly, }; return false; } static void output_state_apply_damage(const struct render_data *data, struct wlr_output_state *state) { pixman_region32_t frame_damage; pixman_region32_init(&frame_damage); pixman_region32_copy(&frame_damage, &data->output->damage_ring.current); transform_output_damage(&frame_damage, data); wlr_output_state_set_damage(state, &frame_damage); pixman_region32_fini(&frame_damage); } static void scene_buffer_send_dmabuf_feedback(const struct wlr_scene *scene, struct wlr_scene_buffer *scene_buffer, const struct wlr_linux_dmabuf_feedback_v1_init_options *options) { if (!scene->linux_dmabuf_v1) { return; } struct wlr_scene_surface *surface = wlr_scene_surface_try_from_buffer(scene_buffer); if (!surface) { return; } // compare to the previous options so that we don't send // duplicate feedback events. if (memcmp(options, &scene_buffer->prev_feedback_options, sizeof(*options)) == 0) { return; } scene_buffer->prev_feedback_options = *options; struct wlr_linux_dmabuf_feedback_v1 feedback = {0}; if (!wlr_linux_dmabuf_feedback_v1_init_with_options(&feedback, options)) { return; } wlr_linux_dmabuf_v1_set_surface_feedback(scene->linux_dmabuf_v1, surface->surface, &feedback); wlr_linux_dmabuf_feedback_v1_finish(&feedback); } static bool scene_entry_try_direct_scanout(struct render_list_entry *entry, struct wlr_output_state *state, const struct render_data *data) { struct wlr_scene_output *scene_output = data->output; struct wlr_scene_node *node = entry->node; if (!scene_output->scene->direct_scanout) { return false; } if (scene_output->scene->debug_damage_option == WLR_SCENE_DEBUG_DAMAGE_HIGHLIGHT) { // We don't want to enter direct scan out if we have highlight regions // enabled. Otherwise, we won't be able to render the damage regions. return false; } if (node->type != WLR_SCENE_NODE_BUFFER) { return false; } if (state->committed & (WLR_OUTPUT_STATE_MODE | WLR_OUTPUT_STATE_ENABLED | WLR_OUTPUT_STATE_RENDER_FORMAT)) { // Legacy DRM will explode if we try to modeset with a direct scanout buffer return false; } if (!wlr_output_is_direct_scanout_allowed(scene_output->output)) { return false; } struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node); struct wlr_fbox default_box = {0}; if (buffer->transform & WL_OUTPUT_TRANSFORM_90) { default_box.width = buffer->buffer->height; default_box.height = buffer->buffer->width; } else { default_box.width = buffer->buffer->width; default_box.height = buffer->buffer->height; } if (!wlr_fbox_empty(&buffer->src_box) && !wlr_fbox_equal(&buffer->src_box, &default_box)) { return false; } if (buffer->transform != data->transform) { return false; } struct wlr_box node_box = { .x = entry->x, .y = entry->y }; scene_node_get_size(node, &node_box.width, &node_box.height); if (!wlr_box_equal(&data->logical, &node_box)) { return false; } if (buffer->primary_output == scene_output) { struct wlr_linux_dmabuf_feedback_v1_init_options options = { .main_renderer = scene_output->output->renderer, .scanout_primary_output = scene_output->output, }; scene_buffer_send_dmabuf_feedback(scene_output->scene, buffer, &options); entry->sent_dmabuf_feedback = true; } struct wlr_output_state pending; wlr_output_state_init(&pending); if (!wlr_output_state_copy(&pending, state)) { return false; } wlr_output_state_set_buffer(&pending, buffer->buffer); output_state_apply_damage(data, &pending); if (!wlr_output_test_state(scene_output->output, &pending)) { wlr_output_state_finish(&pending); return false; } wlr_output_state_copy(state, &pending); wlr_output_state_finish(&pending); struct wlr_scene_output_sample_event sample_event = { .output = scene_output, .direct_scanout = true, }; wl_signal_emit_mutable(&buffer->events.output_sample, &sample_event); return true; } bool wlr_scene_output_commit(struct wlr_scene_output *scene_output, const struct wlr_scene_output_state_options *options) { if (!scene_output->output->needs_frame && !pixman_region32_not_empty( &scene_output->damage_ring.current)) { return true; } bool ok = false; struct wlr_output_state state; wlr_output_state_init(&state); if (!wlr_scene_output_build_state(scene_output, &state, options)) { goto out; } ok = wlr_output_commit_state(scene_output->output, &state); if (!ok) { goto out; } wlr_damage_ring_rotate(&scene_output->damage_ring); out: wlr_output_state_finish(&state); return ok; } bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, struct wlr_output_state *state, const struct wlr_scene_output_state_options *options) { struct wlr_scene_output_state_options default_options = {0}; if (!options) { options = &default_options; } struct wlr_scene_timer *timer = options->timer; struct timespec start_time; if (timer) { clock_gettime(CLOCK_MONOTONIC, &start_time); wlr_scene_timer_finish(timer); *timer = (struct wlr_scene_timer){0}; } if ((state->committed & WLR_OUTPUT_STATE_ENABLED) && !state->enabled) { // if the state is being disabled, do nothing. return true; } struct wlr_output *output = scene_output->output; enum wlr_scene_debug_damage_option debug_damage = scene_output->scene->debug_damage_option; struct render_data render_data = { .transform = output->transform, .scale = output->scale, .logical = { .x = scene_output->x, .y = scene_output->y }, .output = scene_output, }; output_pending_resolution(output, state, &render_data.trans_width, &render_data.trans_height); if (state->committed & WLR_OUTPUT_STATE_TRANSFORM) { if (render_data.transform != state->transform) { wlr_damage_ring_add_whole(&scene_output->damage_ring); } render_data.transform = state->transform; } if (state->committed & WLR_OUTPUT_STATE_SCALE) { if (render_data.scale != state->scale) { wlr_damage_ring_add_whole(&scene_output->damage_ring); } render_data.scale = state->scale; } if (render_data.transform & WL_OUTPUT_TRANSFORM_90) { int tmp = render_data.trans_width; render_data.trans_width = render_data.trans_height; render_data.trans_height = tmp; } render_data.logical.width = render_data.trans_width / render_data.scale; render_data.logical.height = render_data.trans_height / render_data.scale; struct render_list_constructor_data list_con = { .box = render_data.logical, .render_list = &scene_output->render_list, .calculate_visibility = scene_output->scene->calculate_visibility, }; list_con.render_list->size = 0; scene_nodes_in_box(&scene_output->scene->tree.node, &list_con.box, construct_render_list_iterator, &list_con); array_realloc(list_con.render_list, list_con.render_list->size); struct render_list_entry *list_data = list_con.render_list->data; int list_len = list_con.render_list->size / sizeof(*list_data); bool scanout = list_len == 1 && scene_entry_try_direct_scanout(&list_data[0], state, &render_data); if (scene_output->prev_scanout != scanout) { scene_output->prev_scanout = scanout; wlr_log(WLR_DEBUG, "Direct scan-out %s", scanout ? "enabled" : "disabled"); if (!scanout) { // When exiting direct scan-out, damage everything wlr_damage_ring_add_whole(&scene_output->damage_ring); } } if (scanout) { if (timer) { struct timespec end_time, duration; clock_gettime(CLOCK_MONOTONIC, &end_time); timespec_sub(&duration, &end_time, &start_time); timer->pre_render_duration = timespec_to_nsec(&duration); } return true; } if (debug_damage == WLR_SCENE_DEBUG_DAMAGE_RERENDER) { wlr_damage_ring_add_whole(&scene_output->damage_ring); } struct timespec now; if (debug_damage == WLR_SCENE_DEBUG_DAMAGE_HIGHLIGHT) { struct wl_list *regions = &scene_output->damage_highlight_regions; clock_gettime(CLOCK_MONOTONIC, &now); // add the current frame's damage if there is damage if (pixman_region32_not_empty(&scene_output->damage_ring.current)) { struct highlight_region *current_damage = calloc(1, sizeof(*current_damage)); if (current_damage) { pixman_region32_init(¤t_damage->region); pixman_region32_copy(¤t_damage->region, &scene_output->damage_ring.current); current_damage->when = now; wl_list_insert(regions, ¤t_damage->link); } } pixman_region32_t acc_damage; pixman_region32_init(&acc_damage); struct highlight_region *damage, *tmp_damage; wl_list_for_each_safe(damage, tmp_damage, regions, link) { // remove overlaping damage regions pixman_region32_subtract(&damage->region, &damage->region, &acc_damage); pixman_region32_union(&acc_damage, &acc_damage, &damage->region); // if this damage is too old or has nothing in it, get rid of it struct timespec time_diff; timespec_sub(&time_diff, &now, &damage->when); if (timespec_to_msec(&time_diff) >= HIGHLIGHT_DAMAGE_FADEOUT_TIME || !pixman_region32_not_empty(&damage->region)) { highlight_region_destroy(damage); } } wlr_damage_ring_add(&scene_output->damage_ring, &acc_damage); pixman_region32_fini(&acc_damage); } wlr_damage_ring_set_bounds(&scene_output->damage_ring, render_data.trans_width, render_data.trans_height); if (!wlr_output_configure_primary_swapchain(output, state, &output->swapchain)) { return false; } int buffer_age; struct wlr_buffer *buffer = wlr_swapchain_acquire(output->swapchain, &buffer_age); if (buffer == NULL) { return false; } if (timer) { timer->render_timer = wlr_render_timer_create(output->renderer); struct timespec end_time, duration; clock_gettime(CLOCK_MONOTONIC, &end_time); timespec_sub(&duration, &end_time, &start_time); timer->pre_render_duration = timespec_to_nsec(&duration); } struct wlr_render_pass *render_pass = wlr_renderer_begin_buffer_pass(output->renderer, buffer, &(struct wlr_buffer_pass_options){ .timer = timer ? timer->render_timer : NULL, }); if (render_pass == NULL) { wlr_buffer_unlock(buffer); return false; } render_data.render_pass = render_pass; pixman_region32_init(&render_data.damage); wlr_damage_ring_get_buffer_damage(&scene_output->damage_ring, buffer_age, &render_data.damage); pixman_region32_t background; pixman_region32_init(&background); pixman_region32_copy(&background, &render_data.damage); // Cull areas of the background that are occluded by opaque regions of // scene nodes above. Those scene nodes will just render atop having us // never see the background. if (scene_output->scene->calculate_visibility) { for (int i = list_len - 1; i >= 0; i--) { struct render_list_entry *entry = &list_data[i]; // We must only cull opaque regions that are visible by the node. // The node's visibility will have the knowledge of a black rect // that may have been omitted from the render list via the black // rect optimization. In order to ensure we don't cull background // rendering in that black rect region, consider the node's visibility. pixman_region32_t opaque; pixman_region32_init(&opaque); scene_node_opaque_region(entry->node, entry->x, entry->y, &opaque); pixman_region32_intersect(&opaque, &opaque, &entry->node->visible); pixman_region32_translate(&opaque, -scene_output->x, -scene_output->y); wlr_region_scale(&opaque, &opaque, render_data.scale); pixman_region32_subtract(&background, &background, &opaque); pixman_region32_fini(&opaque); } if (floor(render_data.scale) != render_data.scale) { wlr_region_expand(&background, &background, 1); // reintersect with the damage because we never want to render // outside of the damage region pixman_region32_intersect(&background, &background, &render_data.damage); } } transform_output_damage(&background, &render_data); wlr_render_pass_add_rect(render_pass, &(struct wlr_render_rect_options){ .box = { .width = buffer->width, .height = buffer->height }, .color = { .r = 0, .g = 0, .b = 0, .a = 1 }, .clip = &background, }); pixman_region32_fini(&background); for (int i = list_len - 1; i >= 0; i--) { struct render_list_entry *entry = &list_data[i]; scene_entry_render(entry, &render_data); if (entry->node->type == WLR_SCENE_NODE_BUFFER) { struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(entry->node); if (buffer->primary_output == scene_output && !entry->sent_dmabuf_feedback) { struct wlr_linux_dmabuf_feedback_v1_init_options options = { .main_renderer = output->renderer, .scanout_primary_output = NULL, }; scene_buffer_send_dmabuf_feedback(scene_output->scene, buffer, &options); } } } if (debug_damage == WLR_SCENE_DEBUG_DAMAGE_HIGHLIGHT) { struct highlight_region *damage; wl_list_for_each(damage, &scene_output->damage_highlight_regions, link) { struct timespec time_diff; timespec_sub(&time_diff, &now, &damage->when); int64_t time_diff_ms = timespec_to_msec(&time_diff); float alpha = 1.0 - (double)time_diff_ms / HIGHLIGHT_DAMAGE_FADEOUT_TIME; wlr_render_pass_add_rect(render_pass, &(struct wlr_render_rect_options){ .box = { .width = buffer->width, .height = buffer->height }, .color = { .r = alpha * 0.5, .g = 0, .b = 0, .a = alpha * 0.5 }, .clip = &damage->region, }); } } wlr_output_add_software_cursors_to_render_pass(output, render_pass, &render_data.damage); pixman_region32_fini(&render_data.damage); if (!wlr_render_pass_submit(render_pass)) { wlr_buffer_unlock(buffer); return false; } wlr_output_state_set_buffer(state, buffer); wlr_buffer_unlock(buffer); output_state_apply_damage(&render_data, state); if (debug_damage == WLR_SCENE_DEBUG_DAMAGE_HIGHLIGHT && !wl_list_empty(&scene_output->damage_highlight_regions)) { wlr_output_schedule_frame(scene_output->output); } return true; } int64_t wlr_scene_timer_get_duration_ns(struct wlr_scene_timer *timer) { int64_t pre_render = timer->pre_render_duration; if (!timer->render_timer) { return pre_render; } int64_t render = wlr_render_timer_get_duration_ns(timer->render_timer); return render != -1 ? pre_render + render : -1; } void wlr_scene_timer_finish(struct wlr_scene_timer *timer) { if (timer->render_timer) { wlr_render_timer_destroy(timer->render_timer); } } static void scene_node_send_frame_done(struct wlr_scene_node *node, struct wlr_scene_output *scene_output, struct timespec *now) { if (!node->enabled) { return; } if (node->type == WLR_SCENE_NODE_BUFFER) { struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); if (scene_buffer->primary_output == scene_output) { wlr_scene_buffer_send_frame_done(scene_buffer, now); } } else if (node->type == WLR_SCENE_NODE_TREE) { struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node); struct wlr_scene_node *child; wl_list_for_each(child, &scene_tree->children, link) { scene_node_send_frame_done(child, scene_output, now); } } } void wlr_scene_output_send_frame_done(struct wlr_scene_output *scene_output, struct timespec *now) { scene_node_send_frame_done(&scene_output->scene->tree.node, scene_output, now); } static void scene_output_for_each_scene_buffer(const struct wlr_box *output_box, struct wlr_scene_node *node, int lx, int ly, wlr_scene_buffer_iterator_func_t user_iterator, void *user_data) { if (!node->enabled) { return; } lx += node->x; ly += node->y; if (node->type == WLR_SCENE_NODE_BUFFER) { struct wlr_box node_box = { .x = lx, .y = ly }; scene_node_get_size(node, &node_box.width, &node_box.height); struct wlr_box intersection; if (wlr_box_intersection(&intersection, output_box, &node_box)) { struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); user_iterator(scene_buffer, lx, ly, user_data); } } else if (node->type == WLR_SCENE_NODE_TREE) { struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node); struct wlr_scene_node *child; wl_list_for_each(child, &scene_tree->children, link) { scene_output_for_each_scene_buffer(output_box, child, lx, ly, user_iterator, user_data); } } } void wlr_scene_output_for_each_buffer(struct wlr_scene_output *scene_output, wlr_scene_buffer_iterator_func_t iterator, void *user_data) { struct wlr_box box = { .x = scene_output->x, .y = scene_output->y }; wlr_output_effective_resolution(scene_output->output, &box.width, &box.height); scene_output_for_each_scene_buffer(&box, &scene_output->scene->tree.node, 0, 0, iterator, user_data); } wlroots-0.17.1/types/scene/xdg_shell.c000066400000000000000000000110041454110342200176570ustar00rootroot00000000000000#include #include #include struct wlr_scene_xdg_surface { struct wlr_scene_tree *tree; struct wlr_xdg_surface *xdg_surface; struct wlr_scene_tree *surface_tree; struct wl_listener tree_destroy; struct wl_listener xdg_surface_destroy; struct wl_listener xdg_surface_map; struct wl_listener xdg_surface_unmap; struct wl_listener xdg_surface_commit; }; static void scene_xdg_surface_handle_tree_destroy(struct wl_listener *listener, void *data) { struct wlr_scene_xdg_surface *scene_xdg_surface = wl_container_of(listener, scene_xdg_surface, tree_destroy); // tree and surface_node will be cleaned up by scene_node_finish wl_list_remove(&scene_xdg_surface->tree_destroy.link); wl_list_remove(&scene_xdg_surface->xdg_surface_destroy.link); wl_list_remove(&scene_xdg_surface->xdg_surface_map.link); wl_list_remove(&scene_xdg_surface->xdg_surface_unmap.link); wl_list_remove(&scene_xdg_surface->xdg_surface_commit.link); free(scene_xdg_surface); } static void scene_xdg_surface_handle_xdg_surface_destroy(struct wl_listener *listener, void *data) { struct wlr_scene_xdg_surface *scene_xdg_surface = wl_container_of(listener, scene_xdg_surface, xdg_surface_destroy); wlr_scene_node_destroy(&scene_xdg_surface->tree->node); } static void scene_xdg_surface_handle_xdg_surface_map(struct wl_listener *listener, void *data) { struct wlr_scene_xdg_surface *scene_xdg_surface = wl_container_of(listener, scene_xdg_surface, xdg_surface_map); wlr_scene_node_set_enabled(&scene_xdg_surface->tree->node, true); } static void scene_xdg_surface_handle_xdg_surface_unmap(struct wl_listener *listener, void *data) { struct wlr_scene_xdg_surface *scene_xdg_surface = wl_container_of(listener, scene_xdg_surface, xdg_surface_unmap); wlr_scene_node_set_enabled(&scene_xdg_surface->tree->node, false); } static void scene_xdg_surface_update_position( struct wlr_scene_xdg_surface *scene_xdg_surface) { struct wlr_xdg_surface *xdg_surface = scene_xdg_surface->xdg_surface; struct wlr_box geo = {0}; wlr_xdg_surface_get_geometry(xdg_surface, &geo); wlr_scene_node_set_position(&scene_xdg_surface->surface_tree->node, -geo.x, -geo.y); if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { struct wlr_xdg_popup *popup = xdg_surface->popup; if (popup != NULL) { wlr_scene_node_set_position(&scene_xdg_surface->tree->node, popup->current.geometry.x, popup->current.geometry.y); } } } static void scene_xdg_surface_handle_xdg_surface_commit(struct wl_listener *listener, void *data) { struct wlr_scene_xdg_surface *scene_xdg_surface = wl_container_of(listener, scene_xdg_surface, xdg_surface_commit); scene_xdg_surface_update_position(scene_xdg_surface); } struct wlr_scene_tree *wlr_scene_xdg_surface_create( struct wlr_scene_tree *parent, struct wlr_xdg_surface *xdg_surface) { struct wlr_scene_xdg_surface *scene_xdg_surface = calloc(1, sizeof(*scene_xdg_surface)); if (scene_xdg_surface == NULL) { return NULL; } scene_xdg_surface->xdg_surface = xdg_surface; scene_xdg_surface->tree = wlr_scene_tree_create(parent); if (scene_xdg_surface->tree == NULL) { free(scene_xdg_surface); return NULL; } scene_xdg_surface->surface_tree = wlr_scene_subsurface_tree_create( scene_xdg_surface->tree, xdg_surface->surface); if (scene_xdg_surface->surface_tree == NULL) { wlr_scene_node_destroy(&scene_xdg_surface->tree->node); free(scene_xdg_surface); return NULL; } scene_xdg_surface->tree_destroy.notify = scene_xdg_surface_handle_tree_destroy; wl_signal_add(&scene_xdg_surface->tree->node.events.destroy, &scene_xdg_surface->tree_destroy); scene_xdg_surface->xdg_surface_destroy.notify = scene_xdg_surface_handle_xdg_surface_destroy; wl_signal_add(&xdg_surface->events.destroy, &scene_xdg_surface->xdg_surface_destroy); scene_xdg_surface->xdg_surface_map.notify = scene_xdg_surface_handle_xdg_surface_map; wl_signal_add(&xdg_surface->surface->events.map, &scene_xdg_surface->xdg_surface_map); scene_xdg_surface->xdg_surface_unmap.notify = scene_xdg_surface_handle_xdg_surface_unmap; wl_signal_add(&xdg_surface->surface->events.unmap, &scene_xdg_surface->xdg_surface_unmap); scene_xdg_surface->xdg_surface_commit.notify = scene_xdg_surface_handle_xdg_surface_commit; wl_signal_add(&xdg_surface->surface->events.commit, &scene_xdg_surface->xdg_surface_commit); wlr_scene_node_set_enabled(&scene_xdg_surface->tree->node, xdg_surface->surface->mapped); scene_xdg_surface_update_position(scene_xdg_surface); return scene_xdg_surface->tree; } wlroots-0.17.1/types/seat/000077500000000000000000000000001454110342200154055ustar00rootroot00000000000000wlroots-0.17.1/types/seat/wlr_seat.c000066400000000000000000000356601454110342200174030ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include #include #include #include "types/wlr_seat.h" #include "util/global.h" #define SEAT_VERSION 8 static void seat_handle_get_pointer(struct wl_client *client, struct wl_resource *seat_resource, uint32_t id) { uint32_t version = wl_resource_get_version(seat_resource); struct wlr_seat_client *seat_client = wlr_seat_client_from_resource(seat_resource); if (!seat_client) { // The client still needs a resource, so here's a dummy: seat_client_create_inert_pointer(client, version, id); return; } if (!(seat_client->seat->accumulated_capabilities & WL_SEAT_CAPABILITY_POINTER)) { wl_resource_post_error(seat_resource, WL_SEAT_ERROR_MISSING_CAPABILITY, "wl_seat.get_pointer called when no pointer capability has existed"); return; } seat_client_create_pointer(seat_client, version, id); } static void seat_handle_get_keyboard(struct wl_client *client, struct wl_resource *seat_resource, uint32_t id) { uint32_t version = wl_resource_get_version(seat_resource); struct wlr_seat_client *seat_client = wlr_seat_client_from_resource(seat_resource); if (!seat_client) { seat_client_create_inert_keyboard(client, version, id); return; } if (!(seat_client->seat->accumulated_capabilities & WL_SEAT_CAPABILITY_KEYBOARD)) { wl_resource_post_error(seat_resource, WL_SEAT_ERROR_MISSING_CAPABILITY, "wl_seat.get_keyboard called when no keyboard capability has existed"); return; } seat_client_create_keyboard(seat_client, version, id); } static void seat_handle_get_touch(struct wl_client *client, struct wl_resource *seat_resource, uint32_t id) { uint32_t version = wl_resource_get_version(seat_resource); struct wlr_seat_client *seat_client = wlr_seat_client_from_resource(seat_resource); if (!seat_client) { seat_client_create_inert_touch(client, version, id); return; } if (!(seat_client->seat->accumulated_capabilities & WL_SEAT_CAPABILITY_TOUCH)) { wl_resource_post_error(seat_resource, WL_SEAT_ERROR_MISSING_CAPABILITY, "wl_seat.get_touch called when no touch capability has existed"); return; } seat_client_create_touch(seat_client, version, id); } static void seat_client_destroy(struct wlr_seat_client *client) { wl_signal_emit_mutable(&client->events.destroy, client); if (client == client->seat->pointer_state.focused_client) { client->seat->pointer_state.focused_client = NULL; } if (client == client->seat->keyboard_state.focused_client) { client->seat->keyboard_state.focused_client = NULL; } if (client->seat->drag && client == client->seat->drag->seat_client) { client->seat->drag->seat_client = NULL; } struct wl_resource *resource, *tmp; wl_resource_for_each_safe(resource, tmp, &client->pointers) { seat_client_destroy_pointer(resource); } wl_resource_for_each_safe(resource, tmp, &client->keyboards) { seat_client_destroy_keyboard(resource); } wl_resource_for_each_safe(resource, tmp, &client->touches) { seat_client_destroy_touch(resource); } wl_resource_for_each_safe(resource, tmp, &client->data_devices) { // Make the data device inert wl_list_remove(wl_resource_get_link(resource)); wl_list_init(wl_resource_get_link(resource)); wl_resource_set_user_data(resource, NULL); } wl_resource_for_each_safe(resource, tmp, &client->resources) { // Make the seat resource inert wl_list_remove(wl_resource_get_link(resource)); wl_list_init(wl_resource_get_link(resource)); wl_resource_set_user_data(resource, NULL); } wl_list_remove(&client->link); free(client); } static void seat_client_handle_resource_destroy( struct wl_resource *seat_resource) { struct wlr_seat_client *client = wlr_seat_client_from_resource(seat_resource); if (!client) { return; } wl_list_remove(wl_resource_get_link(seat_resource)); if (!wl_list_empty(&client->resources)) { return; } seat_client_destroy(client); } static void seat_handle_release(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static const struct wl_seat_interface seat_impl = { .get_pointer = seat_handle_get_pointer, .get_keyboard = seat_handle_get_keyboard, .get_touch = seat_handle_get_touch, .release = seat_handle_release, }; static struct wlr_seat_client *seat_client_create(struct wlr_seat *wlr_seat, struct wl_client *client, struct wl_resource *wl_resource) { struct wlr_seat_client *seat_client = calloc(1, sizeof(*seat_client)); if (!seat_client) { return NULL; } seat_client->client = client; seat_client->seat = wlr_seat; wl_list_init(&seat_client->resources); wl_list_init(&seat_client->pointers); wl_list_init(&seat_client->keyboards); wl_list_init(&seat_client->touches); wl_list_init(&seat_client->data_devices); wl_signal_init(&seat_client->events.destroy); wl_list_insert(&wlr_seat->clients, &seat_client->link); struct wlr_surface *pointer_focus = wlr_seat->pointer_state.focused_surface; if (pointer_focus != NULL && wl_resource_get_client(pointer_focus->resource) == client) { wlr_seat->pointer_state.focused_client = seat_client; } struct wlr_surface *keyboard_focus = wlr_seat->keyboard_state.focused_surface; if (keyboard_focus != NULL && wl_resource_get_client(keyboard_focus->resource) == client) { wlr_seat->keyboard_state.focused_client = seat_client; } return seat_client; } static void seat_handle_bind(struct wl_client *client, void *_wlr_seat, uint32_t version, uint32_t id) { // `wlr_seat` can be NULL if the seat global is being destroyed struct wlr_seat *wlr_seat = _wlr_seat; struct wl_resource *wl_resource = wl_resource_create(client, &wl_seat_interface, version, id); if (wl_resource == NULL) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(wl_resource, &seat_impl, NULL, seat_client_handle_resource_destroy); wl_list_init(wl_resource_get_link(wl_resource)); if (wlr_seat == NULL) { return; } struct wlr_seat_client *seat_client = wlr_seat_client_for_wl_client(wlr_seat, client); if (!seat_client) { seat_client = seat_client_create(wlr_seat, client, wl_resource); } if (seat_client == NULL) { wl_resource_destroy(wl_resource); wl_client_post_no_memory(client); return; } wl_resource_set_user_data(wl_resource, seat_client); wl_list_insert(&seat_client->resources, wl_resource_get_link(wl_resource)); if (version >= WL_SEAT_NAME_SINCE_VERSION) { wl_seat_send_name(wl_resource, wlr_seat->name); } wl_seat_send_capabilities(wl_resource, wlr_seat->capabilities); } void wlr_seat_destroy(struct wlr_seat *seat) { if (!seat) { return; } wlr_seat_pointer_clear_focus(seat); wlr_seat_keyboard_clear_focus(seat); wlr_seat_set_keyboard(seat, NULL); struct wlr_touch_point *point; wl_list_for_each(point, &seat->touch_state.touch_points, link) { wlr_seat_touch_point_clear_focus(seat, 0, point->touch_id); } wl_signal_emit_mutable(&seat->events.destroy, seat); wl_list_remove(&seat->display_destroy.link); wlr_data_source_destroy(seat->selection_source); wlr_primary_selection_source_destroy(seat->primary_selection_source); struct wlr_seat_client *client, *tmp; wl_list_for_each_safe(client, tmp, &seat->clients, link) { seat_client_destroy(client); } wlr_global_destroy_safe(seat->global); free(seat->pointer_state.default_grab); free(seat->keyboard_state.default_grab); free(seat->touch_state.default_grab); free(seat->name); free(seat); } static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_seat *seat = wl_container_of(listener, seat, display_destroy); wlr_seat_destroy(seat); } struct wlr_seat *wlr_seat_create(struct wl_display *display, const char *name) { struct wlr_seat *seat = calloc(1, sizeof(*seat)); if (!seat) { return NULL; } // pointer state seat->pointer_state.seat = seat; wl_list_init(&seat->pointer_state.surface_destroy.link); struct wlr_seat_pointer_grab *pointer_grab = calloc(1, sizeof(*pointer_grab)); if (!pointer_grab) { free(seat); return NULL; } pointer_grab->interface = &default_pointer_grab_impl; pointer_grab->seat = seat; seat->pointer_state.default_grab = pointer_grab; seat->pointer_state.grab = pointer_grab; wl_signal_init(&seat->pointer_state.events.focus_change); // keyboard state struct wlr_seat_keyboard_grab *keyboard_grab = calloc(1, sizeof(*keyboard_grab)); if (!keyboard_grab) { free(pointer_grab); free(seat); return NULL; } keyboard_grab->interface = &default_keyboard_grab_impl; keyboard_grab->seat = seat; seat->keyboard_state.default_grab = keyboard_grab; seat->keyboard_state.grab = keyboard_grab; seat->keyboard_state.seat = seat; wl_list_init(&seat->keyboard_state.surface_destroy.link); wl_signal_init(&seat->keyboard_state.events.focus_change); // touch state struct wlr_seat_touch_grab *touch_grab = calloc(1, sizeof(*touch_grab)); if (!touch_grab) { free(pointer_grab); free(keyboard_grab); free(seat); return NULL; } touch_grab->interface = &default_touch_grab_impl; touch_grab->seat = seat; seat->touch_state.default_grab = touch_grab; seat->touch_state.grab = touch_grab; seat->touch_state.seat = seat; wl_list_init(&seat->touch_state.touch_points); seat->global = wl_global_create(display, &wl_seat_interface, SEAT_VERSION, seat, seat_handle_bind); if (seat->global == NULL) { free(touch_grab); free(pointer_grab); free(keyboard_grab); free(seat); return NULL; } seat->display = display; seat->name = strdup(name); wl_list_init(&seat->clients); wl_list_init(&seat->selection_offers); wl_list_init(&seat->drag_offers); wl_signal_init(&seat->events.request_start_drag); wl_signal_init(&seat->events.start_drag); wl_signal_init(&seat->events.request_set_cursor); wl_signal_init(&seat->events.request_set_selection); wl_signal_init(&seat->events.set_selection); wl_signal_init(&seat->events.request_set_primary_selection); wl_signal_init(&seat->events.set_primary_selection); wl_signal_init(&seat->events.pointer_grab_begin); wl_signal_init(&seat->events.pointer_grab_end); wl_signal_init(&seat->events.keyboard_grab_begin); wl_signal_init(&seat->events.keyboard_grab_end); wl_signal_init(&seat->events.touch_grab_begin); wl_signal_init(&seat->events.touch_grab_end); wl_signal_init(&seat->events.destroy); seat->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &seat->display_destroy); return seat; } struct wlr_seat_client *wlr_seat_client_for_wl_client(struct wlr_seat *wlr_seat, struct wl_client *wl_client) { struct wlr_seat_client *seat_client; wl_list_for_each(seat_client, &wlr_seat->clients, link) { if (seat_client->client == wl_client) { return seat_client; } } return NULL; } void wlr_seat_set_capabilities(struct wlr_seat *wlr_seat, uint32_t capabilities) { // if the capabilities haven't changed (i.e a redundant mouse was removed), // we don't actually have to do anything if (capabilities == wlr_seat->capabilities) { return; } wlr_seat->capabilities = capabilities; wlr_seat->accumulated_capabilities |= capabilities; struct wlr_seat_client *client; wl_list_for_each(client, &wlr_seat->clients, link) { // Make resources inert if necessary if ((capabilities & WL_SEAT_CAPABILITY_POINTER) == 0) { struct wlr_seat_client *focused_client = wlr_seat->pointer_state.focused_client; struct wlr_surface *focused_surface = wlr_seat->pointer_state.focused_surface; if (focused_client != NULL && focused_surface != NULL) { seat_client_send_pointer_leave_raw(focused_client, focused_surface); } // Note: we don't set focused client/surface to NULL since we need // them to send the enter event if the pointer is recreated struct wl_resource *resource, *tmp; wl_resource_for_each_safe(resource, tmp, &client->pointers) { seat_client_destroy_pointer(resource); } } if ((capabilities & WL_SEAT_CAPABILITY_KEYBOARD) == 0) { struct wlr_seat_client *focused_client = wlr_seat->keyboard_state.focused_client; struct wlr_surface *focused_surface = wlr_seat->keyboard_state.focused_surface; if (focused_client != NULL && focused_surface != NULL) { seat_client_send_keyboard_leave_raw(focused_client, focused_surface); } // Note: we don't set focused client/surface to NULL since we need // them to send the enter event if the keyboard is recreated struct wl_resource *resource, *tmp; wl_resource_for_each_safe(resource, tmp, &client->keyboards) { seat_client_destroy_keyboard(resource); } } if ((capabilities & WL_SEAT_CAPABILITY_TOUCH) == 0) { struct wl_resource *resource, *tmp; wl_resource_for_each_safe(resource, tmp, &client->touches) { seat_client_destroy_touch(resource); } } struct wl_resource *resource; wl_resource_for_each(resource, &client->resources) { wl_seat_send_capabilities(resource, capabilities); } } } void wlr_seat_set_name(struct wlr_seat *wlr_seat, const char *name) { free(wlr_seat->name); wlr_seat->name = strdup(name); struct wlr_seat_client *client; wl_list_for_each(client, &wlr_seat->clients, link) { struct wl_resource *resource; wl_resource_for_each(resource, &client->resources) { wl_seat_send_name(resource, name); } } } struct wlr_seat_client *wlr_seat_client_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &wl_seat_interface, &seat_impl)); return wl_resource_get_user_data(resource); } uint32_t wlr_seat_client_next_serial(struct wlr_seat_client *client) { uint32_t serial = wl_display_next_serial(wl_client_get_display(client->client)); struct wlr_serial_ringset *set = &client->serials; if (set->count == 0) { set->data[0].min_incl = serial; set->data[0].max_incl = serial; set->count = 1; set->end = 0; } else if (set->data[set->end].max_incl + 1 != serial) { if (set->count < WLR_SERIAL_RINGSET_SIZE) { set->count++; } set->end = (set->end + 1) % WLR_SERIAL_RINGSET_SIZE; set->data[set->end].min_incl = serial; set->data[set->end].max_incl = serial; } else { set->data[set->end].max_incl = serial; } return serial; } bool wlr_seat_client_validate_event_serial(struct wlr_seat_client *client, uint32_t serial) { uint32_t cur = wl_display_get_serial(wl_client_get_display(client->client)); struct wlr_serial_ringset *set = &client->serials; uint32_t rev_dist = cur - serial; if (rev_dist >= UINT32_MAX / 2) { // serial is closer to being 'newer' instead of 'older' than // the current serial, so it's either invalid or incredibly old return false; } for (int i = 0; i < set->count; i++) { int j = (set->end - i + WLR_SERIAL_RINGSET_SIZE) % WLR_SERIAL_RINGSET_SIZE; if (rev_dist < cur - set->data[j].max_incl) { return false; } if (rev_dist <= cur - set->data[j].min_incl) { return true; } } // Iff the set is full, then `rev_dist` is large enough that serial // could already have been recycled out of the set. return set->count == WLR_SERIAL_RINGSET_SIZE; } wlroots-0.17.1/types/seat/wlr_seat_keyboard.c000066400000000000000000000353571454110342200212660ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include #include #include "types/wlr_data_device.h" #include "types/wlr_seat.h" static void default_keyboard_enter(struct wlr_seat_keyboard_grab *grab, struct wlr_surface *surface, const uint32_t keycodes[], size_t num_keycodes, const struct wlr_keyboard_modifiers *modifiers) { wlr_seat_keyboard_enter(grab->seat, surface, keycodes, num_keycodes, modifiers); } static void default_keyboard_clear_focus(struct wlr_seat_keyboard_grab *grab) { wlr_seat_keyboard_clear_focus(grab->seat); } static void default_keyboard_key(struct wlr_seat_keyboard_grab *grab, uint32_t time, uint32_t key, uint32_t state) { wlr_seat_keyboard_send_key(grab->seat, time, key, state); } static void default_keyboard_modifiers(struct wlr_seat_keyboard_grab *grab, const struct wlr_keyboard_modifiers *modifiers) { wlr_seat_keyboard_send_modifiers(grab->seat, modifiers); } static void default_keyboard_cancel(struct wlr_seat_keyboard_grab *grab) { // cannot be cancelled } const struct wlr_keyboard_grab_interface default_keyboard_grab_impl = { .enter = default_keyboard_enter, .clear_focus = default_keyboard_clear_focus, .key = default_keyboard_key, .modifiers = default_keyboard_modifiers, .cancel = default_keyboard_cancel, }; static void keyboard_release(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static const struct wl_keyboard_interface keyboard_impl = { .release = keyboard_release, }; static struct wlr_seat_client *seat_client_from_keyboard_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &wl_keyboard_interface, &keyboard_impl)); return wl_resource_get_user_data(resource); } static void keyboard_handle_resource_destroy(struct wl_resource *resource) { seat_client_destroy_keyboard(resource); } void wlr_seat_keyboard_send_key(struct wlr_seat *wlr_seat, uint32_t time, uint32_t key, uint32_t state) { struct wlr_seat_client *client = wlr_seat->keyboard_state.focused_client; if (!client) { return; } uint32_t serial = wlr_seat_client_next_serial(client); struct wl_resource *resource; wl_resource_for_each(resource, &client->keyboards) { if (seat_client_from_keyboard_resource(resource) == NULL) { continue; } wl_keyboard_send_key(resource, serial, time, key, state); } } static void seat_client_send_keymap(struct wlr_seat_client *client, struct wlr_keyboard *keyboard); static void handle_keyboard_keymap(struct wl_listener *listener, void *data) { struct wlr_seat_keyboard_state *state = wl_container_of(listener, state, keyboard_keymap); struct wlr_seat_client *client; struct wlr_keyboard *keyboard = data; if (keyboard == state->keyboard) { wl_list_for_each(client, &state->seat->clients, link) { seat_client_send_keymap(client, state->keyboard); } } } static void seat_client_send_repeat_info(struct wlr_seat_client *client, struct wlr_keyboard *keyboard); static void handle_keyboard_repeat_info(struct wl_listener *listener, void *data) { struct wlr_seat_keyboard_state *state = wl_container_of(listener, state, keyboard_repeat_info); struct wlr_seat_client *client; wl_list_for_each(client, &state->seat->clients, link) { seat_client_send_repeat_info(client, state->keyboard); } } static void handle_keyboard_destroy(struct wl_listener *listener, void *data) { struct wlr_seat_keyboard_state *state = wl_container_of(listener, state, keyboard_destroy); wlr_seat_set_keyboard(state->seat, NULL); } void wlr_seat_set_keyboard(struct wlr_seat *seat, struct wlr_keyboard *keyboard) { // TODO call this on device key event before the event reaches the // compositor and set a pending keyboard and then send the new keyboard // state on the next keyboard notify event. if (seat->keyboard_state.keyboard == keyboard) { return; } if (seat->keyboard_state.keyboard) { wl_list_remove(&seat->keyboard_state.keyboard_destroy.link); wl_list_remove(&seat->keyboard_state.keyboard_keymap.link); wl_list_remove(&seat->keyboard_state.keyboard_repeat_info.link); seat->keyboard_state.keyboard = NULL; } if (keyboard) { seat->keyboard_state.keyboard = keyboard; wl_signal_add(&keyboard->base.events.destroy, &seat->keyboard_state.keyboard_destroy); seat->keyboard_state.keyboard_destroy.notify = handle_keyboard_destroy; wl_signal_add(&keyboard->events.keymap, &seat->keyboard_state.keyboard_keymap); seat->keyboard_state.keyboard_keymap.notify = handle_keyboard_keymap; wl_signal_add(&keyboard->events.repeat_info, &seat->keyboard_state.keyboard_repeat_info); seat->keyboard_state.keyboard_repeat_info.notify = handle_keyboard_repeat_info; struct wlr_seat_client *client; wl_list_for_each(client, &seat->clients, link) { seat_client_send_keymap(client, keyboard); seat_client_send_repeat_info(client, keyboard); } wlr_seat_keyboard_send_modifiers(seat, &keyboard->modifiers); } else { seat->keyboard_state.keyboard = NULL; } } struct wlr_keyboard *wlr_seat_get_keyboard(struct wlr_seat *seat) { return seat->keyboard_state.keyboard; } void wlr_seat_keyboard_start_grab(struct wlr_seat *wlr_seat, struct wlr_seat_keyboard_grab *grab) { grab->seat = wlr_seat; wlr_seat->keyboard_state.grab = grab; wl_signal_emit_mutable(&wlr_seat->events.keyboard_grab_begin, grab); } void wlr_seat_keyboard_end_grab(struct wlr_seat *wlr_seat) { struct wlr_seat_keyboard_grab *grab = wlr_seat->keyboard_state.grab; if (grab != wlr_seat->keyboard_state.default_grab) { wlr_seat->keyboard_state.grab = wlr_seat->keyboard_state.default_grab; wl_signal_emit_mutable(&wlr_seat->events.keyboard_grab_end, grab); if (grab->interface->cancel) { grab->interface->cancel(grab); } } } static void seat_keyboard_handle_surface_destroy(struct wl_listener *listener, void *data) { struct wlr_seat_keyboard_state *state = wl_container_of( listener, state, surface_destroy); wl_list_remove(&state->surface_destroy.link); wl_list_init(&state->surface_destroy.link); wlr_seat_keyboard_clear_focus(state->seat); } void wlr_seat_keyboard_send_modifiers(struct wlr_seat *seat, const struct wlr_keyboard_modifiers *modifiers) { struct wlr_seat_client *client = seat->keyboard_state.focused_client; if (client == NULL) { return; } uint32_t serial = wlr_seat_client_next_serial(client); struct wl_resource *resource; wl_resource_for_each(resource, &client->keyboards) { if (seat_client_from_keyboard_resource(resource) == NULL) { continue; } if (modifiers == NULL) { wl_keyboard_send_modifiers(resource, serial, 0, 0, 0, 0); } else { wl_keyboard_send_modifiers(resource, serial, modifiers->depressed, modifiers->latched, modifiers->locked, modifiers->group); } } } void seat_client_send_keyboard_leave_raw(struct wlr_seat_client *seat_client, struct wlr_surface *surface) { uint32_t serial = wlr_seat_client_next_serial(seat_client); struct wl_resource *resource; wl_resource_for_each(resource, &seat_client->keyboards) { if (seat_client_from_keyboard_resource(resource) == NULL) { continue; } wl_keyboard_send_leave(resource, serial, surface->resource); } } void wlr_seat_keyboard_enter(struct wlr_seat *seat, struct wlr_surface *surface, const uint32_t keycodes[], size_t num_keycodes, const struct wlr_keyboard_modifiers *modifiers) { if (seat->keyboard_state.focused_surface == surface) { // this surface already got an enter notify return; } struct wlr_seat_client *client = NULL; if (surface) { struct wl_client *wl_client = wl_resource_get_client(surface->resource); client = wlr_seat_client_for_wl_client(seat, wl_client); } struct wlr_seat_client *focused_client = seat->keyboard_state.focused_client; struct wlr_surface *focused_surface = seat->keyboard_state.focused_surface; // leave the previously entered surface if (focused_client != NULL && focused_surface != NULL) { seat_client_send_keyboard_leave_raw(focused_client, focused_surface); } // enter the current surface if (client != NULL) { struct wl_array keys = { .data = (void *)keycodes, .size = num_keycodes * sizeof(keycodes[0]), }; uint32_t serial = wlr_seat_client_next_serial(client); struct wl_resource *resource; wl_resource_for_each(resource, &client->keyboards) { if (seat_client_from_keyboard_resource(resource) == NULL) { continue; } wl_keyboard_send_enter(resource, serial, surface->resource, &keys); } } // reinitialize the focus destroy events wl_list_remove(&seat->keyboard_state.surface_destroy.link); wl_list_init(&seat->keyboard_state.surface_destroy.link); if (surface) { wl_signal_add(&surface->events.destroy, &seat->keyboard_state.surface_destroy); seat->keyboard_state.surface_destroy.notify = seat_keyboard_handle_surface_destroy; } seat->keyboard_state.focused_client = client; seat->keyboard_state.focused_surface = surface; if (client != NULL) { // tell new client about any modifier change last, // as it targets seat->keyboard_state.focused_client wlr_seat_keyboard_send_modifiers(seat, modifiers); seat_client_send_selection(client); } struct wlr_seat_keyboard_focus_change_event event = { .seat = seat, .old_surface = focused_surface, .new_surface = surface, }; wl_signal_emit_mutable(&seat->keyboard_state.events.focus_change, &event); } void wlr_seat_keyboard_notify_enter(struct wlr_seat *seat, struct wlr_surface *surface, const uint32_t keycodes[], size_t num_keycodes, const struct wlr_keyboard_modifiers *modifiers) { // NULL surfaces are prohibited in the grab-compatible API. Use // wlr_seat_keyboard_notify_clear_focus() instead. assert(surface); struct wlr_seat_keyboard_grab *grab = seat->keyboard_state.grab; grab->interface->enter(grab, surface, keycodes, num_keycodes, modifiers); } void wlr_seat_keyboard_clear_focus(struct wlr_seat *seat) { wlr_seat_keyboard_enter(seat, NULL, NULL, 0, NULL); } void wlr_seat_keyboard_notify_clear_focus(struct wlr_seat *seat) { struct wlr_seat_keyboard_grab *grab = seat->keyboard_state.grab; grab->interface->clear_focus(grab); } bool wlr_seat_keyboard_has_grab(struct wlr_seat *seat) { return seat->keyboard_state.grab->interface != &default_keyboard_grab_impl; } void wlr_seat_keyboard_notify_modifiers(struct wlr_seat *seat, const struct wlr_keyboard_modifiers *modifiers) { clock_gettime(CLOCK_MONOTONIC, &seat->last_event); struct wlr_seat_keyboard_grab *grab = seat->keyboard_state.grab; grab->interface->modifiers(grab, modifiers); } void wlr_seat_keyboard_notify_key(struct wlr_seat *seat, uint32_t time, uint32_t key, uint32_t state) { clock_gettime(CLOCK_MONOTONIC, &seat->last_event); struct wlr_seat_keyboard_grab *grab = seat->keyboard_state.grab; grab->interface->key(grab, time, key, state); } static void seat_client_send_keymap(struct wlr_seat_client *client, struct wlr_keyboard *keyboard) { if (!keyboard) { return; } enum wl_keyboard_keymap_format format; int fd, devnull = -1; uint32_t size; if (keyboard->keymap != NULL) { format = WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1; fd = keyboard->keymap_fd; size = keyboard->keymap_size; } else { format = WL_KEYBOARD_KEYMAP_FORMAT_NO_KEYMAP; devnull = open("/dev/null", O_RDONLY | O_CLOEXEC); if (devnull < 0) { wlr_log_errno(WLR_ERROR, "Failed to open /dev/null"); return; } fd = devnull; size = 0; } // TODO: We should probably lift all of the keys set by the other // keyboard struct wl_resource *resource; wl_resource_for_each(resource, &client->keyboards) { if (seat_client_from_keyboard_resource(resource) == NULL) { continue; } wl_keyboard_send_keymap(resource, format, fd, size); } if (devnull >= 0) { close(devnull); } } static void seat_client_send_repeat_info(struct wlr_seat_client *client, struct wlr_keyboard *keyboard) { if (!keyboard) { return; } struct wl_resource *resource; wl_resource_for_each(resource, &client->keyboards) { if (seat_client_from_keyboard_resource(resource) == NULL) { continue; } if (wl_resource_get_version(resource) >= WL_KEYBOARD_REPEAT_INFO_SINCE_VERSION) { wl_keyboard_send_repeat_info(resource, keyboard->repeat_info.rate, keyboard->repeat_info.delay); } } } void seat_client_create_keyboard(struct wlr_seat_client *seat_client, uint32_t version, uint32_t id) { struct wl_resource *resource = wl_resource_create(seat_client->client, &wl_keyboard_interface, version, id); if (resource == NULL) { wl_client_post_no_memory(seat_client->client); return; } wl_resource_set_implementation(resource, &keyboard_impl, seat_client, keyboard_handle_resource_destroy); wl_list_insert(&seat_client->keyboards, wl_resource_get_link(resource)); if ((seat_client->seat->capabilities & WL_SEAT_CAPABILITY_KEYBOARD) == 0) { wl_resource_set_user_data(resource, NULL); return; } struct wlr_keyboard *keyboard = seat_client->seat->keyboard_state.keyboard; if (keyboard == NULL) { return; } seat_client_send_keymap(seat_client, keyboard); seat_client_send_repeat_info(seat_client, keyboard); struct wlr_seat_client *focused_client = seat_client->seat->keyboard_state.focused_client; struct wlr_surface *focused_surface = seat_client->seat->keyboard_state.focused_surface; // Send an enter event if there is a focused client/surface stored if (focused_client == seat_client && focused_surface != NULL) { uint32_t *keycodes = keyboard->keycodes; size_t num_keycodes = keyboard->num_keycodes; struct wl_array keys; wl_array_init(&keys); for (size_t i = 0; i < num_keycodes; ++i) { uint32_t *p = wl_array_add(&keys, sizeof(uint32_t)); if (!p) { wlr_log(WLR_ERROR, "Cannot allocate memory, skipping keycode: %" PRIu32 "\n", keycodes[i]); continue; } *p = keycodes[i]; } uint32_t serial = wlr_seat_client_next_serial(focused_client); struct wl_resource *resource; wl_resource_for_each(resource, &focused_client->keyboards) { if (wl_resource_get_id(resource) == id) { if (seat_client_from_keyboard_resource(resource) == NULL) { continue; } wl_keyboard_send_enter(resource, serial, focused_surface->resource, &keys); } } wl_array_release(&keys); wlr_seat_keyboard_send_modifiers(seat_client->seat, &keyboard->modifiers); } } void seat_client_create_inert_keyboard(struct wl_client *client, uint32_t version, uint32_t id) { struct wl_resource *resource = wl_resource_create(client, &wl_keyboard_interface, version, id); if (!resource) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(resource, &keyboard_impl, NULL, NULL); } void seat_client_destroy_keyboard(struct wl_resource *resource) { wl_list_remove(wl_resource_get_link(resource)); wl_list_init(wl_resource_get_link(resource)); wl_resource_set_user_data(resource, NULL); } wlroots-0.17.1/types/seat/wlr_seat_pointer.c000066400000000000000000000442251454110342200211400ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include "types/wlr_seat.h" #include "util/set.h" static void default_pointer_enter(struct wlr_seat_pointer_grab *grab, struct wlr_surface *surface, double sx, double sy) { wlr_seat_pointer_enter(grab->seat, surface, sx, sy); } static void default_pointer_clear_focus(struct wlr_seat_pointer_grab *grab) { wlr_seat_pointer_clear_focus(grab->seat); } static void default_pointer_motion(struct wlr_seat_pointer_grab *grab, uint32_t time, double sx, double sy) { wlr_seat_pointer_send_motion(grab->seat, time, sx, sy); } static uint32_t default_pointer_button(struct wlr_seat_pointer_grab *grab, uint32_t time, uint32_t button, enum wlr_button_state state) { return wlr_seat_pointer_send_button(grab->seat, time, button, state); } static void default_pointer_axis(struct wlr_seat_pointer_grab *grab, uint32_t time, enum wlr_axis_orientation orientation, double value, int32_t value_discrete, enum wlr_axis_source source) { wlr_seat_pointer_send_axis(grab->seat, time, orientation, value, value_discrete, source); } static void default_pointer_frame(struct wlr_seat_pointer_grab *grab) { wlr_seat_pointer_send_frame(grab->seat); } static void default_pointer_cancel(struct wlr_seat_pointer_grab *grab) { // cannot be cancelled } const struct wlr_pointer_grab_interface default_pointer_grab_impl = { .enter = default_pointer_enter, .clear_focus = default_pointer_clear_focus, .motion = default_pointer_motion, .button = default_pointer_button, .axis = default_pointer_axis, .frame = default_pointer_frame, .cancel = default_pointer_cancel, }; static void pointer_send_frame(struct wl_resource *resource) { if (wl_resource_get_version(resource) >= WL_POINTER_FRAME_SINCE_VERSION) { wl_pointer_send_frame(resource); } } static const struct wl_pointer_interface pointer_impl; struct wlr_seat_client *wlr_seat_client_from_pointer_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &wl_pointer_interface, &pointer_impl)); return wl_resource_get_user_data(resource); } static void pointer_cursor_surface_handle_commit(struct wlr_surface *surface) { pixman_region32_clear(&surface->input_region); if (wlr_surface_has_buffer(surface)) { wlr_surface_map(surface); } } static const struct wlr_surface_role pointer_cursor_surface_role = { .name = "wl_pointer-cursor", .no_object = true, .commit = pointer_cursor_surface_handle_commit, }; static void pointer_set_cursor(struct wl_client *client, struct wl_resource *pointer_resource, uint32_t serial, struct wl_resource *surface_resource, int32_t hotspot_x, int32_t hotspot_y) { struct wlr_seat_client *seat_client = wlr_seat_client_from_pointer_resource(pointer_resource); if (seat_client == NULL) { return; } struct wlr_surface *surface = NULL; if (surface_resource != NULL) { surface = wlr_surface_from_resource(surface_resource); if (!wlr_surface_set_role(surface, &pointer_cursor_surface_role, surface_resource, WL_POINTER_ERROR_ROLE)) { return; } pointer_cursor_surface_handle_commit(surface); } struct wlr_seat_pointer_request_set_cursor_event event = { .seat_client = seat_client, .surface = surface, .serial = serial, .hotspot_x = hotspot_x, .hotspot_y = hotspot_y, }; wl_signal_emit_mutable(&seat_client->seat->events.request_set_cursor, &event); } static void pointer_release(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static const struct wl_pointer_interface pointer_impl = { .set_cursor = pointer_set_cursor, .release = pointer_release, }; static void pointer_handle_resource_destroy(struct wl_resource *resource) { seat_client_destroy_pointer(resource); } bool wlr_seat_pointer_surface_has_focus(struct wlr_seat *wlr_seat, struct wlr_surface *surface) { return surface == wlr_seat->pointer_state.focused_surface; } static void seat_pointer_handle_surface_destroy(struct wl_listener *listener, void *data) { struct wlr_seat_pointer_state *state = wl_container_of(listener, state, surface_destroy); wl_list_remove(&state->surface_destroy.link); wl_list_init(&state->surface_destroy.link); wlr_seat_pointer_clear_focus(state->seat); } void seat_client_send_pointer_leave_raw(struct wlr_seat_client *seat_client, struct wlr_surface *surface) { uint32_t serial = wlr_seat_client_next_serial(seat_client); struct wl_resource *resource; wl_resource_for_each(resource, &seat_client->pointers) { if (wlr_seat_client_from_pointer_resource(resource) == NULL) { continue; } wl_pointer_send_leave(resource, serial, surface->resource); pointer_send_frame(resource); } } void wlr_seat_pointer_enter(struct wlr_seat *wlr_seat, struct wlr_surface *surface, double sx, double sy) { if (wlr_seat->pointer_state.focused_surface == surface) { // this surface already got an enter notify return; } struct wlr_seat_client *client = NULL; if (surface) { struct wl_client *wl_client = wl_resource_get_client(surface->resource); client = wlr_seat_client_for_wl_client(wlr_seat, wl_client); } struct wlr_seat_client *focused_client = wlr_seat->pointer_state.focused_client; struct wlr_surface *focused_surface = wlr_seat->pointer_state.focused_surface; // leave the previously entered surface if (focused_client != NULL && focused_surface != NULL) { seat_client_send_pointer_leave_raw(focused_client, focused_surface); } // enter the current surface if (client != NULL && surface != NULL) { uint32_t serial = wlr_seat_client_next_serial(client); struct wl_resource *resource; wl_resource_for_each(resource, &client->pointers) { if (wlr_seat_client_from_pointer_resource(resource) == NULL) { continue; } wl_pointer_send_enter(resource, serial, surface->resource, wl_fixed_from_double(sx), wl_fixed_from_double(sy)); pointer_send_frame(resource); } } // reinitialize the focus destroy events wl_list_remove(&wlr_seat->pointer_state.surface_destroy.link); wl_list_init(&wlr_seat->pointer_state.surface_destroy.link); if (surface != NULL) { wl_signal_add(&surface->events.destroy, &wlr_seat->pointer_state.surface_destroy); wlr_seat->pointer_state.surface_destroy.notify = seat_pointer_handle_surface_destroy; } wlr_seat->pointer_state.focused_client = client; wlr_seat->pointer_state.focused_surface = surface; if (surface != NULL) { wlr_seat_pointer_warp(wlr_seat, sx, sy); } else { wlr_seat_pointer_warp(wlr_seat, NAN, NAN); } struct wlr_seat_pointer_focus_change_event event = { .seat = wlr_seat, .new_surface = surface, .old_surface = focused_surface, .sx = sx, .sy = sy, }; wl_signal_emit_mutable(&wlr_seat->pointer_state.events.focus_change, &event); } void wlr_seat_pointer_clear_focus(struct wlr_seat *wlr_seat) { wlr_seat_pointer_enter(wlr_seat, NULL, 0, 0); } void wlr_seat_pointer_warp(struct wlr_seat *wlr_seat, double sx, double sy) { wlr_seat->pointer_state.sx = sx; wlr_seat->pointer_state.sy = sy; } void wlr_seat_pointer_send_motion(struct wlr_seat *wlr_seat, uint32_t time, double sx, double sy) { struct wlr_seat_client *client = wlr_seat->pointer_state.focused_client; if (client == NULL) { return; } // Ensure we don't send duplicate motion events. Instead of comparing with an // epsilon, chop off some precision by converting to a `wl_fixed_t` first, // since that is what a client receives. wl_fixed_t sx_fixed = wl_fixed_from_double(sx); wl_fixed_t sy_fixed = wl_fixed_from_double(sy); if (wl_fixed_from_double(wlr_seat->pointer_state.sx) != sx_fixed || wl_fixed_from_double(wlr_seat->pointer_state.sy) != sy_fixed) { struct wl_resource *resource; wl_resource_for_each(resource, &client->pointers) { if (wlr_seat_client_from_pointer_resource(resource) == NULL) { continue; } wl_pointer_send_motion(resource, time, sx_fixed, sy_fixed); } } wlr_seat_pointer_warp(wlr_seat, sx, sy); } uint32_t wlr_seat_pointer_send_button(struct wlr_seat *wlr_seat, uint32_t time, uint32_t button, enum wlr_button_state state) { struct wlr_seat_client *client = wlr_seat->pointer_state.focused_client; if (client == NULL) { return 0; } uint32_t serial = wlr_seat_client_next_serial(client); struct wl_resource *resource; wl_resource_for_each(resource, &client->pointers) { if (wlr_seat_client_from_pointer_resource(resource) == NULL) { continue; } wl_pointer_send_button(resource, serial, time, button, state); } return serial; } static bool should_reset_value120_accumulators(int32_t current, int32_t last) { if (last == 0) { return true; } return (current < 0 && last > 0) || (current > 0 && last < 0); } static void update_value120_accumulators(struct wlr_seat_client *client, enum wlr_axis_orientation orientation, double value, int32_t value_discrete, double *low_res_value, int32_t *low_res_value_discrete) { if (value_discrete == 0) { // Continuous scrolling has no effect on accumulators return; } int32_t *acc_discrete = &client->value120.acc_discrete[orientation]; int32_t *last_discrete = &client->value120.last_discrete[orientation]; double *acc_axis = &client->value120.acc_axis[orientation]; if (should_reset_value120_accumulators(value_discrete, *last_discrete)) { *acc_discrete = 0; *acc_axis = 0; } *acc_discrete += value_discrete; *last_discrete = value_discrete; *acc_axis += value; // Compute low resolution event values for older clients and reset // the accumulators if needed *low_res_value_discrete = *acc_discrete / WLR_POINTER_AXIS_DISCRETE_STEP; if (*low_res_value_discrete == 0) { *low_res_value = 0; } else { *acc_discrete -= *low_res_value_discrete * WLR_POINTER_AXIS_DISCRETE_STEP; *low_res_value = *acc_axis; *acc_axis = 0; } } void wlr_seat_pointer_send_axis(struct wlr_seat *wlr_seat, uint32_t time, enum wlr_axis_orientation orientation, double value, int32_t value_discrete, enum wlr_axis_source source) { struct wlr_seat_client *client = wlr_seat->pointer_state.focused_client; if (client == NULL) { return; } bool send_source = false; if (wlr_seat->pointer_state.sent_axis_source) { assert(wlr_seat->pointer_state.cached_axis_source == source); } else { wlr_seat->pointer_state.sent_axis_source = true; wlr_seat->pointer_state.cached_axis_source = source; send_source = true; } double low_res_value = 0.0; int32_t low_res_value_discrete = 0; update_value120_accumulators(client, orientation, value, value_discrete, &low_res_value, &low_res_value_discrete); struct wl_resource *resource; wl_resource_for_each(resource, &client->pointers) { if (wlr_seat_client_from_pointer_resource(resource) == NULL) { continue; } uint32_t version = wl_resource_get_version(resource); if (version < WL_POINTER_AXIS_VALUE120_SINCE_VERSION && value_discrete != 0 && low_res_value_discrete == 0) { // The client doesn't support high resolution discrete scrolling // and we haven't accumulated enough wheel clicks for a single // low resolution event. Don't send anything. continue; } if (send_source && version >= WL_POINTER_AXIS_SOURCE_SINCE_VERSION) { wl_pointer_send_axis_source(resource, source); } if (value) { if (value_discrete) { if (version >= WL_POINTER_AXIS_VALUE120_SINCE_VERSION) { // High resolution discrete scrolling wl_pointer_send_axis_value120(resource, orientation, value_discrete); wl_pointer_send_axis(resource, time, orientation, wl_fixed_from_double(value)); } else { // Low resolution discrete scrolling if (version >= WL_POINTER_AXIS_DISCRETE_SINCE_VERSION) { wl_pointer_send_axis_discrete(resource, orientation, low_res_value_discrete); } wl_pointer_send_axis(resource, time, orientation, wl_fixed_from_double(low_res_value)); } } else { // Continuous scrolling wl_pointer_send_axis(resource, time, orientation, wl_fixed_from_double(value)); } } else if (version >= WL_POINTER_AXIS_STOP_SINCE_VERSION) { wl_pointer_send_axis_stop(resource, time, orientation); } } } void wlr_seat_pointer_send_frame(struct wlr_seat *wlr_seat) { struct wlr_seat_client *client = wlr_seat->pointer_state.focused_client; if (client == NULL) { return; } wlr_seat->pointer_state.sent_axis_source = false; struct wl_resource *resource; wl_resource_for_each(resource, &client->pointers) { if (wlr_seat_client_from_pointer_resource(resource) == NULL) { continue; } pointer_send_frame(resource); } } void wlr_seat_pointer_start_grab(struct wlr_seat *wlr_seat, struct wlr_seat_pointer_grab *grab) { assert(wlr_seat); grab->seat = wlr_seat; wlr_seat->pointer_state.grab = grab; wl_signal_emit_mutable(&wlr_seat->events.pointer_grab_begin, grab); } void wlr_seat_pointer_end_grab(struct wlr_seat *wlr_seat) { struct wlr_seat_pointer_grab *grab = wlr_seat->pointer_state.grab; if (grab != wlr_seat->pointer_state.default_grab) { wlr_seat->pointer_state.grab = wlr_seat->pointer_state.default_grab; wl_signal_emit_mutable(&wlr_seat->events.pointer_grab_end, grab); if (grab->interface->cancel) { grab->interface->cancel(grab); } } } void wlr_seat_pointer_notify_enter(struct wlr_seat *wlr_seat, struct wlr_surface *surface, double sx, double sy) { // NULL surfaces are prohibited in the grab-compatible API. Use // wlr_seat_pointer_notify_clear_focus() instead. assert(surface); struct wlr_seat_pointer_grab *grab = wlr_seat->pointer_state.grab; grab->interface->enter(grab, surface, sx, sy); } void wlr_seat_pointer_notify_clear_focus(struct wlr_seat *wlr_seat) { struct wlr_seat_pointer_grab *grab = wlr_seat->pointer_state.grab; grab->interface->clear_focus(grab); } void wlr_seat_pointer_notify_motion(struct wlr_seat *wlr_seat, uint32_t time, double sx, double sy) { clock_gettime(CLOCK_MONOTONIC, &wlr_seat->last_event); struct wlr_seat_pointer_grab *grab = wlr_seat->pointer_state.grab; grab->interface->motion(grab, time, sx, sy); } uint32_t wlr_seat_pointer_notify_button(struct wlr_seat *wlr_seat, uint32_t time, uint32_t button, enum wlr_button_state state) { clock_gettime(CLOCK_MONOTONIC, &wlr_seat->last_event); struct wlr_seat_pointer_state* pointer_state = &wlr_seat->pointer_state; if (state == WLR_BUTTON_PRESSED) { if (pointer_state->button_count == 0) { pointer_state->grab_button = button; pointer_state->grab_time = time; } set_add(pointer_state->buttons, &pointer_state->button_count, WLR_POINTER_BUTTONS_CAP, button); } else { set_remove(pointer_state->buttons, &pointer_state->button_count, WLR_POINTER_BUTTONS_CAP, button); } struct wlr_seat_pointer_grab *grab = pointer_state->grab; uint32_t serial = grab->interface->button(grab, time, button, state); if (serial && pointer_state->button_count == 1 && state == WLR_BUTTON_PRESSED) { pointer_state->grab_serial = serial; } return serial; } void wlr_seat_pointer_notify_axis(struct wlr_seat *wlr_seat, uint32_t time, enum wlr_axis_orientation orientation, double value, int32_t value_discrete, enum wlr_axis_source source) { clock_gettime(CLOCK_MONOTONIC, &wlr_seat->last_event); struct wlr_seat_pointer_grab *grab = wlr_seat->pointer_state.grab; grab->interface->axis(grab, time, orientation, value, value_discrete, source); } void wlr_seat_pointer_notify_frame(struct wlr_seat *wlr_seat) { clock_gettime(CLOCK_MONOTONIC, &wlr_seat->last_event); struct wlr_seat_pointer_grab *grab = wlr_seat->pointer_state.grab; if (grab->interface->frame) { grab->interface->frame(grab); } } bool wlr_seat_pointer_has_grab(struct wlr_seat *seat) { return seat->pointer_state.grab->interface != &default_pointer_grab_impl; } void seat_client_create_pointer(struct wlr_seat_client *seat_client, uint32_t version, uint32_t id) { struct wl_resource *resource = wl_resource_create(seat_client->client, &wl_pointer_interface, version, id); if (resource == NULL) { wl_client_post_no_memory(seat_client->client); return; } wl_resource_set_implementation(resource, &pointer_impl, seat_client, &pointer_handle_resource_destroy); wl_list_insert(&seat_client->pointers, wl_resource_get_link(resource)); if ((seat_client->seat->capabilities & WL_SEAT_CAPABILITY_POINTER) == 0) { wl_resource_set_user_data(resource, NULL); return; } struct wlr_seat_client *focused_client = seat_client->seat->pointer_state.focused_client; struct wlr_surface *focused_surface = seat_client->seat->pointer_state.focused_surface; // Send an enter event if there is a focused client/surface stored if (focused_client == seat_client && focused_surface != NULL) { double sx = seat_client->seat->pointer_state.sx; double sy = seat_client->seat->pointer_state.sy; uint32_t serial = wlr_seat_client_next_serial(focused_client); struct wl_resource *resource; wl_resource_for_each(resource, &focused_client->pointers) { if (wl_resource_get_id(resource) == id) { if (wlr_seat_client_from_pointer_resource(resource) == NULL) { continue; } wl_pointer_send_enter(resource, serial, focused_surface->resource, wl_fixed_from_double(sx), wl_fixed_from_double(sy)); pointer_send_frame(resource); } } } } void seat_client_create_inert_pointer(struct wl_client *client, uint32_t version, uint32_t id) { struct wl_resource *resource = wl_resource_create(client, &wl_pointer_interface, version, id); if (!resource) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(resource, &pointer_impl, NULL, NULL); } void seat_client_destroy_pointer(struct wl_resource *resource) { wl_list_remove(wl_resource_get_link(resource)); wl_list_init(wl_resource_get_link(resource)); wl_resource_set_user_data(resource, NULL); } bool wlr_seat_validate_pointer_grab_serial(struct wlr_seat *seat, struct wlr_surface *origin, uint32_t serial) { if (seat->pointer_state.button_count != 1 || seat->pointer_state.grab_serial != serial) { wlr_log(WLR_DEBUG, "Pointer grab serial validation failed: " "button_count=%zu grab_serial=%"PRIu32" (got %"PRIu32")", seat->pointer_state.button_count, seat->pointer_state.grab_serial, serial); return false; } if (origin != NULL && seat->pointer_state.focused_surface != origin) { wlr_log(WLR_DEBUG, "Pointer grab serial validation failed: " "invalid origin surface"); return false; } return true; } wlroots-0.17.1/types/seat/wlr_seat_touch.c000066400000000000000000000346241454110342200206040ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include "types/wlr_seat.h" static uint32_t default_touch_down(struct wlr_seat_touch_grab *grab, uint32_t time, struct wlr_touch_point *point) { return wlr_seat_touch_send_down(grab->seat, point->surface, time, point->touch_id, point->sx, point->sy); } static void default_touch_up(struct wlr_seat_touch_grab *grab, uint32_t time, struct wlr_touch_point *point) { wlr_seat_touch_send_up(grab->seat, time, point->touch_id); } static void default_touch_motion(struct wlr_seat_touch_grab *grab, uint32_t time, struct wlr_touch_point *point) { if (!point->focus_surface || point->focus_surface == point->surface) { wlr_seat_touch_send_motion(grab->seat, time, point->touch_id, point->sx, point->sy); } } static void default_touch_enter(struct wlr_seat_touch_grab *grab, uint32_t time, struct wlr_touch_point *point) { // not handled by default } static void default_touch_frame(struct wlr_seat_touch_grab *grab) { wlr_seat_touch_send_frame(grab->seat); } static void default_touch_cancel(struct wlr_seat_touch_grab *grab) { // cannot be cancelled } static void default_touch_wl_cancel(struct wlr_seat_touch_grab *grab, struct wlr_surface *surface) { wlr_seat_touch_send_cancel(grab->seat, surface); } const struct wlr_touch_grab_interface default_touch_grab_impl = { .down = default_touch_down, .up = default_touch_up, .motion = default_touch_motion, .enter = default_touch_enter, .frame = default_touch_frame, .cancel = default_touch_cancel, .wl_cancel = default_touch_wl_cancel, }; static void touch_release(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static const struct wl_touch_interface touch_impl = { .release = touch_release, }; static void touch_handle_resource_destroy(struct wl_resource *resource) { seat_client_destroy_touch(resource); } static struct wlr_seat_client *seat_client_from_touch_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &wl_touch_interface, &touch_impl)); return wl_resource_get_user_data(resource); } void wlr_seat_touch_start_grab(struct wlr_seat *wlr_seat, struct wlr_seat_touch_grab *grab) { grab->seat = wlr_seat; wlr_seat->touch_state.grab = grab; wl_signal_emit_mutable(&wlr_seat->events.touch_grab_begin, grab); } void wlr_seat_touch_end_grab(struct wlr_seat *wlr_seat) { struct wlr_seat_touch_grab *grab = wlr_seat->touch_state.grab; if (grab != wlr_seat->touch_state.default_grab) { wlr_seat->touch_state.grab = wlr_seat->touch_state.default_grab; wl_signal_emit_mutable(&wlr_seat->events.touch_grab_end, grab); if (grab->interface->cancel) { grab->interface->cancel(grab); } } } static void touch_point_clear_focus(struct wlr_touch_point *point) { if (point->focus_surface) { wl_list_remove(&point->focus_surface_destroy.link); point->focus_client = NULL; point->focus_surface = NULL; } } static void touch_point_destroy(struct wlr_touch_point *point) { wl_signal_emit_mutable(&point->events.destroy, point); touch_point_clear_focus(point); wl_list_remove(&point->surface_destroy.link); wl_list_remove(&point->client_destroy.link); wl_list_remove(&point->link); free(point); } static void touch_point_handle_surface_destroy(struct wl_listener *listener, void *data) { struct wlr_touch_point *point = wl_container_of(listener, point, surface_destroy); // Touch point itself is destroyed on up event point->surface = NULL; wl_list_remove(&point->surface_destroy.link); wl_list_init(&point->surface_destroy.link); } static void touch_point_handle_client_destroy(struct wl_listener *listener, void *data) { struct wlr_touch_point *point = wl_container_of(listener, point, client_destroy); touch_point_destroy(point); } static struct wlr_touch_point *touch_point_create( struct wlr_seat *seat, int32_t touch_id, struct wlr_surface *surface, double sx, double sy) { struct wl_client *wl_client = wl_resource_get_client(surface->resource); struct wlr_seat_client *client = wlr_seat_client_for_wl_client(seat, wl_client); if (client == NULL || wl_list_empty(&client->touches)) { // touch points are not valid without a connected client with touch return NULL; } struct wlr_touch_point *point = calloc(1, sizeof(*point)); if (!point) { return NULL; } point->touch_id = touch_id; point->surface = surface; point->client = client; point->sx = sx; point->sy = sy; wl_signal_init(&point->events.destroy); wl_signal_add(&surface->events.destroy, &point->surface_destroy); point->surface_destroy.notify = touch_point_handle_surface_destroy; wl_signal_add(&client->events.destroy, &point->client_destroy); point->client_destroy.notify = touch_point_handle_client_destroy; wl_list_insert(&seat->touch_state.touch_points, &point->link); return point; } struct wlr_touch_point *wlr_seat_touch_get_point( struct wlr_seat *seat, int32_t touch_id) { struct wlr_touch_point *point = NULL; wl_list_for_each(point, &seat->touch_state.touch_points, link) { if (point->touch_id == touch_id) { return point; } } return NULL; } uint32_t wlr_seat_touch_notify_down(struct wlr_seat *seat, struct wlr_surface *surface, uint32_t time, int32_t touch_id, double sx, double sy) { clock_gettime(CLOCK_MONOTONIC, &seat->last_event); struct wlr_seat_touch_grab *grab = seat->touch_state.grab; struct wlr_touch_point *point = touch_point_create(seat, touch_id, surface, sx, sy); if (!point) { wlr_log(WLR_ERROR, "could not create touch point"); return 0; } uint32_t serial = grab->interface->down(grab, time, point); if (!serial) { touch_point_destroy(point); return 0; } if (serial && wlr_seat_touch_num_points(seat) == 1) { seat->touch_state.grab_serial = serial; seat->touch_state.grab_id = touch_id; } return serial; } void wlr_seat_touch_notify_up(struct wlr_seat *seat, uint32_t time, int32_t touch_id) { clock_gettime(CLOCK_MONOTONIC, &seat->last_event); struct wlr_seat_touch_grab *grab = seat->touch_state.grab; struct wlr_touch_point *point = wlr_seat_touch_get_point(seat, touch_id); if (!point) { return; } grab->interface->up(grab, time, point); touch_point_destroy(point); } void wlr_seat_touch_notify_motion(struct wlr_seat *seat, uint32_t time, int32_t touch_id, double sx, double sy) { clock_gettime(CLOCK_MONOTONIC, &seat->last_event); struct wlr_seat_touch_grab *grab = seat->touch_state.grab; struct wlr_touch_point *point = wlr_seat_touch_get_point(seat, touch_id); if (!point) { return; } point->sx = sx; point->sy = sy; grab->interface->motion(grab, time, point); } void wlr_seat_touch_notify_frame(struct wlr_seat *seat) { struct wlr_seat_touch_grab *grab = seat->touch_state.grab; if (grab->interface->frame) { grab->interface->frame(grab); } } void wlr_seat_touch_notify_cancel(struct wlr_seat *seat, struct wlr_surface *surface) { struct wlr_seat_touch_grab *grab = seat->touch_state.grab; if (grab->interface->wl_cancel) { grab->interface->wl_cancel(grab, surface); } struct wl_client *client = wl_resource_get_client(surface->resource); struct wlr_seat_client *seat_client = wlr_seat_client_for_wl_client(seat, client); if (seat_client == NULL) { return; } struct wlr_touch_point *point, *tmp; wl_list_for_each_safe(point, tmp, &seat->touch_state.touch_points, link) { if (point->client == seat_client) { touch_point_destroy(point); } } } static void handle_point_focus_destroy(struct wl_listener *listener, void *data) { struct wlr_touch_point *point = wl_container_of(listener, point, focus_surface_destroy); touch_point_clear_focus(point); } static void touch_point_set_focus(struct wlr_touch_point *point, struct wlr_surface *surface, double sx, double sy) { if (point->focus_surface == surface) { return; } touch_point_clear_focus(point); if (surface && surface->resource) { struct wlr_seat_client *client = wlr_seat_client_for_wl_client(point->client->seat, wl_resource_get_client(surface->resource)); if (client && !wl_list_empty(&client->touches)) { wl_signal_add(&surface->events.destroy, &point->focus_surface_destroy); point->focus_surface_destroy.notify = handle_point_focus_destroy; point->focus_surface = surface; point->focus_client = client; point->sx = sx; point->sy = sy; } } } void wlr_seat_touch_point_focus(struct wlr_seat *seat, struct wlr_surface *surface, uint32_t time, int32_t touch_id, double sx, double sy) { assert(surface); struct wlr_touch_point *point = wlr_seat_touch_get_point(seat, touch_id); if (!point) { wlr_log(WLR_ERROR, "got touch point focus for unknown touch point"); return; } struct wlr_surface *focus = point->focus_surface; touch_point_set_focus(point, surface, sx, sy); if (focus != point->focus_surface) { struct wlr_seat_touch_grab *grab = seat->touch_state.grab; grab->interface->enter(grab, time, point); } } void wlr_seat_touch_point_clear_focus(struct wlr_seat *seat, uint32_t time, int32_t touch_id) { struct wlr_touch_point *point = wlr_seat_touch_get_point(seat, touch_id); if (!point) { wlr_log(WLR_ERROR, "got touch point focus for unknown touch point"); return; } touch_point_clear_focus(point); } uint32_t wlr_seat_touch_send_down(struct wlr_seat *seat, struct wlr_surface *surface, uint32_t time, int32_t touch_id, double sx, double sy) { struct wlr_touch_point *point = wlr_seat_touch_get_point(seat, touch_id); if (!point) { wlr_log(WLR_ERROR, "got touch down for unknown touch point"); return 0; } uint32_t serial = wlr_seat_client_next_serial(point->client); struct wl_resource *resource; wl_resource_for_each(resource, &point->client->touches) { if (seat_client_from_touch_resource(resource) == NULL) { continue; } wl_touch_send_down(resource, serial, time, surface->resource, touch_id, wl_fixed_from_double(sx), wl_fixed_from_double(sy)); } point->client->needs_touch_frame = true; return serial; } void wlr_seat_touch_send_up(struct wlr_seat *seat, uint32_t time, int32_t touch_id) { struct wlr_touch_point *point = wlr_seat_touch_get_point(seat, touch_id); if (!point) { wlr_log(WLR_ERROR, "got touch up for unknown touch point"); return; } uint32_t serial = wlr_seat_client_next_serial(point->client); struct wl_resource *resource; wl_resource_for_each(resource, &point->client->touches) { if (seat_client_from_touch_resource(resource) == NULL) { continue; } wl_touch_send_up(resource, serial, time, touch_id); } point->client->needs_touch_frame = true; } void wlr_seat_touch_send_motion(struct wlr_seat *seat, uint32_t time, int32_t touch_id, double sx, double sy) { struct wlr_touch_point *point = wlr_seat_touch_get_point(seat, touch_id); if (!point) { wlr_log(WLR_ERROR, "got touch motion for unknown touch point"); return; } struct wl_resource *resource; wl_resource_for_each(resource, &point->client->touches) { if (seat_client_from_touch_resource(resource) == NULL) { continue; } wl_touch_send_motion(resource, time, touch_id, wl_fixed_from_double(sx), wl_fixed_from_double(sy)); } point->client->needs_touch_frame = true; } void wlr_seat_touch_send_frame(struct wlr_seat *seat) { struct wlr_seat_client *seat_client; wl_list_for_each(seat_client, &seat->clients, link) { if (!seat_client->needs_touch_frame) { continue; } struct wl_resource *resource; wl_resource_for_each(resource, &seat_client->touches) { wl_touch_send_frame(resource); } seat_client->needs_touch_frame = false; } } void wlr_seat_touch_send_cancel(struct wlr_seat *seat, struct wlr_surface *surface) { struct wl_client *client = wl_resource_get_client(surface->resource); struct wlr_seat_client *seat_client = wlr_seat_client_for_wl_client(seat, client); if (seat_client == NULL) { return; } struct wl_resource *resource; wl_resource_for_each(resource, &seat_client->touches) { if (seat_client_from_touch_resource(resource) == NULL) { continue; } wl_touch_send_cancel(resource); } } int wlr_seat_touch_num_points(struct wlr_seat *seat) { return wl_list_length(&seat->touch_state.touch_points); } bool wlr_seat_touch_has_grab(struct wlr_seat *seat) { return seat->touch_state.grab->interface != &default_touch_grab_impl; } void seat_client_create_touch(struct wlr_seat_client *seat_client, uint32_t version, uint32_t id) { struct wl_resource *resource = wl_resource_create(seat_client->client, &wl_touch_interface, version, id); if (resource == NULL) { wl_client_post_no_memory(seat_client->client); return; } wl_resource_set_implementation(resource, &touch_impl, seat_client, &touch_handle_resource_destroy); wl_list_insert(&seat_client->touches, wl_resource_get_link(resource)); if ((seat_client->seat->capabilities & WL_SEAT_CAPABILITY_TOUCH) == 0) { wl_resource_set_user_data(resource, NULL); } } void seat_client_create_inert_touch(struct wl_client *client, uint32_t version, uint32_t id) { struct wl_resource *resource = wl_resource_create(client, &wl_touch_interface, version, id); if (!resource) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(resource, &touch_impl, NULL, NULL); } void seat_client_destroy_touch(struct wl_resource *resource) { wl_list_remove(wl_resource_get_link(resource)); wl_list_init(wl_resource_get_link(resource)); wl_resource_set_user_data(resource, NULL); } bool wlr_seat_validate_touch_grab_serial(struct wlr_seat *seat, struct wlr_surface *origin, uint32_t serial, struct wlr_touch_point **point_ptr) { if (wlr_seat_touch_num_points(seat) != 1 || seat->touch_state.grab_serial != serial) { wlr_log(WLR_DEBUG, "Touch grab serial validation failed: " "num_points=%d grab_serial=%"PRIu32" (got %"PRIu32")", wlr_seat_touch_num_points(seat), seat->touch_state.grab_serial, serial); return false; } struct wlr_touch_point *point; wl_list_for_each(point, &seat->touch_state.touch_points, link) { if (origin == NULL || point->surface == origin) { if (point_ptr != NULL) { *point_ptr = point; } return true; } } wlr_log(WLR_DEBUG, "Touch grab serial validation failed: " "invalid origin surface"); return false; } bool wlr_surface_accepts_touch(struct wlr_seat *wlr_seat, struct wlr_surface *surface) { struct wl_client *client = wl_resource_get_client(surface->resource); struct wlr_seat_client *seat_client = wlr_seat_client_for_wl_client(wlr_seat, client); if (!seat_client) { return false; } return !wl_list_empty(&seat_client->touches); } wlroots-0.17.1/types/tablet_v2/000077500000000000000000000000001454110342200163335ustar00rootroot00000000000000wlroots-0.17.1/types/tablet_v2/wlr_tablet_v2.c000066400000000000000000000217011454110342200212460ustar00rootroot00000000000000#ifndef _POSIX_C_SOURCE #define _POSIX_C_SOURCE 200809L #endif #include #include #include #include #include #include #include #include #include #include #include "tablet-unstable-v2-protocol.h" #define TABLET_MANAGER_VERSION 1 struct wlr_tablet_manager_client_v2 { struct wl_list link; struct wl_client *client; struct wl_resource *resource; struct wlr_tablet_manager_v2 *manager; struct wl_list tablet_seats; // wlr_tablet_seat_client_v2.link }; static void tablet_seat_destroy(struct wlr_tablet_seat_v2 *seat) { struct wlr_tablet_seat_client_v2 *client, *client_tmp; wl_list_for_each_safe(client, client_tmp, &seat->clients, seat_link) { tablet_seat_client_v2_destroy(client->resource); } wl_list_remove(&seat->link); wl_list_remove(&seat->seat_destroy.link); free(seat); } static void handle_wlr_seat_destroy(struct wl_listener *listener, void *data) { struct wlr_tablet_seat_v2 *seat = wl_container_of(listener, seat, seat_destroy); tablet_seat_destroy(seat); } static struct wlr_tablet_seat_v2 *create_tablet_seat( struct wlr_tablet_manager_v2 *manager, struct wlr_seat *wlr_seat) { struct wlr_tablet_seat_v2 *tablet_seat = calloc(1, sizeof(*tablet_seat)); if (!tablet_seat) { return NULL; } tablet_seat->manager = manager; tablet_seat->wlr_seat = wlr_seat; wl_list_init(&tablet_seat->clients); wl_list_init(&tablet_seat->tablets); wl_list_init(&tablet_seat->tools); wl_list_init(&tablet_seat->pads); tablet_seat->seat_destroy.notify = handle_wlr_seat_destroy; wl_signal_add(&wlr_seat->events.destroy, &tablet_seat->seat_destroy); wl_list_insert(&manager->seats, &tablet_seat->link); return tablet_seat; } struct wlr_tablet_seat_v2 *get_or_create_tablet_seat( struct wlr_tablet_manager_v2 *manager, struct wlr_seat *wlr_seat) { struct wlr_tablet_seat_v2 *pos; wl_list_for_each(pos, &manager->seats, link) { if (pos->wlr_seat == wlr_seat) { return pos; } } return create_tablet_seat(manager, wlr_seat); } static void tablet_seat_handle_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static const struct zwp_tablet_seat_v2_interface seat_impl = { .destroy = tablet_seat_handle_destroy, }; struct wlr_tablet_seat_client_v2 *tablet_seat_client_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &zwp_tablet_seat_v2_interface, &seat_impl)); return wl_resource_get_user_data(resource); } void tablet_seat_client_v2_destroy(struct wl_resource *resource) { struct wlr_tablet_seat_client_v2 *seat = tablet_seat_client_from_resource(resource); if (!seat) { return; } struct wlr_tablet_client_v2 *tablet; struct wlr_tablet_client_v2 *tmp_tablet; wl_list_for_each_safe(tablet, tmp_tablet, &seat->tablets, seat_link) { destroy_tablet_v2(tablet->resource); } struct wlr_tablet_pad_client_v2 *pad; struct wlr_tablet_pad_client_v2 *tmp_pad; wl_list_for_each_safe(pad, tmp_pad, &seat->pads, seat_link) { destroy_tablet_pad_v2(pad->resource); } struct wlr_tablet_tool_client_v2 *tool; struct wlr_tablet_tool_client_v2 *tmp_tool; wl_list_for_each_safe(tool, tmp_tool, &seat->tools, seat_link) { destroy_tablet_tool_v2(tool->resource); } wl_list_remove(&seat->seat_link); wl_list_remove(&seat->client_link); wl_list_remove(&seat->seat_client_destroy.link); free(seat); wl_resource_set_user_data(resource, NULL); } static void handle_seat_client_destroy(struct wl_listener *listener, void *data) { struct wlr_tablet_seat_client_v2 *seat = wl_container_of(listener, seat, seat_client_destroy); tablet_seat_client_v2_destroy(seat->resource); } static void tablet_manager_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static struct wlr_tablet_manager_client_v2 *tablet_manager_client_from_resource( struct wl_resource *resource); static void get_tablet_seat(struct wl_client *wl_client, struct wl_resource *resource, uint32_t id, struct wl_resource *seat_resource) { struct wlr_tablet_manager_client_v2 *manager = tablet_manager_client_from_resource(resource); if (!manager) { /* Inert manager, just set up the resource for later * destruction, without allocations or advertising things */ wl_resource_set_implementation(seat_resource, &seat_impl, NULL, tablet_seat_client_v2_destroy); return; } struct wl_resource *tablet_seat_resource = wl_resource_create(wl_client, &zwp_tablet_seat_v2_interface, TABLET_MANAGER_VERSION, id); if (tablet_seat_resource == NULL) { wl_client_post_no_memory(wl_client); return; } wl_resource_set_implementation(tablet_seat_resource, &seat_impl, NULL, tablet_seat_client_v2_destroy); struct wlr_seat_client *seat = wlr_seat_client_from_resource(seat_resource); if (seat == NULL) { return; } struct wlr_tablet_seat_v2 *tablet_seat = get_or_create_tablet_seat(manager->manager, seat->seat); if (!tablet_seat) { // This can only happen when we ran out of memory wl_client_post_no_memory(wl_client); return; } struct wlr_tablet_seat_client_v2 *seat_client = calloc(1, sizeof(*seat_client)); if (seat_client == NULL) { wl_client_post_no_memory(wl_client); return; } seat_client->resource = tablet_seat_resource; seat_client->seat_client = seat; seat_client->client = manager; seat_client->wl_client = wl_client; wl_list_init(&seat_client->tools); wl_list_init(&seat_client->tablets); wl_list_init(&seat_client->pads); wl_resource_set_user_data(tablet_seat_resource, seat_client); seat_client->seat_client_destroy.notify = handle_seat_client_destroy; wl_signal_add(&seat->events.destroy, &seat_client->seat_client_destroy); wl_list_insert(&manager->tablet_seats, &seat_client->client_link); wl_list_insert(&tablet_seat->clients, &seat_client->seat_link); // We need to emit the devices already on the seat struct wlr_tablet_v2_tablet *tablet_pos; wl_list_for_each(tablet_pos, &tablet_seat->tablets, link) { add_tablet_client(seat_client, tablet_pos); } struct wlr_tablet_v2_tablet_pad *pad_pos; wl_list_for_each(pad_pos, &tablet_seat->pads, link) { add_tablet_pad_client(seat_client, pad_pos); } struct wlr_tablet_v2_tablet_tool *tool_pos; wl_list_for_each(tool_pos, &tablet_seat->tools, link) { add_tablet_tool_client(seat_client, tool_pos); } } static const struct zwp_tablet_manager_v2_interface manager_impl = { .get_tablet_seat = get_tablet_seat, .destroy = tablet_manager_destroy, }; static struct wlr_tablet_manager_client_v2 *tablet_manager_client_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &zwp_tablet_manager_v2_interface, &manager_impl)); return wl_resource_get_user_data(resource); } static void wlr_tablet_manager_v2_destroy(struct wl_resource *resource) { struct wlr_tablet_manager_client_v2 *client = tablet_manager_client_from_resource(resource); if (!client) { return; } struct wlr_tablet_seat_client_v2 *pos; struct wlr_tablet_seat_client_v2 *tmp; wl_list_for_each_safe(pos, tmp, &client->tablet_seats, client_link) { tablet_seat_client_v2_destroy(pos->resource); } wl_list_remove(&client->link); free(client); wl_resource_set_user_data(resource, NULL); } static void tablet_v2_bind(struct wl_client *wl_client, void *data, uint32_t version, uint32_t id) { struct wlr_tablet_manager_v2 *manager = data; struct wlr_tablet_manager_client_v2 *client = calloc(1, sizeof(*client)); if (client == NULL) { wl_client_post_no_memory(wl_client); return; } wl_list_init(&client->tablet_seats); client->resource = wl_resource_create(wl_client, &zwp_tablet_manager_v2_interface, version, id); if (client->resource == NULL) { free(client); wl_client_post_no_memory(wl_client); return; } client->client = wl_client; client->manager = manager; wl_resource_set_implementation(client->resource, &manager_impl, client, wlr_tablet_manager_v2_destroy); wl_list_insert(&manager->clients, &client->link); } static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_tablet_manager_v2 *manager = wl_container_of(listener, manager, display_destroy); wl_signal_emit_mutable(&manager->events.destroy, manager); wl_list_remove(&manager->display_destroy.link); wl_global_destroy(manager->wl_global); free(manager); } struct wlr_tablet_manager_v2 *wlr_tablet_v2_create(struct wl_display *display) { struct wlr_tablet_manager_v2 *tablet = calloc(1, sizeof(*tablet)); if (!tablet) { return NULL; } tablet->wl_global = wl_global_create(display, &zwp_tablet_manager_v2_interface, TABLET_MANAGER_VERSION, tablet, tablet_v2_bind); if (tablet->wl_global == NULL) { free(tablet); return NULL; } wl_signal_init(&tablet->events.destroy); wl_list_init(&tablet->clients); wl_list_init(&tablet->seats); tablet->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &tablet->display_destroy); return tablet; } wlroots-0.17.1/types/tablet_v2/wlr_tablet_v2_pad.c000066400000000000000000000507401454110342200220770ustar00rootroot00000000000000#ifndef _POSIX_C_SOURCE #define _POSIX_C_SOURCE 200809L #endif #include #include #include #include #include #include #include #include #include #include "tablet-unstable-v2-protocol.h" static const struct wlr_tablet_pad_v2_grab_interface default_pad_grab_interface; struct tablet_pad_auxiliary_user_data { struct wlr_tablet_pad_client_v2 *pad; size_t index; }; static void handle_tablet_pad_v2_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static void destroy_tablet_pad_ring_v2(struct wl_resource *resource) { struct tablet_pad_auxiliary_user_data *aux = wl_resource_get_user_data(resource); if (!aux) { return; } aux->pad->rings[aux->index] = NULL; free(aux); wl_resource_set_user_data(resource, NULL); } static void handle_tablet_pad_ring_v2_set_feedback(struct wl_client *client, struct wl_resource *resource, const char *description, uint32_t serial) { struct tablet_pad_auxiliary_user_data *aux = wl_resource_get_user_data(resource); if (!aux) { return; } struct wlr_tablet_v2_event_feedback evt = { .serial = serial, .description = description, .index = aux->index }; wl_signal_emit_mutable(&aux->pad->pad->events.ring_feedback, &evt); } static void handle_tablet_pad_ring_v2_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static const struct zwp_tablet_pad_ring_v2_interface tablet_pad_ring_impl = { .set_feedback = handle_tablet_pad_ring_v2_set_feedback, .destroy = handle_tablet_pad_ring_v2_destroy, }; static void destroy_tablet_pad_strip_v2(struct wl_resource *resource) { struct tablet_pad_auxiliary_user_data *aux = wl_resource_get_user_data(resource); if (!aux) { return; } aux->pad->strips[aux->index] = NULL; free(aux); wl_resource_set_user_data(resource, NULL); } static void handle_tablet_pad_strip_v2_set_feedback(struct wl_client *client, struct wl_resource *resource, const char *description, uint32_t serial) { struct tablet_pad_auxiliary_user_data *aux = wl_resource_get_user_data(resource); if (!aux) { return; } struct wlr_tablet_v2_event_feedback evt = { .serial = serial, .description = description, .index = aux->index }; wl_signal_emit_mutable(&aux->pad->pad->events.strip_feedback, &evt); } static void handle_tablet_pad_strip_v2_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static const struct zwp_tablet_pad_strip_v2_interface tablet_pad_strip_impl = { .set_feedback = handle_tablet_pad_strip_v2_set_feedback, .destroy = handle_tablet_pad_strip_v2_destroy, }; static void handle_tablet_pad_v2_set_feedback( struct wl_client *client, struct wl_resource *resource, uint32_t button, const char *description, uint32_t serial) { struct wlr_tablet_pad_client_v2 *pad = tablet_pad_client_from_resource(resource); if (!pad) { return; } struct wlr_tablet_v2_event_feedback evt = { .serial = serial, .index = button, .description = description, }; wl_signal_emit_mutable(&pad->pad->events.button_feedback, &evt); } static const struct zwp_tablet_pad_v2_interface tablet_pad_impl = { .set_feedback = handle_tablet_pad_v2_set_feedback, .destroy = handle_tablet_pad_v2_destroy, }; static void destroy_tablet_pad_group_v2(struct wl_resource *resource) { struct tablet_pad_auxiliary_user_data *aux = wl_resource_get_user_data(resource); if (!aux) { return; } aux->pad->groups[aux->index] = NULL; free(aux); wl_resource_set_user_data(resource, NULL); } void destroy_tablet_pad_v2(struct wl_resource *resource) { struct wlr_tablet_pad_client_v2 *pad = tablet_pad_client_from_resource(resource); if (!pad) { return; } wl_list_remove(&pad->seat_link); wl_list_remove(&pad->pad_link); /* This isn't optimal, if the client destroys the resources in another * order, it will be disconnected. * But this makes things *way* easier for us, and (untested) I doubt * clients will destroy it in another order. */ for (size_t i = 0; i < pad->group_count; ++i) { if (pad->groups[i]) { destroy_tablet_pad_group_v2(pad->groups[i]); } } free(pad->groups); for (size_t i = 0; i < pad->ring_count; ++i) { if (pad->rings[i]) { destroy_tablet_pad_ring_v2(pad->rings[i]); } } free(pad->rings); for (size_t i = 0; i < pad->strip_count; ++i) { if (pad->strips[i]) { destroy_tablet_pad_strip_v2(pad->strips[i]); } } free(pad->strips); if (pad->pad->current_client == pad) { pad->pad->current_client = NULL; } free(pad); wl_resource_set_user_data(resource, NULL); } static void handle_tablet_pad_group_v2_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static const struct zwp_tablet_pad_group_v2_interface tablet_pad_group_impl = { .destroy = handle_tablet_pad_group_v2_destroy, }; static void add_tablet_pad_group(struct wlr_tablet_v2_tablet_pad *pad, struct wlr_tablet_pad_client_v2 *client, struct wlr_tablet_pad_group *group, size_t index) { uint32_t version = wl_resource_get_version(client->resource); client->groups[index] = wl_resource_create(client->client, &zwp_tablet_pad_group_v2_interface, version, 0); if (!client->groups[index]) { wl_client_post_no_memory(client->client); return; } struct tablet_pad_auxiliary_user_data *user_data = calloc(1, sizeof(*user_data)); if (!user_data) { wl_client_post_no_memory(client->client); return; } user_data->pad = client; user_data->index = index; wl_resource_set_implementation(client->groups[index], &tablet_pad_group_impl, user_data, destroy_tablet_pad_group_v2); zwp_tablet_pad_v2_send_group(client->resource, client->groups[index]); zwp_tablet_pad_group_v2_send_modes(client->groups[index], group->mode_count); struct wl_array button_array; wl_array_init(&button_array); wl_array_add(&button_array, group->button_count * sizeof(int)); memcpy(button_array.data, group->buttons, group->button_count * sizeof(int)); zwp_tablet_pad_group_v2_send_buttons(client->groups[index], &button_array); wl_array_release(&button_array); client->strip_count = group->strip_count; for (size_t i = 0; i < group->strip_count; ++i) { size_t strip = group->strips[i]; struct tablet_pad_auxiliary_user_data *user_data = calloc(1, sizeof(*user_data)); if (!user_data) { wl_client_post_no_memory(client->client); return; } user_data->pad = client; user_data->index = strip; client->strips[strip] = wl_resource_create(client->client, &zwp_tablet_pad_strip_v2_interface, version, 0); if (!client->strips[strip]) { free(user_data); wl_client_post_no_memory(client->client); return; } wl_resource_set_implementation(client->strips[strip], &tablet_pad_strip_impl, user_data, destroy_tablet_pad_strip_v2); zwp_tablet_pad_group_v2_send_strip(client->groups[index], client->strips[strip]); } client->ring_count = group->ring_count; for (size_t i = 0; i < group->ring_count; ++i) { size_t ring = group->rings[i]; struct tablet_pad_auxiliary_user_data *user_data = calloc(1, sizeof(*user_data)); if (!user_data) { wl_client_post_no_memory(client->client); return; } user_data->pad = client; user_data->index = ring; client->rings[ring] = wl_resource_create(client->client, &zwp_tablet_pad_ring_v2_interface, version, 0); if (!client->rings[ring]) { free(user_data); wl_client_post_no_memory(client->client); return; } wl_resource_set_implementation(client->rings[ring], &tablet_pad_ring_impl, user_data, destroy_tablet_pad_ring_v2); zwp_tablet_pad_group_v2_send_ring(client->groups[index], client->rings[ring]); } zwp_tablet_pad_group_v2_send_done(client->groups[index]); } void add_tablet_pad_client(struct wlr_tablet_seat_client_v2 *seat, struct wlr_tablet_v2_tablet_pad *pad) { struct wlr_tablet_pad_client_v2 *client = calloc(1, sizeof(*client)); if (!client) { wl_client_post_no_memory(seat->wl_client); return; } client->pad = pad; client->seat = seat; client->groups = calloc(wl_list_length(&pad->wlr_pad->groups), sizeof(*client->groups)); if (!client->groups) { wl_client_post_no_memory(seat->wl_client); free(client); return; } client->rings = calloc(pad->wlr_pad->ring_count, sizeof(*client->rings)); if (!client->rings) { wl_client_post_no_memory(seat->wl_client); free(client->groups); free(client); return; } client->strips = calloc(pad->wlr_pad->strip_count, sizeof(*client->strips)); if (!client->strips) { wl_client_post_no_memory(seat->wl_client); free(client->groups); free(client->rings); free(client); return; } uint32_t version = wl_resource_get_version(seat->resource); client->resource = wl_resource_create(seat->wl_client, &zwp_tablet_pad_v2_interface, version, 0); if (!client->resource) { wl_client_post_no_memory(seat->wl_client); free(client->groups); free(client->rings); free(client->strips); free(client); return; } wl_resource_set_implementation(client->resource, &tablet_pad_impl, client, destroy_tablet_pad_v2); zwp_tablet_seat_v2_send_pad_added(seat->resource, client->resource); client->client = seat->wl_client; // Send the expected events if (pad->wlr_pad->button_count) { zwp_tablet_pad_v2_send_buttons(client->resource, pad->wlr_pad->button_count); } const char **path_ptr; wl_array_for_each(path_ptr, &pad->wlr_pad->paths) { zwp_tablet_pad_v2_send_path(client->resource, *path_ptr); } size_t i = 0; struct wlr_tablet_pad_group *group; client->group_count = pad->group_count; wl_list_for_each(group, &pad->wlr_pad->groups, link) { add_tablet_pad_group(pad, client, group, i++); } zwp_tablet_pad_v2_send_done(client->resource); wl_list_insert(&seat->pads, &client->seat_link); wl_list_insert(&pad->clients, &client->pad_link); } static void handle_wlr_tablet_pad_destroy(struct wl_listener *listener, void *data) { struct wlr_tablet_v2_tablet_pad *pad = wl_container_of(listener, pad, pad_destroy); struct wlr_tablet_pad_client_v2 *client; struct wlr_tablet_pad_client_v2 *tmp_client; wl_list_for_each_safe(client, tmp_client, &pad->clients, pad_link) { zwp_tablet_pad_v2_send_removed(client->resource); destroy_tablet_pad_v2(client->resource); } wl_list_remove(&pad->clients); wl_list_remove(&pad->link); wl_list_remove(&pad->pad_destroy.link); wl_list_remove(&pad->events.button_feedback.listener_list); wl_list_remove(&pad->events.strip_feedback.listener_list); wl_list_remove(&pad->events.ring_feedback.listener_list); free(pad); } struct wlr_tablet_v2_tablet_pad *wlr_tablet_pad_create( struct wlr_tablet_manager_v2 *manager, struct wlr_seat *wlr_seat, struct wlr_input_device *wlr_device) { assert(wlr_device->type == WLR_INPUT_DEVICE_TABLET_PAD); struct wlr_tablet_seat_v2 *seat = get_or_create_tablet_seat(manager, wlr_seat); if (!seat) { return NULL; } struct wlr_tablet_pad *wlr_pad = wlr_tablet_pad_from_input_device(wlr_device); struct wlr_tablet_v2_tablet_pad *pad = calloc(1, sizeof(*pad)); if (!pad) { return NULL; } pad->default_grab.interface = &default_pad_grab_interface; pad->default_grab.pad = pad; pad->grab = &pad->default_grab; pad->group_count = wl_list_length(&wlr_pad->groups); pad->groups = calloc(pad->group_count, sizeof(uint32_t)); if (!pad->groups) { free(pad); return NULL; } pad->wlr_pad = wlr_pad; wl_list_init(&pad->clients); pad->pad_destroy.notify = handle_wlr_tablet_pad_destroy; wl_signal_add(&wlr_device->events.destroy, &pad->pad_destroy); wl_list_insert(&seat->pads, &pad->link); // We need to create a tablet client for all clients on the seat struct wlr_tablet_seat_client_v2 *pos; wl_list_for_each(pos, &seat->clients, seat_link) { // Tell the clients about the new tool add_tablet_pad_client(pos, pad); } wl_signal_init(&pad->events.button_feedback); wl_signal_init(&pad->events.strip_feedback); wl_signal_init(&pad->events.ring_feedback); return pad; } struct wlr_tablet_pad_client_v2 *tablet_pad_client_from_resource(struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &zwp_tablet_pad_v2_interface, &tablet_pad_impl)); return wl_resource_get_user_data(resource); } /* Actual protocol foo */ uint32_t wlr_send_tablet_v2_tablet_pad_enter( struct wlr_tablet_v2_tablet_pad *pad, struct wlr_tablet_v2_tablet *tablet, struct wlr_surface *surface) { struct wl_client *client = wl_resource_get_client(surface->resource); struct wlr_tablet_client_v2 *tablet_tmp; struct wlr_tablet_client_v2 *tablet_client = NULL; wl_list_for_each(tablet_tmp, &tablet->clients, tablet_link) { if (tablet_tmp->client == client) { tablet_client = tablet_tmp; break; } } // Couldn't find the client binding for the surface's client. Either // the client didn't bind tablet_v2 at all, or not for the relevant // seat if (!tablet_client) { return 0; } struct wlr_tablet_pad_client_v2 *pad_tmp = NULL; struct wlr_tablet_pad_client_v2 *pad_client = NULL; wl_list_for_each(pad_tmp, &pad->clients, pad_link) { if (pad_tmp->client == client) { pad_client = pad_tmp; break; } } // Couldn't find the client binding for the surface's client. Either // the client didn't bind tablet_v2 at all, or not for the relevant // seat if (!pad_client) { return 0; } pad->current_client = pad_client; uint32_t serial = wlr_seat_client_next_serial( pad_client->seat->seat_client); zwp_tablet_pad_v2_send_enter(pad_client->resource, serial, tablet_client->resource, surface->resource); struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); uint32_t time = now.tv_nsec / 1000; for (size_t i = 0; i < pad->group_count; ++i) { if (pad_client->groups[i]) { zwp_tablet_pad_group_v2_send_mode_switch( pad_client->groups[i], time, serial, pad->groups[i]); } } return serial; } void wlr_send_tablet_v2_tablet_pad_button( struct wlr_tablet_v2_tablet_pad *pad, size_t button, uint32_t time, enum zwp_tablet_pad_v2_button_state state) { if (pad->current_client) { zwp_tablet_pad_v2_send_button(pad->current_client->resource, time, button, state); } } void wlr_send_tablet_v2_tablet_pad_strip(struct wlr_tablet_v2_tablet_pad *pad, uint32_t strip, double position, bool finger, uint32_t time) { if (!pad->current_client || !pad->current_client->strips || !pad->current_client->strips[strip]) { return; } struct wl_resource *resource = pad->current_client->strips[strip]; if (finger) { zwp_tablet_pad_strip_v2_send_source(resource, ZWP_TABLET_PAD_STRIP_V2_SOURCE_FINGER); } if (position < 0) { zwp_tablet_pad_strip_v2_send_stop(resource); } else { zwp_tablet_pad_strip_v2_send_position(resource, position * 65535); } zwp_tablet_pad_strip_v2_send_frame(resource, time); } void wlr_send_tablet_v2_tablet_pad_ring(struct wlr_tablet_v2_tablet_pad *pad, uint32_t ring, double position, bool finger, uint32_t time) { if (!pad->current_client || !pad->current_client->rings || !pad->current_client->rings[ring]) { return; } struct wl_resource *resource = pad->current_client->rings[ring]; if (finger) { zwp_tablet_pad_ring_v2_send_source(resource, ZWP_TABLET_PAD_RING_V2_SOURCE_FINGER); } if (position < 0) { zwp_tablet_pad_ring_v2_send_stop(resource); } else { zwp_tablet_pad_ring_v2_send_angle(resource, wl_fixed_from_double(position)); } zwp_tablet_pad_ring_v2_send_frame(resource, time); } uint32_t wlr_send_tablet_v2_tablet_pad_leave(struct wlr_tablet_v2_tablet_pad *pad, struct wlr_surface *surface) { struct wl_client *client = wl_resource_get_client(surface->resource); if (!pad->current_client || client != pad->current_client->client) { return 0; } uint32_t serial = wlr_seat_client_next_serial( pad->current_client->seat->seat_client); zwp_tablet_pad_v2_send_leave(pad->current_client->resource, serial, surface->resource); return serial; } uint32_t wlr_send_tablet_v2_tablet_pad_mode(struct wlr_tablet_v2_tablet_pad *pad, size_t group, uint32_t mode, uint32_t time) { if (!pad->current_client || !pad->current_client->groups || !pad->current_client->groups[group] ) { return 0; } if (pad->groups[group] == mode) { return 0; } pad->groups[group] = mode; uint32_t serial = wlr_seat_client_next_serial( pad->current_client->seat->seat_client); zwp_tablet_pad_group_v2_send_mode_switch( pad->current_client->groups[group], time, serial, mode); return serial; } bool wlr_surface_accepts_tablet_v2(struct wlr_tablet_v2_tablet *tablet, struct wlr_surface *surface) { struct wl_client *client = wl_resource_get_client(surface->resource); if (tablet->current_client && tablet->current_client->client == client) { return true; } struct wlr_tablet_client_v2 *tablet_tmp; wl_list_for_each(tablet_tmp, &tablet->clients, tablet_link) { if (tablet_tmp->client == client) { return true; } } return false; } uint32_t wlr_tablet_v2_tablet_pad_notify_enter( struct wlr_tablet_v2_tablet_pad *pad, struct wlr_tablet_v2_tablet *tablet, struct wlr_surface *surface) { if (pad->grab && pad->grab->interface->enter) { return pad->grab->interface->enter(pad->grab, tablet, surface); } return 0; } void wlr_tablet_v2_tablet_pad_notify_button( struct wlr_tablet_v2_tablet_pad *pad, size_t button, uint32_t time, enum zwp_tablet_pad_v2_button_state state) { if (pad->grab && pad->grab->interface->button) { pad->grab->interface->button(pad->grab, button, time, state); } } void wlr_tablet_v2_tablet_pad_notify_strip( struct wlr_tablet_v2_tablet_pad *pad, uint32_t strip, double position, bool finger, uint32_t time) { if (pad->grab && pad->grab->interface->strip) { pad->grab->interface->strip(pad->grab, strip, position, finger, time); } } void wlr_tablet_v2_tablet_pad_notify_ring( struct wlr_tablet_v2_tablet_pad *pad, uint32_t ring, double position, bool finger, uint32_t time) { if (pad->grab && pad->grab->interface->ring) { pad->grab->interface->ring(pad->grab, ring, position, finger, time); } } uint32_t wlr_tablet_v2_tablet_pad_notify_leave( struct wlr_tablet_v2_tablet_pad *pad, struct wlr_surface *surface) { if (pad->grab && pad->grab->interface->leave) { return pad->grab->interface->leave(pad->grab, surface); } return 0; } uint32_t wlr_tablet_v2_tablet_pad_notify_mode( struct wlr_tablet_v2_tablet_pad *pad, size_t group, uint32_t mode, uint32_t time) { if (pad->grab && pad->grab->interface->mode) { return pad->grab->interface->mode(pad->grab, group, mode, time); } return 0; } void wlr_tablet_v2_start_grab(struct wlr_tablet_v2_tablet_pad *pad, struct wlr_tablet_pad_v2_grab *grab) { if (grab != &pad->default_grab) { struct wlr_tablet_pad_v2_grab *prev = pad->grab; grab->pad = pad; pad->grab = grab; if (prev && prev->interface->cancel) { prev->interface->cancel(prev); } } } void wlr_tablet_v2_end_grab(struct wlr_tablet_v2_tablet_pad *pad) { struct wlr_tablet_pad_v2_grab *grab = pad->grab; if (grab && grab != &pad->default_grab) { pad->grab = &pad->default_grab; if (grab->interface->cancel) { grab->interface->cancel(grab); } } } static uint32_t default_pad_enter( struct wlr_tablet_pad_v2_grab *grab, struct wlr_tablet_v2_tablet *tablet, struct wlr_surface *surface) { return wlr_send_tablet_v2_tablet_pad_enter(grab->pad, tablet, surface); } static void default_pad_button(struct wlr_tablet_pad_v2_grab *grab,size_t button, uint32_t time, enum zwp_tablet_pad_v2_button_state state) { wlr_send_tablet_v2_tablet_pad_button(grab->pad, button, time, state); } static void default_pad_strip(struct wlr_tablet_pad_v2_grab *grab, uint32_t strip, double position, bool finger, uint32_t time) { wlr_send_tablet_v2_tablet_pad_strip(grab->pad, strip, position, finger, time); } static void default_pad_ring(struct wlr_tablet_pad_v2_grab *grab, uint32_t ring, double position, bool finger, uint32_t time) { wlr_send_tablet_v2_tablet_pad_ring(grab->pad, ring, position, finger, time); } static uint32_t default_pad_leave(struct wlr_tablet_pad_v2_grab *grab, struct wlr_surface *surface) { return wlr_send_tablet_v2_tablet_pad_leave(grab->pad, surface); } static uint32_t default_pad_mode(struct wlr_tablet_pad_v2_grab *grab, size_t group, uint32_t mode, uint32_t time) { return wlr_send_tablet_v2_tablet_pad_mode(grab->pad, group, mode, time); } static void default_pad_cancel(struct wlr_tablet_pad_v2_grab *grab) { // Do nothing, the default cancel can be ignored. } static const struct wlr_tablet_pad_v2_grab_interface default_pad_grab_interface = { .enter = default_pad_enter, .button = default_pad_button, .strip = default_pad_strip, .ring = default_pad_ring, .leave = default_pad_leave, .mode = default_pad_mode, .cancel = default_pad_cancel, }; wlroots-0.17.1/types/tablet_v2/wlr_tablet_v2_tablet.c000066400000000000000000000074161454110342200226100ustar00rootroot00000000000000#ifndef _POSIX_C_SOURCE #define _POSIX_C_SOURCE 200809L #endif #include #include #include #include #include #include #include #include #include "tablet-unstable-v2-protocol.h" void destroy_tablet_v2(struct wl_resource *resource) { struct wlr_tablet_client_v2 *tablet = tablet_client_from_resource(resource); if (!tablet) { return; } wl_list_remove(&tablet->seat_link); wl_list_remove(&tablet->tablet_link); free(tablet); wl_resource_set_user_data(resource, NULL); } static void handle_tablet_v2_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static const struct zwp_tablet_v2_interface tablet_impl = { .destroy = handle_tablet_v2_destroy, }; static void handle_wlr_tablet_destroy(struct wl_listener *listener, void *data) { struct wlr_tablet_v2_tablet *tablet = wl_container_of(listener, tablet, tool_destroy); struct wlr_tablet_client_v2 *pos; struct wlr_tablet_client_v2 *tmp; wl_list_for_each_safe(pos, tmp, &tablet->clients, tablet_link) { zwp_tablet_v2_send_removed(pos->resource); } wl_list_remove(&tablet->clients); wl_list_remove(&tablet->link); wl_list_remove(&tablet->tool_destroy.link); free(tablet); } struct wlr_tablet_v2_tablet *wlr_tablet_create( struct wlr_tablet_manager_v2 *manager, struct wlr_seat *wlr_seat, struct wlr_input_device *wlr_device) { assert(wlr_device->type == WLR_INPUT_DEVICE_TABLET_TOOL); struct wlr_tablet_seat_v2 *seat = get_or_create_tablet_seat(manager, wlr_seat); if (!seat) { return NULL; } struct wlr_tablet *wlr_tablet = wlr_tablet_from_input_device(wlr_device); struct wlr_tablet_v2_tablet *tablet = calloc(1, sizeof(*tablet)); if (!tablet) { return NULL; } tablet->wlr_tablet = wlr_tablet; tablet->wlr_device = wlr_device; wl_list_init(&tablet->clients); tablet->tool_destroy.notify = handle_wlr_tablet_destroy; wl_signal_add(&wlr_device->events.destroy, &tablet->tool_destroy); wl_list_insert(&seat->tablets, &tablet->link); // We need to create a tablet client for all clients on the seat struct wlr_tablet_seat_client_v2 *pos; wl_list_for_each(pos, &seat->clients, seat_link) { // Tell the clients about the new tool add_tablet_client(pos, tablet); } return tablet; } void add_tablet_client(struct wlr_tablet_seat_client_v2 *seat, struct wlr_tablet_v2_tablet *tablet) { struct wlr_tablet_client_v2 *client = calloc(1, sizeof(*client)); if (!client) { return; } uint32_t version = wl_resource_get_version(seat->resource); client->resource = wl_resource_create(seat->wl_client, &zwp_tablet_v2_interface, version, 0); if (!client->resource) { wl_resource_post_no_memory(seat->resource); free(client); return; } wl_resource_set_implementation(client->resource, &tablet_impl, client, destroy_tablet_v2); zwp_tablet_seat_v2_send_tablet_added(seat->resource, client->resource); // Send the expected events if (tablet->wlr_tablet->base.name) { zwp_tablet_v2_send_name(client->resource, tablet->wlr_tablet->base.name); } zwp_tablet_v2_send_id(client->resource, tablet->wlr_device->vendor, tablet->wlr_device->product); const char **path_ptr; wl_array_for_each(path_ptr, &tablet->wlr_tablet->paths) { zwp_tablet_v2_send_path(client->resource, *path_ptr); } zwp_tablet_v2_send_done(client->resource); client->client = seat->wl_client; wl_list_insert(&seat->tablets, &client->seat_link); wl_list_insert(&tablet->clients, &client->tablet_link); } struct wlr_tablet_client_v2 *tablet_client_from_resource(struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &zwp_tablet_v2_interface, &tablet_impl)); return wl_resource_get_user_data(resource); } wlroots-0.17.1/types/tablet_v2/wlr_tablet_v2_tool.c000066400000000000000000000577731454110342200223250ustar00rootroot00000000000000#ifndef _POSIX_C_SOURCE #define _POSIX_C_SOURCE 200809L #endif #include #include #include #include #include #include #include #include #include "util/set.h" #include "util/time.h" #include "tablet-unstable-v2-protocol.h" static const struct wlr_tablet_tool_v2_grab_interface default_tool_grab_interface; static void tablet_tool_cursor_surface_handle_commit(struct wlr_surface *surface) { pixman_region32_clear(&surface->input_region); if (wlr_surface_has_buffer(surface)) { wlr_surface_map(surface); } } static const struct wlr_surface_role tablet_tool_cursor_surface_role = { .name = "wp_tablet_tool-cursor", .no_object = true, .commit = tablet_tool_cursor_surface_handle_commit, }; static void handle_tablet_tool_v2_set_cursor(struct wl_client *client, struct wl_resource *resource, uint32_t serial, struct wl_resource *surface_resource, int32_t hotspot_x, int32_t hotspot_y) { struct wlr_tablet_tool_client_v2 *tool = tablet_tool_client_from_resource(resource); if (!tool || !tool->tool) { return; } struct wlr_surface *surface = NULL; if (surface_resource != NULL) { surface = wlr_surface_from_resource(surface_resource); if (!wlr_surface_set_role(surface, &tablet_tool_cursor_surface_role, surface_resource, ZWP_TABLET_TOOL_V2_ERROR_ROLE)) { return; } tablet_tool_cursor_surface_handle_commit(surface); } struct wlr_tablet_v2_event_cursor evt = { .surface = surface, .serial = serial, .hotspot_x = hotspot_x, .hotspot_y = hotspot_y, .seat_client = tool->seat->seat_client, }; wl_signal_emit_mutable(&tool->tool->events.set_cursor, &evt); } static void handle_tablet_tool_v2_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static const struct zwp_tablet_tool_v2_interface tablet_tool_impl = { .set_cursor = handle_tablet_tool_v2_set_cursor, .destroy = handle_tablet_tool_v2_destroy, }; static enum zwp_tablet_tool_v2_type tablet_type_from_wlr_type( enum wlr_tablet_tool_type wlr_type) { switch(wlr_type) { case WLR_TABLET_TOOL_TYPE_PEN: return ZWP_TABLET_TOOL_V2_TYPE_PEN; case WLR_TABLET_TOOL_TYPE_ERASER: return ZWP_TABLET_TOOL_V2_TYPE_ERASER; case WLR_TABLET_TOOL_TYPE_BRUSH: return ZWP_TABLET_TOOL_V2_TYPE_BRUSH; case WLR_TABLET_TOOL_TYPE_PENCIL: return ZWP_TABLET_TOOL_V2_TYPE_PENCIL; case WLR_TABLET_TOOL_TYPE_AIRBRUSH: return ZWP_TABLET_TOOL_V2_TYPE_AIRBRUSH; case WLR_TABLET_TOOL_TYPE_MOUSE: return ZWP_TABLET_TOOL_V2_TYPE_MOUSE; case WLR_TABLET_TOOL_TYPE_LENS: return ZWP_TABLET_TOOL_V2_TYPE_LENS; case WLR_TABLET_TOOL_TYPE_TOTEM: // missing, see: // https://gitlab.freedesktop.org/wayland/wayland-protocols/-/issues/19 abort(); } abort(); // unreachable } void destroy_tablet_tool_v2(struct wl_resource *resource) { struct wlr_tablet_tool_client_v2 *client = tablet_tool_client_from_resource(resource); if (!client) { return; } if (client->frame_source) { wl_event_source_remove(client->frame_source); } if (client->tool && client->tool->current_client == client) { client->tool->current_client = NULL; } wl_list_remove(&client->seat_link); wl_list_remove(&client->tool_link); free(client); wl_resource_set_user_data(resource, NULL); } void add_tablet_tool_client(struct wlr_tablet_seat_client_v2 *seat, struct wlr_tablet_v2_tablet_tool *tool) { struct wlr_tablet_tool_client_v2 *client = calloc(1, sizeof(*client)); if (!client) { return; } client->tool = tool; client->seat = seat; uint32_t version = wl_resource_get_version(seat->resource); client->resource = wl_resource_create(seat->wl_client, &zwp_tablet_tool_v2_interface, version, 0); if (!client->resource) { free(client); return; } wl_resource_set_implementation(client->resource, &tablet_tool_impl, client, destroy_tablet_tool_v2); zwp_tablet_seat_v2_send_tool_added(seat->resource, client->resource); // Send the expected events if (tool->wlr_tool->hardware_serial) { zwp_tablet_tool_v2_send_hardware_serial( client->resource, tool->wlr_tool->hardware_serial >> 32, tool->wlr_tool->hardware_serial & 0xFFFFFFFF); } if (tool->wlr_tool->hardware_wacom) { zwp_tablet_tool_v2_send_hardware_id_wacom( client->resource, tool->wlr_tool->hardware_wacom >> 32, tool->wlr_tool->hardware_wacom & 0xFFFFFFFF); } zwp_tablet_tool_v2_send_type(client->resource, tablet_type_from_wlr_type(tool->wlr_tool->type)); if (tool->wlr_tool->tilt) { zwp_tablet_tool_v2_send_capability(client->resource, ZWP_TABLET_TOOL_V2_CAPABILITY_TILT); } if (tool->wlr_tool->pressure) { zwp_tablet_tool_v2_send_capability(client->resource, ZWP_TABLET_TOOL_V2_CAPABILITY_PRESSURE); } if (tool->wlr_tool->distance) { zwp_tablet_tool_v2_send_capability(client->resource, ZWP_TABLET_TOOL_V2_CAPABILITY_DISTANCE); } if (tool->wlr_tool->rotation) { zwp_tablet_tool_v2_send_capability(client->resource, ZWP_TABLET_TOOL_V2_CAPABILITY_ROTATION); } if (tool->wlr_tool->slider) { zwp_tablet_tool_v2_send_capability(client->resource, ZWP_TABLET_TOOL_V2_CAPABILITY_SLIDER); } if (tool->wlr_tool->wheel) { zwp_tablet_tool_v2_send_capability(client->resource, ZWP_TABLET_TOOL_V2_CAPABILITY_WHEEL); } zwp_tablet_tool_v2_send_done(client->resource); client->client = seat->wl_client; wl_list_insert(&seat->tools, &client->seat_link); wl_list_insert(&tool->clients, &client->tool_link); } static void handle_wlr_tablet_tool_destroy(struct wl_listener *listener, void *data) { struct wlr_tablet_v2_tablet_tool *tool = wl_container_of(listener, tool, tool_destroy); struct wlr_tablet_tool_client_v2 *pos; struct wlr_tablet_tool_client_v2 *tmp; wl_list_for_each_safe(pos, tmp, &tool->clients, tool_link) { zwp_tablet_tool_v2_send_removed(pos->resource); pos->tool = NULL; } wl_list_remove(&tool->clients); wl_list_remove(&tool->link); wl_list_remove(&tool->tool_destroy.link); wl_list_remove(&tool->events.set_cursor.listener_list); wl_list_remove(&tool->surface_destroy.link); free(tool); } struct wlr_tablet_v2_tablet_tool *wlr_tablet_tool_create( struct wlr_tablet_manager_v2 *manager, struct wlr_seat *wlr_seat, struct wlr_tablet_tool *wlr_tool) { switch (wlr_tool->type) { case WLR_TABLET_TOOL_TYPE_PEN: case WLR_TABLET_TOOL_TYPE_ERASER: case WLR_TABLET_TOOL_TYPE_BRUSH: case WLR_TABLET_TOOL_TYPE_PENCIL: case WLR_TABLET_TOOL_TYPE_AIRBRUSH: case WLR_TABLET_TOOL_TYPE_MOUSE: case WLR_TABLET_TOOL_TYPE_LENS: /* supported */ break; default: /* Unsupported */ return NULL; } struct wlr_tablet_seat_v2 *seat = get_or_create_tablet_seat(manager, wlr_seat); if (!seat) { return NULL; } struct wlr_tablet_v2_tablet_tool *tool = calloc(1, sizeof(*tool)); if (!tool) { return NULL; } tool->wlr_tool = wlr_tool; wl_list_init(&tool->clients); wl_list_init(&tool->surface_destroy.link); tool->default_grab.tool = tool; tool->default_grab.interface = &default_tool_grab_interface; tool->grab = &tool->default_grab; tool->tool_destroy.notify = handle_wlr_tablet_tool_destroy; wl_signal_add(&wlr_tool->events.destroy, &tool->tool_destroy); wl_list_insert(&seat->tools, &tool->link); // We need to create a tablet client for all clients on the seat struct wlr_tablet_seat_client_v2 *pos; wl_list_for_each(pos, &seat->clients, seat_link) { // Tell the clients about the new tool add_tablet_tool_client(pos, tool); } wl_signal_init(&tool->events.set_cursor); return tool; } struct wlr_tablet_tool_client_v2 *tablet_tool_client_from_resource(struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &zwp_tablet_tool_v2_interface, &tablet_tool_impl)); return wl_resource_get_user_data(resource); } static ssize_t tablet_tool_button_update(struct wlr_tablet_v2_tablet_tool *tool, uint32_t button, enum zwp_tablet_pad_v2_button_state state) { ssize_t i; if (state == ZWP_TABLET_PAD_V2_BUTTON_STATE_PRESSED) { i = set_add(tool->pressed_buttons, &tool->num_buttons, WLR_TABLET_V2_TOOL_BUTTONS_CAP, button); if (i != -1) { tool->pressed_serials[i] = -1; } else { wlr_log(WLR_ERROR, "Failed to add tablet tool button %x", button); } } else { i = set_remove(tool->pressed_buttons, &tool->num_buttons, WLR_TABLET_V2_TOOL_BUTTONS_CAP, button); if (i != -1) { tool->pressed_serials[i] = tool->pressed_serials[tool->num_buttons]; } else { wlr_log(WLR_ERROR, "Failed to remove tablet tool button %x", button); } } return i; } static void send_tool_frame(void *data) { struct wlr_tablet_tool_client_v2 *tool = data; zwp_tablet_tool_v2_send_frame(tool->resource, get_current_time_msec()); tool->frame_source = NULL; } static void queue_tool_frame(struct wlr_tablet_tool_client_v2 *tool) { struct wl_display *display = wl_client_get_display(tool->client); struct wl_event_loop *loop = wl_display_get_event_loop(display); if (!tool->frame_source) { tool->frame_source = wl_event_loop_add_idle(loop, send_tool_frame, tool); } } static void handle_tablet_tool_surface_destroy(struct wl_listener *listener, void *data) { struct wlr_tablet_v2_tablet_tool *tool = wl_container_of(listener, tool, surface_destroy); wlr_send_tablet_v2_tablet_tool_proximity_out(tool); } void wlr_send_tablet_v2_tablet_tool_proximity_in( struct wlr_tablet_v2_tablet_tool *tool, struct wlr_tablet_v2_tablet *tablet, struct wlr_surface *surface) { struct wl_client *client = wl_resource_get_client(surface->resource); if (tool->focused_surface == surface) { return; } wlr_send_tablet_v2_tablet_tool_proximity_out(tool); struct wlr_tablet_client_v2 *tablet_tmp; struct wlr_tablet_client_v2 *tablet_client = NULL; wl_list_for_each(tablet_tmp, &tablet->clients, tablet_link) { if (tablet_tmp->client == client) { tablet_client = tablet_tmp; break; } } // Couldn't find the client binding for the surface's client. Either // the client didn't bind tablet_v2 at all, or not for the relevant // seat if (!tablet_client) { return; } struct wlr_tablet_tool_client_v2 *tool_tmp = NULL; struct wlr_tablet_tool_client_v2 *tool_client = NULL; wl_list_for_each(tool_tmp, &tool->clients, tool_link) { if (tool_tmp->client == client) { tool_client = tool_tmp; break; } } // Couldn't find the client binding for the surface's client. Either // the client didn't bind tablet_v2 at all, or not for the relevant // seat if (!tool_client) { return; } // Reinitialize the focus destroy events wl_list_remove(&tool->surface_destroy.link); wl_signal_add(&surface->events.destroy, &tool->surface_destroy); tool->surface_destroy.notify = handle_tablet_tool_surface_destroy; tool->current_client = tool_client; uint32_t serial = wlr_seat_client_next_serial(tool_client->seat->seat_client); tool->focused_surface = surface; tool->proximity_serial = serial; zwp_tablet_tool_v2_send_proximity_in(tool_client->resource, serial, tablet_client->resource, surface->resource); /* Send all the pressed buttons */ for (size_t i = 0; i < tool->num_buttons; ++i) { wlr_send_tablet_v2_tablet_tool_button(tool, tool->pressed_buttons[i], ZWP_TABLET_PAD_V2_BUTTON_STATE_PRESSED); } if (tool->is_down) { wlr_send_tablet_v2_tablet_tool_down(tool); } queue_tool_frame(tool_client); } void wlr_send_tablet_v2_tablet_tool_motion( struct wlr_tablet_v2_tablet_tool *tool, double x, double y) { if (!tool->current_client) { return; } zwp_tablet_tool_v2_send_motion(tool->current_client->resource, wl_fixed_from_double(x), wl_fixed_from_double(y)); queue_tool_frame(tool->current_client); } void wlr_send_tablet_v2_tablet_tool_proximity_out( struct wlr_tablet_v2_tablet_tool *tool) { if (tool->current_client) { for (size_t i = 0; i < tool->num_buttons; ++i) { zwp_tablet_tool_v2_send_button(tool->current_client->resource, tool->pressed_serials[i], tool->pressed_buttons[i], ZWP_TABLET_PAD_V2_BUTTON_STATE_RELEASED); } if (tool->is_down) { zwp_tablet_tool_v2_send_up(tool->current_client->resource); } if (tool->current_client->frame_source) { wl_event_source_remove(tool->current_client->frame_source); send_tool_frame(tool->current_client); } zwp_tablet_tool_v2_send_proximity_out(tool->current_client->resource); send_tool_frame(tool->current_client); wl_list_remove(&tool->surface_destroy.link); wl_list_init(&tool->surface_destroy.link); tool->current_client = NULL; tool->focused_surface = NULL; } } void wlr_send_tablet_v2_tablet_tool_pressure( struct wlr_tablet_v2_tablet_tool *tool, double pressure) { if (tool->current_client) { zwp_tablet_tool_v2_send_pressure(tool->current_client->resource, pressure * 65535); queue_tool_frame(tool->current_client); } } void wlr_send_tablet_v2_tablet_tool_distance( struct wlr_tablet_v2_tablet_tool *tool, double distance) { if (tool->current_client) { zwp_tablet_tool_v2_send_distance(tool->current_client->resource, distance * 65535); queue_tool_frame(tool->current_client); } } void wlr_send_tablet_v2_tablet_tool_tilt( struct wlr_tablet_v2_tablet_tool *tool, double x, double y) { if (!tool->current_client) { return; } zwp_tablet_tool_v2_send_tilt(tool->current_client->resource, wl_fixed_from_double(x), wl_fixed_from_double(y)); queue_tool_frame(tool->current_client); } void wlr_send_tablet_v2_tablet_tool_rotation( struct wlr_tablet_v2_tablet_tool *tool, double degrees) { if (!tool->current_client) { return; } zwp_tablet_tool_v2_send_rotation(tool->current_client->resource, wl_fixed_from_double(degrees)); queue_tool_frame(tool->current_client); } void wlr_send_tablet_v2_tablet_tool_slider( struct wlr_tablet_v2_tablet_tool *tool, double position) { if (!tool->current_client) { return; } zwp_tablet_tool_v2_send_slider(tool->current_client->resource, position * 65535); queue_tool_frame(tool->current_client); } void wlr_send_tablet_v2_tablet_tool_button( struct wlr_tablet_v2_tablet_tool *tool, uint32_t button, enum zwp_tablet_pad_v2_button_state state) { ssize_t index = tablet_tool_button_update(tool, button, state); if (tool->current_client) { uint32_t serial = wlr_seat_client_next_serial( tool->current_client->seat->seat_client); if (index >= 0) { tool->pressed_serials[index] = serial; } zwp_tablet_tool_v2_send_button(tool->current_client->resource, serial, button, state); queue_tool_frame(tool->current_client); } } void wlr_send_tablet_v2_tablet_tool_wheel( struct wlr_tablet_v2_tablet_tool *tool, double degrees, int32_t clicks) { if (tool->current_client) { zwp_tablet_tool_v2_send_wheel(tool->current_client->resource, wl_fixed_from_double(degrees), clicks); queue_tool_frame(tool->current_client); } } void wlr_send_tablet_v2_tablet_tool_down(struct wlr_tablet_v2_tablet_tool *tool) { if (tool->is_down) { return; } tool->is_down = true; if (tool->current_client) { uint32_t serial = wlr_seat_client_next_serial( tool->current_client->seat->seat_client); zwp_tablet_tool_v2_send_down(tool->current_client->resource, serial); queue_tool_frame(tool->current_client); tool->down_serial = serial; } } void wlr_send_tablet_v2_tablet_tool_up(struct wlr_tablet_v2_tablet_tool *tool) { if (!tool->is_down) { return; } tool->is_down = false; tool->down_serial = 0; if (tool->current_client) { zwp_tablet_tool_v2_send_up(tool->current_client->resource); queue_tool_frame(tool->current_client); } } void wlr_tablet_v2_tablet_tool_notify_proximity_in( struct wlr_tablet_v2_tablet_tool *tool, struct wlr_tablet_v2_tablet *tablet, struct wlr_surface *surface) { if (tool->grab->interface->proximity_in) { tool->grab->interface->proximity_in(tool->grab, tablet, surface); } } void wlr_tablet_v2_tablet_tool_notify_down(struct wlr_tablet_v2_tablet_tool *tool) { if (tool->grab->interface->down) { tool->grab->interface->down(tool->grab); } } void wlr_tablet_v2_tablet_tool_notify_up(struct wlr_tablet_v2_tablet_tool *tool) { if (tool->grab->interface->up) { tool->grab->interface->up(tool->grab); } } void wlr_tablet_v2_tablet_tool_notify_motion( struct wlr_tablet_v2_tablet_tool *tool, double x, double y) { if (tool->grab->interface->motion) { tool->grab->interface->motion(tool->grab, x, y); } } void wlr_tablet_v2_tablet_tool_notify_pressure( struct wlr_tablet_v2_tablet_tool *tool, double pressure) { if (tool->grab->interface->pressure) { tool->grab->interface->pressure(tool->grab, pressure); } } void wlr_tablet_v2_tablet_tool_notify_distance( struct wlr_tablet_v2_tablet_tool *tool, double distance) { if (tool->grab->interface->distance) { tool->grab->interface->distance(tool->grab, distance); } } void wlr_tablet_v2_tablet_tool_notify_tilt( struct wlr_tablet_v2_tablet_tool *tool, double x, double y) { if (tool->grab->interface->tilt) { tool->grab->interface->tilt(tool->grab, x, y); } } void wlr_tablet_v2_tablet_tool_notify_rotation( struct wlr_tablet_v2_tablet_tool *tool, double degrees) { if (tool->grab->interface->rotation) { tool->grab->interface->rotation(tool->grab, degrees); } } void wlr_tablet_v2_tablet_tool_notify_slider( struct wlr_tablet_v2_tablet_tool *tool, double position) { if (tool->grab->interface->slider) { tool->grab->interface->slider(tool->grab, position); } } void wlr_tablet_v2_tablet_tool_notify_wheel( struct wlr_tablet_v2_tablet_tool *tool, double degrees, int32_t clicks) { if (tool->grab->interface->wheel) { tool->grab->interface->wheel(tool->grab, degrees, clicks); } } void wlr_tablet_v2_tablet_tool_notify_proximity_out( struct wlr_tablet_v2_tablet_tool *tool) { if (tool->grab->interface->proximity_out) { tool->grab->interface->proximity_out(tool->grab); } } void wlr_tablet_v2_tablet_tool_notify_button( struct wlr_tablet_v2_tablet_tool *tool, uint32_t button, enum zwp_tablet_pad_v2_button_state state) { if (tool->grab->interface->button) { tool->grab->interface->button(tool->grab, button, state); } } void wlr_tablet_tool_v2_start_grab(struct wlr_tablet_v2_tablet_tool *tool, struct wlr_tablet_tool_v2_grab *grab) { wlr_tablet_tool_v2_end_grab(tool); tool->grab = grab; } void wlr_tablet_tool_v2_end_grab(struct wlr_tablet_v2_tablet_tool *tool) { if (tool->grab->interface->cancel) { tool->grab->interface->cancel(tool->grab); } tool->grab = &tool->default_grab; } static void default_tool_proximity_in( struct wlr_tablet_tool_v2_grab *grab, struct wlr_tablet_v2_tablet *tablet, struct wlr_surface *surface) { wlr_send_tablet_v2_tablet_tool_proximity_in(grab->tool, tablet, surface); } static void default_tool_down(struct wlr_tablet_tool_v2_grab *grab) { wlr_send_tablet_v2_tablet_tool_down(grab->tool); } static void default_tool_up(struct wlr_tablet_tool_v2_grab *grab) { wlr_send_tablet_v2_tablet_tool_up(grab->tool); } static void default_tool_motion( struct wlr_tablet_tool_v2_grab *grab, double x, double y) { wlr_send_tablet_v2_tablet_tool_motion(grab->tool, x, y); } static void default_tool_pressure( struct wlr_tablet_tool_v2_grab *grab, double pressure) { wlr_send_tablet_v2_tablet_tool_pressure(grab->tool, pressure); } static void default_tool_distance( struct wlr_tablet_tool_v2_grab *grab, double distance) { wlr_send_tablet_v2_tablet_tool_distance(grab->tool, distance); } static void default_tool_tilt( struct wlr_tablet_tool_v2_grab *grab, double x, double y) { wlr_send_tablet_v2_tablet_tool_tilt(grab->tool, x, y); } static void default_tool_rotation( struct wlr_tablet_tool_v2_grab *grab, double degrees) { wlr_send_tablet_v2_tablet_tool_rotation(grab->tool, degrees); } static void default_tool_slider( struct wlr_tablet_tool_v2_grab *grab, double position) { wlr_send_tablet_v2_tablet_tool_slider(grab->tool, position); } static void default_tool_wheel( struct wlr_tablet_tool_v2_grab *grab, double degrees, int32_t clicks) { wlr_send_tablet_v2_tablet_tool_wheel(grab->tool, degrees, clicks); } static void default_tool_proximity_out(struct wlr_tablet_tool_v2_grab *grab) { wlr_send_tablet_v2_tablet_tool_proximity_out(grab->tool); } static void default_tool_button( struct wlr_tablet_tool_v2_grab *grab, uint32_t button, enum zwp_tablet_pad_v2_button_state state) { wlr_send_tablet_v2_tablet_tool_button(grab->tool, button, state); } static void default_tool_cancel(struct wlr_tablet_tool_v2_grab *grab) { /* Do nothing. Default grab can't be canceled */ } static const struct wlr_tablet_tool_v2_grab_interface default_tool_grab_interface = { .proximity_in = default_tool_proximity_in, .down = default_tool_down, .up = default_tool_up, .motion = default_tool_motion, .pressure = default_tool_pressure, .distance = default_tool_distance, .tilt = default_tool_tilt, .rotation = default_tool_rotation, .slider = default_tool_slider, .wheel = default_tool_wheel, .proximity_out = default_tool_proximity_out, .button = default_tool_button, .cancel = default_tool_cancel, }; struct implicit_grab_state { struct wlr_surface *original; bool released; struct wlr_surface *focused; struct wlr_tablet_v2_tablet *tablet; }; static void check_and_release_implicit_grab(struct wlr_tablet_tool_v2_grab *grab) { struct implicit_grab_state *state = grab->data; /* Still button or tip pressed. We should hold the grab */ if (grab->tool->is_down || grab->tool->num_buttons > 0 || state->released) { return; } state->released = true; /* We should still focus the same surface. Do nothing */ if (state->original == state->focused) { wlr_tablet_tool_v2_end_grab(grab->tool); return; } wlr_send_tablet_v2_tablet_tool_proximity_out(grab->tool); if (state->focused) { wlr_send_tablet_v2_tablet_tool_proximity_in(grab->tool, state->tablet, state->focused); } wlr_tablet_tool_v2_end_grab(grab->tool); } static void implicit_tool_proximity_in( struct wlr_tablet_tool_v2_grab *grab, struct wlr_tablet_v2_tablet *tablet, struct wlr_surface *surface) { /* As long as we got an implicit grab, proximity won't change * But should track the currently focused surface to change to it when * the grab is released. */ struct implicit_grab_state *state = grab->data; state->focused = surface; state->tablet = tablet; } static void implicit_tool_proximity_out(struct wlr_tablet_tool_v2_grab *grab) { struct implicit_grab_state *state = grab->data; state->focused = NULL; } static void implicit_tool_down(struct wlr_tablet_tool_v2_grab *grab) { wlr_send_tablet_v2_tablet_tool_down(grab->tool); } static void implicit_tool_up(struct wlr_tablet_tool_v2_grab *grab) { wlr_send_tablet_v2_tablet_tool_up(grab->tool); check_and_release_implicit_grab(grab); } static void implicit_tool_button( struct wlr_tablet_tool_v2_grab *grab, uint32_t button, enum zwp_tablet_pad_v2_button_state state) { wlr_send_tablet_v2_tablet_tool_button(grab->tool, button, state); check_and_release_implicit_grab(grab); } static void implicit_tool_cancel(struct wlr_tablet_tool_v2_grab *grab) { check_and_release_implicit_grab(grab); free(grab->data); free(grab); } static const struct wlr_tablet_tool_v2_grab_interface implicit_tool_grab_interface = { .proximity_in = implicit_tool_proximity_in, .down = implicit_tool_down, .up = implicit_tool_up, .motion = default_tool_motion, .pressure = default_tool_pressure, .distance = default_tool_distance, .tilt = default_tool_tilt, .rotation = default_tool_rotation, .slider = default_tool_slider, .wheel = default_tool_wheel, .proximity_out = implicit_tool_proximity_out, .button = implicit_tool_button, .cancel = implicit_tool_cancel, }; bool wlr_tablet_tool_v2_has_implicit_grab( struct wlr_tablet_v2_tablet_tool *tool) { return tool->grab->interface == &implicit_tool_grab_interface; } void wlr_tablet_tool_v2_start_implicit_grab( struct wlr_tablet_v2_tablet_tool *tool) { if (wlr_tablet_tool_v2_has_implicit_grab(tool) || !tool->focused_surface) { return; } /* No current implicit grab */ if (!(tool->is_down || tool->num_buttons > 0)) { return; } struct wlr_tablet_tool_v2_grab *grab = calloc(1, sizeof(*grab)); if (!grab) { return; } grab->interface = &implicit_tool_grab_interface; grab->tool = tool; struct implicit_grab_state *state = calloc(1, sizeof(*state)); if (!state) { free(grab); return; } state->original = tool->focused_surface; state->focused = tool->focused_surface; grab->data = state; wlr_tablet_tool_v2_start_grab(tool, grab); } wlroots-0.17.1/types/wlr_compositor.c000066400000000000000000001161451454110342200177070ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include "types/wlr_buffer.h" #include "types/wlr_region.h" #include "types/wlr_subcompositor.h" #include "util/time.h" #define COMPOSITOR_VERSION 6 #define CALLBACK_VERSION 1 static int min(int fst, int snd) { if (fst < snd) { return fst; } else { return snd; } } static int max(int fst, int snd) { if (fst > snd) { return fst; } else { return snd; } } static void surface_handle_destroy(struct wl_client *client, struct wl_resource *resource) { struct wlr_surface *surface = wlr_surface_from_resource(resource); if (surface->role_resource != NULL) { wl_resource_post_error(resource, WL_SURFACE_ERROR_DEFUNCT_ROLE_OBJECT, "surface was destroyed before its role object"); return; } wl_resource_destroy(resource); } static void surface_handle_attach(struct wl_client *client, struct wl_resource *resource, struct wl_resource *buffer_resource, int32_t dx, int32_t dy) { struct wlr_surface *surface = wlr_surface_from_resource(resource); if (wl_resource_get_version(resource) >= WL_SURFACE_OFFSET_SINCE_VERSION && (dx != 0 || dy != 0)) { wl_resource_post_error(resource, WL_SURFACE_ERROR_INVALID_OFFSET, "Offset must be zero on wl_surface.attach version >= %"PRIu32, WL_SURFACE_OFFSET_SINCE_VERSION); return; } struct wlr_buffer *buffer = NULL; if (buffer_resource != NULL) { buffer = wlr_buffer_try_from_resource(buffer_resource); if (buffer == NULL) { wl_resource_post_error(buffer_resource, 0, "unknown buffer type"); return; } } surface->pending.committed |= WLR_SURFACE_STATE_BUFFER; wlr_buffer_unlock(surface->pending.buffer); surface->pending.buffer = buffer; if (wl_resource_get_version(resource) < WL_SURFACE_OFFSET_SINCE_VERSION) { surface->pending.committed |= WLR_SURFACE_STATE_OFFSET; surface->pending.dx = dx; surface->pending.dy = dy; } } static void surface_handle_damage(struct wl_client *client, struct wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) { struct wlr_surface *surface = wlr_surface_from_resource(resource); if (width < 0 || height < 0) { return; } surface->pending.committed |= WLR_SURFACE_STATE_SURFACE_DAMAGE; pixman_region32_union_rect(&surface->pending.surface_damage, &surface->pending.surface_damage, x, y, width, height); } static void callback_handle_resource_destroy(struct wl_resource *resource) { wl_list_remove(wl_resource_get_link(resource)); } static void surface_handle_frame(struct wl_client *client, struct wl_resource *resource, uint32_t callback) { struct wlr_surface *surface = wlr_surface_from_resource(resource); struct wl_resource *callback_resource = wl_resource_create(client, &wl_callback_interface, CALLBACK_VERSION, callback); if (callback_resource == NULL) { wl_resource_post_no_memory(resource); return; } wl_resource_set_implementation(callback_resource, NULL, NULL, callback_handle_resource_destroy); wl_list_insert(surface->pending.frame_callback_list.prev, wl_resource_get_link(callback_resource)); surface->pending.committed |= WLR_SURFACE_STATE_FRAME_CALLBACK_LIST; } static void surface_handle_set_opaque_region(struct wl_client *client, struct wl_resource *resource, struct wl_resource *region_resource) { struct wlr_surface *surface = wlr_surface_from_resource(resource); surface->pending.committed |= WLR_SURFACE_STATE_OPAQUE_REGION; if (region_resource) { const pixman_region32_t *region = wlr_region_from_resource(region_resource); pixman_region32_copy(&surface->pending.opaque, region); } else { pixman_region32_clear(&surface->pending.opaque); } } static void surface_handle_set_input_region(struct wl_client *client, struct wl_resource *resource, struct wl_resource *region_resource) { struct wlr_surface *surface = wlr_surface_from_resource(resource); surface->pending.committed |= WLR_SURFACE_STATE_INPUT_REGION; if (region_resource) { const pixman_region32_t *region = wlr_region_from_resource(region_resource); pixman_region32_copy(&surface->pending.input, region); } else { pixman_region32_fini(&surface->pending.input); pixman_region32_init_rect(&surface->pending.input, INT32_MIN, INT32_MIN, UINT32_MAX, UINT32_MAX); } } static void surface_state_transformed_buffer_size(struct wlr_surface_state *state, int *out_width, int *out_height) { int width = state->buffer_width; int height = state->buffer_height; if ((state->transform & WL_OUTPUT_TRANSFORM_90) != 0) { int tmp = width; width = height; height = tmp; } *out_width = width; *out_height = height; } /** * Computes the surface viewport source size, ie. the size after applying the * surface's scale, transform and cropping (via the viewport's source * rectangle) but before applying the viewport scaling (via the viewport's * destination rectangle). */ static void surface_state_viewport_src_size(struct wlr_surface_state *state, int *out_width, int *out_height) { if (state->buffer_width == 0 && state->buffer_height == 0) { *out_width = *out_height = 0; return; } if (state->viewport.has_src) { *out_width = state->viewport.src.width; *out_height = state->viewport.src.height; } else { surface_state_transformed_buffer_size(state, out_width, out_height); *out_width /= state->scale; *out_height /= state->scale; } } static void surface_finalize_pending(struct wlr_surface *surface) { struct wlr_surface_state *pending = &surface->pending; if ((pending->committed & WLR_SURFACE_STATE_BUFFER)) { if (pending->buffer != NULL) { pending->buffer_width = pending->buffer->width; pending->buffer_height = pending->buffer->height; } else { pending->buffer_width = pending->buffer_height = 0; } } if (!pending->viewport.has_src && (pending->buffer_width % pending->scale != 0 || pending->buffer_height % pending->scale != 0)) { // TODO: send WL_SURFACE_ERROR_INVALID_SIZE error to cursor surfaces // once this issue is resolved: // https://gitlab.freedesktop.org/wayland/wayland/-/issues/194 if (!surface->role || strcmp(surface->role->name, "wl_pointer-cursor") == 0 || strcmp(surface->role->name, "wp_tablet_tool-cursor") == 0) { wlr_log(WLR_DEBUG, "Client bug: submitted a buffer whose size (%dx%d) " "is not divisible by scale (%d)", pending->buffer_width, pending->buffer_height, pending->scale); } else { wl_resource_post_error(surface->resource, WL_SURFACE_ERROR_INVALID_SIZE, "Buffer size (%dx%d) is not divisible by scale (%d)", pending->buffer_width, pending->buffer_height, pending->scale); } } if (pending->viewport.has_dst) { if (pending->buffer_width == 0 && pending->buffer_height == 0) { pending->width = pending->height = 0; } else { pending->width = pending->viewport.dst_width; pending->height = pending->viewport.dst_height; } } else { surface_state_viewport_src_size(pending, &pending->width, &pending->height); } pixman_region32_intersect_rect(&pending->surface_damage, &pending->surface_damage, 0, 0, pending->width, pending->height); pixman_region32_intersect_rect(&pending->buffer_damage, &pending->buffer_damage, 0, 0, pending->buffer_width, pending->buffer_height); } static void surface_update_damage(pixman_region32_t *buffer_damage, struct wlr_surface_state *current, struct wlr_surface_state *pending) { pixman_region32_clear(buffer_damage); if (pending->width != current->width || pending->height != current->height || !wlr_fbox_equal(&pending->viewport.src, ¤t->viewport.src)) { // Damage the whole buffer on resize or viewport source box change pixman_region32_union_rect(buffer_damage, buffer_damage, 0, 0, pending->buffer_width, pending->buffer_height); } else { // Copy over surface damage + buffer damage pixman_region32_t surface_damage; pixman_region32_init(&surface_damage); pixman_region32_copy(&surface_damage, &pending->surface_damage); if (pending->viewport.has_dst) { int src_width, src_height; surface_state_viewport_src_size(pending, &src_width, &src_height); float scale_x = (float)pending->viewport.dst_width / src_width; float scale_y = (float)pending->viewport.dst_height / src_height; wlr_region_scale_xy(&surface_damage, &surface_damage, 1.0 / scale_x, 1.0 / scale_y); } if (pending->viewport.has_src) { // This is lossy: do a best-effort conversion pixman_region32_translate(&surface_damage, floor(pending->viewport.src.x), floor(pending->viewport.src.y)); } wlr_region_scale(&surface_damage, &surface_damage, pending->scale); int width, height; surface_state_transformed_buffer_size(pending, &width, &height); wlr_region_transform(&surface_damage, &surface_damage, wlr_output_transform_invert(pending->transform), width, height); pixman_region32_union(buffer_damage, &pending->buffer_damage, &surface_damage); pixman_region32_fini(&surface_damage); } } /** * Append pending state to current state and clear pending state. */ static void surface_state_move(struct wlr_surface_state *state, struct wlr_surface_state *next) { state->width = next->width; state->height = next->height; state->buffer_width = next->buffer_width; state->buffer_height = next->buffer_height; if (next->committed & WLR_SURFACE_STATE_SCALE) { state->scale = next->scale; } if (next->committed & WLR_SURFACE_STATE_TRANSFORM) { state->transform = next->transform; } if (next->committed & WLR_SURFACE_STATE_OFFSET) { state->dx = next->dx; state->dy = next->dy; next->dx = next->dy = 0; } else { state->dx = state->dy = 0; } if (next->committed & WLR_SURFACE_STATE_BUFFER) { wlr_buffer_unlock(state->buffer); state->buffer = NULL; if (next->buffer) { state->buffer = wlr_buffer_lock(next->buffer); } wlr_buffer_unlock(next->buffer); next->buffer = NULL; } if (next->committed & WLR_SURFACE_STATE_SURFACE_DAMAGE) { pixman_region32_copy(&state->surface_damage, &next->surface_damage); pixman_region32_clear(&next->surface_damage); } else { pixman_region32_clear(&state->surface_damage); } if (next->committed & WLR_SURFACE_STATE_BUFFER_DAMAGE) { pixman_region32_copy(&state->buffer_damage, &next->buffer_damage); pixman_region32_clear(&next->buffer_damage); } else { pixman_region32_clear(&state->buffer_damage); } if (next->committed & WLR_SURFACE_STATE_OPAQUE_REGION) { pixman_region32_copy(&state->opaque, &next->opaque); } if (next->committed & WLR_SURFACE_STATE_INPUT_REGION) { pixman_region32_copy(&state->input, &next->input); } if (next->committed & WLR_SURFACE_STATE_VIEWPORT) { state->viewport = next->viewport; } if (next->committed & WLR_SURFACE_STATE_FRAME_CALLBACK_LIST) { wl_list_insert_list(&state->frame_callback_list, &next->frame_callback_list); wl_list_init(&next->frame_callback_list); } state->committed |= next->committed; next->committed = 0; state->seq = next->seq; state->cached_state_locks = next->cached_state_locks; next->cached_state_locks = 0; } static void surface_apply_damage(struct wlr_surface *surface) { surface->has_buffer = surface->current.buffer; if (surface->current.buffer == NULL) { // NULL commit if (surface->buffer != NULL) { wlr_buffer_unlock(&surface->buffer->base); } surface->buffer = NULL; surface->opaque = false; return; } surface->opaque = buffer_is_opaque(surface->current.buffer); if (surface->buffer != NULL) { if (wlr_client_buffer_apply_damage(surface->buffer, surface->current.buffer, &surface->buffer_damage)) { wlr_buffer_unlock(surface->current.buffer); surface->current.buffer = NULL; return; } } if (surface->renderer == NULL) { return; } struct wlr_client_buffer *buffer = wlr_client_buffer_create( surface->current.buffer, surface->renderer); if (buffer == NULL) { wlr_log(WLR_ERROR, "Failed to upload buffer"); return; } if (surface->buffer != NULL) { wlr_buffer_unlock(&surface->buffer->base); } surface->buffer = buffer; } static void surface_update_opaque_region(struct wlr_surface *surface) { if (!surface->has_buffer) { pixman_region32_clear(&surface->opaque_region); return; } if (surface->opaque) { pixman_region32_fini(&surface->opaque_region); pixman_region32_init_rect(&surface->opaque_region, 0, 0, surface->current.width, surface->current.height); return; } pixman_region32_intersect_rect(&surface->opaque_region, &surface->current.opaque, 0, 0, surface->current.width, surface->current.height); } static void surface_update_input_region(struct wlr_surface *surface) { pixman_region32_intersect_rect(&surface->input_region, &surface->current.input, 0, 0, surface->current.width, surface->current.height); } static void surface_state_init(struct wlr_surface_state *state); static void surface_cache_pending(struct wlr_surface *surface) { struct wlr_surface_state *cached = calloc(1, sizeof(*cached)); if (!cached) { wl_resource_post_no_memory(surface->resource); return; } surface_state_init(cached); surface_state_move(cached, &surface->pending); wl_list_insert(surface->cached.prev, &cached->cached_state_link); surface->pending.seq++; } static void surface_commit_state(struct wlr_surface *surface, struct wlr_surface_state *next) { assert(next->cached_state_locks == 0); wl_signal_emit_mutable(&surface->events.precommit, next); bool invalid_buffer = next->committed & WLR_SURFACE_STATE_BUFFER; if (invalid_buffer && next->buffer == NULL) { surface->unmap_commit = surface->mapped; wlr_surface_unmap(surface); } else { surface->unmap_commit = false; } surface_update_damage(&surface->buffer_damage, &surface->current, next); pixman_region32_clear(&surface->external_damage); if (surface->current.width > next->width || surface->current.height > next->height || next->dx != 0 || next->dy != 0) { pixman_region32_union_rect(&surface->external_damage, &surface->external_damage, -next->dx, -next->dy, surface->current.width, surface->current.height); } surface->previous.scale = surface->current.scale; surface->previous.transform = surface->current.transform; surface->previous.width = surface->current.width; surface->previous.height = surface->current.height; surface->previous.buffer_width = surface->current.buffer_width; surface->previous.buffer_height = surface->current.buffer_height; surface_state_move(&surface->current, next); if (invalid_buffer) { surface_apply_damage(surface); } surface_update_opaque_region(surface); surface_update_input_region(surface); // commit subsurface order struct wlr_subsurface *subsurface; wl_list_for_each(subsurface, &surface->pending.subsurfaces_below, pending.link) { wl_list_remove(&subsurface->current.link); wl_list_insert(surface->current.subsurfaces_below.prev, &subsurface->current.link); subsurface_handle_parent_commit(subsurface); } wl_list_for_each(subsurface, &surface->pending.subsurfaces_above, pending.link) { wl_list_remove(&subsurface->current.link); wl_list_insert(surface->current.subsurfaces_above.prev, &subsurface->current.link); subsurface_handle_parent_commit(subsurface); } // If we're committing the pending state, bump the pending sequence number // here, to allow commit listeners to lock the new pending state. if (next == &surface->pending) { surface->pending.seq++; } if (surface->role != NULL && surface->role->commit != NULL && (surface->role_resource != NULL || surface->role->no_object)) { surface->role->commit(surface); } wl_signal_emit_mutable(&surface->events.commit, surface); // Release the buffer after emitting the commit event, so that listeners can // access it. Don't leave the buffer locked so that wl_shm buffers can be // released immediately on commit when they are uploaded to the GPU. wlr_buffer_unlock(surface->current.buffer); surface->current.buffer = NULL; } static void surface_handle_commit(struct wl_client *client, struct wl_resource *resource) { struct wlr_surface *surface = wlr_surface_from_resource(resource); surface_finalize_pending(surface); wl_signal_emit_mutable(&surface->events.client_commit, NULL); if (surface->pending.cached_state_locks > 0 || !wl_list_empty(&surface->cached)) { surface_cache_pending(surface); } else { surface_commit_state(surface, &surface->pending); } } static void surface_handle_set_buffer_transform(struct wl_client *client, struct wl_resource *resource, int32_t transform) { if (transform < WL_OUTPUT_TRANSFORM_NORMAL || transform > WL_OUTPUT_TRANSFORM_FLIPPED_270) { wl_resource_post_error(resource, WL_SURFACE_ERROR_INVALID_TRANSFORM, "Specified transform value (%d) is invalid", transform); return; } struct wlr_surface *surface = wlr_surface_from_resource(resource); surface->pending.committed |= WLR_SURFACE_STATE_TRANSFORM; surface->pending.transform = transform; } static void surface_handle_set_buffer_scale(struct wl_client *client, struct wl_resource *resource, int32_t scale) { if (scale <= 0) { wl_resource_post_error(resource, WL_SURFACE_ERROR_INVALID_SCALE, "Specified scale value (%d) is not positive", scale); return; } struct wlr_surface *surface = wlr_surface_from_resource(resource); surface->pending.committed |= WLR_SURFACE_STATE_SCALE; surface->pending.scale = scale; } static void surface_handle_damage_buffer(struct wl_client *client, struct wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) { struct wlr_surface *surface = wlr_surface_from_resource(resource); if (width < 0 || height < 0) { return; } surface->pending.committed |= WLR_SURFACE_STATE_BUFFER_DAMAGE; pixman_region32_union_rect(&surface->pending.buffer_damage, &surface->pending.buffer_damage, x, y, width, height); } static void surface_handle_offset(struct wl_client *client, struct wl_resource *resource, int32_t x, int32_t y) { struct wlr_surface *surface = wlr_surface_from_resource(resource); surface->pending.committed |= WLR_SURFACE_STATE_OFFSET; surface->pending.dx = x; surface->pending.dy = y; } static const struct wl_surface_interface surface_implementation = { .destroy = surface_handle_destroy, .attach = surface_handle_attach, .damage = surface_handle_damage, .frame = surface_handle_frame, .set_opaque_region = surface_handle_set_opaque_region, .set_input_region = surface_handle_set_input_region, .commit = surface_handle_commit, .set_buffer_transform = surface_handle_set_buffer_transform, .set_buffer_scale = surface_handle_set_buffer_scale, .damage_buffer = surface_handle_damage_buffer, .offset = surface_handle_offset, }; struct wlr_surface *wlr_surface_from_resource(struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &wl_surface_interface, &surface_implementation)); return wl_resource_get_user_data(resource); } static void surface_state_init(struct wlr_surface_state *state) { *state = (struct wlr_surface_state){ .scale = 1, .transform = WL_OUTPUT_TRANSFORM_NORMAL, }; wl_list_init(&state->subsurfaces_above); wl_list_init(&state->subsurfaces_below); wl_list_init(&state->frame_callback_list); pixman_region32_init(&state->surface_damage); pixman_region32_init(&state->buffer_damage); pixman_region32_init(&state->opaque); pixman_region32_init_rect(&state->input, INT32_MIN, INT32_MIN, UINT32_MAX, UINT32_MAX); } static void surface_state_finish(struct wlr_surface_state *state) { wlr_buffer_unlock(state->buffer); struct wl_resource *resource, *tmp; wl_resource_for_each_safe(resource, tmp, &state->frame_callback_list) { wl_resource_destroy(resource); } pixman_region32_fini(&state->surface_damage); pixman_region32_fini(&state->buffer_damage); pixman_region32_fini(&state->opaque); pixman_region32_fini(&state->input); } static void surface_state_destroy_cached(struct wlr_surface_state *state) { surface_state_finish(state); wl_list_remove(&state->cached_state_link); free(state); } static void surface_output_destroy(struct wlr_surface_output *surface_output); static void surface_destroy_role_object(struct wlr_surface *surface); static void surface_handle_resource_destroy(struct wl_resource *resource) { struct wlr_surface *surface = wlr_surface_from_resource(resource); struct wlr_surface_output *surface_output, *surface_output_tmp; wl_list_for_each_safe(surface_output, surface_output_tmp, &surface->current_outputs, link) { surface_output_destroy(surface_output); } surface_destroy_role_object(surface); wl_signal_emit_mutable(&surface->events.destroy, surface); wlr_addon_set_finish(&surface->addons); struct wlr_surface_state *cached, *cached_tmp; wl_list_for_each_safe(cached, cached_tmp, &surface->cached, cached_state_link) { surface_state_destroy_cached(cached); } wl_list_remove(&surface->renderer_destroy.link); wl_list_remove(&surface->role_resource_destroy.link); surface_state_finish(&surface->pending); surface_state_finish(&surface->current); pixman_region32_fini(&surface->buffer_damage); pixman_region32_fini(&surface->external_damage); pixman_region32_fini(&surface->opaque_region); pixman_region32_fini(&surface->input_region); if (surface->buffer != NULL) { wlr_buffer_unlock(&surface->buffer->base); } free(surface); } static void surface_handle_renderer_destroy(struct wl_listener *listener, void *data) { struct wlr_surface *surface = wl_container_of(listener, surface, renderer_destroy); wl_resource_destroy(surface->resource); } static struct wlr_surface *surface_create(struct wl_client *client, uint32_t version, uint32_t id, struct wlr_renderer *renderer) { struct wlr_surface *surface = calloc(1, sizeof(*surface)); if (!surface) { wl_client_post_no_memory(client); return NULL; } surface->resource = wl_resource_create(client, &wl_surface_interface, version, id); if (surface->resource == NULL) { free(surface); wl_client_post_no_memory(client); return NULL; } wl_resource_set_implementation(surface->resource, &surface_implementation, surface, surface_handle_resource_destroy); wlr_log(WLR_DEBUG, "New wlr_surface %p (res %p)", surface, surface->resource); surface->renderer = renderer; surface_state_init(&surface->current); surface_state_init(&surface->pending); surface->pending.seq = 1; wl_signal_init(&surface->events.client_commit); wl_signal_init(&surface->events.precommit); wl_signal_init(&surface->events.commit); wl_signal_init(&surface->events.map); wl_signal_init(&surface->events.unmap); wl_signal_init(&surface->events.destroy); wl_signal_init(&surface->events.new_subsurface); wl_list_init(&surface->current_outputs); wl_list_init(&surface->cached); pixman_region32_init(&surface->buffer_damage); pixman_region32_init(&surface->external_damage); pixman_region32_init(&surface->opaque_region); pixman_region32_init(&surface->input_region); wlr_addon_set_init(&surface->addons); if (renderer != NULL) { wl_signal_add(&renderer->events.destroy, &surface->renderer_destroy); surface->renderer_destroy.notify = surface_handle_renderer_destroy; } else { wl_list_init(&surface->renderer_destroy.link); } wl_list_init(&surface->role_resource_destroy.link); return surface; } struct wlr_texture *wlr_surface_get_texture(struct wlr_surface *surface) { if (surface->buffer == NULL) { return NULL; } return surface->buffer->texture; } bool wlr_surface_has_buffer(struct wlr_surface *surface) { return surface->has_buffer; } void wlr_surface_map(struct wlr_surface *surface) { if (surface->mapped) { return; } assert(wlr_surface_has_buffer(surface)); surface->mapped = true; struct wlr_subsurface *subsurface; wl_list_for_each(subsurface, &surface->current.subsurfaces_below, current.link) { subsurface_consider_map(subsurface); } wl_list_for_each(subsurface, &surface->current.subsurfaces_above, current.link) { subsurface_consider_map(subsurface); } wl_signal_emit_mutable(&surface->events.map, NULL); } void wlr_surface_unmap(struct wlr_surface *surface) { if (!surface->mapped) { return; } surface->mapped = false; wl_signal_emit_mutable(&surface->events.unmap, NULL); if (surface->role != NULL && surface->role->unmap != NULL && (surface->role_resource != NULL || surface->role->no_object)) { surface->role->unmap(surface); } struct wlr_subsurface *subsurface; wl_list_for_each(subsurface, &surface->current.subsurfaces_below, current.link) { wlr_surface_unmap(subsurface->surface); } wl_list_for_each(subsurface, &surface->current.subsurfaces_above, current.link) { wlr_surface_unmap(subsurface->surface); } } bool wlr_surface_set_role(struct wlr_surface *surface, const struct wlr_surface_role *role, struct wl_resource *error_resource, uint32_t error_code) { assert(role != NULL); if (surface->role != NULL && surface->role != role) { if (error_resource != NULL) { wl_resource_post_error(error_resource, error_code, "Cannot assign role %s to wl_surface@%" PRIu32 ", already has role %s", role->name, wl_resource_get_id(surface->resource), surface->role->name); } return false; } if (surface->role_resource != NULL) { wl_resource_post_error(error_resource, error_code, "Cannot reassign role %s to wl_surface@%" PRIu32 ", role object still exists", role->name, wl_resource_get_id(surface->resource)); return false; } surface->role = role; return true; } static void surface_handle_role_resource_destroy(struct wl_listener *listener, void *data) { struct wlr_surface *surface = wl_container_of(listener, surface, role_resource_destroy); surface_destroy_role_object(surface); } void wlr_surface_set_role_object(struct wlr_surface *surface, struct wl_resource *role_resource) { assert(surface->role != NULL); assert(!surface->role->no_object); assert(surface->role_resource == NULL); assert(role_resource != NULL); surface->role_resource = role_resource; surface->role_resource_destroy.notify = surface_handle_role_resource_destroy; wl_resource_add_destroy_listener(role_resource, &surface->role_resource_destroy); } static void surface_destroy_role_object(struct wlr_surface *surface) { if (surface->role_resource == NULL) { return; } wlr_surface_unmap(surface); if (surface->role->destroy != NULL) { surface->role->destroy(surface); } surface->role_resource = NULL; wl_list_remove(&surface->role_resource_destroy.link); wl_list_init(&surface->role_resource_destroy.link); } uint32_t wlr_surface_lock_pending(struct wlr_surface *surface) { surface->pending.cached_state_locks++; return surface->pending.seq; } void wlr_surface_unlock_cached(struct wlr_surface *surface, uint32_t seq) { if (surface->pending.seq == seq) { assert(surface->pending.cached_state_locks > 0); surface->pending.cached_state_locks--; return; } bool found = false; struct wlr_surface_state *cached; wl_list_for_each(cached, &surface->cached, cached_state_link) { if (cached->seq == seq) { found = true; break; } } assert(found); assert(cached->cached_state_locks > 0); cached->cached_state_locks--; if (cached->cached_state_locks != 0) { return; } if (cached->cached_state_link.prev != &surface->cached) { // This isn't the first cached state. This means we're blocked on a // previous cached state. return; } // TODO: consider merging all committed states together struct wlr_surface_state *next, *tmp; wl_list_for_each_safe(next, tmp, &surface->cached, cached_state_link) { if (next->cached_state_locks > 0) { break; } surface_commit_state(surface, next); surface_state_destroy_cached(next); } } struct wlr_surface *wlr_surface_get_root_surface(struct wlr_surface *surface) { struct wlr_subsurface *subsurface; while ((subsurface = wlr_subsurface_try_from_wlr_surface(surface))) { surface = subsurface->parent; } return surface; } bool wlr_surface_point_accepts_input(struct wlr_surface *surface, double sx, double sy) { return sx >= 0 && sx < surface->current.width && sy >= 0 && sy < surface->current.height && pixman_region32_contains_point(&surface->input_region, floor(sx), floor(sy), NULL); } struct wlr_surface *wlr_surface_surface_at(struct wlr_surface *surface, double sx, double sy, double *sub_x, double *sub_y) { struct wlr_subsurface *subsurface; wl_list_for_each_reverse(subsurface, &surface->current.subsurfaces_above, current.link) { if (!subsurface->surface->mapped) { continue; } double _sub_x = subsurface->current.x; double _sub_y = subsurface->current.y; struct wlr_surface *sub = wlr_surface_surface_at(subsurface->surface, sx - _sub_x, sy - _sub_y, sub_x, sub_y); if (sub != NULL) { return sub; } } if (wlr_surface_point_accepts_input(surface, sx, sy)) { if (sub_x) { *sub_x = sx; } if (sub_y) { *sub_y = sy; } return surface; } wl_list_for_each_reverse(subsurface, &surface->current.subsurfaces_below, current.link) { if (!subsurface->surface->mapped) { continue; } double _sub_x = subsurface->current.x; double _sub_y = subsurface->current.y; struct wlr_surface *sub = wlr_surface_surface_at(subsurface->surface, sx - _sub_x, sy - _sub_y, sub_x, sub_y); if (sub != NULL) { return sub; } } return NULL; } static void surface_output_destroy(struct wlr_surface_output *surface_output) { wl_list_remove(&surface_output->bind.link); wl_list_remove(&surface_output->destroy.link); wl_list_remove(&surface_output->link); free(surface_output); } static void surface_handle_output_bind(struct wl_listener *listener, void *data) { struct wlr_output_event_bind *evt = data; struct wlr_surface_output *surface_output = wl_container_of(listener, surface_output, bind); struct wl_client *client = wl_resource_get_client( surface_output->surface->resource); if (client == wl_resource_get_client(evt->resource)) { wl_surface_send_enter(surface_output->surface->resource, evt->resource); } } static void surface_handle_output_destroy(struct wl_listener *listener, void *data) { struct wlr_surface_output *surface_output = wl_container_of(listener, surface_output, destroy); surface_output_destroy(surface_output); } void wlr_surface_send_enter(struct wlr_surface *surface, struct wlr_output *output) { struct wl_client *client = wl_resource_get_client(surface->resource); struct wlr_surface_output *surface_output; struct wl_resource *resource; wl_list_for_each(surface_output, &surface->current_outputs, link) { if (surface_output->output == output) { return; } } surface_output = calloc(1, sizeof(*surface_output)); if (surface_output == NULL) { return; } surface_output->bind.notify = surface_handle_output_bind; surface_output->destroy.notify = surface_handle_output_destroy; wl_signal_add(&output->events.bind, &surface_output->bind); wl_signal_add(&output->events.destroy, &surface_output->destroy); surface_output->surface = surface; surface_output->output = output; wl_list_insert(&surface->current_outputs, &surface_output->link); wl_resource_for_each(resource, &output->resources) { if (client == wl_resource_get_client(resource)) { wl_surface_send_enter(surface->resource, resource); } } } void wlr_surface_send_leave(struct wlr_surface *surface, struct wlr_output *output) { struct wl_client *client = wl_resource_get_client(surface->resource); struct wlr_surface_output *surface_output, *tmp; struct wl_resource *resource; wl_list_for_each_safe(surface_output, tmp, &surface->current_outputs, link) { if (surface_output->output == output) { surface_output_destroy(surface_output); wl_resource_for_each(resource, &output->resources) { if (client == wl_resource_get_client(resource)) { wl_surface_send_leave(surface->resource, resource); } } break; } } } void wlr_surface_send_frame_done(struct wlr_surface *surface, const struct timespec *when) { struct wl_resource *resource, *tmp; wl_resource_for_each_safe(resource, tmp, &surface->current.frame_callback_list) { wl_callback_send_done(resource, timespec_to_msec(when)); wl_resource_destroy(resource); } } static void surface_for_each_surface(struct wlr_surface *surface, int x, int y, wlr_surface_iterator_func_t iterator, void *user_data) { struct wlr_subsurface *subsurface; wl_list_for_each(subsurface, &surface->current.subsurfaces_below, current.link) { if (!subsurface->surface->mapped) { continue; } struct wlr_subsurface_parent_state *state = &subsurface->current; int sx = state->x; int sy = state->y; surface_for_each_surface(subsurface->surface, x + sx, y + sy, iterator, user_data); } iterator(surface, x, y, user_data); wl_list_for_each(subsurface, &surface->current.subsurfaces_above, current.link) { if (!subsurface->surface->mapped) { continue; } struct wlr_subsurface_parent_state *state = &subsurface->current; int sx = state->x; int sy = state->y; surface_for_each_surface(subsurface->surface, x + sx, y + sy, iterator, user_data); } } void wlr_surface_for_each_surface(struct wlr_surface *surface, wlr_surface_iterator_func_t iterator, void *user_data) { surface_for_each_surface(surface, 0, 0, iterator, user_data); } struct bound_acc { int32_t min_x, min_y; int32_t max_x, max_y; }; static void handle_bounding_box_surface(struct wlr_surface *surface, int x, int y, void *data) { struct bound_acc *acc = data; acc->min_x = min(x, acc->min_x); acc->min_y = min(y, acc->min_y); acc->max_x = max(x + surface->current.width, acc->max_x); acc->max_y = max(y + surface->current.height, acc->max_y); } void wlr_surface_get_extends(struct wlr_surface *surface, struct wlr_box *box) { struct bound_acc acc = { .min_x = 0, .min_y = 0, .max_x = surface->current.width, .max_y = surface->current.height, }; wlr_surface_for_each_surface(surface, handle_bounding_box_surface, &acc); box->x = acc.min_x; box->y = acc.min_y; box->width = acc.max_x - acc.min_x; box->height = acc.max_y - acc.min_y; } static void crop_region(pixman_region32_t *dst, pixman_region32_t *src, const struct wlr_box *box) { pixman_region32_intersect_rect(dst, src, box->x, box->y, box->width, box->height); pixman_region32_translate(dst, -box->x, -box->y); } void wlr_surface_get_effective_damage(struct wlr_surface *surface, pixman_region32_t *damage) { pixman_region32_clear(damage); // Transform and copy the buffer damage in terms of surface coordinates. wlr_region_transform(damage, &surface->buffer_damage, surface->current.transform, surface->current.buffer_width, surface->current.buffer_height); wlr_region_scale(damage, damage, 1.0 / (float)surface->current.scale); if (surface->current.viewport.has_src) { struct wlr_box src_box = { .x = floor(surface->current.viewport.src.x), .y = floor(surface->current.viewport.src.y), .width = ceil(surface->current.viewport.src.width), .height = ceil(surface->current.viewport.src.height), }; crop_region(damage, damage, &src_box); } if (surface->current.viewport.has_dst) { int src_width, src_height; surface_state_viewport_src_size(&surface->current, &src_width, &src_height); float scale_x = (float)surface->current.viewport.dst_width / src_width; float scale_y = (float)surface->current.viewport.dst_height / src_height; wlr_region_scale_xy(damage, damage, scale_x, scale_y); } pixman_region32_union(damage, damage, &surface->external_damage); } void wlr_surface_get_buffer_source_box(struct wlr_surface *surface, struct wlr_fbox *box) { box->x = box->y = 0; box->width = surface->current.buffer_width; box->height = surface->current.buffer_height; if (surface->current.viewport.has_src) { box->x = surface->current.viewport.src.x * surface->current.scale; box->y = surface->current.viewport.src.y * surface->current.scale; box->width = surface->current.viewport.src.width * surface->current.scale; box->height = surface->current.viewport.src.height * surface->current.scale; int width, height; surface_state_transformed_buffer_size(&surface->current, &width, &height); wlr_fbox_transform(box, box, wlr_output_transform_invert(surface->current.transform), width, height); } } void wlr_surface_set_preferred_buffer_scale(struct wlr_surface *surface, int32_t scale) { assert(scale > 0); if (wl_resource_get_version(surface->resource) < WL_SURFACE_PREFERRED_BUFFER_SCALE_SINCE_VERSION) { return; } if (surface->preferred_buffer_scale == scale) { return; } wl_surface_send_preferred_buffer_scale(surface->resource, scale); surface->preferred_buffer_scale = scale; } void wlr_surface_set_preferred_buffer_transform(struct wlr_surface *surface, enum wl_output_transform transform) { if (wl_resource_get_version(surface->resource) < WL_SURFACE_PREFERRED_BUFFER_TRANSFORM_SINCE_VERSION) { return; } if (surface->preferred_buffer_transform == transform && surface->preferred_buffer_transform_sent) { return; } wl_surface_send_preferred_buffer_transform(surface->resource, transform); surface->preferred_buffer_transform_sent = true; surface->preferred_buffer_transform = transform; } static const struct wl_compositor_interface compositor_impl; static struct wlr_compositor *compositor_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &wl_compositor_interface, &compositor_impl)); return wl_resource_get_user_data(resource); } static void compositor_create_surface(struct wl_client *client, struct wl_resource *resource, uint32_t id) { struct wlr_compositor *compositor = compositor_from_resource(resource); struct wlr_surface *surface = surface_create(client, wl_resource_get_version(resource), id, compositor->renderer); if (surface == NULL) { wl_client_post_no_memory(client); return; } wl_signal_emit_mutable(&compositor->events.new_surface, surface); } static void compositor_create_region(struct wl_client *client, struct wl_resource *resource, uint32_t id) { region_create(client, wl_resource_get_version(resource), id); } static const struct wl_compositor_interface compositor_impl = { .create_surface = compositor_create_surface, .create_region = compositor_create_region, }; static void compositor_bind(struct wl_client *wl_client, void *data, uint32_t version, uint32_t id) { struct wlr_compositor *compositor = data; struct wl_resource *resource = wl_resource_create(wl_client, &wl_compositor_interface, version, id); if (resource == NULL) { wl_client_post_no_memory(wl_client); return; } wl_resource_set_implementation(resource, &compositor_impl, compositor, NULL); } static void compositor_handle_display_destroy( struct wl_listener *listener, void *data) { struct wlr_compositor *compositor = wl_container_of(listener, compositor, display_destroy); wl_signal_emit_mutable(&compositor->events.destroy, NULL); wl_list_remove(&compositor->display_destroy.link); wl_global_destroy(compositor->global); free(compositor); } struct wlr_compositor *wlr_compositor_create(struct wl_display *display, uint32_t version, struct wlr_renderer *renderer) { assert(version <= COMPOSITOR_VERSION); struct wlr_compositor *compositor = calloc(1, sizeof(*compositor)); if (!compositor) { return NULL; } compositor->global = wl_global_create(display, &wl_compositor_interface, version, compositor, compositor_bind); if (!compositor->global) { free(compositor); return NULL; } compositor->renderer = renderer; wl_signal_init(&compositor->events.new_surface); wl_signal_init(&compositor->events.destroy); compositor->display_destroy.notify = compositor_handle_display_destroy; wl_display_add_destroy_listener(display, &compositor->display_destroy); return compositor; } wlroots-0.17.1/types/wlr_content_type_v1.c000066400000000000000000000146721454110342200206340ustar00rootroot00000000000000#include #include #include #include #define CONTENT_TYPE_VERSION 1 struct wlr_content_type_v1_surface { struct wl_resource *resource; struct wlr_addon addon; struct wl_listener commit; enum wp_content_type_v1_type pending, current; }; static void resource_handle_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static const struct wp_content_type_v1_interface content_type_surface_impl; static const struct wp_content_type_manager_v1_interface manager_impl; // Returns NULL if the resource is inert static struct wlr_content_type_v1_surface *content_type_surface_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &wp_content_type_v1_interface, &content_type_surface_impl)); return wl_resource_get_user_data(resource); } static struct wlr_content_type_manager_v1 *manager_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &wp_content_type_manager_v1_interface, &manager_impl)); return wl_resource_get_user_data(resource); } static void content_type_surface_handle_set_content_type(struct wl_client *client, struct wl_resource *resource, uint32_t type) { struct wlr_content_type_v1_surface *content_type_surface = content_type_surface_from_resource(resource); if (content_type_surface == NULL) { return; } content_type_surface->pending = type; } static const struct wp_content_type_v1_interface content_type_surface_impl = { .destroy = resource_handle_destroy, .set_content_type = content_type_surface_handle_set_content_type, }; static void content_type_surface_destroy( struct wlr_content_type_v1_surface *content_type_surface) { if (content_type_surface == NULL) { return; } wlr_addon_finish(&content_type_surface->addon); wl_list_remove(&content_type_surface->commit.link); wl_resource_set_user_data(content_type_surface->resource, NULL); free(content_type_surface); } static void surface_addon_destroy(struct wlr_addon *addon) { struct wlr_content_type_v1_surface *content_type_surface = wl_container_of(addon, content_type_surface, addon); content_type_surface_destroy(content_type_surface); } static const struct wlr_addon_interface surface_addon_impl = { .name = "wp_content_type_v1", .destroy = surface_addon_destroy, }; static void content_type_surface_handle_commit(struct wl_listener *listener, void *data) { struct wlr_content_type_v1_surface *content_type_surface = wl_container_of(listener, content_type_surface, commit); content_type_surface->current = content_type_surface->pending; } static void content_type_surface_handle_resource_destroy( struct wl_resource *resource) { struct wlr_content_type_v1_surface *content_type_surface = content_type_surface_from_resource(resource); content_type_surface_destroy(content_type_surface); } static void manager_handle_get_surface_content_type(struct wl_client *client, struct wl_resource *manager_resource, uint32_t id, struct wl_resource *surface_resource) { struct wlr_content_type_manager_v1 *manager = manager_from_resource(manager_resource); struct wlr_surface *surface = wlr_surface_from_resource(surface_resource); if (wlr_addon_find(&surface->addons, manager, &surface_addon_impl) != NULL) { wl_resource_post_error(manager_resource, WP_CONTENT_TYPE_MANAGER_V1_ERROR_ALREADY_CONSTRUCTED, "wp_content_type_v1 already constructed for this surface"); return; } struct wlr_content_type_v1_surface *content_type_surface = calloc(1, sizeof(*content_type_surface)); if (content_type_surface == NULL) { wl_resource_post_no_memory(manager_resource); return; } uint32_t version = wl_resource_get_version(manager_resource); content_type_surface->resource = wl_resource_create(client, &wp_content_type_v1_interface, version, id); if (content_type_surface->resource == NULL) { free(content_type_surface); wl_resource_post_no_memory(manager_resource); return; } wl_resource_set_implementation(content_type_surface->resource, &content_type_surface_impl, content_type_surface, content_type_surface_handle_resource_destroy); wlr_addon_init(&content_type_surface->addon, &surface->addons, manager, &surface_addon_impl); content_type_surface->commit.notify = content_type_surface_handle_commit; wl_signal_add(&surface->events.destroy, &content_type_surface->commit); } static const struct wp_content_type_manager_v1_interface manager_impl = { .destroy = resource_handle_destroy, .get_surface_content_type = manager_handle_get_surface_content_type, }; static void manager_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id) { struct wlr_content_type_manager_v1 *manager = data; struct wl_resource *resource = wl_resource_create(client, &wp_content_type_manager_v1_interface, version, id); if (resource == NULL) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(resource, &manager_impl, manager, NULL); } static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_content_type_manager_v1 *manager = wl_container_of(listener, manager, display_destroy); wl_signal_emit_mutable(&manager->events.destroy, NULL); assert(wl_list_empty(&manager->events.destroy.listener_list)); wl_global_destroy(manager->global); wl_list_remove(&manager->display_destroy.link); free(manager); } struct wlr_content_type_manager_v1 *wlr_content_type_manager_v1_create( struct wl_display *display, uint32_t version) { assert(version <= CONTENT_TYPE_VERSION); struct wlr_content_type_manager_v1 *manager = calloc(1, sizeof(*manager)); if (manager == NULL) { return NULL; } manager->global = wl_global_create(display, &wp_content_type_manager_v1_interface, version, manager, manager_bind); if (manager->global == NULL) { free(manager); return NULL; } wl_signal_init(&manager->events.destroy); manager->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &manager->display_destroy); return manager; } enum wp_content_type_v1_type wlr_surface_get_content_type_v1( struct wlr_content_type_manager_v1 *manager, struct wlr_surface *surface) { struct wlr_addon *addon = wlr_addon_find(&surface->addons, manager, &surface_addon_impl); if (addon == NULL) { return WP_CONTENT_TYPE_V1_TYPE_NONE; } struct wlr_content_type_v1_surface *content_type_surface = wl_container_of(addon, content_type_surface, addon); return content_type_surface->current; } wlroots-0.17.1/types/wlr_cursor.c000066400000000000000000001142121454110342200170170ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "types/wlr_buffer.h" #include "types/wlr_output.h" struct wlr_cursor_device { struct wlr_cursor *cursor; struct wlr_input_device *device; struct wl_list link; struct wlr_output *mapped_output; struct wlr_box mapped_box; // empty if unset struct wl_listener motion; struct wl_listener motion_absolute; struct wl_listener button; struct wl_listener axis; struct wl_listener frame; struct wl_listener swipe_begin; struct wl_listener swipe_update; struct wl_listener swipe_end; struct wl_listener pinch_begin; struct wl_listener pinch_update; struct wl_listener pinch_end; struct wl_listener hold_begin; struct wl_listener hold_end; struct wl_listener touch_down; struct wl_listener touch_up; struct wl_listener touch_motion; struct wl_listener touch_cancel; struct wl_listener touch_frame; struct wl_listener tablet_tool_axis; struct wl_listener tablet_tool_proximity; struct wl_listener tablet_tool_tip; struct wl_listener tablet_tool_button; struct wl_listener destroy; }; struct wlr_cursor_output_cursor { struct wlr_cursor *cursor; struct wlr_output_cursor *output_cursor; struct wl_list link; struct wl_listener layout_output_destroy; // only when using a surface as the cursor image struct wl_listener output_commit; // only when using an XCursor as the cursor image struct wlr_xcursor *xcursor; size_t xcursor_index; struct wl_event_source *xcursor_timer; }; struct wlr_cursor_state { struct wlr_cursor cursor; struct wl_list devices; // wlr_cursor_device.link struct wl_list output_cursors; // wlr_cursor_output_cursor.link struct wlr_output_layout *layout; struct wlr_output *mapped_output; struct wlr_box mapped_box; // empty if unset struct wl_listener layout_add; struct wl_listener layout_change; struct wl_listener layout_destroy; // only when using a buffer as the cursor image struct wlr_buffer *buffer; struct { int32_t x, y; } buffer_hotspot; float buffer_scale; // only when using a surface as the cursor image struct wlr_surface *surface; struct { int32_t x, y; } surface_hotspot; struct wl_listener surface_commit; struct wl_listener surface_destroy; // only when using an XCursor as the cursor image struct wlr_xcursor_manager *xcursor_manager; char *xcursor_name; }; struct wlr_cursor *wlr_cursor_create(void) { struct wlr_cursor_state *state = calloc(1, sizeof(*state)); if (!state) { wlr_log(WLR_ERROR, "Failed to allocate wlr_cursor_state"); return NULL; } struct wlr_cursor *cur = &state->cursor; cur->state = state; wl_list_init(&cur->state->devices); wl_list_init(&cur->state->output_cursors); // pointer signals wl_signal_init(&cur->events.motion); wl_signal_init(&cur->events.motion_absolute); wl_signal_init(&cur->events.button); wl_signal_init(&cur->events.axis); wl_signal_init(&cur->events.frame); wl_signal_init(&cur->events.swipe_begin); wl_signal_init(&cur->events.swipe_update); wl_signal_init(&cur->events.swipe_end); wl_signal_init(&cur->events.pinch_begin); wl_signal_init(&cur->events.pinch_update); wl_signal_init(&cur->events.pinch_end); wl_signal_init(&cur->events.hold_begin); wl_signal_init(&cur->events.hold_end); // touch signals wl_signal_init(&cur->events.touch_up); wl_signal_init(&cur->events.touch_down); wl_signal_init(&cur->events.touch_motion); wl_signal_init(&cur->events.touch_cancel); wl_signal_init(&cur->events.touch_frame); // tablet tool signals wl_signal_init(&cur->events.tablet_tool_tip); wl_signal_init(&cur->events.tablet_tool_axis); wl_signal_init(&cur->events.tablet_tool_button); wl_signal_init(&cur->events.tablet_tool_proximity); wl_list_init(&cur->state->surface_destroy.link); wl_list_init(&cur->state->surface_commit.link); cur->x = 100; cur->y = 100; return cur; } static void cursor_output_cursor_reset_image(struct wlr_cursor_output_cursor *output_cursor); static void output_cursor_destroy(struct wlr_cursor_output_cursor *output_cursor) { cursor_output_cursor_reset_image(output_cursor); wl_list_remove(&output_cursor->layout_output_destroy.link); wl_list_remove(&output_cursor->link); wl_list_remove(&output_cursor->output_commit.link); wlr_output_cursor_destroy(output_cursor->output_cursor); free(output_cursor); } static void cursor_detach_output_layout(struct wlr_cursor *cur) { if (!cur->state->layout) { return; } struct wlr_cursor_output_cursor *output_cursor, *tmp; wl_list_for_each_safe(output_cursor, tmp, &cur->state->output_cursors, link) { output_cursor_destroy(output_cursor); } wl_list_remove(&cur->state->layout_destroy.link); wl_list_remove(&cur->state->layout_change.link); wl_list_remove(&cur->state->layout_add.link); cur->state->layout = NULL; } static void cursor_device_destroy(struct wlr_cursor_device *c_device) { struct wlr_input_device *dev = c_device->device; if (dev->type == WLR_INPUT_DEVICE_POINTER) { wl_list_remove(&c_device->motion.link); wl_list_remove(&c_device->motion_absolute.link); wl_list_remove(&c_device->button.link); wl_list_remove(&c_device->axis.link); wl_list_remove(&c_device->frame.link); wl_list_remove(&c_device->swipe_begin.link); wl_list_remove(&c_device->swipe_update.link); wl_list_remove(&c_device->swipe_end.link); wl_list_remove(&c_device->pinch_begin.link); wl_list_remove(&c_device->pinch_update.link); wl_list_remove(&c_device->pinch_end.link); wl_list_remove(&c_device->hold_begin.link); wl_list_remove(&c_device->hold_end.link); } else if (dev->type == WLR_INPUT_DEVICE_TOUCH) { wl_list_remove(&c_device->touch_down.link); wl_list_remove(&c_device->touch_up.link); wl_list_remove(&c_device->touch_motion.link); wl_list_remove(&c_device->touch_cancel.link); wl_list_remove(&c_device->touch_frame.link); } else if (dev->type == WLR_INPUT_DEVICE_TABLET_TOOL) { wl_list_remove(&c_device->tablet_tool_axis.link); wl_list_remove(&c_device->tablet_tool_proximity.link); wl_list_remove(&c_device->tablet_tool_tip.link); wl_list_remove(&c_device->tablet_tool_button.link); } wl_list_remove(&c_device->link); wl_list_remove(&c_device->destroy.link); free(c_device); } static void cursor_reset_image(struct wlr_cursor *cur) { wlr_buffer_unlock(cur->state->buffer); cur->state->buffer = NULL; if (cur->state->surface != NULL) { struct wlr_cursor_output_cursor *output_cursor; wl_list_for_each(output_cursor, &cur->state->output_cursors, link) { wlr_surface_send_leave(cur->state->surface, output_cursor->output_cursor->output); } } wl_list_remove(&cur->state->surface_destroy.link); wl_list_remove(&cur->state->surface_commit.link); wl_list_init(&cur->state->surface_destroy.link); wl_list_init(&cur->state->surface_commit.link); cur->state->surface = NULL; cur->state->xcursor_manager = NULL; free(cur->state->xcursor_name); cur->state->xcursor_name = NULL; } void wlr_cursor_destroy(struct wlr_cursor *cur) { cursor_reset_image(cur); cursor_detach_output_layout(cur); struct wlr_cursor_device *device, *device_tmp = NULL; wl_list_for_each_safe(device, device_tmp, &cur->state->devices, link) { cursor_device_destroy(device); } free(cur->state); } static struct wlr_cursor_device *get_cursor_device(struct wlr_cursor *cur, struct wlr_input_device *device) { struct wlr_cursor_device *c_device, *ret = NULL; wl_list_for_each(c_device, &cur->state->devices, link) { if (c_device->device == device) { ret = c_device; break; } } return ret; } static void output_cursor_move(struct wlr_cursor_output_cursor *output_cursor) { struct wlr_cursor *cur = output_cursor->cursor; double output_x = cur->x, output_y = cur->y; wlr_output_layout_output_coords(cur->state->layout, output_cursor->output_cursor->output, &output_x, &output_y); wlr_output_cursor_move(output_cursor->output_cursor, output_x, output_y); } static void cursor_warp_unchecked(struct wlr_cursor *cur, double lx, double ly) { assert(cur->state->layout); if (!isfinite(lx) || !isfinite(ly)) { assert(false); return; } cur->x = lx; cur->y = ly; struct wlr_cursor_output_cursor *output_cursor; wl_list_for_each(output_cursor, &cur->state->output_cursors, link) { output_cursor_move(output_cursor); } } /** * Get the most specific mapping box for the device in this order: * * 1. device geometry mapping * 2. device output mapping * 3. cursor geometry mapping * 4. cursor output mapping * * Absolute movement for touch and pen devices will be relative to this box and * pointer movement will be constrained to this box. * * If none of these are set, the box is empty and absolute movement should be * relative to the extents of the layout. */ static void get_mapping(struct wlr_cursor *cur, struct wlr_input_device *dev, struct wlr_box *box) { assert(cur->state->layout); *box = (struct wlr_box){0}; struct wlr_cursor_device *c_device = get_cursor_device(cur, dev); if (c_device) { if (!wlr_box_empty(&c_device->mapped_box)) { *box = c_device->mapped_box; return; } if (c_device->mapped_output) { wlr_output_layout_get_box(cur->state->layout, c_device->mapped_output, box); return; } } if (!wlr_box_empty(&cur->state->mapped_box)) { *box = cur->state->mapped_box; return; } if (cur->state->mapped_output) { wlr_output_layout_get_box(cur->state->layout, cur->state->mapped_output, box); return; } } bool wlr_cursor_warp(struct wlr_cursor *cur, struct wlr_input_device *dev, double lx, double ly) { assert(cur->state->layout); bool result = false; struct wlr_box mapping; get_mapping(cur, dev, &mapping); if (!wlr_box_empty(&mapping)) { result = wlr_box_contains_point(&mapping, lx, ly); } else { result = wlr_output_layout_contains_point(cur->state->layout, NULL, lx, ly); } if (result) { cursor_warp_unchecked(cur, lx, ly); } return result; } void wlr_cursor_warp_closest(struct wlr_cursor *cur, struct wlr_input_device *dev, double lx, double ly) { struct wlr_box mapping; get_mapping(cur, dev, &mapping); if (!wlr_box_empty(&mapping)) { wlr_box_closest_point(&mapping, lx, ly, &lx, &ly); } else if (!wl_list_empty(&cur->state->layout->outputs)) { wlr_output_layout_closest_point(cur->state->layout, NULL, lx, ly, &lx, &ly); } else { /* * There is no mapping box for the input device and the * output layout is empty. This can happen for example * when external monitors are turned off/disconnected. * In this case, all (x,y) points are equally invalid, * so leave the cursor in its current location (better * from a user standpoint than warping it to (0,0)). */ return; } cursor_warp_unchecked(cur, lx, ly); } void wlr_cursor_absolute_to_layout_coords(struct wlr_cursor *cur, struct wlr_input_device *dev, double x, double y, double *lx, double *ly) { assert(cur->state->layout); struct wlr_box mapping; get_mapping(cur, dev, &mapping); if (wlr_box_empty(&mapping)) { wlr_output_layout_get_box(cur->state->layout, NULL, &mapping); } *lx = !isnan(x) ? mapping.width * x + mapping.x : cur->x; *ly = !isnan(y) ? mapping.height * y + mapping.y : cur->y; } void wlr_cursor_warp_absolute(struct wlr_cursor *cur, struct wlr_input_device *dev, double x, double y) { assert(cur->state->layout); double lx, ly; wlr_cursor_absolute_to_layout_coords(cur, dev, x, y, &lx, &ly); wlr_cursor_warp_closest(cur, dev, lx, ly); } void wlr_cursor_move(struct wlr_cursor *cur, struct wlr_input_device *dev, double delta_x, double delta_y) { assert(cur->state->layout); double lx = !isnan(delta_x) ? cur->x + delta_x : cur->x; double ly = !isnan(delta_y) ? cur->y + delta_y : cur->y; wlr_cursor_warp_closest(cur, dev, lx, ly); } static void cursor_output_cursor_reset_image(struct wlr_cursor_output_cursor *output_cursor) { output_cursor->xcursor = NULL; output_cursor->xcursor_index = 0; if (output_cursor->xcursor_timer != NULL) { wl_event_source_remove(output_cursor->xcursor_timer); } output_cursor->xcursor_timer = NULL; } static void cursor_update_outputs(struct wlr_cursor *cur); void wlr_cursor_set_buffer(struct wlr_cursor *cur, struct wlr_buffer *buffer, int32_t hotspot_x, int32_t hotspot_y, float scale) { if (buffer == cur->state->buffer && hotspot_x == cur->state->buffer_hotspot.x && hotspot_y == cur->state->buffer_hotspot.y && scale == cur->state->buffer_scale) { return; } cursor_reset_image(cur); if (buffer != NULL) { cur->state->buffer = wlr_buffer_lock(buffer); cur->state->buffer_hotspot.x = hotspot_x; cur->state->buffer_hotspot.y = hotspot_y; cur->state->buffer_scale = scale; } cursor_update_outputs(cur); } void wlr_cursor_unset_image(struct wlr_cursor *cur) { cursor_reset_image(cur); cursor_update_outputs(cur); } static void output_cursor_set_xcursor_image(struct wlr_cursor_output_cursor *output_cursor, size_t i); static int handle_xcursor_timer(void *data) { struct wlr_cursor_output_cursor *output_cursor = data; size_t i = (output_cursor->xcursor_index + 1) % output_cursor->xcursor->image_count; output_cursor_set_xcursor_image(output_cursor, i); return 0; } static void output_cursor_set_xcursor_image(struct wlr_cursor_output_cursor *output_cursor, size_t i) { struct wlr_xcursor_image *image = output_cursor->xcursor->images[i]; struct wlr_readonly_data_buffer *ro_buffer = readonly_data_buffer_create( DRM_FORMAT_ARGB8888, 4 * image->width, image->width, image->height, image->buffer); if (ro_buffer == NULL) { return; } wlr_output_cursor_set_buffer(output_cursor->output_cursor, &ro_buffer->base, image->hotspot_x, image->hotspot_y); wlr_buffer_drop(&ro_buffer->base); output_cursor->xcursor_index = i; if (output_cursor->xcursor->image_count == 1 || image->delay == 0) { return; } if (output_cursor->xcursor_timer == NULL) { struct wl_event_loop *event_loop = wl_display_get_event_loop(output_cursor->output_cursor->output->display); output_cursor->xcursor_timer = wl_event_loop_add_timer(event_loop, handle_xcursor_timer, output_cursor); if (output_cursor->xcursor_timer == NULL) { wlr_log(WLR_ERROR, "wl_event_loop_add_timer failed"); return; } } wl_event_source_timer_update(output_cursor->xcursor_timer, image->delay); } static void cursor_output_cursor_update(struct wlr_cursor_output_cursor *output_cursor) { struct wlr_cursor *cur = output_cursor->cursor; struct wlr_output *output = output_cursor->output_cursor->output; if (!output->enabled) { return; } cursor_output_cursor_reset_image(output_cursor); if (cur->state->buffer != NULL) { struct wlr_renderer *renderer = output->renderer; assert(renderer != NULL); struct wlr_buffer *buffer = cur->state->buffer; int32_t hotspot_x = cur->state->buffer_hotspot.x; int32_t hotspot_y = cur->state->buffer_hotspot.y; float scale = cur->state->buffer_scale; struct wlr_texture *texture = NULL; struct wlr_fbox src_box = {0}; int dst_width = 0, dst_height = 0; if (buffer != NULL) { texture = wlr_texture_from_buffer(renderer, buffer); if (texture) { src_box = (struct wlr_fbox){ .width = texture->width, .height = texture->height, }; dst_width = texture->width / scale; dst_height = texture->height / scale; } } output_cursor_set_texture(output_cursor->output_cursor, texture, true, &src_box, dst_width, dst_height, WL_OUTPUT_TRANSFORM_NORMAL, hotspot_x, hotspot_y); } else if (cur->state->surface != NULL) { struct wlr_surface *surface = cur->state->surface; struct wlr_texture *texture = wlr_surface_get_texture(surface); int32_t hotspot_x = cur->state->surface_hotspot.x; int32_t hotspot_y = cur->state->surface_hotspot.y; struct wlr_fbox src_box; wlr_surface_get_buffer_source_box(surface, &src_box); int dst_width = surface->current.width; int dst_height = surface->current.height; output_cursor_set_texture(output_cursor->output_cursor, texture, false, &src_box, dst_width, dst_height, surface->current.transform, hotspot_x, hotspot_y); if (output_cursor->output_cursor->visible) { wlr_surface_send_enter(surface, output); } else { wlr_surface_send_leave(surface, output); } float scale = 1; struct wlr_surface_output *surface_output; wl_list_for_each(surface_output, &surface->current_outputs, link) { if (surface_output->output->scale > scale) { scale = surface_output->output->scale; } } wlr_fractional_scale_v1_notify_scale(surface, scale); wlr_surface_set_preferred_buffer_scale(surface, ceil(scale)); } else if (cur->state->xcursor_name != NULL) { struct wlr_xcursor_manager *manager = cur->state->xcursor_manager; const char *name = cur->state->xcursor_name; float scale = output->scale; wlr_xcursor_manager_load(manager, scale); struct wlr_xcursor *xcursor = wlr_xcursor_manager_get_xcursor(manager, name, scale); if (xcursor == NULL) { wlr_log(WLR_DEBUG, "XCursor theme is missing '%s' cursor", name); wlr_output_cursor_set_buffer(output_cursor->output_cursor, NULL, 0, 0); return; } output_cursor->xcursor = xcursor; output_cursor_set_xcursor_image(output_cursor, 0); } else { wlr_output_cursor_set_buffer(output_cursor->output_cursor, NULL, 0, 0); } } static void output_cursor_output_handle_output_commit( struct wl_listener *listener, void *data) { struct wlr_cursor_output_cursor *output_cursor = wl_container_of(listener, output_cursor, output_commit); const struct wlr_output_event_commit *event = data; if (event->state->committed & (WLR_OUTPUT_STATE_SCALE | WLR_OUTPUT_STATE_TRANSFORM | WLR_OUTPUT_STATE_ENABLED)) { cursor_output_cursor_update(output_cursor); } struct wlr_surface *surface = output_cursor->cursor->state->surface; if (surface && output_cursor->output_cursor->visible && (event->state->committed & WLR_OUTPUT_STATE_BUFFER)) { wlr_surface_send_frame_done(surface, event->when); } } static void cursor_update_outputs(struct wlr_cursor *cur) { struct wlr_cursor_output_cursor *output_cursor; wl_list_for_each(output_cursor, &cur->state->output_cursors, link) { cursor_output_cursor_update(output_cursor); } } void wlr_cursor_set_xcursor(struct wlr_cursor *cur, struct wlr_xcursor_manager *manager, const char *name) { if (manager == cur->state->xcursor_manager && cur->state->xcursor_name != NULL && strcmp(name, cur->state->xcursor_name) == 0) { return; } cursor_reset_image(cur); cur->state->xcursor_manager = manager; cur->state->xcursor_name = strdup(name); cursor_update_outputs(cur); } static void cursor_handle_surface_destroy(struct wl_listener *listener, void *data) { struct wlr_cursor_state *state = wl_container_of(listener, state, surface_destroy); assert(state->surface != NULL); wlr_cursor_unset_image(&state->cursor); } static void cursor_handle_surface_commit(struct wl_listener *listener, void *data) { struct wlr_cursor_state *state = wl_container_of(listener, state, surface_commit); struct wlr_surface *surface = state->surface; state->surface_hotspot.x -= surface->current.dx; state->surface_hotspot.y -= surface->current.dy; cursor_update_outputs(&state->cursor); } void wlr_cursor_set_surface(struct wlr_cursor *cur, struct wlr_surface *surface, int32_t hotspot_x, int32_t hotspot_y) { if (surface == NULL) { wlr_cursor_unset_image(cur); return; } if (surface == cur->state->surface && hotspot_x == cur->state->surface_hotspot.x && hotspot_y == cur->state->surface_hotspot.y) { return; } if (surface != cur->state->surface) { // Only send wl_surface.leave if the surface changes cursor_reset_image(cur); cur->state->surface = surface; wl_signal_add(&surface->events.destroy, &cur->state->surface_destroy); cur->state->surface_destroy.notify = cursor_handle_surface_destroy; wl_signal_add(&surface->events.commit, &cur->state->surface_commit); cur->state->surface_commit.notify = cursor_handle_surface_commit; } cur->state->surface_hotspot.x = hotspot_x; cur->state->surface_hotspot.y = hotspot_y; cursor_update_outputs(cur); } static void handle_pointer_motion(struct wl_listener *listener, void *data) { struct wlr_pointer_motion_event *event = data; struct wlr_cursor_device *device = wl_container_of(listener, device, motion); wl_signal_emit_mutable(&device->cursor->events.motion, event); } static void apply_output_transform(double *x, double *y, enum wl_output_transform transform) { double dx = 0.0, dy = 0.0; double width = 1.0, height = 1.0; switch (transform) { case WL_OUTPUT_TRANSFORM_NORMAL: dx = *x; dy = *y; break; case WL_OUTPUT_TRANSFORM_90: dx = height - *y; dy = *x; break; case WL_OUTPUT_TRANSFORM_180: dx = width - *x; dy = height - *y; break; case WL_OUTPUT_TRANSFORM_270: dx = *y; dy = width - *x; break; case WL_OUTPUT_TRANSFORM_FLIPPED: dx = width - *x; dy = *y; break; case WL_OUTPUT_TRANSFORM_FLIPPED_90: dx = *y; dy = *x; break; case WL_OUTPUT_TRANSFORM_FLIPPED_180: dx = *x; dy = height - *y; break; case WL_OUTPUT_TRANSFORM_FLIPPED_270: dx = height - *y; dy = width - *x; break; } *x = dx; *y = dy; } static struct wlr_output *get_mapped_output(struct wlr_cursor_device *cursor_device) { if (cursor_device->mapped_output) { return cursor_device->mapped_output; } struct wlr_cursor *cursor = cursor_device->cursor; assert(cursor); if (cursor->state->mapped_output) { return cursor->state->mapped_output; } return NULL; } static void handle_pointer_motion_absolute(struct wl_listener *listener, void *data) { struct wlr_pointer_motion_absolute_event *event = data; struct wlr_cursor_device *device = wl_container_of(listener, device, motion_absolute); struct wlr_output *output = get_mapped_output(device); if (output) { apply_output_transform(&event->x, &event->y, output->transform); } wl_signal_emit_mutable(&device->cursor->events.motion_absolute, event); } static void handle_pointer_button(struct wl_listener *listener, void *data) { struct wlr_pointer_button_event *event = data; struct wlr_cursor_device *device = wl_container_of(listener, device, button); wl_signal_emit_mutable(&device->cursor->events.button, event); } static void handle_pointer_axis(struct wl_listener *listener, void *data) { struct wlr_pointer_axis_event *event = data; struct wlr_cursor_device *device = wl_container_of(listener, device, axis); wl_signal_emit_mutable(&device->cursor->events.axis, event); } static void handle_pointer_frame(struct wl_listener *listener, void *data) { struct wlr_cursor_device *device = wl_container_of(listener, device, frame); wl_signal_emit_mutable(&device->cursor->events.frame, device->cursor); } static void handle_pointer_swipe_begin(struct wl_listener *listener, void *data) { struct wlr_pointer_swipe_begin_event *event = data; struct wlr_cursor_device *device = wl_container_of(listener, device, swipe_begin); wl_signal_emit_mutable(&device->cursor->events.swipe_begin, event); } static void handle_pointer_swipe_update(struct wl_listener *listener, void *data) { struct wlr_pointer_swipe_update_event *event = data; struct wlr_cursor_device *device = wl_container_of(listener, device, swipe_update); wl_signal_emit_mutable(&device->cursor->events.swipe_update, event); } static void handle_pointer_swipe_end(struct wl_listener *listener, void *data) { struct wlr_pointer_swipe_end_event *event = data; struct wlr_cursor_device *device = wl_container_of(listener, device, swipe_end); wl_signal_emit_mutable(&device->cursor->events.swipe_end, event); } static void handle_pointer_pinch_begin(struct wl_listener *listener, void *data) { struct wlr_pointer_pinch_begin_event *event = data; struct wlr_cursor_device *device = wl_container_of(listener, device, pinch_begin); wl_signal_emit_mutable(&device->cursor->events.pinch_begin, event); } static void handle_pointer_pinch_update(struct wl_listener *listener, void *data) { struct wlr_pointer_pinch_update_event *event = data; struct wlr_cursor_device *device = wl_container_of(listener, device, pinch_update); wl_signal_emit_mutable(&device->cursor->events.pinch_update, event); } static void handle_pointer_pinch_end(struct wl_listener *listener, void *data) { struct wlr_pointer_pinch_end_event *event = data; struct wlr_cursor_device *device = wl_container_of(listener, device, pinch_end); wl_signal_emit_mutable(&device->cursor->events.pinch_end, event); } static void handle_pointer_hold_begin(struct wl_listener *listener, void *data) { struct wlr_pointer_hold_begin_event *event = data; struct wlr_cursor_device *device = wl_container_of(listener, device, hold_begin); wl_signal_emit_mutable(&device->cursor->events.hold_begin, event); } static void handle_pointer_hold_end(struct wl_listener *listener, void *data) { struct wlr_pointer_hold_end_event *event = data; struct wlr_cursor_device *device = wl_container_of(listener, device, hold_end); wl_signal_emit_mutable(&device->cursor->events.hold_end, event); } static void handle_touch_up(struct wl_listener *listener, void *data) { struct wlr_touch_up_event *event = data; struct wlr_cursor_device *device; device = wl_container_of(listener, device, touch_up); wl_signal_emit_mutable(&device->cursor->events.touch_up, event); } static void handle_touch_down(struct wl_listener *listener, void *data) { struct wlr_touch_down_event *event = data; struct wlr_cursor_device *device; device = wl_container_of(listener, device, touch_down); struct wlr_output *output = get_mapped_output(device); if (output) { apply_output_transform(&event->x, &event->y, output->transform); } wl_signal_emit_mutable(&device->cursor->events.touch_down, event); } static void handle_touch_motion(struct wl_listener *listener, void *data) { struct wlr_touch_motion_event *event = data; struct wlr_cursor_device *device; device = wl_container_of(listener, device, touch_motion); struct wlr_output *output = get_mapped_output(device); if (output) { apply_output_transform(&event->x, &event->y, output->transform); } wl_signal_emit_mutable(&device->cursor->events.touch_motion, event); } static void handle_touch_cancel(struct wl_listener *listener, void *data) { struct wlr_touch_cancel_event *event = data; struct wlr_cursor_device *device; device = wl_container_of(listener, device, touch_cancel); wl_signal_emit_mutable(&device->cursor->events.touch_cancel, event); } static void handle_touch_frame(struct wl_listener *listener, void *data) { struct wlr_cursor_device *device = wl_container_of(listener, device, touch_frame); wl_signal_emit_mutable(&device->cursor->events.touch_frame, NULL); } static void handle_tablet_tool_tip(struct wl_listener *listener, void *data) { struct wlr_tablet_tool_tip_event *event = data; struct wlr_cursor_device *device; device = wl_container_of(listener, device, tablet_tool_tip); struct wlr_output *output = get_mapped_output(device); if (output) { apply_output_transform(&event->x, &event->y, output->transform); } wl_signal_emit_mutable(&device->cursor->events.tablet_tool_tip, event); } static void handle_tablet_tool_axis(struct wl_listener *listener, void *data) { struct wlr_tablet_tool_axis_event *event = data; struct wlr_cursor_device *device; device = wl_container_of(listener, device, tablet_tool_axis); struct wlr_output *output = get_mapped_output(device); if (output) { // In the case that only one axis received an event, rotating the input can // cause the change to actually happen on the other axis, as far as clients // are concerned. // // Here, we feed apply_output_transform NAN on the axis that didn't change, // and remap the axes flags based on whether it returns NAN itself. double x = event->updated_axes & WLR_TABLET_TOOL_AXIS_X ? event->x : NAN; double y = event->updated_axes & WLR_TABLET_TOOL_AXIS_Y ? event->y : NAN; apply_output_transform(&x, &y, output->transform); event->updated_axes &= ~(WLR_TABLET_TOOL_AXIS_X | WLR_TABLET_TOOL_AXIS_Y); event->x = event->y = 0; if (!isnan(x)) { event->updated_axes |= WLR_TABLET_TOOL_AXIS_X; event->x = x; } if (!isnan(y)) { event->updated_axes |= WLR_TABLET_TOOL_AXIS_Y; event->y = y; } } wl_signal_emit_mutable(&device->cursor->events.tablet_tool_axis, event); } static void handle_tablet_tool_button(struct wl_listener *listener, void *data) { struct wlr_tablet_tool_button *event = data; struct wlr_cursor_device *device; device = wl_container_of(listener, device, tablet_tool_button); wl_signal_emit_mutable(&device->cursor->events.tablet_tool_button, event); } static void handle_tablet_tool_proximity(struct wl_listener *listener, void *data) { struct wlr_tablet_tool_proximity_event *event = data; struct wlr_cursor_device *device; device = wl_container_of(listener, device, tablet_tool_proximity); struct wlr_output *output = get_mapped_output(device); if (output) { apply_output_transform(&event->x, &event->y, output->transform); } wl_signal_emit_mutable(&device->cursor->events.tablet_tool_proximity, event); } static void handle_device_destroy(struct wl_listener *listener, void *data) { struct wlr_cursor_device *c_device; c_device = wl_container_of(listener, c_device, destroy); wlr_cursor_detach_input_device(c_device->cursor, c_device->device); } static struct wlr_cursor_device *cursor_device_create( struct wlr_cursor *cursor, struct wlr_input_device *device) { struct wlr_cursor_device *c_device = calloc(1, sizeof(*c_device)); if (!c_device) { wlr_log(WLR_ERROR, "Failed to allocate wlr_cursor_device"); return NULL; } c_device->cursor = cursor; c_device->device = device; // listen to events wl_signal_add(&device->events.destroy, &c_device->destroy); c_device->destroy.notify = handle_device_destroy; if (device->type == WLR_INPUT_DEVICE_POINTER) { struct wlr_pointer *pointer = wlr_pointer_from_input_device(device); wl_signal_add(&pointer->events.motion, &c_device->motion); c_device->motion.notify = handle_pointer_motion; wl_signal_add(&pointer->events.motion_absolute, &c_device->motion_absolute); c_device->motion_absolute.notify = handle_pointer_motion_absolute; wl_signal_add(&pointer->events.button, &c_device->button); c_device->button.notify = handle_pointer_button; wl_signal_add(&pointer->events.axis, &c_device->axis); c_device->axis.notify = handle_pointer_axis; wl_signal_add(&pointer->events.frame, &c_device->frame); c_device->frame.notify = handle_pointer_frame; wl_signal_add(&pointer->events.swipe_begin, &c_device->swipe_begin); c_device->swipe_begin.notify = handle_pointer_swipe_begin; wl_signal_add(&pointer->events.swipe_update, &c_device->swipe_update); c_device->swipe_update.notify = handle_pointer_swipe_update; wl_signal_add(&pointer->events.swipe_end, &c_device->swipe_end); c_device->swipe_end.notify = handle_pointer_swipe_end; wl_signal_add(&pointer->events.pinch_begin, &c_device->pinch_begin); c_device->pinch_begin.notify = handle_pointer_pinch_begin; wl_signal_add(&pointer->events.pinch_update, &c_device->pinch_update); c_device->pinch_update.notify = handle_pointer_pinch_update; wl_signal_add(&pointer->events.pinch_end, &c_device->pinch_end); c_device->pinch_end.notify = handle_pointer_pinch_end; wl_signal_add(&pointer->events.hold_begin, &c_device->hold_begin); c_device->hold_begin.notify = handle_pointer_hold_begin; wl_signal_add(&pointer->events.hold_end, &c_device->hold_end); c_device->hold_end.notify = handle_pointer_hold_end; } else if (device->type == WLR_INPUT_DEVICE_TOUCH) { struct wlr_touch *touch = wlr_touch_from_input_device(device); wl_signal_add(&touch->events.motion, &c_device->touch_motion); c_device->touch_motion.notify = handle_touch_motion; wl_signal_add(&touch->events.down, &c_device->touch_down); c_device->touch_down.notify = handle_touch_down; wl_signal_add(&touch->events.up, &c_device->touch_up); c_device->touch_up.notify = handle_touch_up; wl_signal_add(&touch->events.cancel, &c_device->touch_cancel); c_device->touch_cancel.notify = handle_touch_cancel; wl_signal_add(&touch->events.frame, &c_device->touch_frame); c_device->touch_frame.notify = handle_touch_frame; } else if (device->type == WLR_INPUT_DEVICE_TABLET_TOOL) { struct wlr_tablet *tablet = wlr_tablet_from_input_device(device); wl_signal_add(&tablet->events.tip, &c_device->tablet_tool_tip); c_device->tablet_tool_tip.notify = handle_tablet_tool_tip; wl_signal_add(&tablet->events.proximity, &c_device->tablet_tool_proximity); c_device->tablet_tool_proximity.notify = handle_tablet_tool_proximity; wl_signal_add(&tablet->events.axis, &c_device->tablet_tool_axis); c_device->tablet_tool_axis.notify = handle_tablet_tool_axis; wl_signal_add(&tablet->events.button, &c_device->tablet_tool_button); c_device->tablet_tool_button.notify = handle_tablet_tool_button; } wl_list_insert(&cursor->state->devices, &c_device->link); return c_device; } void wlr_cursor_attach_input_device(struct wlr_cursor *cur, struct wlr_input_device *dev) { if (dev->type != WLR_INPUT_DEVICE_POINTER && dev->type != WLR_INPUT_DEVICE_TOUCH && dev->type != WLR_INPUT_DEVICE_TABLET_TOOL) { wlr_log(WLR_ERROR, "only device types of pointer, touch or tablet tool" "are supported"); return; } // make sure it is not already attached struct wlr_cursor_device *_dev; wl_list_for_each(_dev, &cur->state->devices, link) { if (_dev->device == dev) { return; } } cursor_device_create(cur, dev); } void wlr_cursor_detach_input_device(struct wlr_cursor *cur, struct wlr_input_device *dev) { struct wlr_cursor_device *c_device, *tmp = NULL; wl_list_for_each_safe(c_device, tmp, &cur->state->devices, link) { if (c_device->device == dev) { cursor_device_destroy(c_device); } } } static void handle_layout_destroy(struct wl_listener *listener, void *data) { struct wlr_cursor_state *state = wl_container_of(listener, state, layout_destroy); cursor_detach_output_layout(&state->cursor); } static void handle_layout_output_destroy(struct wl_listener *listener, void *data) { struct wlr_cursor_output_cursor *output_cursor = wl_container_of(listener, output_cursor, layout_output_destroy); //struct wlr_output_layout_output *l_output = data; output_cursor_destroy(output_cursor); } static void layout_add(struct wlr_cursor_state *state, struct wlr_output_layout_output *l_output) { struct wlr_cursor_output_cursor *output_cursor; wl_list_for_each(output_cursor, &state->output_cursors, link) { if (output_cursor->output_cursor->output == l_output->output) { return; // already added } } output_cursor = calloc(1, sizeof(*output_cursor)); if (output_cursor == NULL) { wlr_log(WLR_ERROR, "Failed to allocate wlr_cursor_output_cursor"); return; } output_cursor->cursor = &state->cursor; output_cursor->output_cursor = wlr_output_cursor_create(l_output->output); if (output_cursor->output_cursor == NULL) { wlr_log(WLR_ERROR, "Failed to create wlr_output_cursor"); free(output_cursor); return; } output_cursor->layout_output_destroy.notify = handle_layout_output_destroy; wl_signal_add(&l_output->events.destroy, &output_cursor->layout_output_destroy); wl_list_insert(&state->output_cursors, &output_cursor->link); wl_signal_add(&output_cursor->output_cursor->output->events.commit, &output_cursor->output_commit); output_cursor->output_commit.notify = output_cursor_output_handle_output_commit; output_cursor_move(output_cursor); cursor_output_cursor_update(output_cursor); } static void handle_layout_add(struct wl_listener *listener, void *data) { struct wlr_cursor_state *state = wl_container_of(listener, state, layout_add); struct wlr_output_layout_output *l_output = data; layout_add(state, l_output); } static void handle_layout_change(struct wl_listener *listener, void *data) { struct wlr_cursor_state *state = wl_container_of(listener, state, layout_change); struct wlr_output_layout *layout = data; if (!wlr_output_layout_contains_point(layout, NULL, state->cursor.x, state->cursor.y) && !wl_list_empty(&layout->outputs)) { // the output we were on has gone away so go to the closest boundary // point (unless the layout is empty; compare warp_closest()) double x, y; wlr_output_layout_closest_point(layout, NULL, state->cursor.x, state->cursor.y, &x, &y); cursor_warp_unchecked(&state->cursor, x, y); } } void wlr_cursor_attach_output_layout(struct wlr_cursor *cur, struct wlr_output_layout *l) { cursor_detach_output_layout(cur); if (l == NULL) { return; } wl_signal_add(&l->events.add, &cur->state->layout_add); cur->state->layout_add.notify = handle_layout_add; wl_signal_add(&l->events.change, &cur->state->layout_change); cur->state->layout_change.notify = handle_layout_change; wl_signal_add(&l->events.destroy, &cur->state->layout_destroy); cur->state->layout_destroy.notify = handle_layout_destroy; cur->state->layout = l; struct wlr_output_layout_output *l_output; wl_list_for_each(l_output, &l->outputs, link) { layout_add(cur->state, l_output); } } void wlr_cursor_map_to_output(struct wlr_cursor *cur, struct wlr_output *output) { cur->state->mapped_output = output; } void wlr_cursor_map_input_to_output(struct wlr_cursor *cur, struct wlr_input_device *dev, struct wlr_output *output) { struct wlr_cursor_device *c_device = get_cursor_device(cur, dev); if (!c_device) { wlr_log(WLR_ERROR, "Cannot map device \"%s\" to output" " (not found in this cursor)", dev->name); return; } c_device->mapped_output = output; } void wlr_cursor_map_to_region(struct wlr_cursor *cur, const struct wlr_box *box) { cur->state->mapped_box = (struct wlr_box){0}; if (box) { if (wlr_box_empty(box)) { wlr_log(WLR_ERROR, "cannot map cursor to an empty region"); return; } cur->state->mapped_box = *box; } } void wlr_cursor_map_input_to_region(struct wlr_cursor *cur, struct wlr_input_device *dev, const struct wlr_box *box) { cur->state->mapped_box = (struct wlr_box){0}; struct wlr_cursor_device *c_device = get_cursor_device(cur, dev); if (!c_device) { wlr_log(WLR_ERROR, "Cannot map device \"%s\" to geometry (not found in" "this cursor)", dev->name); return; } if (box) { if (wlr_box_empty(box)) { wlr_log(WLR_ERROR, "cannot map device \"%s\" input to an empty region", dev->name); return; } c_device->mapped_box = *box; } } wlroots-0.17.1/types/wlr_cursor_shape_v1.c000066400000000000000000000211531454110342200206060ustar00rootroot00000000000000#include #include #include #include #include "types/wlr_tablet_v2.h" #define CURSOR_SHAPE_MANAGER_V1_VERSION 1 struct wlr_cursor_shape_device_v1 { struct wl_resource *resource; struct wlr_cursor_shape_manager_v1 *manager; enum wlr_cursor_shape_manager_v1_device_type type; struct wlr_seat_client *seat_client; struct wl_listener seat_client_destroy; }; static const struct wp_cursor_shape_device_v1_interface device_impl; static const struct wp_cursor_shape_manager_v1_interface manager_impl; // Returns NULL if the resource is inert static struct wlr_cursor_shape_device_v1 *device_from_resource(struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &wp_cursor_shape_device_v1_interface, &device_impl)); return wl_resource_get_user_data(resource); } static struct wlr_cursor_shape_manager_v1 *manager_from_resource(struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &wp_cursor_shape_manager_v1_interface, &manager_impl)); return wl_resource_get_user_data(resource); } static void resource_handle_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static void device_handle_set_shape(struct wl_client *client, struct wl_resource *device_resource, uint32_t serial, uint32_t shape) { struct wlr_cursor_shape_device_v1 *device = device_from_resource(device_resource); if (device == NULL) { return; } if (shape == 0 || shape > WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_ZOOM_OUT) { wl_resource_post_error(device_resource, WP_CURSOR_SHAPE_DEVICE_V1_ERROR_INVALID_SHAPE, "Invalid shape %"PRIu32, shape); return; } struct wlr_cursor_shape_manager_v1_request_set_shape_event event = { .seat_client = device->seat_client, .device_type = device->type, .serial = serial, .shape = shape, }; wl_signal_emit_mutable(&device->manager->events.request_set_shape, &event); } static const struct wp_cursor_shape_device_v1_interface device_impl = { .destroy = resource_handle_destroy, .set_shape = device_handle_set_shape, }; static void device_destroy(struct wlr_cursor_shape_device_v1 *device) { if (device == NULL) { return; } wl_list_remove(&device->seat_client_destroy.link); wl_resource_set_user_data(device->resource, NULL); // make inert free(device); } static void device_handle_resource_destroy(struct wl_resource *resource) { struct wlr_cursor_shape_device_v1 *device = device_from_resource(resource); device_destroy(device); } static void device_handle_seat_client_destroy(struct wl_listener *listener, void *data) { struct wlr_cursor_shape_device_v1 *device = wl_container_of(listener, device, seat_client_destroy); device_destroy(device); } static void create_device(struct wl_resource *manager_resource, uint32_t id, struct wlr_seat_client *seat_client, enum wlr_cursor_shape_manager_v1_device_type type) { struct wlr_cursor_shape_manager_v1 *manager = manager_from_resource(manager_resource); struct wl_client *client = wl_resource_get_client(manager_resource); uint32_t version = wl_resource_get_version(manager_resource); struct wl_resource *device_resource = wl_resource_create(client, &wp_cursor_shape_device_v1_interface, version, id); if (device_resource == NULL) { wl_resource_post_no_memory(manager_resource); return; } wl_resource_set_implementation(device_resource, &device_impl, NULL, device_handle_resource_destroy); if (seat_client == NULL) { return; // leave the resource inert } struct wlr_cursor_shape_device_v1 *device = calloc(1, sizeof(*device)); if (device == NULL) { wl_resource_post_no_memory(manager_resource); return; } device->resource = device_resource; device->manager = manager; device->type = type; device->seat_client = seat_client; device->seat_client_destroy.notify = device_handle_seat_client_destroy; wl_signal_add(&seat_client->events.destroy, &device->seat_client_destroy); wl_resource_set_user_data(device_resource, device); } static void manager_handle_get_pointer(struct wl_client *client, struct wl_resource *manager_resource, uint32_t id, struct wl_resource *pointer_resource) { struct wlr_seat_client *seat_client = wlr_seat_client_from_pointer_resource(pointer_resource); create_device(manager_resource, id, seat_client, WLR_CURSOR_SHAPE_MANAGER_V1_DEVICE_TYPE_POINTER); } static void manager_handle_get_tablet_tool_v2(struct wl_client *client, struct wl_resource *manager_resource, uint32_t id, struct wl_resource *tablet_tool_resource) { struct wlr_tablet_tool_client_v2 *tablet_tool_client = tablet_tool_client_from_resource(tablet_tool_resource); struct wlr_seat_client *seat_client = tablet_tool_client->seat->seat_client; create_device(manager_resource, id, seat_client, WLR_CURSOR_SHAPE_MANAGER_V1_DEVICE_TYPE_TABLET_TOOL); } static const struct wp_cursor_shape_manager_v1_interface manager_impl = { .destroy = resource_handle_destroy, .get_pointer = manager_handle_get_pointer, .get_tablet_tool_v2 = manager_handle_get_tablet_tool_v2, }; static void manager_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id) { struct wlr_cursor_shape_manager_v1 *manager = data; struct wl_resource *resource = wl_resource_create(client, &wp_cursor_shape_manager_v1_interface, version, id); if (resource == NULL) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(resource, &manager_impl, manager, NULL); } static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_cursor_shape_manager_v1 *manager = wl_container_of(listener, manager, display_destroy); wl_signal_emit_mutable(&manager->events.destroy, NULL); assert(wl_list_empty(&manager->events.destroy.listener_list)); wl_global_destroy(manager->global); wl_list_remove(&manager->display_destroy.link); free(manager); } struct wlr_cursor_shape_manager_v1 *wlr_cursor_shape_manager_v1_create( struct wl_display *display, uint32_t version) { assert(version <= CURSOR_SHAPE_MANAGER_V1_VERSION); struct wlr_cursor_shape_manager_v1 *manager = calloc(1, sizeof(*manager)); if (manager == NULL) { return NULL; } manager->global = wl_global_create(display, &wp_cursor_shape_manager_v1_interface, version, manager, manager_bind); if (manager->global == NULL) { free(manager); return NULL; } wl_signal_init(&manager->events.request_set_shape); wl_signal_init(&manager->events.destroy); manager->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &manager->display_destroy); return manager; } static const char *const shape_names[] = { [WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT] = "default", [WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_CONTEXT_MENU] = "context-menu", [WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_HELP] = "help", [WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_POINTER] = "pointer", [WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_PROGRESS] = "progress", [WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_WAIT] = "wait", [WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_CELL] = "cell", [WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_CROSSHAIR] = "crosshair", [WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_TEXT] = "text", [WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_VERTICAL_TEXT] = "vertical-text", [WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_ALIAS] = "alias", [WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_COPY] = "copy", [WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_MOVE] = "move", [WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NO_DROP] = "no-drop", [WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NOT_ALLOWED] = "not-allowed", [WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_GRAB] = "grab", [WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_GRABBING] = "grabbing", [WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_E_RESIZE] = "e-resize", [WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_N_RESIZE] = "n-resize", [WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NE_RESIZE] = "ne-resize", [WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NW_RESIZE] = "nw-resize", [WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_S_RESIZE] = "s-resize", [WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_SE_RESIZE] = "se-resize", [WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_SW_RESIZE] = "sw-resize", [WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_W_RESIZE] = "w-resize", [WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_EW_RESIZE] = "ew-resize", [WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NS_RESIZE] = "ns-resize", [WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NESW_RESIZE] = "nesw-resize", [WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NWSE_RESIZE] = "nwse-resize", [WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_COL_RESIZE] = "col-resize", [WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_ROW_RESIZE] = "row-resize", [WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_ALL_SCROLL] = "all-scroll", [WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_ZOOM_IN] = "zoom-in", [WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_ZOOM_OUT] = "zoom-out", }; const char *wlr_cursor_shape_v1_name(enum wp_cursor_shape_device_v1_shape shape) { assert(shape < sizeof(shape_names) / sizeof(shape_names[0])); return shape_names[shape]; } wlroots-0.17.1/types/wlr_damage_ring.c000066400000000000000000000063071454110342200177440ustar00rootroot00000000000000#include #include #include #include #include #include #define WLR_DAMAGE_RING_MAX_RECTS 20 void wlr_damage_ring_init(struct wlr_damage_ring *ring) { *ring = (struct wlr_damage_ring){ .width = INT_MAX, .height = INT_MAX, }; pixman_region32_init(&ring->current); for (size_t i = 0; i < WLR_DAMAGE_RING_PREVIOUS_LEN; ++i) { pixman_region32_init(&ring->previous[i]); } } void wlr_damage_ring_finish(struct wlr_damage_ring *ring) { pixman_region32_fini(&ring->current); for (size_t i = 0; i < WLR_DAMAGE_RING_PREVIOUS_LEN; ++i) { pixman_region32_fini(&ring->previous[i]); } } void wlr_damage_ring_set_bounds(struct wlr_damage_ring *ring, int32_t width, int32_t height) { if (width == 0 || height == 0) { width = INT_MAX; height = INT_MAX; } if (ring->width == width && ring->height == height) { return; } ring->width = width; ring->height = height; wlr_damage_ring_add_whole(ring); } bool wlr_damage_ring_add(struct wlr_damage_ring *ring, const pixman_region32_t *damage) { pixman_region32_t clipped; pixman_region32_init(&clipped); pixman_region32_intersect_rect(&clipped, damage, 0, 0, ring->width, ring->height); bool intersects = pixman_region32_not_empty(&clipped); if (intersects) { pixman_region32_union(&ring->current, &ring->current, &clipped); } pixman_region32_fini(&clipped); return intersects; } bool wlr_damage_ring_add_box(struct wlr_damage_ring *ring, const struct wlr_box *box) { struct wlr_box clipped = { .x = 0, .y = 0, .width = ring->width, .height = ring->height, }; if (wlr_box_intersection(&clipped, &clipped, box)) { pixman_region32_union_rect(&ring->current, &ring->current, clipped.x, clipped.y, clipped.width, clipped.height); return true; } return false; } void wlr_damage_ring_add_whole(struct wlr_damage_ring *ring) { pixman_region32_union_rect(&ring->current, &ring->current, 0, 0, ring->width, ring->height); } void wlr_damage_ring_rotate(struct wlr_damage_ring *ring) { // modular decrement ring->previous_idx = ring->previous_idx + WLR_DAMAGE_RING_PREVIOUS_LEN - 1; ring->previous_idx %= WLR_DAMAGE_RING_PREVIOUS_LEN; pixman_region32_copy(&ring->previous[ring->previous_idx], &ring->current); pixman_region32_clear(&ring->current); } void wlr_damage_ring_get_buffer_damage(struct wlr_damage_ring *ring, int buffer_age, pixman_region32_t *damage) { if (buffer_age <= 0 || buffer_age - 1 > WLR_DAMAGE_RING_PREVIOUS_LEN) { pixman_region32_clear(damage); pixman_region32_union_rect(damage, damage, 0, 0, ring->width, ring->height); } else { pixman_region32_copy(damage, &ring->current); // Accumulate damage from old buffers for (int i = 0; i < buffer_age - 1; ++i) { int j = (ring->previous_idx + i) % WLR_DAMAGE_RING_PREVIOUS_LEN; pixman_region32_union(damage, damage, &ring->previous[j]); } // Check the number of rectangles int n_rects = pixman_region32_n_rects(damage); if (n_rects > WLR_DAMAGE_RING_MAX_RECTS) { pixman_box32_t *extents = pixman_region32_extents(damage); pixman_region32_union_rect(damage, damage, extents->x1, extents->y1, extents->x2 - extents->x1, extents->y2 - extents->y1); } } } wlroots-0.17.1/types/wlr_data_control_v1.c000066400000000000000000000503661454110342200205720ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include #include "wlr-data-control-unstable-v1-protocol.h" #define DATA_CONTROL_MANAGER_VERSION 2 struct data_control_source { struct wl_resource *resource; struct wl_array mime_types; bool finalized; // Only one of these is non-NULL. struct wlr_data_source *active_source; struct wlr_primary_selection_source *active_primary_source; }; static const struct zwlr_data_control_source_v1_interface source_impl; static struct data_control_source *source_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &zwlr_data_control_source_v1_interface, &source_impl)); return wl_resource_get_user_data(resource); } static void source_handle_offer(struct wl_client *client, struct wl_resource *resource, const char *mime_type) { struct data_control_source *source = source_from_resource(resource); if (source == NULL) { return; } if (source->finalized) { wl_resource_post_error(resource, ZWLR_DATA_CONTROL_SOURCE_V1_ERROR_INVALID_OFFER, "cannot mutate offer after set_selection or " "set_primary_selection"); return; } const char **mime_type_ptr; wl_array_for_each(mime_type_ptr, &source->mime_types) { if (strcmp(*mime_type_ptr, mime_type) == 0) { wlr_log(WLR_DEBUG, "Ignoring duplicate MIME type offer %s", mime_type); return; } } char *dup_mime_type = strdup(mime_type); if (dup_mime_type == NULL) { wl_resource_post_no_memory(resource); return; } char **p = wl_array_add(&source->mime_types, sizeof(char *)); if (p == NULL) { free(dup_mime_type); wl_resource_post_no_memory(resource); return; } *p = dup_mime_type; } static void source_handle_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static const struct zwlr_data_control_source_v1_interface source_impl = { .offer = source_handle_offer, .destroy = source_handle_destroy, }; static void data_control_source_destroy(struct data_control_source *source) { if (source == NULL) { return; } char **p; wl_array_for_each(p, &source->mime_types) { free(*p); } wl_array_release(&source->mime_types); // Prevent destructors below from calling this recursively. wl_resource_set_user_data(source->resource, NULL); if (source->active_source != NULL) { wlr_data_source_destroy(source->active_source); } else if (source->active_primary_source != NULL) { wlr_primary_selection_source_destroy( source->active_primary_source); } free(source); } static void source_handle_resource_destroy(struct wl_resource *resource) { struct data_control_source *source = source_from_resource(resource); data_control_source_destroy(source); } struct client_data_source { struct wlr_data_source source; struct wl_resource *resource; }; static const struct wlr_data_source_impl client_source_impl; static struct client_data_source * client_data_source_from_source(struct wlr_data_source *wlr_source) { assert(wlr_source->impl == &client_source_impl); struct client_data_source *source = wl_container_of(wlr_source, source, source); return source; } static void client_source_send(struct wlr_data_source *wlr_source, const char *mime_type, int fd) { struct client_data_source *source = client_data_source_from_source(wlr_source); zwlr_data_control_source_v1_send_send(source->resource, mime_type, fd); close(fd); } static void client_source_destroy(struct wlr_data_source *wlr_source) { struct client_data_source *client_source = client_data_source_from_source(wlr_source); struct data_control_source *source = source_from_resource(client_source->resource); free(client_source); if (source == NULL) { return; } source->active_source = NULL; zwlr_data_control_source_v1_send_cancelled(source->resource); data_control_source_destroy(source); } static const struct wlr_data_source_impl client_source_impl = { .send = client_source_send, .destroy = client_source_destroy, }; struct client_primary_selection_source { struct wlr_primary_selection_source source; struct wl_resource *resource; }; static const struct wlr_primary_selection_source_impl client_primary_selection_source_impl; static struct client_primary_selection_source * client_primary_selection_source_from_source( struct wlr_primary_selection_source *wlr_source) { assert(wlr_source->impl == &client_primary_selection_source_impl); struct client_primary_selection_source *source = wl_container_of(wlr_source, source, source); return source; } static void client_primary_selection_source_send( struct wlr_primary_selection_source *wlr_source, const char *mime_type, int fd) { struct client_primary_selection_source *source = client_primary_selection_source_from_source(wlr_source); zwlr_data_control_source_v1_send_send(source->resource, mime_type, fd); close(fd); } static void client_primary_selection_source_destroy( struct wlr_primary_selection_source *wlr_source) { struct client_primary_selection_source *client_source = client_primary_selection_source_from_source(wlr_source); struct data_control_source *source = source_from_resource(client_source->resource); free(client_source); if (source == NULL) { return; } source->active_primary_source = NULL; zwlr_data_control_source_v1_send_cancelled(source->resource); data_control_source_destroy(source); } static const struct wlr_primary_selection_source_impl client_primary_selection_source_impl = { .send = client_primary_selection_source_send, .destroy = client_primary_selection_source_destroy, }; struct data_offer { struct wl_resource *resource; struct wlr_data_control_device_v1 *device; bool is_primary; }; static void data_offer_destroy(struct data_offer *offer) { if (offer == NULL) { return; } struct wlr_data_control_device_v1 *device = offer->device; if (device != NULL) { if (offer->is_primary) { device->primary_selection_offer_resource = NULL; } else { device->selection_offer_resource = NULL; } } wl_resource_set_user_data(offer->resource, NULL); free(offer); } static const struct zwlr_data_control_offer_v1_interface offer_impl; static struct data_offer *data_offer_from_offer_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &zwlr_data_control_offer_v1_interface, &offer_impl)); return wl_resource_get_user_data(resource); } static void offer_handle_receive(struct wl_client *client, struct wl_resource *resource, const char *mime_type, int fd) { struct data_offer *offer = data_offer_from_offer_resource(resource); if (offer == NULL) { close(fd); return; } struct wlr_data_control_device_v1 *device = offer->device; if (device == NULL) { close(fd); return; } if (offer->is_primary) { if (device->seat->primary_selection_source == NULL) { close(fd); return; } wlr_primary_selection_source_send( device->seat->primary_selection_source, mime_type, fd); } else { if (device->seat->selection_source == NULL) { close(fd); return; } wlr_data_source_send(device->seat->selection_source, mime_type, fd); } } static void offer_handle_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static const struct zwlr_data_control_offer_v1_interface offer_impl = { .receive = offer_handle_receive, .destroy = offer_handle_destroy, }; static void offer_handle_resource_destroy(struct wl_resource *resource) { struct data_offer *offer = data_offer_from_offer_resource(resource); data_offer_destroy(offer); } static struct wl_resource *create_offer(struct wlr_data_control_device_v1 *device, struct wl_array *mime_types, bool is_primary) { struct wl_client *client = wl_resource_get_client(device->resource); struct data_offer *offer = calloc(1, sizeof(*offer)); if (offer == NULL) { wl_client_post_no_memory(client); return NULL; } offer->device = device; offer->is_primary = is_primary; uint32_t version = wl_resource_get_version(device->resource); struct wl_resource *resource = wl_resource_create(client, &zwlr_data_control_offer_v1_interface, version, 0); if (resource == NULL) { free(offer); return NULL; } offer->resource = resource; wl_resource_set_implementation(resource, &offer_impl, offer, offer_handle_resource_destroy); zwlr_data_control_device_v1_send_data_offer(device->resource, resource); char **p; wl_array_for_each(p, mime_types) { zwlr_data_control_offer_v1_send_offer(resource, *p); } return resource; } static const struct zwlr_data_control_device_v1_interface control_impl; static struct wlr_data_control_device_v1 *control_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &zwlr_data_control_device_v1_interface, &control_impl)); return wl_resource_get_user_data(resource); } static void control_handle_set_selection(struct wl_client *client, struct wl_resource *control_resource, struct wl_resource *source_resource) { struct wlr_data_control_device_v1 *device = control_from_resource(control_resource); if (device == NULL) { return; } struct data_control_source *source = NULL; if (source_resource != NULL) { source = source_from_resource(source_resource); } if (source == NULL) { wlr_seat_request_set_selection(device->seat, NULL, NULL, wl_display_next_serial(device->seat->display)); return; } if (source->active_source != NULL || source->active_primary_source != NULL) { wl_resource_post_error(control_resource, ZWLR_DATA_CONTROL_DEVICE_V1_ERROR_USED_SOURCE, "cannot use a data source in set_selection or " "set_primary_selection more than once"); return; } struct client_data_source *client_source = calloc(1, sizeof(*client_source)); if (client_source == NULL) { wl_client_post_no_memory(client); return; } client_source->resource = source_resource; struct wlr_data_source *wlr_source = &client_source->source; wlr_data_source_init(wlr_source, &client_source_impl); source->active_source = wlr_source; wl_array_release(&wlr_source->mime_types); wlr_source->mime_types = source->mime_types; wl_array_init(&source->mime_types); source->finalized = true; wlr_seat_request_set_selection(device->seat, NULL, wlr_source, wl_display_next_serial(device->seat->display)); } static void control_handle_set_primary_selection(struct wl_client *client, struct wl_resource *control_resource, struct wl_resource *source_resource) { struct wlr_data_control_device_v1 *device = control_from_resource(control_resource); if (device == NULL) { return; } struct data_control_source *source = NULL; if (source_resource != NULL) { source = source_from_resource(source_resource); } if (source == NULL) { wlr_seat_request_set_primary_selection(device->seat, NULL, NULL, wl_display_next_serial(device->seat->display)); return; } if (source->active_source != NULL || source->active_primary_source != NULL) { wl_resource_post_error(control_resource, ZWLR_DATA_CONTROL_DEVICE_V1_ERROR_USED_SOURCE, "cannot use a data source in set_selection or " "set_primary_selection more than once"); return; } struct client_primary_selection_source *client_source = calloc(1, sizeof(*client_source)); if (client_source == NULL) { wl_client_post_no_memory(client); return; } client_source->resource = source_resource; struct wlr_primary_selection_source *wlr_source = &client_source->source; wlr_primary_selection_source_init(wlr_source, &client_primary_selection_source_impl); source->active_primary_source = wlr_source; wl_array_release(&wlr_source->mime_types); wlr_source->mime_types = source->mime_types; wl_array_init(&source->mime_types); source->finalized = true; wlr_seat_request_set_primary_selection(device->seat, NULL, wlr_source, wl_display_next_serial(device->seat->display)); } static void control_handle_destroy(struct wl_client *client, struct wl_resource *control_resource) { wl_resource_destroy(control_resource); } static const struct zwlr_data_control_device_v1_interface control_impl = { .set_selection = control_handle_set_selection, .set_primary_selection = control_handle_set_primary_selection, .destroy = control_handle_destroy, }; static void control_send_selection(struct wlr_data_control_device_v1 *device) { struct wlr_data_source *source = device->seat->selection_source; if (device->selection_offer_resource != NULL) { // Make the offer inert struct data_offer *offer = data_offer_from_offer_resource( device->selection_offer_resource); data_offer_destroy(offer); } device->selection_offer_resource = NULL; if (source != NULL) { device->selection_offer_resource = create_offer(device, &source->mime_types, false); if (device->selection_offer_resource == NULL) { wl_resource_post_no_memory(device->resource); return; } } zwlr_data_control_device_v1_send_selection(device->resource, device->selection_offer_resource); } static void control_send_primary_selection( struct wlr_data_control_device_v1 *device) { uint32_t version = wl_resource_get_version(device->resource); if (version < ZWLR_DATA_CONTROL_DEVICE_V1_PRIMARY_SELECTION_SINCE_VERSION) { return; } struct wlr_primary_selection_source *source = device->seat->primary_selection_source; if (device->primary_selection_offer_resource != NULL) { // Make the offer inert struct data_offer *offer = data_offer_from_offer_resource( device->primary_selection_offer_resource); data_offer_destroy(offer); } device->primary_selection_offer_resource = NULL; if (source != NULL) { device->primary_selection_offer_resource = create_offer(device, &source->mime_types, true); if (device->primary_selection_offer_resource == NULL) { wl_resource_post_no_memory(device->resource); return; } } zwlr_data_control_device_v1_send_primary_selection(device->resource, device->primary_selection_offer_resource); } static void control_handle_resource_destroy(struct wl_resource *resource) { struct wlr_data_control_device_v1 *device = control_from_resource(resource); wlr_data_control_device_v1_destroy(device); } static void control_handle_seat_destroy(struct wl_listener *listener, void *data) { struct wlr_data_control_device_v1 *device = wl_container_of(listener, device, seat_destroy); wlr_data_control_device_v1_destroy(device); } static void control_handle_seat_set_selection(struct wl_listener *listener, void *data) { struct wlr_data_control_device_v1 *device = wl_container_of(listener, device, seat_set_selection); control_send_selection(device); } static void control_handle_seat_set_primary_selection( struct wl_listener *listener, void *data) { struct wlr_data_control_device_v1 *device = wl_container_of(listener, device, seat_set_primary_selection); control_send_primary_selection(device); } void wlr_data_control_device_v1_destroy(struct wlr_data_control_device_v1 *device) { if (device == NULL) { return; } zwlr_data_control_device_v1_send_finished(device->resource); // Make the resources inert wl_resource_set_user_data(device->resource, NULL); if (device->selection_offer_resource != NULL) { struct data_offer *offer = data_offer_from_offer_resource( device->selection_offer_resource); data_offer_destroy(offer); } if (device->primary_selection_offer_resource != NULL) { struct data_offer *offer = data_offer_from_offer_resource( device->primary_selection_offer_resource); data_offer_destroy(offer); } wl_list_remove(&device->seat_destroy.link); wl_list_remove(&device->seat_set_selection.link); wl_list_remove(&device->seat_set_primary_selection.link); wl_list_remove(&device->link); free(device); } static const struct zwlr_data_control_manager_v1_interface manager_impl; static struct wlr_data_control_manager_v1 *manager_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &zwlr_data_control_manager_v1_interface, &manager_impl)); return wl_resource_get_user_data(resource); } static void manager_handle_create_data_source(struct wl_client *client, struct wl_resource *manager_resource, uint32_t id) { struct data_control_source *source = calloc(1, sizeof(*source)); if (source == NULL) { wl_resource_post_no_memory(manager_resource); return; } wl_array_init(&source->mime_types); uint32_t version = wl_resource_get_version(manager_resource); source->resource = wl_resource_create(client, &zwlr_data_control_source_v1_interface, version, id); if (source->resource == NULL) { wl_resource_post_no_memory(manager_resource); wl_array_release(&source->mime_types); free(source); return; } wl_resource_set_implementation(source->resource, &source_impl, source, source_handle_resource_destroy); } static void manager_handle_get_data_device(struct wl_client *client, struct wl_resource *manager_resource, uint32_t id, struct wl_resource *seat_resource) { struct wlr_data_control_manager_v1 *manager = manager_from_resource(manager_resource); struct wlr_seat_client *seat_client = wlr_seat_client_from_resource(seat_resource); uint32_t version = wl_resource_get_version(manager_resource); struct wl_resource *resource = wl_resource_create(client, &zwlr_data_control_device_v1_interface, version, id); if (resource == NULL) { wl_resource_post_no_memory(manager_resource); return; } wl_resource_set_implementation(resource, &control_impl, NULL, control_handle_resource_destroy); if (seat_client == NULL) { return; } struct wlr_data_control_device_v1 *device = calloc(1, sizeof(*device)); if (device == NULL) { wl_resource_post_no_memory(manager_resource); return; } device->manager = manager; device->seat = seat_client->seat; device->resource = resource; wl_resource_set_user_data(resource, device); device->seat_destroy.notify = control_handle_seat_destroy; wl_signal_add(&device->seat->events.destroy, &device->seat_destroy); device->seat_set_selection.notify = control_handle_seat_set_selection; wl_signal_add(&device->seat->events.set_selection, &device->seat_set_selection); device->seat_set_primary_selection.notify = control_handle_seat_set_primary_selection; wl_signal_add(&device->seat->events.set_primary_selection, &device->seat_set_primary_selection); wl_list_insert(&manager->devices, &device->link); wl_signal_emit_mutable(&manager->events.new_device, device); // At this point maybe the compositor decided to destroy the device. If // it's the case then the resource will be inert. device = control_from_resource(resource); if (device != NULL) { control_send_selection(device); control_send_primary_selection(device); } } static void manager_handle_destroy(struct wl_client *client, struct wl_resource *manager_resource) { wl_resource_destroy(manager_resource); } static const struct zwlr_data_control_manager_v1_interface manager_impl = { .create_data_source = manager_handle_create_data_source, .get_data_device = manager_handle_get_data_device, .destroy = manager_handle_destroy, }; static void manager_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id) { struct wlr_data_control_manager_v1 *manager = data; struct wl_resource *resource = wl_resource_create(client, &zwlr_data_control_manager_v1_interface, version, id); if (resource == NULL) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(resource, &manager_impl, manager, NULL); } static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_data_control_manager_v1 *manager = wl_container_of(listener, manager, display_destroy); wl_signal_emit_mutable(&manager->events.destroy, manager); wl_list_remove(&manager->display_destroy.link); wl_global_destroy(manager->global); free(manager); } struct wlr_data_control_manager_v1 *wlr_data_control_manager_v1_create( struct wl_display *display) { struct wlr_data_control_manager_v1 *manager = calloc(1, sizeof(*manager)); if (manager == NULL) { return NULL; } wl_list_init(&manager->devices); wl_signal_init(&manager->events.destroy); wl_signal_init(&manager->events.new_device); manager->global = wl_global_create(display, &zwlr_data_control_manager_v1_interface, DATA_CONTROL_MANAGER_VERSION, manager, manager_bind); if (manager->global == NULL) { free(manager); return NULL; } manager->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &manager->display_destroy); return manager; } wlroots-0.17.1/types/wlr_drm.c000066400000000000000000000174371454110342200162770ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include #include #include #include #include "drm-protocol.h" #include "render/drm_format_set.h" #define WLR_DRM_VERSION 2 static void buffer_handle_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static const struct wl_buffer_interface wl_buffer_impl = { .destroy = buffer_handle_destroy, }; static const struct wlr_buffer_impl buffer_impl; static struct wlr_drm_buffer *drm_buffer_from_buffer( struct wlr_buffer *wlr_buffer) { assert(wlr_buffer->impl == &buffer_impl); struct wlr_drm_buffer *buffer = wl_container_of(wlr_buffer, buffer, base); return buffer; } static void buffer_destroy(struct wlr_buffer *wlr_buffer) { struct wlr_drm_buffer *buffer = drm_buffer_from_buffer(wlr_buffer); if (buffer->resource != NULL) { wl_resource_set_user_data(buffer->resource, NULL); } wlr_dmabuf_attributes_finish(&buffer->dmabuf); wl_list_remove(&buffer->release.link); free(buffer); } static bool buffer_get_dmabuf(struct wlr_buffer *wlr_buffer, struct wlr_dmabuf_attributes *dmabuf) { struct wlr_drm_buffer *buffer = drm_buffer_from_buffer(wlr_buffer); *dmabuf = buffer->dmabuf; return true; } static const struct wlr_buffer_impl buffer_impl = { .destroy = buffer_destroy, .get_dmabuf = buffer_get_dmabuf, }; static bool buffer_resource_is_instance(struct wl_resource *resource) { return wl_resource_instance_of(resource, &wl_buffer_interface, &wl_buffer_impl); } struct wlr_drm_buffer *wlr_drm_buffer_try_from_resource( struct wl_resource *resource) { if (!buffer_resource_is_instance(resource)) { return NULL; } return wl_resource_get_user_data(resource); } static void buffer_handle_resource_destroy(struct wl_resource *resource) { struct wlr_drm_buffer *buffer = wlr_drm_buffer_try_from_resource(resource); assert(buffer != NULL); buffer->resource = NULL; wlr_buffer_drop(&buffer->base); } static void buffer_handle_release(struct wl_listener *listener, void *data) { struct wlr_drm_buffer *buffer = wl_container_of(listener, buffer, release); if (buffer->resource != NULL) { wl_buffer_send_release(buffer->resource); } } static void drm_handle_authenticate(struct wl_client *client, struct wl_resource *resource, uint32_t id) { // We only use render nodes, which don't need authentication wl_drm_send_authenticated(resource); } static void drm_handle_create_buffer(struct wl_client *client, struct wl_resource *resource, uint32_t id, uint32_t name, int32_t width, int32_t height, uint32_t stride, uint32_t format) { wl_resource_post_error(resource, WL_DRM_ERROR_INVALID_NAME, "Flink handles are not supported, use DMA-BUF instead"); } static void drm_handle_create_planar_buffer(struct wl_client *client, struct wl_resource *resource, uint32_t id, uint32_t name, int32_t width, int32_t height, uint32_t format, int32_t offset0, int32_t stride0, int32_t offset1, int32_t stride1, int32_t offset2, int32_t stride2) { wl_resource_post_error(resource, WL_DRM_ERROR_INVALID_NAME, "Flink handles are not supported, use DMA-BUF instead"); } static void drm_handle_create_prime_buffer(struct wl_client *client, struct wl_resource *resource, uint32_t id, int fd, int32_t width, int32_t height, uint32_t format, int32_t offset0, int32_t stride0, int32_t offset1, int32_t stride1, int32_t offset2, int32_t stride2) { struct wlr_dmabuf_attributes dmabuf = { .width = width, .height = height, .format = format, .modifier = DRM_FORMAT_MOD_INVALID, .n_planes = 1, .offset[0] = offset0, .stride[0] = stride0, .fd[0] = fd, }; struct wlr_drm_buffer *buffer = calloc(1, sizeof(*buffer)); if (buffer == NULL) { close(fd); wl_resource_post_no_memory(resource); return; } wlr_buffer_init(&buffer->base, &buffer_impl, width, height); buffer->resource = wl_resource_create(client, &wl_buffer_interface, 1, id); if (buffer->resource == NULL) { free(buffer); close(fd); wl_resource_post_no_memory(resource); return; } wl_resource_set_implementation(buffer->resource, &wl_buffer_impl, buffer, buffer_handle_resource_destroy); buffer->dmabuf = dmabuf; buffer->release.notify = buffer_handle_release; wl_signal_add(&buffer->base.events.release, &buffer->release); } static const struct wl_drm_interface drm_impl = { .authenticate = drm_handle_authenticate, .create_buffer = drm_handle_create_buffer, .create_planar_buffer = drm_handle_create_planar_buffer, .create_prime_buffer = drm_handle_create_prime_buffer, }; static void drm_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id) { struct wlr_drm *drm = data; struct wl_resource *resource = wl_resource_create(client, &wl_drm_interface, version, id); if (resource == NULL) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(resource, &drm_impl, drm, NULL); wl_drm_send_device(resource, drm->node_name); wl_drm_send_capabilities(resource, WL_DRM_CAPABILITY_PRIME); for (size_t i = 0; i < drm->formats.len; i++) { const struct wlr_drm_format *fmt = &drm->formats.formats[i]; if (wlr_drm_format_has(fmt, DRM_FORMAT_MOD_INVALID)) { wl_drm_send_format(resource, fmt->format); } } } static struct wlr_buffer *buffer_from_resource(struct wl_resource *resource) { struct wlr_drm_buffer *buffer = wlr_drm_buffer_try_from_resource(resource); assert(buffer != NULL); return &buffer->base; } static const struct wlr_buffer_resource_interface buffer_resource_interface = { .name = "wlr_drm_buffer", .is_instance = buffer_resource_is_instance, .from_resource = buffer_from_resource, }; static void drm_destroy(struct wlr_drm *drm) { wl_signal_emit_mutable(&drm->events.destroy, NULL); wl_list_remove(&drm->display_destroy.link); wlr_drm_format_set_finish(&drm->formats); free(drm->node_name); wl_global_destroy(drm->global); free(drm); } static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_drm *drm = wl_container_of(listener, drm, display_destroy); drm_destroy(drm); } struct wlr_drm *wlr_drm_create(struct wl_display *display, struct wlr_renderer *renderer) { int drm_fd = wlr_renderer_get_drm_fd(renderer); if (drm_fd < 0) { wlr_log(WLR_ERROR, "Failed to get DRM FD from renderer"); return NULL; } drmDevice *dev = NULL; if (drmGetDevice2(drm_fd, 0, &dev) != 0) { wlr_log(WLR_ERROR, "drmGetDevice2 failed"); return NULL; } char *node_name = NULL; if (dev->available_nodes & (1 << DRM_NODE_RENDER)) { node_name = strdup(dev->nodes[DRM_NODE_RENDER]); } else { assert(dev->available_nodes & (1 << DRM_NODE_PRIMARY)); wlr_log(WLR_DEBUG, "No DRM render node available, " "falling back to primary node '%s'", dev->nodes[DRM_NODE_PRIMARY]); node_name = strdup(dev->nodes[DRM_NODE_PRIMARY]); } drmFreeDevice(&dev); if (node_name == NULL) { return NULL; } struct wlr_drm *drm = calloc(1, sizeof(*drm)); if (drm == NULL) { free(node_name); return NULL; } drm->node_name = node_name; wl_signal_init(&drm->events.destroy); const struct wlr_drm_format_set *formats = wlr_renderer_get_dmabuf_texture_formats(renderer); if (formats == NULL) { goto error; } if (!wlr_drm_format_set_copy(&drm->formats, formats)) { goto error; } drm->global = wl_global_create(display, &wl_drm_interface, WLR_DRM_VERSION, drm, drm_bind); if (drm->global == NULL) { goto error; } drm->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &drm->display_destroy); wlr_buffer_register_resource_interface(&buffer_resource_interface); return drm; error: wlr_drm_format_set_finish(&drm->formats); free(drm->node_name); free(drm); return NULL; } wlroots-0.17.1/types/wlr_drm_lease_v1.c000066400000000000000000000527351454110342200200560ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include "backend/drm/drm.h" #include "drm-lease-v1-protocol.h" #include "util/global.h" #define DRM_LEASE_DEVICE_V1_VERSION 1 static struct wp_drm_lease_device_v1_interface lease_device_impl; static struct wp_drm_lease_connector_v1_interface lease_connector_impl; static struct wp_drm_lease_request_v1_interface lease_request_impl; static struct wp_drm_lease_v1_interface lease_impl; static struct wlr_drm_lease_device_v1 *drm_lease_device_v1_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &wp_drm_lease_device_v1_interface, &lease_device_impl)); return wl_resource_get_user_data(resource); } static struct wlr_drm_lease_connector_v1 * drm_lease_connector_v1_from_resource(struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &wp_drm_lease_connector_v1_interface, &lease_connector_impl)); return wl_resource_get_user_data(resource); } static struct wlr_drm_lease_request_v1 *drm_lease_request_v1_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &wp_drm_lease_request_v1_interface, &lease_request_impl)); return wl_resource_get_user_data(resource); } static struct wlr_drm_lease_v1 *drm_lease_v1_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &wp_drm_lease_v1_interface, &lease_impl)); return wl_resource_get_user_data(resource); } static void drm_lease_request_v1_destroy( struct wlr_drm_lease_request_v1 *request) { if (!request) { return; } wlr_log(WLR_DEBUG, "Destroying request %p", request); wl_list_remove(&request->link); wl_resource_set_user_data(request->resource, NULL); free(request->connectors); free(request); } static void drm_lease_connector_v1_destroy( struct wlr_drm_lease_connector_v1 *connector) { if (!connector) { return; } wlr_log(WLR_DEBUG, "Destroying connector %s", connector->output->name); if (connector->active_lease) { wlr_drm_lease_terminate(connector->active_lease->drm_lease); } struct wl_resource *resource, *tmp; wl_resource_for_each_safe(resource, tmp, &connector->resources) { wp_drm_lease_connector_v1_send_withdrawn(resource); wl_resource_set_user_data(resource, NULL); wl_list_remove(wl_resource_get_link(resource)); wl_list_init(wl_resource_get_link(resource)); } struct wl_resource *device_resource; wl_resource_for_each(device_resource, &connector->device->resources) { wp_drm_lease_device_v1_send_done(device_resource); } wl_list_remove(&connector->link); wl_list_remove(&connector->destroy.link); free(connector); } static void drm_lease_device_v1_destroy( struct wlr_drm_lease_device_v1 *device) { if (!device) { return; } struct wlr_drm_backend *backend = get_drm_backend_from_backend(device->backend); wlr_log(WLR_DEBUG, "Destroying wlr_drm_lease_device_v1 for %s", backend->name); struct wl_resource *resource, *tmp_resource; wl_resource_for_each_safe(resource, tmp_resource, &device->resources) { wl_list_remove(wl_resource_get_link(resource)); wl_list_init(wl_resource_get_link(resource)); wl_resource_set_user_data(resource, NULL); } struct wlr_drm_lease_request_v1 *request, *tmp_request; wl_list_for_each_safe(request, tmp_request, &device->requests, link) { drm_lease_request_v1_destroy(request); } struct wlr_drm_lease_v1 *lease, *tmp_lease; wl_list_for_each_safe(lease, tmp_lease, &device->leases, link) { wlr_drm_lease_terminate(lease->drm_lease); } struct wlr_drm_lease_connector_v1 *connector, *tmp_connector; wl_list_for_each_safe(connector, tmp_connector, &device->connectors, link) { drm_lease_connector_v1_destroy(connector); } wl_list_remove(&device->link); wl_list_remove(&device->backend_destroy.link); wlr_global_destroy_safe(device->global); free(device); } static void lease_handle_destroy(struct wl_listener *listener, void *data) { struct wlr_drm_lease_v1 *lease = wl_container_of(listener, lease, destroy); wlr_log(WLR_DEBUG, "Destroying lease %"PRIu32, lease->drm_lease->lessee_id); wp_drm_lease_v1_send_finished(lease->resource); wl_list_remove(&lease->destroy.link); for (size_t i = 0; i < lease->n_connectors; ++i) { lease->connectors[i]->active_lease = NULL; } wl_list_remove(&lease->link); wl_resource_set_user_data(lease->resource, NULL); free(lease->connectors); free(lease); } struct wlr_drm_lease_v1 *wlr_drm_lease_request_v1_grant( struct wlr_drm_lease_request_v1 *request) { assert(!request->invalid); wlr_log(WLR_DEBUG, "Attempting to grant request %p", request); struct wlr_drm_lease_v1 *lease = calloc(1, sizeof(*lease)); if (!lease) { wl_resource_post_no_memory(request->resource); return NULL; } lease->device = request->device; lease->resource = request->lease_resource; /* Transform connectors list into wlr_output for leasing */ struct wlr_output *outputs[request->n_connectors + 1]; for(size_t i = 0; i < request->n_connectors; ++i) { outputs[i] = request->connectors[i]->output; } int fd; lease->drm_lease = wlr_drm_create_lease(outputs, request->n_connectors, &fd); if (!lease->drm_lease) { wlr_log(WLR_ERROR, "wlr_drm_create_lease failed"); wp_drm_lease_v1_send_finished(lease->resource); return NULL; } lease->connectors = calloc(request->n_connectors, sizeof(*lease->connectors)); if (!lease->connectors) { wlr_log(WLR_ERROR, "Failed to allocate lease connectors list"); close(fd); wp_drm_lease_v1_send_finished(lease->resource); return NULL; } lease->n_connectors = request->n_connectors; for (size_t i = 0; i < request->n_connectors; ++i) { lease->connectors[i] = request->connectors[i]; lease->connectors[i]->active_lease = lease; } lease->destroy.notify = lease_handle_destroy; wl_signal_add(&lease->drm_lease->events.destroy, &lease->destroy); wl_list_insert(&lease->device->leases, &lease->link); wl_resource_set_user_data(lease->resource, lease); wlr_log(WLR_DEBUG, "Granting request %p", request); wp_drm_lease_v1_send_lease_fd(lease->resource, fd); close(fd); return lease; } void wlr_drm_lease_request_v1_reject( struct wlr_drm_lease_request_v1 *request) { assert(request); wlr_log(WLR_DEBUG, "Rejecting request %p", request); request->invalid = true; wp_drm_lease_v1_send_finished(request->lease_resource); } void wlr_drm_lease_v1_revoke(struct wlr_drm_lease_v1 *lease) { assert(lease); wlr_log(WLR_DEBUG, "Revoking lease %"PRIu32, lease->drm_lease->lessee_id); wlr_drm_lease_terminate(lease->drm_lease); } static void drm_lease_v1_handle_resource_destroy(struct wl_resource *resource) { struct wlr_drm_lease_v1 *lease = drm_lease_v1_from_resource(resource); if (lease != NULL) { wlr_drm_lease_terminate(lease->drm_lease); } } static void drm_lease_v1_handle_destroy( struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static struct wp_drm_lease_v1_interface lease_impl = { .destroy = drm_lease_v1_handle_destroy, }; static void drm_lease_request_v1_handle_resource_destroy( struct wl_resource *resource) { struct wlr_drm_lease_request_v1 *request = drm_lease_request_v1_from_resource(resource); drm_lease_request_v1_destroy(request); } static void drm_lease_request_v1_handle_request_connector( struct wl_client *client, struct wl_resource *request_resource, struct wl_resource *connector_resource) { struct wlr_drm_lease_request_v1 *request = drm_lease_request_v1_from_resource(request_resource); if (!request) { wlr_log(WLR_ERROR, "Request has been destroyed"); return; } struct wlr_drm_lease_connector_v1 *connector = drm_lease_connector_v1_from_resource(connector_resource); if (!connector) { /* This connector offer has been withdrawn or is leased */ wlr_log(WLR_ERROR, "Failed to request connector"); request->invalid = true; return; } wlr_log(WLR_DEBUG, "Requesting connector %s", connector->output->name); if (request->device != connector->device) { wlr_log(WLR_ERROR, "The connector belongs to another device"); wl_resource_post_error(request_resource, WP_DRM_LEASE_REQUEST_V1_ERROR_WRONG_DEVICE, "The requested connector belongs to another device"); return; } for (size_t i = 0; i < request->n_connectors; ++i) { struct wlr_drm_lease_connector_v1 *tmp = request->connectors[i]; if (connector == tmp) { wlr_log(WLR_ERROR, "The connector has already been requested"); wl_resource_post_error(request_resource, WP_DRM_LEASE_REQUEST_V1_ERROR_DUPLICATE_CONNECTOR, "The connector has already been requested"); return; } } size_t n_connectors = request->n_connectors + 1; struct wlr_drm_lease_connector_v1 **tmp_connectors = realloc(request->connectors, n_connectors * sizeof(struct wlr_drm_lease_connector_v1 *)); if (!tmp_connectors) { wlr_log(WLR_ERROR, "Failed to grow connectors request array"); return; } request->connectors = tmp_connectors; request->connectors[request->n_connectors] = connector; request->n_connectors = n_connectors; } static void drm_lease_request_v1_handle_submit( struct wl_client *client, struct wl_resource *resource, uint32_t id) { uint32_t version = wl_resource_get_version(resource); struct wl_resource *lease_resource = wl_resource_create(client, &wp_drm_lease_v1_interface, version, id); if (!lease_resource) { wlr_log(WLR_ERROR, "Failed to allocate wl_resource"); wl_resource_post_no_memory(resource); return; } wl_resource_set_implementation(lease_resource, &lease_impl, NULL, drm_lease_v1_handle_resource_destroy); struct wlr_drm_lease_request_v1 *request = drm_lease_request_v1_from_resource(resource); if (!request) { wlr_log(WLR_DEBUG, "Request has been destroyed"); wp_drm_lease_v1_send_finished(lease_resource); return; } /* Pre-emptively reject invalid lease requests */ if (request->invalid) { wlr_log(WLR_ERROR, "Invalid request"); wp_drm_lease_v1_send_finished(lease_resource); return; } else if (request->n_connectors == 0) { wl_resource_post_error(lease_resource, WP_DRM_LEASE_REQUEST_V1_ERROR_EMPTY_LEASE, "Lease request has no connectors"); return; } for (size_t i = 0; i < request->n_connectors; ++i) { struct wlr_drm_lease_connector_v1 *conn = request->connectors[i]; if (conn->active_lease) { wlr_log(WLR_ERROR, "Failed to create lease, connector %s has " "already been leased", conn->output->name); wp_drm_lease_v1_send_finished(lease_resource); return; } } request->lease_resource = lease_resource; wl_signal_emit_mutable(&request->device->manager->events.request, request); /* If the compositor didn't act upon the request, reject it */ if (!request->invalid && wl_resource_get_user_data(lease_resource) == NULL) { wlr_drm_lease_request_v1_reject(request); } /* Request is done */ wl_resource_destroy(resource); } static struct wp_drm_lease_request_v1_interface lease_request_impl = { .request_connector = drm_lease_request_v1_handle_request_connector, .submit = drm_lease_request_v1_handle_submit, }; static void drm_lease_device_v1_handle_resource_destroy( struct wl_resource *resource) { wl_list_remove(wl_resource_get_link(resource)); } static void drm_lease_device_v1_handle_release( struct wl_client *client, struct wl_resource *resource) { wp_drm_lease_device_v1_send_released(resource); wl_resource_destroy(resource); } static void drm_lease_device_v1_handle_create_lease_request( struct wl_client *client, struct wl_resource *resource, uint32_t id) { uint32_t version = wl_resource_get_version(resource); struct wl_resource *request_resource = wl_resource_create(client, &wp_drm_lease_request_v1_interface, version, id); if (!request_resource) { wlr_log(WLR_ERROR, "Failed to allocate wl_resource"); return; } wl_resource_set_implementation(request_resource, &lease_request_impl, NULL, drm_lease_request_v1_handle_resource_destroy); struct wlr_drm_lease_device_v1 *device = drm_lease_device_v1_from_resource(resource); if (!device) { wlr_log(WLR_DEBUG, "Failed to create lease request, " "wlr_drm_lease_device_v1 has been destroyed"); return; } struct wlr_drm_lease_request_v1 *req = calloc(1, sizeof(*req)); if (!req) { wlr_log(WLR_ERROR, "Failed to allocate wlr_drm_lease_request_v1"); wl_resource_post_no_memory(resource); return; } wlr_log(WLR_DEBUG, "Created request %p", req); req->device = device; req->resource = request_resource; req->connectors = NULL; req->n_connectors = 0; wl_resource_set_user_data(request_resource, req); wl_list_insert(&device->requests, &req->link); } static struct wp_drm_lease_device_v1_interface lease_device_impl = { .release = drm_lease_device_v1_handle_release, .create_lease_request = drm_lease_device_v1_handle_create_lease_request, }; static void drm_connector_v1_handle_resource_destroy( struct wl_resource *resource) { wl_list_remove(wl_resource_get_link(resource)); } static void drm_connector_v1_handle_destroy( struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static struct wp_drm_lease_connector_v1_interface lease_connector_impl = { .destroy = drm_connector_v1_handle_destroy, }; static void drm_lease_connector_v1_send_to_client( struct wlr_drm_lease_connector_v1 *connector, struct wl_resource *resource) { if (connector->active_lease) { return; } struct wl_client *client = wl_resource_get_client(resource); uint32_t version = wl_resource_get_version(resource); struct wl_resource *connector_resource = wl_resource_create(client, &wp_drm_lease_connector_v1_interface, version, 0); if (!connector_resource) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(connector_resource, &lease_connector_impl, connector, drm_connector_v1_handle_resource_destroy); wp_drm_lease_device_v1_send_connector(resource, connector_resource); struct wlr_output *output = connector->output; wp_drm_lease_connector_v1_send_name(connector_resource, output->name); // TODO: re-send the description when it's updated wp_drm_lease_connector_v1_send_description(connector_resource, output->description); wp_drm_lease_connector_v1_send_connector_id(connector_resource, wlr_drm_connector_get_id(output)); wp_drm_lease_connector_v1_send_done(connector_resource); wl_list_insert(&connector->resources, wl_resource_get_link(connector_resource)); } static void lease_device_bind(struct wl_client *wl_client, void *data, uint32_t version, uint32_t id) { struct wl_resource *device_resource = wl_resource_create(wl_client, &wp_drm_lease_device_v1_interface, version, id); if (!device_resource) { wl_client_post_no_memory(wl_client); return; } wl_resource_set_implementation(device_resource, &lease_device_impl, NULL, drm_lease_device_v1_handle_resource_destroy); struct wlr_drm_lease_device_v1 *device = data; if (!device) { wlr_log(WLR_DEBUG, "Failed to bind lease device, " "the wlr_drm_lease_device_v1 has been destroyed"); return; } wl_resource_set_user_data(device_resource, device); int fd = wlr_drm_backend_get_non_master_fd(device->backend); if (fd < 0) { wlr_log(WLR_ERROR, "Unable to get read only DRM fd for leasing"); wl_client_post_no_memory(wl_client); return; } wp_drm_lease_device_v1_send_drm_fd(device_resource, fd); close(fd); wl_list_insert(&device->resources, wl_resource_get_link(device_resource)); struct wlr_drm_lease_connector_v1 *connector; wl_list_for_each(connector, &device->connectors, link) { drm_lease_connector_v1_send_to_client(connector, device_resource); } wp_drm_lease_device_v1_send_done(device_resource); } static void handle_output_destroy(struct wl_listener *listener, void *data) { struct wlr_drm_lease_connector_v1 *conn = wl_container_of(listener, conn, destroy); wlr_log(WLR_DEBUG, "Handle destruction of output %s", conn->output->name); wlr_drm_lease_v1_manager_withdraw_output(conn->device->manager, conn->output); } bool wlr_drm_lease_v1_manager_offer_output( struct wlr_drm_lease_v1_manager *manager, struct wlr_output *output) { assert(manager && output); assert(wlr_output_is_drm(output)); wlr_log(WLR_DEBUG, "Offering output %s", output->name); struct wlr_drm_lease_device_v1 *device = NULL, *tmp_device; wl_list_for_each(tmp_device, &manager->devices, link) { if (tmp_device->backend == output->backend) { device = tmp_device; break; } } if (!device) { wlr_log(WLR_ERROR, "No wlr_drm_lease_device_v1 associated with the " "offered output"); return false; } struct wlr_drm_lease_connector_v1 *tmp_connector; wl_list_for_each(tmp_connector, &device->connectors, link) { if (tmp_connector->output == output) { wlr_log(WLR_ERROR, "Output %s has already been offered", output->name); return false; } } struct wlr_drm_lease_connector_v1 *connector = calloc(1, sizeof(*connector)); if (!connector) { wlr_log(WLR_ERROR, "Failed to allocate wlr_drm_lease_connector_v1"); return false; } connector->output = output; connector->device = device; connector->destroy.notify = handle_output_destroy; wl_signal_add(&output->events.destroy, &connector->destroy); wl_list_init(&connector->resources); wl_list_insert(&device->connectors, &connector->link); struct wl_resource *resource; wl_resource_for_each(resource, &device->resources) { drm_lease_connector_v1_send_to_client(connector, resource); wp_drm_lease_device_v1_send_done(resource); } return true; } void wlr_drm_lease_v1_manager_withdraw_output( struct wlr_drm_lease_v1_manager *manager, struct wlr_output *output) { assert(manager && output); wlr_log(WLR_DEBUG, "Withdrawing output %s", output->name); struct wlr_drm_lease_device_v1 *device = NULL, *tmp_device; wl_list_for_each(tmp_device, &manager->devices, link) { if (tmp_device->backend == output->backend) { device = tmp_device; break; } } if (!device) { wlr_log(WLR_ERROR, "No wlr_drm_lease_device_v1 associated with the " "given output"); return; } struct wlr_drm_lease_connector_v1 *connector = NULL, *tmp_conn; wl_list_for_each(tmp_conn, &device->connectors, link) { if (tmp_conn->output == output) { connector = tmp_conn; break; } } if (!connector) { wlr_log(WLR_DEBUG, "No wlr_drm_connector_v1 associated with the given " "output"); return; } drm_lease_connector_v1_destroy(connector); } static void handle_backend_destroy(struct wl_listener *listener, void *data) { struct wlr_drm_lease_device_v1 *device = wl_container_of(listener, device, backend_destroy); drm_lease_device_v1_destroy(device); } static void drm_lease_device_v1_create(struct wlr_drm_lease_v1_manager *manager, struct wlr_backend *backend) { struct wlr_drm_backend *drm_backend = get_drm_backend_from_backend(backend); // Make sure we can get a non-master FD for the DRM backend. On some setups // we don't have the permission for this. int fd = wlr_drm_backend_get_non_master_fd(backend); if (fd < 0) { wlr_log(WLR_INFO, "Skipping %s: failed to get read-only DRM FD", drm_backend->name); return; } close(fd); wlr_log(WLR_DEBUG, "Creating wlr_drm_lease_device_v1 for %s", drm_backend->name); struct wlr_drm_lease_device_v1 *lease_device = calloc(1, sizeof(*lease_device)); if (!lease_device) { wlr_log(WLR_ERROR, "Failed to allocate wlr_drm_lease_device_v1"); return; } lease_device->manager = manager; lease_device->backend = backend; wl_list_init(&lease_device->resources); wl_list_init(&lease_device->connectors); wl_list_init(&lease_device->requests); wl_list_init(&lease_device->leases); wl_list_init(&lease_device->link); lease_device->global = wl_global_create(manager->display, &wp_drm_lease_device_v1_interface, DRM_LEASE_DEVICE_V1_VERSION, lease_device, lease_device_bind); if (!lease_device->global) { wlr_log(WLR_ERROR, "Failed to allocate wp_drm_lease_device_v1 global"); free(lease_device); return; } lease_device->backend_destroy.notify = handle_backend_destroy; wl_signal_add(&backend->events.destroy, &lease_device->backend_destroy); wl_list_insert(&manager->devices, &lease_device->link); } static void multi_backend_cb(struct wlr_backend *backend, void *data) { if (!wlr_backend_is_drm(backend)) { return; } struct wlr_drm_lease_v1_manager *manager = data; drm_lease_device_v1_create(manager, backend); } static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_drm_lease_v1_manager *manager = wl_container_of(listener, manager, display_destroy); wlr_log(WLR_DEBUG, "Destroying wlr_drm_lease_v1_manager"); struct wlr_drm_lease_device_v1 *device, *tmp; wl_list_for_each_safe(device, tmp, &manager->devices, link) { drm_lease_device_v1_destroy(device); } free(manager); } struct wlr_drm_lease_v1_manager *wlr_drm_lease_v1_manager_create( struct wl_display *display, struct wlr_backend *backend) { struct wlr_drm_lease_v1_manager *manager = calloc(1, sizeof(*manager)); if (!manager) { wlr_log(WLR_ERROR, "Failed to allocate wlr_drm_lease_v1_manager"); return NULL; } wl_list_init(&manager->devices); manager->display = display; if (wlr_backend_is_multi(backend)) { /* TODO: handle backends added after the manager is created */ wlr_multi_for_each_backend(backend, multi_backend_cb, manager); } else if (wlr_backend_is_drm(backend)) { drm_lease_device_v1_create(manager, backend); } if (wl_list_empty(&manager->devices)) { wlr_log(WLR_DEBUG, "No DRM backend supplied, failed to create " "wlr_drm_lease_v1_manager"); free(manager); return NULL; } manager->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &manager->display_destroy); wl_signal_init(&manager->events.request); return manager; } wlroots-0.17.1/types/wlr_export_dmabuf_v1.c000066400000000000000000000161771454110342200207620ustar00rootroot00000000000000#include #include #include #include #include #include #include #include "wlr-export-dmabuf-unstable-v1-protocol.h" #define EXPORT_DMABUF_MANAGER_VERSION 1 static const struct zwlr_export_dmabuf_frame_v1_interface frame_impl; static struct wlr_export_dmabuf_frame_v1 *frame_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &zwlr_export_dmabuf_frame_v1_interface, &frame_impl)); return wl_resource_get_user_data(resource); } static void frame_handle_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static const struct zwlr_export_dmabuf_frame_v1_interface frame_impl = { .destroy = frame_handle_destroy, }; static void frame_destroy(struct wlr_export_dmabuf_frame_v1 *frame) { if (frame == NULL) { return; } if (frame->output != NULL) { wlr_output_lock_attach_render(frame->output, false); if (frame->cursor_locked) { wlr_output_lock_software_cursors(frame->output, false); } } wl_list_remove(&frame->link); wl_list_remove(&frame->output_commit.link); wl_list_remove(&frame->output_destroy.link); // Make the frame resource inert wl_resource_set_user_data(frame->resource, NULL); free(frame); } static void frame_handle_resource_destroy(struct wl_resource *resource) { struct wlr_export_dmabuf_frame_v1 *frame = frame_from_resource(resource); frame_destroy(frame); } static void frame_output_handle_commit(struct wl_listener *listener, void *data) { struct wlr_export_dmabuf_frame_v1 *frame = wl_container_of(listener, frame, output_commit); struct wlr_output_event_commit *event = data; if (!(event->state->committed & WLR_OUTPUT_STATE_BUFFER)) { return; } wl_list_remove(&frame->output_commit.link); wl_list_init(&frame->output_commit.link); struct wlr_dmabuf_attributes attribs = {0}; if (!wlr_buffer_get_dmabuf(event->state->buffer, &attribs)) { zwlr_export_dmabuf_frame_v1_send_cancel(frame->resource, ZWLR_EXPORT_DMABUF_FRAME_V1_CANCEL_REASON_TEMPORARY); frame_destroy(frame); return; } uint32_t frame_flags = ZWLR_EXPORT_DMABUF_FRAME_V1_FLAGS_TRANSIENT; uint32_t mod_high = attribs.modifier >> 32; uint32_t mod_low = attribs.modifier & 0xFFFFFFFF; zwlr_export_dmabuf_frame_v1_send_frame(frame->resource, attribs.width, attribs.height, 0, 0, 0, frame_flags, attribs.format, mod_high, mod_low, attribs.n_planes); for (int i = 0; i < attribs.n_planes; ++i) { off_t size = lseek(attribs.fd[i], 0, SEEK_END); zwlr_export_dmabuf_frame_v1_send_object(frame->resource, i, attribs.fd[i], size, attribs.offset[i], attribs.stride[i], i); } time_t tv_sec = event->when->tv_sec; uint32_t tv_sec_hi = (sizeof(tv_sec) > 4) ? tv_sec >> 32 : 0; uint32_t tv_sec_lo = tv_sec & 0xFFFFFFFF; zwlr_export_dmabuf_frame_v1_send_ready(frame->resource, tv_sec_hi, tv_sec_lo, event->when->tv_nsec); frame_destroy(frame); } static void frame_output_handle_destroy(struct wl_listener *listener, void *data) { struct wlr_export_dmabuf_frame_v1 *frame = wl_container_of(listener, frame, output_destroy); frame_destroy(frame); } static const struct zwlr_export_dmabuf_manager_v1_interface manager_impl; static struct wlr_export_dmabuf_manager_v1 *manager_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &zwlr_export_dmabuf_manager_v1_interface, &manager_impl)); return wl_resource_get_user_data(resource); } static void manager_handle_capture_output(struct wl_client *client, struct wl_resource *manager_resource, uint32_t id, int32_t overlay_cursor, struct wl_resource *output_resource) { struct wlr_export_dmabuf_manager_v1 *manager = manager_from_resource(manager_resource); struct wlr_output *output = wlr_output_from_resource(output_resource); struct wlr_export_dmabuf_frame_v1 *frame = calloc(1, sizeof(*frame)); if (frame == NULL) { wl_resource_post_no_memory(manager_resource); return; } frame->manager = manager; wl_list_init(&frame->output_commit.link); wl_list_init(&frame->output_destroy.link); uint32_t version = wl_resource_get_version(manager_resource); frame->resource = wl_resource_create(client, &zwlr_export_dmabuf_frame_v1_interface, version, id); if (frame->resource == NULL) { wl_client_post_no_memory(client); free(frame); return; } wl_resource_set_implementation(frame->resource, &frame_impl, frame, frame_handle_resource_destroy); wl_list_insert(&manager->frames, &frame->link); if (output == NULL || !output->enabled) { zwlr_export_dmabuf_frame_v1_send_cancel(frame->resource, ZWLR_EXPORT_DMABUF_FRAME_V1_CANCEL_REASON_PERMANENT); frame_destroy(frame); return; } frame->output = output; wlr_output_lock_attach_render(frame->output, true); if (overlay_cursor) { wlr_output_lock_software_cursors(frame->output, true); frame->cursor_locked = true; } wl_list_remove(&frame->output_commit.link); wl_signal_add(&output->events.commit, &frame->output_commit); frame->output_commit.notify = frame_output_handle_commit; wl_signal_add(&output->events.destroy, &frame->output_destroy); frame->output_destroy.notify = frame_output_handle_destroy; // Request a frame because we can't assume that the current front buffer is still usable. It may // have been released already, and we shouldn't lock it here because compositors want to render // into the least damaged buffer. wlr_output_update_needs_frame(output); } static void manager_handle_destroy(struct wl_client *client, struct wl_resource *manager_resource) { wl_resource_destroy(manager_resource); } static const struct zwlr_export_dmabuf_manager_v1_interface manager_impl = { .capture_output = manager_handle_capture_output, .destroy = manager_handle_destroy, }; static void manager_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id) { struct wlr_export_dmabuf_manager_v1 *manager = data; struct wl_resource *resource = wl_resource_create(client, &zwlr_export_dmabuf_manager_v1_interface, version, id); if (resource == NULL) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(resource, &manager_impl, manager, NULL); } static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_export_dmabuf_manager_v1 *manager = wl_container_of(listener, manager, display_destroy); wl_signal_emit_mutable(&manager->events.destroy, manager); wl_list_remove(&manager->display_destroy.link); wl_global_destroy(manager->global); free(manager); } struct wlr_export_dmabuf_manager_v1 *wlr_export_dmabuf_manager_v1_create( struct wl_display *display) { struct wlr_export_dmabuf_manager_v1 *manager = calloc(1, sizeof(*manager)); if (manager == NULL) { return NULL; } wl_list_init(&manager->frames); wl_signal_init(&manager->events.destroy); manager->global = wl_global_create(display, &zwlr_export_dmabuf_manager_v1_interface, EXPORT_DMABUF_MANAGER_VERSION, manager, manager_bind); if (manager->global == NULL) { free(manager); return NULL; } manager->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &manager->display_destroy); return manager; } wlroots-0.17.1/types/wlr_foreign_toplevel_management_v1.c000066400000000000000000000543661454110342200236640ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include "wlr-foreign-toplevel-management-unstable-v1-protocol.h" #define FOREIGN_TOPLEVEL_MANAGEMENT_V1_VERSION 3 static const struct zwlr_foreign_toplevel_handle_v1_interface toplevel_handle_impl; static struct wlr_foreign_toplevel_handle_v1 *toplevel_handle_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &zwlr_foreign_toplevel_handle_v1_interface, &toplevel_handle_impl)); return wl_resource_get_user_data(resource); } static void toplevel_handle_send_maximized_event(struct wl_resource *resource, bool state) { struct wlr_foreign_toplevel_handle_v1 *toplevel = toplevel_handle_from_resource(resource); if (!toplevel) { return; } struct wlr_foreign_toplevel_handle_v1_maximized_event event = { .toplevel = toplevel, .maximized = state, }; wl_signal_emit_mutable(&toplevel->events.request_maximize, &event); } static void foreign_toplevel_handle_set_maximized(struct wl_client *client, struct wl_resource *resource) { toplevel_handle_send_maximized_event(resource, true); } static void foreign_toplevel_handle_unset_maximized(struct wl_client *client, struct wl_resource *resource) { toplevel_handle_send_maximized_event(resource, false); } static void toplevel_send_minimized_event(struct wl_resource *resource, bool state) { struct wlr_foreign_toplevel_handle_v1 *toplevel = toplevel_handle_from_resource(resource); if (!toplevel) { return; } struct wlr_foreign_toplevel_handle_v1_minimized_event event = { .toplevel = toplevel, .minimized = state, }; wl_signal_emit_mutable(&toplevel->events.request_minimize, &event); } static void foreign_toplevel_handle_set_minimized(struct wl_client *client, struct wl_resource *resource) { toplevel_send_minimized_event(resource, true); } static void foreign_toplevel_handle_unset_minimized(struct wl_client *client, struct wl_resource *resource) { toplevel_send_minimized_event(resource, false); } static void toplevel_send_fullscreen_event(struct wl_resource *resource, bool state, struct wl_resource *output_resource) { struct wlr_foreign_toplevel_handle_v1 *toplevel = toplevel_handle_from_resource(resource); if (!toplevel) { return; } struct wlr_output *output = NULL; if (output_resource) { output = wlr_output_from_resource(output_resource); } struct wlr_foreign_toplevel_handle_v1_fullscreen_event event = { .toplevel = toplevel, .fullscreen = state, .output = output, }; wl_signal_emit_mutable(&toplevel->events.request_fullscreen, &event); } static void foreign_toplevel_handle_set_fullscreen(struct wl_client *client, struct wl_resource *resource, struct wl_resource *output) { toplevel_send_fullscreen_event(resource, true, output); } static void foreign_toplevel_handle_unset_fullscreen(struct wl_client *client, struct wl_resource *resource) { toplevel_send_fullscreen_event(resource, false, NULL); } static void foreign_toplevel_handle_activate(struct wl_client *client, struct wl_resource *resource, struct wl_resource *seat) { struct wlr_foreign_toplevel_handle_v1 *toplevel = toplevel_handle_from_resource(resource); if (!toplevel) { return; } struct wlr_seat_client *seat_client = wlr_seat_client_from_resource(seat); if (!seat_client) { return; } struct wlr_foreign_toplevel_handle_v1_activated_event event = { .toplevel = toplevel, .seat = seat_client->seat, }; wl_signal_emit_mutable(&toplevel->events.request_activate, &event); } static void foreign_toplevel_handle_close(struct wl_client *client, struct wl_resource *resource) { struct wlr_foreign_toplevel_handle_v1 *toplevel = toplevel_handle_from_resource(resource); if (!toplevel) { return; } wl_signal_emit_mutable(&toplevel->events.request_close, toplevel); } static void foreign_toplevel_handle_set_rectangle(struct wl_client *client, struct wl_resource *resource, struct wl_resource *surface, int32_t x, int32_t y, int32_t width, int32_t height) { struct wlr_foreign_toplevel_handle_v1 *toplevel = toplevel_handle_from_resource(resource); if (!toplevel) { return; } if (width < 0 || height < 0) { wl_resource_post_error(resource, ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_ERROR_INVALID_RECTANGLE, "invalid rectangle passed to set_rectangle: width/height < 0"); return; } struct wlr_foreign_toplevel_handle_v1_set_rectangle_event event = { .toplevel = toplevel, .surface = wlr_surface_from_resource(surface), .x = x, .y = y, .width = width, .height = height, }; wl_signal_emit_mutable(&toplevel->events.set_rectangle, &event); } static void foreign_toplevel_handle_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static const struct zwlr_foreign_toplevel_handle_v1_interface toplevel_handle_impl = { .set_maximized = foreign_toplevel_handle_set_maximized, .unset_maximized = foreign_toplevel_handle_unset_maximized, .set_minimized = foreign_toplevel_handle_set_minimized, .unset_minimized = foreign_toplevel_handle_unset_minimized, .activate = foreign_toplevel_handle_activate, .close = foreign_toplevel_handle_close, .set_rectangle = foreign_toplevel_handle_set_rectangle, .destroy = foreign_toplevel_handle_destroy, .set_fullscreen = foreign_toplevel_handle_set_fullscreen, .unset_fullscreen = foreign_toplevel_handle_unset_fullscreen, }; static void toplevel_idle_send_done(void *data) { struct wlr_foreign_toplevel_handle_v1 *toplevel = data; struct wl_resource *resource; wl_resource_for_each(resource, &toplevel->resources) { zwlr_foreign_toplevel_handle_v1_send_done(resource); } toplevel->idle_source = NULL; } static void toplevel_update_idle_source( struct wlr_foreign_toplevel_handle_v1 *toplevel) { if (toplevel->idle_source) { return; } toplevel->idle_source = wl_event_loop_add_idle(toplevel->manager->event_loop, toplevel_idle_send_done, toplevel); } void wlr_foreign_toplevel_handle_v1_set_title( struct wlr_foreign_toplevel_handle_v1 *toplevel, const char *title) { free(toplevel->title); toplevel->title = strdup(title); if (toplevel->title == NULL) { wlr_log(WLR_ERROR, "failed to allocate memory for toplevel title"); return; } struct wl_resource *resource; wl_resource_for_each(resource, &toplevel->resources) { zwlr_foreign_toplevel_handle_v1_send_title(resource, title); } toplevel_update_idle_source(toplevel); } void wlr_foreign_toplevel_handle_v1_set_app_id( struct wlr_foreign_toplevel_handle_v1 *toplevel, const char *app_id) { free(toplevel->app_id); toplevel->app_id = strdup(app_id); if (toplevel->app_id == NULL) { wlr_log(WLR_ERROR, "failed to allocate memory for toplevel app_id"); return; } struct wl_resource *resource; wl_resource_for_each(resource, &toplevel->resources) { zwlr_foreign_toplevel_handle_v1_send_app_id(resource, app_id); } toplevel_update_idle_source(toplevel); } static void send_output_to_resource(struct wl_resource *resource, struct wlr_output *output, bool enter) { struct wl_client *client = wl_resource_get_client(resource); struct wl_resource *output_resource; wl_resource_for_each(output_resource, &output->resources) { if (wl_resource_get_client(output_resource) == client) { if (enter) { zwlr_foreign_toplevel_handle_v1_send_output_enter(resource, output_resource); } else { zwlr_foreign_toplevel_handle_v1_send_output_leave(resource, output_resource); } } } } static void toplevel_send_output(struct wlr_foreign_toplevel_handle_v1 *toplevel, struct wlr_output *output, bool enter) { struct wl_resource *resource; wl_resource_for_each(resource, &toplevel->resources) { send_output_to_resource(resource, output, enter); } toplevel_update_idle_source(toplevel); } static void toplevel_handle_output_bind(struct wl_listener *listener, void *data) { struct wlr_foreign_toplevel_handle_v1_output *toplevel_output = wl_container_of(listener, toplevel_output, output_bind); struct wlr_output_event_bind *event = data; struct wl_client *client = wl_resource_get_client(event->resource); struct wl_resource *resource; wl_resource_for_each(resource, &toplevel_output->toplevel->resources) { if (wl_resource_get_client(resource) == client) { send_output_to_resource(resource, toplevel_output->output, true); } } toplevel_update_idle_source(toplevel_output->toplevel); } static void toplevel_handle_output_destroy(struct wl_listener *listener, void *data) { struct wlr_foreign_toplevel_handle_v1_output *toplevel_output = wl_container_of(listener, toplevel_output, output_destroy); wlr_foreign_toplevel_handle_v1_output_leave(toplevel_output->toplevel, toplevel_output->output); } void wlr_foreign_toplevel_handle_v1_output_enter( struct wlr_foreign_toplevel_handle_v1 *toplevel, struct wlr_output *output) { struct wlr_foreign_toplevel_handle_v1_output *toplevel_output; wl_list_for_each(toplevel_output, &toplevel->outputs, link) { if (toplevel_output->output == output) { return; // we have already sent output_enter event } } toplevel_output = calloc(1, sizeof(*toplevel_output)); if (!toplevel_output) { wlr_log(WLR_ERROR, "failed to allocate memory for toplevel output"); return; } toplevel_output->output = output; toplevel_output->toplevel = toplevel; wl_list_insert(&toplevel->outputs, &toplevel_output->link); toplevel_output->output_bind.notify = toplevel_handle_output_bind; wl_signal_add(&output->events.bind, &toplevel_output->output_bind); toplevel_output->output_destroy.notify = toplevel_handle_output_destroy; wl_signal_add(&output->events.destroy, &toplevel_output->output_destroy); toplevel_send_output(toplevel, output, true); } static void toplevel_output_destroy( struct wlr_foreign_toplevel_handle_v1_output *toplevel_output) { wl_list_remove(&toplevel_output->link); wl_list_remove(&toplevel_output->output_bind.link); wl_list_remove(&toplevel_output->output_destroy.link); free(toplevel_output); } void wlr_foreign_toplevel_handle_v1_output_leave( struct wlr_foreign_toplevel_handle_v1 *toplevel, struct wlr_output *output) { struct wlr_foreign_toplevel_handle_v1_output *toplevel_output_iterator; struct wlr_foreign_toplevel_handle_v1_output *toplevel_output = NULL; wl_list_for_each(toplevel_output_iterator, &toplevel->outputs, link) { if (toplevel_output_iterator->output == output) { toplevel_output = toplevel_output_iterator; break; } } if (toplevel_output) { toplevel_send_output(toplevel, output, false); toplevel_output_destroy(toplevel_output); } else { // XXX: log an error? crash? } } static bool fill_array_from_toplevel_state(struct wl_array *array, uint32_t state) { if (state & WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MAXIMIZED) { uint32_t *index = wl_array_add(array, sizeof(uint32_t)); if (index == NULL) { return false; } *index = ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MAXIMIZED; } if (state & WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MINIMIZED) { uint32_t *index = wl_array_add(array, sizeof(uint32_t)); if (index == NULL) { return false; } *index = ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MINIMIZED; } if (state & WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED) { uint32_t *index = wl_array_add(array, sizeof(uint32_t)); if (index == NULL) { return false; } *index = ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED; } if (state & WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN) { uint32_t *index = wl_array_add(array, sizeof(uint32_t)); if (index == NULL) { return false; } *index = ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN; } return true; } static void toplevel_send_state(struct wlr_foreign_toplevel_handle_v1 *toplevel) { struct wl_array states; wl_array_init(&states); bool r = fill_array_from_toplevel_state(&states, toplevel->state); if (!r) { struct wl_resource *resource; wl_resource_for_each(resource, &toplevel->resources) { wl_resource_post_no_memory(resource); } wl_array_release(&states); return; } struct wl_resource *resource; wl_resource_for_each(resource, &toplevel->resources) { zwlr_foreign_toplevel_handle_v1_send_state(resource, &states); } wl_array_release(&states); toplevel_update_idle_source(toplevel); } void wlr_foreign_toplevel_handle_v1_set_maximized( struct wlr_foreign_toplevel_handle_v1 *toplevel, bool maximized) { if (maximized == !!(toplevel->state & WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MAXIMIZED)) { return; } if (maximized) { toplevel->state |= WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MAXIMIZED; } else { toplevel->state &= ~WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MAXIMIZED; } toplevel_send_state(toplevel); } void wlr_foreign_toplevel_handle_v1_set_minimized( struct wlr_foreign_toplevel_handle_v1 *toplevel, bool minimized) { if (minimized == !!(toplevel->state & WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MINIMIZED)) { return; } if (minimized) { toplevel->state |= WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MINIMIZED; } else { toplevel->state &= ~WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MINIMIZED; } toplevel_send_state(toplevel); } void wlr_foreign_toplevel_handle_v1_set_activated( struct wlr_foreign_toplevel_handle_v1 *toplevel, bool activated) { if (activated == !!(toplevel->state & WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED)) { return; } if (activated) { toplevel->state |= WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED; } else { toplevel->state &= ~WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED; } toplevel_send_state(toplevel); } void wlr_foreign_toplevel_handle_v1_set_fullscreen( struct wlr_foreign_toplevel_handle_v1 * toplevel, bool fullscreen) { if (fullscreen == !!(toplevel->state & WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN)) { return; } if (fullscreen) { toplevel->state |= WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN; } else { toplevel->state &= ~WLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN; } toplevel_send_state(toplevel); } static void toplevel_resource_send_parent( struct wl_resource *toplevel_resource, struct wlr_foreign_toplevel_handle_v1 *parent) { if (wl_resource_get_version(toplevel_resource) < ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_PARENT_SINCE_VERSION) { return; } struct wl_client *client = wl_resource_get_client(toplevel_resource); struct wl_resource *parent_resource = NULL; if (parent) { parent_resource = wl_resource_find_for_client(&parent->resources, client); if (!parent_resource) { /* don't send an event if this client destroyed the parent handle */ return; } } zwlr_foreign_toplevel_handle_v1_send_parent(toplevel_resource, parent_resource); } void wlr_foreign_toplevel_handle_v1_set_parent( struct wlr_foreign_toplevel_handle_v1 *toplevel, struct wlr_foreign_toplevel_handle_v1 *parent) { if (parent == toplevel->parent) { /* only send parent event to the clients if there was a change */ return; } struct wl_resource *toplevel_resource, *tmp; wl_resource_for_each_safe(toplevel_resource, tmp, &toplevel->resources) { toplevel_resource_send_parent(toplevel_resource, parent); } toplevel->parent = parent; toplevel_update_idle_source(toplevel); } void wlr_foreign_toplevel_handle_v1_destroy( struct wlr_foreign_toplevel_handle_v1 *toplevel) { if (!toplevel) { return; } wl_signal_emit_mutable(&toplevel->events.destroy, toplevel); struct wl_resource *resource, *tmp; wl_resource_for_each_safe(resource, tmp, &toplevel->resources) { zwlr_foreign_toplevel_handle_v1_send_closed(resource); wl_resource_set_user_data(resource, NULL); wl_list_remove(wl_resource_get_link(resource)); wl_list_init(wl_resource_get_link(resource)); } struct wlr_foreign_toplevel_handle_v1_output *toplevel_output, *tmp2; wl_list_for_each_safe(toplevel_output, tmp2, &toplevel->outputs, link) { toplevel_output_destroy(toplevel_output); } if (toplevel->idle_source) { wl_event_source_remove(toplevel->idle_source); } wl_list_remove(&toplevel->link); /* need to ensure no other toplevels hold a pointer to this one as * a parent, so that a later call to foreign_toplevel_manager_bind() * will not result in a segfault */ struct wlr_foreign_toplevel_handle_v1 *tl, *tmp3; wl_list_for_each_safe(tl, tmp3, &toplevel->manager->toplevels, link) { if (tl->parent == toplevel) { /* Note: we send a parent signal to all clients in this case; * the caller should first destroy the child handles if it * wishes to avoid this behavior. */ wlr_foreign_toplevel_handle_v1_set_parent(tl, NULL); } } free(toplevel->title); free(toplevel->app_id); free(toplevel); } static void foreign_toplevel_resource_destroy(struct wl_resource *resource) { wl_list_remove(wl_resource_get_link(resource)); } static struct wl_resource *create_toplevel_resource_for_resource( struct wlr_foreign_toplevel_handle_v1 *toplevel, struct wl_resource *manager_resource) { struct wl_client *client = wl_resource_get_client(manager_resource); struct wl_resource *resource = wl_resource_create(client, &zwlr_foreign_toplevel_handle_v1_interface, wl_resource_get_version(manager_resource), 0); if (!resource) { wl_client_post_no_memory(client); return NULL; } wl_resource_set_implementation(resource, &toplevel_handle_impl, toplevel, foreign_toplevel_resource_destroy); wl_list_insert(&toplevel->resources, wl_resource_get_link(resource)); zwlr_foreign_toplevel_manager_v1_send_toplevel(manager_resource, resource); return resource; } struct wlr_foreign_toplevel_handle_v1 * wlr_foreign_toplevel_handle_v1_create( struct wlr_foreign_toplevel_manager_v1 *manager) { struct wlr_foreign_toplevel_handle_v1 *toplevel = calloc(1, sizeof(*toplevel)); if (!toplevel) { return NULL; } wl_list_insert(&manager->toplevels, &toplevel->link); toplevel->manager = manager; wl_list_init(&toplevel->resources); wl_list_init(&toplevel->outputs); wl_signal_init(&toplevel->events.request_maximize); wl_signal_init(&toplevel->events.request_minimize); wl_signal_init(&toplevel->events.request_activate); wl_signal_init(&toplevel->events.request_fullscreen); wl_signal_init(&toplevel->events.request_close); wl_signal_init(&toplevel->events.set_rectangle); wl_signal_init(&toplevel->events.destroy); struct wl_resource *manager_resource, *tmp; wl_resource_for_each_safe(manager_resource, tmp, &manager->resources) { create_toplevel_resource_for_resource(toplevel, manager_resource); } return toplevel; } static const struct zwlr_foreign_toplevel_manager_v1_interface foreign_toplevel_manager_impl; static void foreign_toplevel_manager_handle_stop(struct wl_client *client, struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &zwlr_foreign_toplevel_manager_v1_interface, &foreign_toplevel_manager_impl)); zwlr_foreign_toplevel_manager_v1_send_finished(resource); wl_resource_destroy(resource); } static const struct zwlr_foreign_toplevel_manager_v1_interface foreign_toplevel_manager_impl = { .stop = foreign_toplevel_manager_handle_stop }; static void foreign_toplevel_manager_resource_destroy( struct wl_resource *resource) { wl_list_remove(wl_resource_get_link(resource)); } static void toplevel_send_details_to_toplevel_resource( struct wlr_foreign_toplevel_handle_v1 *toplevel, struct wl_resource *resource) { if (toplevel->title) { zwlr_foreign_toplevel_handle_v1_send_title(resource, toplevel->title); } if (toplevel->app_id) { zwlr_foreign_toplevel_handle_v1_send_app_id(resource, toplevel->app_id); } struct wlr_foreign_toplevel_handle_v1_output *output; wl_list_for_each(output, &toplevel->outputs, link) { send_output_to_resource(resource, output->output, true); } struct wl_array states; wl_array_init(&states); bool r = fill_array_from_toplevel_state(&states, toplevel->state); if (!r) { wl_resource_post_no_memory(resource); wl_array_release(&states); return; } zwlr_foreign_toplevel_handle_v1_send_state(resource, &states); wl_array_release(&states); toplevel_resource_send_parent(resource, toplevel->parent); zwlr_foreign_toplevel_handle_v1_send_done(resource); } static void foreign_toplevel_manager_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id) { struct wlr_foreign_toplevel_manager_v1 *manager = data; struct wl_resource *resource = wl_resource_create(client, &zwlr_foreign_toplevel_manager_v1_interface, version, id); if (!resource) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(resource, &foreign_toplevel_manager_impl, manager, foreign_toplevel_manager_resource_destroy); wl_list_insert(&manager->resources, wl_resource_get_link(resource)); struct wlr_foreign_toplevel_handle_v1 *toplevel, *tmp; /* First loop: create a handle for all toplevels for all clients. * Separation into two loops avoid the case where a child handle * is created before a parent handle, so the parent relationship * could not be sent to a client. */ wl_list_for_each_safe(toplevel, tmp, &manager->toplevels, link) { create_toplevel_resource_for_resource(toplevel, resource); } /* Second loop: send details about each toplevel. */ wl_list_for_each_safe(toplevel, tmp, &manager->toplevels, link) { struct wl_resource *toplevel_resource = wl_resource_find_for_client(&toplevel->resources, client); toplevel_send_details_to_toplevel_resource(toplevel, toplevel_resource); } } static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_foreign_toplevel_manager_v1 *manager = wl_container_of(listener, manager, display_destroy); wl_signal_emit_mutable(&manager->events.destroy, manager); wl_list_remove(&manager->display_destroy.link); wl_global_destroy(manager->global); free(manager); } struct wlr_foreign_toplevel_manager_v1 *wlr_foreign_toplevel_manager_v1_create( struct wl_display *display) { struct wlr_foreign_toplevel_manager_v1 *manager = calloc(1, sizeof(*manager)); if (!manager) { return NULL; } manager->event_loop = wl_display_get_event_loop(display); manager->global = wl_global_create(display, &zwlr_foreign_toplevel_manager_v1_interface, FOREIGN_TOPLEVEL_MANAGEMENT_V1_VERSION, manager, foreign_toplevel_manager_bind); if (!manager->global) { free(manager); return NULL; } wl_signal_init(&manager->events.destroy); wl_list_init(&manager->resources); wl_list_init(&manager->toplevels); manager->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &manager->display_destroy); return manager; } wlroots-0.17.1/types/wlr_fractional_scale_v1.c000066400000000000000000000141061454110342200214020ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include "fractional-scale-v1-protocol.h" #define FRACTIONAL_SCALE_VERSION 1 struct wlr_fractional_scale_v1 { struct wl_resource *resource; struct wlr_addon addon; // Used for dummy objects to store scale double scale; }; static const struct wp_fractional_scale_v1_interface fractional_scale_interface; static struct wlr_fractional_scale_v1 *fractional_scale_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &wp_fractional_scale_v1_interface, &fractional_scale_interface)); return wl_resource_get_user_data(resource); } static const struct wp_fractional_scale_manager_v1_interface scale_manager_interface; static void fractional_scale_destroy(struct wlr_fractional_scale_v1 *info) { if (info == NULL) { return; } // If this is a dummy object then we do not have a wl_resource if (info->resource != NULL) { wl_resource_set_user_data(info->resource, NULL); } wlr_addon_finish(&info->addon); free(info); } static void fractional_scale_handle_resource_destroy(struct wl_resource *resource) { struct wlr_fractional_scale_v1 *info = fractional_scale_from_resource(resource); fractional_scale_destroy(info); } static void fractional_scale_handle_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static const struct wp_fractional_scale_v1_interface fractional_scale_interface = { .destroy = fractional_scale_handle_destroy, }; static void fractional_scale_addon_destroy(struct wlr_addon *addon) { struct wlr_fractional_scale_v1 *info = wl_container_of(addon, info, addon); fractional_scale_destroy(info); } static const struct wlr_addon_interface fractional_scale_addon_impl = { .name = "wlr_fractional_scale_v1", .destroy = fractional_scale_addon_destroy, }; static uint32_t double_to_v120(double d) { return round(d * 120); } void wlr_fractional_scale_v1_notify_scale(struct wlr_surface *surface, double scale) { struct wlr_addon *addon = wlr_addon_find(&surface->addons, NULL, &fractional_scale_addon_impl); if (addon == NULL) { // Create a dummy object to store the scale struct wlr_fractional_scale_v1 *info = calloc(1, sizeof(*info)); if (info == NULL) { return; } wlr_addon_init(&info->addon, &surface->addons, NULL, &fractional_scale_addon_impl); info->scale = scale; return; } struct wlr_fractional_scale_v1 *info = wl_container_of(addon, info, addon); if (info->scale == scale) { return; } info->scale = scale; if (!info->resource) { // Update existing dummy object return; } wp_fractional_scale_v1_send_preferred_scale(info->resource, double_to_v120(scale)); } static void handle_get_fractional_scale(struct wl_client *client, struct wl_resource *mgr_resource, uint32_t id, struct wl_resource *surface_resource) { struct wlr_surface *surface = wlr_surface_from_resource(surface_resource); struct wlr_fractional_scale_v1 *info = NULL; // If no fractional_scale object had been created but scale had been set, then // there will be a dummy object for us to fill out with a resource. Check if // that's the case. struct wlr_addon *addon = wlr_addon_find(&surface->addons, NULL, &fractional_scale_addon_impl); if (addon != NULL) { info = wl_container_of(addon, info, addon); if (info->resource != NULL) { wl_resource_post_error(mgr_resource, WP_FRACTIONAL_SCALE_MANAGER_V1_ERROR_FRACTIONAL_SCALE_EXISTS, "a surface scale object for that surface already exists"); return; } } if (info == NULL) { info = calloc(1, sizeof(*info)); if (info == NULL) { wl_client_post_no_memory(client); return; } wlr_addon_init(&info->addon, &surface->addons, NULL, &fractional_scale_addon_impl); } uint32_t version = wl_resource_get_version(mgr_resource); info->resource = wl_resource_create(client, &wp_fractional_scale_v1_interface, version, id); if (info->resource == NULL) { wl_client_post_no_memory(client); fractional_scale_destroy(info); return; } wl_resource_set_implementation(info->resource, &fractional_scale_interface, info, fractional_scale_handle_resource_destroy); if (info->scale != 0) { wp_fractional_scale_v1_send_preferred_scale(info->resource, double_to_v120(info->scale)); } } static void fractional_scale_manager_handle_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static const struct wp_fractional_scale_manager_v1_interface scale_manager_interface = { .destroy = fractional_scale_manager_handle_destroy, .get_fractional_scale = handle_get_fractional_scale, }; static void fractional_scale_manager_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id) { struct wlr_fractional_scale_manager_v1 *mgr = data; struct wl_resource *resource = wl_resource_create(client, &wp_fractional_scale_manager_v1_interface, version, id); if (resource == NULL) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(resource, &scale_manager_interface, mgr, NULL); } static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_fractional_scale_manager_v1 *mgr = wl_container_of(listener, mgr, display_destroy); wl_signal_emit_mutable(&mgr->events.destroy, NULL); assert(wl_list_empty(&mgr->events.destroy.listener_list)); wl_list_remove(&mgr->display_destroy.link); free(mgr); } struct wlr_fractional_scale_manager_v1 *wlr_fractional_scale_manager_v1_create( struct wl_display *display, uint32_t version) { assert(version <= FRACTIONAL_SCALE_VERSION); struct wlr_fractional_scale_manager_v1 *mgr = calloc(1, sizeof(*mgr)); if (mgr == NULL) { return NULL; } mgr->global = wl_global_create(display, &wp_fractional_scale_manager_v1_interface, version, mgr, fractional_scale_manager_bind); if (mgr->global == NULL) { free(mgr); return NULL; } mgr->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &mgr->display_destroy); wl_signal_init(&mgr->events.destroy); return mgr; } wlroots-0.17.1/types/wlr_fullscreen_shell_v1.c000066400000000000000000000103721454110342200214430ustar00rootroot00000000000000#include #include #include #include #include #include #define FULLSCREEN_SHELL_VERSION 1 static const struct zwp_fullscreen_shell_v1_interface shell_impl; static void shell_handle_release(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static struct wlr_fullscreen_shell_v1 *shell_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &zwp_fullscreen_shell_v1_interface, &shell_impl)); return wl_resource_get_user_data(resource); } static void fullscreen_shell_surface_handle_commit(struct wlr_surface *surface) { if (wlr_surface_has_buffer(surface)) { wlr_surface_map(surface); } } static const struct wlr_surface_role fullscreen_shell_surface_role = { .name = "zwp_fullscreen_shell_v1-surface", .no_object = true, .commit = fullscreen_shell_surface_handle_commit, }; static void shell_handle_present_surface(struct wl_client *client, struct wl_resource *shell_resource, struct wl_resource *surface_resource, uint32_t method, struct wl_resource *output_resource) { struct wlr_fullscreen_shell_v1 *shell = shell_from_resource(shell_resource); struct wlr_surface *surface = NULL; if (surface_resource != NULL) { surface = wlr_surface_from_resource(surface_resource); } struct wlr_output *output = NULL; if (output_resource != NULL) { output = wlr_output_from_resource(output_resource); } if (!wlr_surface_set_role(surface, &fullscreen_shell_surface_role, shell_resource, ZWP_FULLSCREEN_SHELL_V1_ERROR_ROLE)) { return; } struct wlr_fullscreen_shell_v1_present_surface_event event = { .client = client, .surface = surface, .method = method, .output = output, }; wl_signal_emit_mutable(&shell->events.present_surface, &event); } static void shell_handle_present_surface_for_mode(struct wl_client *client, struct wl_resource *shell_resource, struct wl_resource *surface_resource, struct wl_resource *output_resource, int32_t framerate, uint32_t feedback_id) { struct wlr_surface *surface = wlr_surface_from_resource(surface_resource); if (!wlr_surface_set_role(surface, &fullscreen_shell_surface_role, shell_resource, ZWP_FULLSCREEN_SHELL_V1_ERROR_ROLE)) { return; } uint32_t version = wl_resource_get_version(shell_resource); struct wl_resource *feedback_resource = wl_resource_create(client, NULL, version, feedback_id); if (feedback_resource == NULL) { wl_resource_post_no_memory(shell_resource); return; } // TODO: add support for mode switch zwp_fullscreen_shell_mode_feedback_v1_send_mode_failed(feedback_resource); wl_resource_destroy(feedback_resource); } static const struct zwp_fullscreen_shell_v1_interface shell_impl = { .release = shell_handle_release, .present_surface = shell_handle_present_surface, .present_surface_for_mode = shell_handle_present_surface_for_mode, }; static void shell_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id) { struct wlr_fullscreen_shell_v1 *shell = data; struct wl_resource *resource = wl_resource_create(client, &zwp_fullscreen_shell_v1_interface, version, id); if (resource == NULL) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(resource, &shell_impl, shell, NULL); } static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_fullscreen_shell_v1 *shell = wl_container_of(listener, shell, display_destroy); wl_signal_emit_mutable(&shell->events.destroy, shell); wl_list_remove(&shell->display_destroy.link); wl_global_destroy(shell->global); free(shell); } struct wlr_fullscreen_shell_v1 *wlr_fullscreen_shell_v1_create( struct wl_display *display) { struct wlr_fullscreen_shell_v1 *shell = calloc(1, sizeof(*shell)); if (shell == NULL) { return NULL; } wl_signal_init(&shell->events.destroy); wl_signal_init(&shell->events.present_surface); shell->global = wl_global_create(display, &zwp_fullscreen_shell_v1_interface, FULLSCREEN_SHELL_VERSION, shell, shell_bind); if (shell->global == NULL) { free(shell); return NULL; } shell->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &shell->display_destroy); return shell; } wlroots-0.17.1/types/wlr_gamma_control_v1.c000066400000000000000000000214341454110342200207350ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include #include #include "wlr-gamma-control-unstable-v1-protocol.h" #define GAMMA_CONTROL_MANAGER_V1_VERSION 1 static void gamma_control_handle_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static void gamma_control_destroy(struct wlr_gamma_control_v1 *gamma_control) { if (gamma_control == NULL) { return; } struct wlr_gamma_control_manager_v1 *manager = gamma_control->manager; struct wlr_output *output = gamma_control->output; wl_resource_set_user_data(gamma_control->resource, NULL); wl_list_remove(&gamma_control->output_destroy_listener.link); wl_list_remove(&gamma_control->link); free(gamma_control->table); free(gamma_control); struct wlr_gamma_control_manager_v1_set_gamma_event event = { .output = output, }; wl_signal_emit_mutable(&manager->events.set_gamma, &event); } static const struct zwlr_gamma_control_v1_interface gamma_control_impl; static struct wlr_gamma_control_v1 *gamma_control_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &zwlr_gamma_control_v1_interface, &gamma_control_impl)); return wl_resource_get_user_data(resource); } static void gamma_control_handle_resource_destroy(struct wl_resource *resource) { struct wlr_gamma_control_v1 *gamma_control = gamma_control_from_resource(resource); gamma_control_destroy(gamma_control); } static void gamma_control_handle_output_destroy(struct wl_listener *listener, void *data) { struct wlr_gamma_control_v1 *gamma_control = wl_container_of(listener, gamma_control, output_destroy_listener); gamma_control_destroy(gamma_control); } static void gamma_control_handle_set_gamma(struct wl_client *client, struct wl_resource *gamma_control_resource, int fd) { struct wlr_gamma_control_v1 *gamma_control = gamma_control_from_resource(gamma_control_resource); if (gamma_control == NULL) { goto error_fd; } uint32_t ramp_size = wlr_output_get_gamma_size(gamma_control->output); size_t table_size = ramp_size * 3 * sizeof(uint16_t); // Refuse to block when reading int fd_flags = fcntl(fd, F_GETFL, 0); if (fd_flags == -1) { wlr_log_errno(WLR_ERROR, "failed to get FD flags"); wlr_gamma_control_v1_send_failed_and_destroy(gamma_control); goto error_fd; } if (fcntl(fd, F_SETFL, fd_flags | O_NONBLOCK) == -1) { wlr_log_errno(WLR_ERROR, "failed to set FD flags"); wlr_gamma_control_v1_send_failed_and_destroy(gamma_control); goto error_fd; } // Use the heap since gamma tables can be large uint16_t *table = malloc(table_size); if (table == NULL) { wl_resource_post_no_memory(gamma_control_resource); goto error_fd; } ssize_t n_read = pread(fd, table, table_size, 0); if (n_read < 0) { wlr_log_errno(WLR_ERROR, "failed to read gamma table"); wlr_gamma_control_v1_send_failed_and_destroy(gamma_control); goto error_table; } else if ((size_t)n_read != table_size) { wl_resource_post_error(gamma_control_resource, ZWLR_GAMMA_CONTROL_V1_ERROR_INVALID_GAMMA, "The gamma ramps don't have the correct size"); goto error_table; } close(fd); fd = -1; free(gamma_control->table); gamma_control->table = table; gamma_control->ramp_size = ramp_size; struct wlr_gamma_control_manager_v1_set_gamma_event event = { .output = gamma_control->output, .control = gamma_control, }; wl_signal_emit_mutable(&gamma_control->manager->events.set_gamma, &event); return; error_table: free(table); error_fd: close(fd); } static const struct zwlr_gamma_control_v1_interface gamma_control_impl = { .destroy = gamma_control_handle_destroy, .set_gamma = gamma_control_handle_set_gamma, }; static const struct zwlr_gamma_control_manager_v1_interface gamma_control_manager_impl; static struct wlr_gamma_control_manager_v1 *gamma_control_manager_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &zwlr_gamma_control_manager_v1_interface, &gamma_control_manager_impl)); return wl_resource_get_user_data(resource); } static void gamma_control_manager_get_gamma_control(struct wl_client *client, struct wl_resource *manager_resource, uint32_t id, struct wl_resource *output_resource) { struct wlr_gamma_control_manager_v1 *manager = gamma_control_manager_from_resource(manager_resource); struct wlr_output *output = wlr_output_from_resource(output_resource); uint32_t version = wl_resource_get_version(manager_resource); struct wl_resource *resource = wl_resource_create(client, &zwlr_gamma_control_v1_interface, version, id); if (resource == NULL) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(resource, &gamma_control_impl, NULL, gamma_control_handle_resource_destroy); if (output == NULL) { zwlr_gamma_control_v1_send_failed(resource); return; } size_t gamma_size = wlr_output_get_gamma_size(output); if (gamma_size == 0) { zwlr_gamma_control_v1_send_failed(resource); return; } if (wlr_gamma_control_manager_v1_get_control(manager, output) != NULL) { zwlr_gamma_control_v1_send_failed(resource); return; } struct wlr_gamma_control_v1 *gamma_control = calloc(1, sizeof(*gamma_control)); if (gamma_control == NULL) { wl_client_post_no_memory(client); return; } gamma_control->output = output; gamma_control->manager = manager; gamma_control->resource = resource; wl_resource_set_user_data(resource, gamma_control); wl_signal_add(&output->events.destroy, &gamma_control->output_destroy_listener); gamma_control->output_destroy_listener.notify = gamma_control_handle_output_destroy; wl_list_insert(&manager->controls, &gamma_control->link); zwlr_gamma_control_v1_send_gamma_size(gamma_control->resource, gamma_size); } static void gamma_control_manager_destroy(struct wl_client *client, struct wl_resource *manager_resource) { wl_resource_destroy(manager_resource); } static const struct zwlr_gamma_control_manager_v1_interface gamma_control_manager_impl = { .get_gamma_control = gamma_control_manager_get_gamma_control, .destroy = gamma_control_manager_destroy, }; static void gamma_control_manager_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id) { struct wlr_gamma_control_manager_v1 *manager = data; struct wl_resource *resource = wl_resource_create(client, &zwlr_gamma_control_manager_v1_interface, version, id); if (resource == NULL) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(resource, &gamma_control_manager_impl, manager, NULL); } static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_gamma_control_manager_v1 *manager = wl_container_of(listener, manager, display_destroy); wl_signal_emit_mutable(&manager->events.destroy, manager); wl_list_remove(&manager->display_destroy.link); wl_global_destroy(manager->global); free(manager); } struct wlr_gamma_control_manager_v1 *wlr_gamma_control_manager_v1_create( struct wl_display *display) { struct wlr_gamma_control_manager_v1 *manager = calloc(1, sizeof(*manager)); if (!manager) { return NULL; } manager->global = wl_global_create(display, &zwlr_gamma_control_manager_v1_interface, GAMMA_CONTROL_MANAGER_V1_VERSION, manager, gamma_control_manager_bind); if (manager->global == NULL) { free(manager); return NULL; } wl_signal_init(&manager->events.destroy); wl_signal_init(&manager->events.set_gamma); wl_list_init(&manager->controls); manager->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &manager->display_destroy); return manager; } struct wlr_gamma_control_v1 *wlr_gamma_control_manager_v1_get_control( struct wlr_gamma_control_manager_v1 *manager, struct wlr_output *output) { struct wlr_gamma_control_v1 *gamma_control; wl_list_for_each(gamma_control, &manager->controls, link) { if (gamma_control->output == output) { return gamma_control; } } return NULL; } bool wlr_gamma_control_v1_apply(struct wlr_gamma_control_v1 *gamma_control, struct wlr_output_state *output_state) { if (gamma_control == NULL || gamma_control->table == NULL) { return wlr_output_state_set_gamma_lut(output_state, 0, NULL, NULL, NULL); } const uint16_t *r = gamma_control->table; const uint16_t *g = gamma_control->table + gamma_control->ramp_size; const uint16_t *b = gamma_control->table + 2 * gamma_control->ramp_size; return wlr_output_state_set_gamma_lut(output_state, gamma_control->ramp_size, r, g, b); } void wlr_gamma_control_v1_send_failed_and_destroy(struct wlr_gamma_control_v1 *gamma_control) { if (gamma_control == NULL) { return; } zwlr_gamma_control_v1_send_failed(gamma_control->resource); gamma_control_destroy(gamma_control); } wlroots-0.17.1/types/wlr_idle_inhibit_v1.c000066400000000000000000000121211454110342200205270ustar00rootroot00000000000000#include #include #include #include #include #include #include #include "idle-inhibit-unstable-v1-protocol.h" static const struct zwp_idle_inhibit_manager_v1_interface idle_inhibit_impl; static const struct zwp_idle_inhibitor_v1_interface idle_inhibitor_impl; static struct wlr_idle_inhibit_manager_v1 * wlr_idle_inhibit_manager_v1_from_resource(struct wl_resource *manager_resource) { assert(wl_resource_instance_of(manager_resource, &zwp_idle_inhibit_manager_v1_interface, &idle_inhibit_impl)); return wl_resource_get_user_data(manager_resource); } static struct wlr_idle_inhibitor_v1 * wlr_idle_inhibitor_v1_from_resource(struct wl_resource *inhibitor_resource) { assert(wl_resource_instance_of(inhibitor_resource, &zwp_idle_inhibitor_v1_interface, &idle_inhibitor_impl)); return wl_resource_get_user_data(inhibitor_resource); } static void idle_inhibitor_v1_destroy(struct wlr_idle_inhibitor_v1 *inhibitor) { if (!inhibitor) { return; } wl_signal_emit_mutable(&inhibitor->events.destroy, inhibitor->surface); wl_resource_set_user_data(inhibitor->resource, NULL); wl_list_remove(&inhibitor->link); wl_list_remove(&inhibitor->surface_destroy.link); free(inhibitor); } static void idle_inhibitor_v1_handle_resource_destroy( struct wl_resource *inhibitor_resource) { struct wlr_idle_inhibitor_v1 *inhibitor = wlr_idle_inhibitor_v1_from_resource(inhibitor_resource); idle_inhibitor_v1_destroy(inhibitor); } static void idle_inhibitor_handle_surface_destroy( struct wl_listener *listener, void *data) { struct wlr_idle_inhibitor_v1 *inhibitor = wl_container_of(listener, inhibitor, surface_destroy); idle_inhibitor_v1_destroy(inhibitor); } static void idle_inhibitor_v1_handle_destroy(struct wl_client *client, struct wl_resource *inhibitor_resource) { wl_resource_destroy(inhibitor_resource); } static const struct zwp_idle_inhibitor_v1_interface idle_inhibitor_impl = { .destroy = idle_inhibitor_v1_handle_destroy, }; static void manager_handle_create_inhibitor(struct wl_client *client, struct wl_resource *manager_resource, uint32_t id, struct wl_resource *surface_resource) { struct wlr_surface *surface = wlr_surface_from_resource(surface_resource); struct wlr_idle_inhibit_manager_v1 *manager = wlr_idle_inhibit_manager_v1_from_resource(manager_resource); struct wlr_idle_inhibitor_v1 *inhibitor = calloc(1, sizeof(*inhibitor)); if (!inhibitor) { wl_client_post_no_memory(client); return; } uint32_t version = wl_resource_get_version(manager_resource); struct wl_resource *inhibitor_resource = wl_resource_create(client, &zwp_idle_inhibitor_v1_interface, version, id); if (!inhibitor_resource) { wl_client_post_no_memory(client); free(inhibitor); return; } inhibitor->resource = inhibitor_resource; inhibitor->surface = surface; wl_signal_init(&inhibitor->events.destroy); inhibitor->surface_destroy.notify = idle_inhibitor_handle_surface_destroy; wl_signal_add(&surface->events.destroy, &inhibitor->surface_destroy); wl_resource_set_implementation(inhibitor_resource, &idle_inhibitor_impl, inhibitor, idle_inhibitor_v1_handle_resource_destroy); wl_list_insert(&manager->inhibitors, &inhibitor->link); wl_signal_emit_mutable(&manager->events.new_inhibitor, inhibitor); } static void manager_handle_destroy(struct wl_client *client, struct wl_resource *manager_resource) { wl_resource_destroy(manager_resource); } static const struct zwp_idle_inhibit_manager_v1_interface idle_inhibit_impl = { .destroy = manager_handle_destroy, .create_inhibitor = manager_handle_create_inhibitor, }; static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_idle_inhibit_manager_v1 *manager = wl_container_of(listener, manager, display_destroy); wl_signal_emit_mutable(&manager->events.destroy, manager); wl_list_remove(&manager->display_destroy.link); wl_global_destroy(manager->global); free(manager); } static void idle_inhibit_bind(struct wl_client *wl_client, void *data, uint32_t version, uint32_t id) { struct wlr_idle_inhibit_manager_v1 *manager = data; struct wl_resource *manager_resource = wl_resource_create(wl_client, &zwp_idle_inhibit_manager_v1_interface, version, id); if (!manager_resource) { wl_client_post_no_memory(wl_client); return; } wl_resource_set_implementation(manager_resource, &idle_inhibit_impl, manager, NULL); } struct wlr_idle_inhibit_manager_v1 *wlr_idle_inhibit_v1_create(struct wl_display *display) { struct wlr_idle_inhibit_manager_v1 *manager = calloc(1, sizeof(*manager)); if (!manager) { return NULL; } wl_list_init(&manager->inhibitors); wl_signal_init(&manager->events.new_inhibitor); wl_signal_init(&manager->events.destroy); manager->global = wl_global_create(display, &zwp_idle_inhibit_manager_v1_interface, 1, manager, idle_inhibit_bind); if (!manager->global) { free(manager); return NULL; } manager->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &manager->display_destroy); return manager; } wlroots-0.17.1/types/wlr_idle_notify_v1.c000066400000000000000000000163501454110342200204210ustar00rootroot00000000000000#include #include #include #include #include "ext-idle-notify-v1-protocol.h" #define IDLE_NOTIFIER_VERSION 1 struct wlr_idle_notifier_v1 { struct wl_global *global; bool inhibited; struct wl_list notifications; // wlr_idle_notification_v1.link struct wl_listener display_destroy; }; struct wlr_idle_notification_v1 { struct wl_resource *resource; struct wl_list link; // wlr_idle_notifier_v1.notifications struct wlr_idle_notifier_v1 *notifier; struct wlr_seat *seat; uint32_t timeout_ms; struct wl_event_source *timer; bool idle; struct wl_listener seat_destroy; }; static void resource_handle_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static const struct ext_idle_notifier_v1_interface notifier_impl; static const struct ext_idle_notification_v1_interface notification_impl = { .destroy = resource_handle_destroy, }; // Returns NULL if the resource is inert static struct wlr_idle_notification_v1 *notification_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &ext_idle_notification_v1_interface, ¬ification_impl)); return wl_resource_get_user_data(resource); } static struct wlr_idle_notifier_v1 *notifier_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &ext_idle_notifier_v1_interface, ¬ifier_impl)); return wl_resource_get_user_data(resource); } static void notification_set_idle(struct wlr_idle_notification_v1 *notification, bool idle) { if (notification->idle == idle) { return; } if (idle) { ext_idle_notification_v1_send_idled(notification->resource); } else { ext_idle_notification_v1_send_resumed(notification->resource); } notification->idle = idle; } static int notification_handle_timer(void *data) { struct wlr_idle_notification_v1 *notification = data; notification_set_idle(notification, true); return 0; } static void notification_destroy(struct wlr_idle_notification_v1 *notification) { if (notification == NULL) { return; } wl_list_remove(¬ification->link); wl_list_remove(¬ification->seat_destroy.link); if (notification->timer != NULL) { wl_event_source_remove(notification->timer); } wl_resource_set_user_data(notification->resource, NULL); // make inert free(notification); } static void notification_reset_timer(struct wlr_idle_notification_v1 *notification) { if (notification->notifier->inhibited) { notification_set_idle(notification, false); if (notification->timer != NULL) { wl_event_source_timer_update(notification->timer, 0); } return; } if (notification->timer != NULL) { wl_event_source_timer_update(notification->timer, notification->timeout_ms); } else { notification_set_idle(notification, true); } } static void notification_handle_activity(struct wlr_idle_notification_v1 *notification) { notification_set_idle(notification, false); notification_reset_timer(notification); } static void notification_handle_seat_destroy(struct wl_listener *listener, void *data) { struct wlr_idle_notification_v1 *notification = wl_container_of(listener, notification, seat_destroy); notification_destroy(notification); } static void notification_handle_resource_destroy(struct wl_resource *resource) { struct wlr_idle_notification_v1 *notification = notification_from_resource(resource); notification_destroy(notification); } static void notifier_handle_get_idle_notification(struct wl_client *client, struct wl_resource *notifier_resource, uint32_t id, uint32_t timeout, struct wl_resource *seat_resource) { struct wlr_idle_notifier_v1 *notifier = notifier_from_resource(notifier_resource); struct wlr_seat_client *seat_client = wlr_seat_client_from_resource(seat_resource); uint32_t version = wl_resource_get_version(notifier_resource); struct wl_resource *resource = wl_resource_create(client, &ext_idle_notification_v1_interface, version, id); if (resource == NULL) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(resource, ¬ification_impl, NULL, notification_handle_resource_destroy); if (seat_client == NULL) { return; // leave the resource inert } struct wlr_idle_notification_v1 *notification = calloc(1, sizeof(*notification)); if (notification == NULL) { wl_client_post_no_memory(client); return; } notification->notifier = notifier; notification->resource = resource; notification->timeout_ms = timeout; notification->seat = seat_client->seat; if (timeout > 0) { struct wl_display *display = wl_client_get_display(client); struct wl_event_loop *loop = wl_display_get_event_loop(display); notification->timer = wl_event_loop_add_timer(loop, notification_handle_timer, notification); if (notification->timer == NULL) { free(notification); wl_client_post_no_memory(client); return; } } notification->seat_destroy.notify = notification_handle_seat_destroy; wl_signal_add(&seat_client->seat->events.destroy, ¬ification->seat_destroy); wl_resource_set_user_data(resource, notification); wl_list_insert(¬ifier->notifications, ¬ification->link); notification_reset_timer(notification); } static const struct ext_idle_notifier_v1_interface notifier_impl = { .destroy = resource_handle_destroy, .get_idle_notification = notifier_handle_get_idle_notification, }; static void notifier_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id) { struct wlr_idle_notifier_v1 *notifier = data; struct wl_resource *resource = wl_resource_create(client, &ext_idle_notifier_v1_interface, version, id); if (resource == NULL) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(resource, ¬ifier_impl, notifier, NULL); } static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_idle_notifier_v1 *notifier = wl_container_of(listener, notifier, display_destroy); wl_global_destroy(notifier->global); free(notifier); } struct wlr_idle_notifier_v1 *wlr_idle_notifier_v1_create(struct wl_display *display) { struct wlr_idle_notifier_v1 *notifier = calloc(1, sizeof(*notifier)); if (notifier == NULL) { return NULL; } notifier->global = wl_global_create(display, &ext_idle_notifier_v1_interface, IDLE_NOTIFIER_VERSION, notifier, notifier_bind); if (notifier->global == NULL) { free(notifier); return NULL; } wl_list_init(¬ifier->notifications); notifier->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, ¬ifier->display_destroy); return notifier; } void wlr_idle_notifier_v1_set_inhibited(struct wlr_idle_notifier_v1 *notifier, bool inhibited) { if (notifier->inhibited == inhibited) { return; } notifier->inhibited = inhibited; struct wlr_idle_notification_v1 *notification; wl_list_for_each(notification, ¬ifier->notifications, link) { notification_reset_timer(notification); } } void wlr_idle_notifier_v1_notify_activity(struct wlr_idle_notifier_v1 *notifier, struct wlr_seat *seat) { if (notifier->inhibited) { return; } struct wlr_idle_notification_v1 *notification; wl_list_for_each(notification, ¬ifier->notifications, link) { if (notification->seat == seat) { notification_handle_activity(notification); } } } wlroots-0.17.1/types/wlr_input_device.c000066400000000000000000000011361454110342200201600ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200809L #include #include #include "interfaces/wlr_input_device.h" void wlr_input_device_init(struct wlr_input_device *dev, enum wlr_input_device_type type, const char *name) { *dev = (struct wlr_input_device){ .type = type, .name = strdup(name), }; wl_signal_init(&dev->events.destroy); } void wlr_input_device_finish(struct wlr_input_device *wlr_device) { if (!wlr_device) { return; } wl_signal_emit_mutable(&wlr_device->events.destroy, wlr_device); wl_list_remove(&wlr_device->events.destroy.listener_list); free(wlr_device->name); } wlroots-0.17.1/types/wlr_input_inhibitor.c000066400000000000000000000110151454110342200207050ustar00rootroot00000000000000#include #include #include #include #include "wlr/types/wlr_input_inhibitor.h" #include "wlr-input-inhibitor-unstable-v1-protocol.h" static const struct zwlr_input_inhibit_manager_v1_interface inhibit_manager_implementation; static const struct zwlr_input_inhibitor_v1_interface input_inhibitor_implementation; static struct wlr_input_inhibit_manager *input_inhibit_manager_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &zwlr_input_inhibit_manager_v1_interface, &inhibit_manager_implementation) || wl_resource_instance_of(resource, &zwlr_input_inhibitor_v1_interface, &input_inhibitor_implementation)); return wl_resource_get_user_data(resource); } static void input_inhibit_manager_deactivate( struct wlr_input_inhibit_manager *manager) { if (manager->active_client == NULL && manager->active_inhibitor == NULL) { return; } manager->active_client = NULL; manager->active_inhibitor = NULL; wl_signal_emit_mutable(&manager->events.deactivate, manager); } static void input_inhibitor_destroy(struct wl_client *client, struct wl_resource *resource) { struct wlr_input_inhibit_manager *manager = input_inhibit_manager_from_resource(resource); input_inhibit_manager_deactivate(manager); wl_resource_destroy(resource); } static void input_inhibitor_resource_destroy(struct wl_resource *resource) { struct wlr_input_inhibit_manager *manager = input_inhibit_manager_from_resource(resource); input_inhibit_manager_deactivate(manager); } static const struct zwlr_input_inhibitor_v1_interface input_inhibitor_implementation = { .destroy = input_inhibitor_destroy, }; static void inhibit_manager_get_inhibitor(struct wl_client *client, struct wl_resource *resource, uint32_t id) { struct wlr_input_inhibit_manager *manager = input_inhibit_manager_from_resource(resource); if (manager->active_client || manager->active_inhibitor) { wl_resource_post_error(resource, ZWLR_INPUT_INHIBIT_MANAGER_V1_ERROR_ALREADY_INHIBITED, "this compositor already has input inhibited"); return; } struct wl_resource *wl_resource = wl_resource_create(client, &zwlr_input_inhibitor_v1_interface, wl_resource_get_version(resource), id); if (!wl_resource) { wl_client_post_no_memory(client); } wl_resource_set_implementation(wl_resource, &input_inhibitor_implementation, manager, input_inhibitor_resource_destroy); manager->active_client = client; manager->active_inhibitor = wl_resource; wl_signal_emit_mutable(&manager->events.activate, manager); } static const struct zwlr_input_inhibit_manager_v1_interface inhibit_manager_implementation = { .get_inhibitor = inhibit_manager_get_inhibitor }; static void input_manager_resource_destroy(struct wl_resource *resource) { struct wlr_input_inhibit_manager *manager = input_inhibit_manager_from_resource(resource); struct wl_client *client = wl_resource_get_client(resource); if (manager->active_client == client) { input_inhibit_manager_deactivate(manager); } } static void inhibit_manager_bind(struct wl_client *wl_client, void *data, uint32_t version, uint32_t id) { struct wlr_input_inhibit_manager *manager = data; struct wl_resource *wl_resource = wl_resource_create(wl_client, &zwlr_input_inhibit_manager_v1_interface, version, id); if (wl_resource == NULL) { wl_client_post_no_memory(wl_client); return; } wl_resource_set_implementation(wl_resource, &inhibit_manager_implementation, manager, input_manager_resource_destroy); } static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_input_inhibit_manager *manager = wl_container_of(listener, manager, display_destroy); wl_signal_emit_mutable(&manager->events.destroy, manager); wl_list_remove(&manager->display_destroy.link); wl_global_destroy(manager->global); free(manager); } struct wlr_input_inhibit_manager *wlr_input_inhibit_manager_create( struct wl_display *display) { // TODO: Client destroy struct wlr_input_inhibit_manager *manager = calloc(1, sizeof(*manager)); if (!manager) { return NULL; } manager->global = wl_global_create(display, &zwlr_input_inhibit_manager_v1_interface, 1, manager, inhibit_manager_bind); if (manager->global == NULL){ free(manager); return NULL; } wl_signal_init(&manager->events.activate); wl_signal_init(&manager->events.deactivate); wl_signal_init(&manager->events.destroy); manager->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &manager->display_destroy); return manager; } wlroots-0.17.1/types/wlr_input_method_v2.c000066400000000000000000000507711454110342200206210ustar00rootroot00000000000000#ifndef _POSIX_C_SOURCE #define _POSIX_C_SOURCE 200809L #endif #include #include #include #include #include #include #include #include #include #include "input-method-unstable-v2-protocol.h" #include "util/shm.h" // Note: zwp_input_popup_surface_v2 and zwp_input_method_keyboard_grab_v2 objects // become inert when the corresponding zwp_input_method_v2 is destroyed static const struct zwp_input_method_v2_interface input_method_impl; static const struct zwp_input_method_keyboard_grab_v2_interface keyboard_grab_impl; static void popup_surface_destroy(struct wlr_input_popup_surface_v2 *popup_surface) { wlr_surface_unmap(popup_surface->surface); wl_signal_emit_mutable(&popup_surface->events.destroy, NULL); wl_list_remove(&popup_surface->link); wl_resource_set_user_data(popup_surface->resource, NULL); free(popup_surface); } static struct wlr_input_method_v2 *input_method_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &zwp_input_method_v2_interface, &input_method_impl)); return wl_resource_get_user_data(resource); } static struct wlr_input_method_keyboard_grab_v2 *keyboard_grab_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &zwp_input_method_keyboard_grab_v2_interface, &keyboard_grab_impl)); return wl_resource_get_user_data(resource); } static void input_method_destroy(struct wlr_input_method_v2 *input_method) { struct wlr_input_popup_surface_v2 *popup_surface, *tmp; wl_list_for_each_safe( popup_surface, tmp, &input_method->popup_surfaces, link) { popup_surface_destroy(popup_surface); } wl_signal_emit_mutable(&input_method->events.destroy, input_method); wl_list_remove(wl_resource_get_link(input_method->resource)); wl_list_remove(&input_method->seat_client_destroy.link); wlr_input_method_keyboard_grab_v2_destroy(input_method->keyboard_grab); free(input_method->pending.commit_text); free(input_method->pending.preedit.text); free(input_method->current.commit_text); free(input_method->current.preedit.text); free(input_method); } static void input_method_resource_destroy(struct wl_resource *resource) { struct wlr_input_method_v2 *input_method = input_method_from_resource(resource); if (!input_method) { return; } input_method_destroy(input_method); } static void im_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static void im_commit(struct wl_client *client, struct wl_resource *resource, uint32_t serial) { struct wlr_input_method_v2 *input_method = input_method_from_resource(resource); if (!input_method) { return; } if (serial != input_method->current_serial) { free(input_method->pending.commit_text); free(input_method->pending.preedit.text); input_method->pending = (struct wlr_input_method_v2_state){0}; return; } free(input_method->current.commit_text); free(input_method->current.preedit.text); input_method->current = input_method->pending; input_method->pending = (struct wlr_input_method_v2_state){0}; wl_signal_emit_mutable(&input_method->events.commit, input_method); } static void im_commit_string(struct wl_client *client, struct wl_resource *resource, const char *text) { struct wlr_input_method_v2 *input_method = input_method_from_resource(resource); if (!input_method) { return; } free(input_method->pending.commit_text); input_method->pending.commit_text = strdup(text); if (input_method->pending.commit_text == NULL) { wl_client_post_no_memory(client); return; } } static void im_set_preedit_string(struct wl_client *client, struct wl_resource *resource, const char *text, int32_t cursor_begin, int32_t cursor_end) { struct wlr_input_method_v2 *input_method = input_method_from_resource(resource); if (!input_method) { return; } input_method->pending.preedit.cursor_begin = cursor_begin; input_method->pending.preedit.cursor_end = cursor_end; free(input_method->pending.preedit.text); input_method->pending.preedit.text = strdup(text); if (input_method->pending.preedit.text == NULL) { wl_client_post_no_memory(client); return; } } static void im_delete_surrounding_text(struct wl_client *client, struct wl_resource *resource, uint32_t before_length, uint32_t after_length) { struct wlr_input_method_v2 *input_method = input_method_from_resource(resource); if (!input_method) { return; } input_method->pending.delete.before_length = before_length; input_method->pending.delete.after_length = after_length; } void wlr_input_popup_surface_v2_send_text_input_rectangle( struct wlr_input_popup_surface_v2 *popup_surface, struct wlr_box *sbox) { zwp_input_popup_surface_v2_send_text_input_rectangle( popup_surface->resource, sbox->x, sbox->y, sbox->width, sbox->height); } static void popup_surface_consider_map(struct wlr_input_popup_surface_v2 *popup_surface) { if (wlr_surface_has_buffer(popup_surface->surface) && popup_surface->input_method->client_active) { wlr_surface_map(popup_surface->surface); } } static void popup_surface_surface_role_commit(struct wlr_surface *surface) { struct wlr_input_popup_surface_v2 *popup_surface = wlr_input_popup_surface_v2_try_from_wlr_surface(surface); if (popup_surface == NULL) { return; } popup_surface_consider_map(popup_surface); } static void popup_surface_surface_role_destroy(struct wlr_surface *surface) { struct wlr_input_popup_surface_v2 *popup_surface = wlr_input_popup_surface_v2_try_from_wlr_surface(surface); if (popup_surface == NULL) { return; } popup_surface_destroy(popup_surface); } static const struct wlr_surface_role input_popup_surface_v2_role = { .name = "zwp_input_popup_surface_v2", .commit = popup_surface_surface_role_commit, .destroy = popup_surface_surface_role_destroy, }; static const struct zwp_input_popup_surface_v2_interface input_popup_impl; static struct wlr_input_popup_surface_v2 *popup_surface_from_resource(struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &zwp_input_popup_surface_v2_interface, &input_popup_impl)); return wl_resource_get_user_data(resource); } struct wlr_input_popup_surface_v2 *wlr_input_popup_surface_v2_try_from_wlr_surface( struct wlr_surface *surface) { if (surface->role != &input_popup_surface_v2_role || surface->role_resource == NULL) { return NULL; } return popup_surface_from_resource(surface->role_resource); } static void popup_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static const struct zwp_input_popup_surface_v2_interface input_popup_impl = { .destroy = popup_destroy, }; static void im_get_input_popup_surface(struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *surface_resource) { struct wlr_input_method_v2 *input_method = input_method_from_resource(resource); if (!input_method) { return; } struct wlr_input_popup_surface_v2 *popup_surface = calloc(1, sizeof(*popup_surface)); if (!popup_surface) { wl_client_post_no_memory(client); return; } struct wlr_surface *surface = wlr_surface_from_resource(surface_resource); if (!wlr_surface_set_role(surface, &input_popup_surface_v2_role, resource, ZWP_INPUT_METHOD_V2_ERROR_ROLE)) { free(popup_surface); return; } struct wl_resource *popup_resource = wl_resource_create( client, &zwp_input_popup_surface_v2_interface, wl_resource_get_version(resource), id); if (!popup_resource) { free(popup_surface); wl_client_post_no_memory(client); return; } wl_resource_set_implementation(popup_resource, &input_popup_impl, popup_surface, NULL); wlr_surface_set_role_object(surface, popup_resource); popup_surface->resource = popup_resource; popup_surface->input_method = input_method; popup_surface->surface = surface; wl_signal_init(&popup_surface->events.destroy); popup_surface_consider_map(popup_surface); wl_list_insert(&input_method->popup_surfaces, &popup_surface->link); wl_signal_emit_mutable(&input_method->events.new_popup_surface, popup_surface); } void wlr_input_method_keyboard_grab_v2_destroy( struct wlr_input_method_keyboard_grab_v2 *keyboard_grab) { if (!keyboard_grab) { return; } wl_signal_emit_mutable(&keyboard_grab->events.destroy, keyboard_grab); keyboard_grab->input_method->keyboard_grab = NULL; if (keyboard_grab->keyboard) { wl_list_remove(&keyboard_grab->keyboard_keymap.link); wl_list_remove(&keyboard_grab->keyboard_repeat_info.link); wl_list_remove(&keyboard_grab->keyboard_destroy.link); } wl_resource_set_user_data(keyboard_grab->resource, NULL); free(keyboard_grab); } static void keyboard_grab_resource_destroy(struct wl_resource *resource) { struct wlr_input_method_keyboard_grab_v2 *keyboard_grab = keyboard_grab_from_resource(resource); wlr_input_method_keyboard_grab_v2_destroy(keyboard_grab); } static void keyboard_grab_release(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static const struct zwp_input_method_keyboard_grab_v2_interface keyboard_grab_impl = { .release = keyboard_grab_release, }; void wlr_input_method_keyboard_grab_v2_send_key( struct wlr_input_method_keyboard_grab_v2 *keyboard_grab, uint32_t time, uint32_t key, uint32_t state) { zwp_input_method_keyboard_grab_v2_send_key( keyboard_grab->resource, wlr_seat_client_next_serial(keyboard_grab->input_method->seat_client), time, key, state); } void wlr_input_method_keyboard_grab_v2_send_modifiers( struct wlr_input_method_keyboard_grab_v2 *keyboard_grab, struct wlr_keyboard_modifiers *modifiers) { zwp_input_method_keyboard_grab_v2_send_modifiers( keyboard_grab->resource, wlr_seat_client_next_serial(keyboard_grab->input_method->seat_client), modifiers->depressed, modifiers->latched, modifiers->locked, modifiers->group); } static bool keyboard_grab_send_keymap( struct wlr_input_method_keyboard_grab_v2 *keyboard_grab, struct wlr_keyboard *keyboard) { int keymap_fd = allocate_shm_file(keyboard->keymap_size); if (keymap_fd < 0) { wlr_log(WLR_ERROR, "creating a keymap file for %zu bytes failed", keyboard->keymap_size); return false; } void *ptr = mmap(NULL, keyboard->keymap_size, PROT_READ | PROT_WRITE, MAP_SHARED, keymap_fd, 0); if (ptr == MAP_FAILED) { wlr_log(WLR_ERROR, "failed to mmap() %zu bytes", keyboard->keymap_size); close(keymap_fd); return false; } memcpy(ptr, keyboard->keymap_string, keyboard->keymap_size); munmap(ptr, keyboard->keymap_size); zwp_input_method_keyboard_grab_v2_send_keymap(keyboard_grab->resource, WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1, keymap_fd, keyboard->keymap_size); close(keymap_fd); return true; } static void keyboard_grab_send_repeat_info( struct wlr_input_method_keyboard_grab_v2 *keyboard_grab, struct wlr_keyboard *keyboard) { zwp_input_method_keyboard_grab_v2_send_repeat_info( keyboard_grab->resource, keyboard->repeat_info.rate, keyboard->repeat_info.delay); } static void handle_keyboard_keymap(struct wl_listener *listener, void *data) { struct wlr_input_method_keyboard_grab_v2 *keyboard_grab = wl_container_of(listener, keyboard_grab, keyboard_keymap); keyboard_grab_send_keymap(keyboard_grab, data); } static void handle_keyboard_repeat_info(struct wl_listener *listener, void *data) { struct wlr_input_method_keyboard_grab_v2 *keyboard_grab = wl_container_of(listener, keyboard_grab, keyboard_repeat_info); keyboard_grab_send_repeat_info(keyboard_grab, data); } static void handle_keyboard_destroy(struct wl_listener *listener, void *data) { struct wlr_input_method_keyboard_grab_v2 *keyboard_grab = wl_container_of(listener, keyboard_grab, keyboard_destroy); wlr_input_method_keyboard_grab_v2_set_keyboard(keyboard_grab, NULL); } void wlr_input_method_keyboard_grab_v2_set_keyboard( struct wlr_input_method_keyboard_grab_v2 *keyboard_grab, struct wlr_keyboard *keyboard) { if (keyboard == keyboard_grab->keyboard) { return; } if (keyboard_grab->keyboard) { wl_list_remove(&keyboard_grab->keyboard_keymap.link); wl_list_remove(&keyboard_grab->keyboard_repeat_info.link); wl_list_remove(&keyboard_grab->keyboard_destroy.link); } if (keyboard) { if (keyboard_grab->keyboard == NULL || strcmp(keyboard_grab->keyboard->keymap_string, keyboard->keymap_string) != 0) { // send keymap only if it is changed, or if input method is not // aware that it did not change and blindly send it back with // virtual keyboard, it may cause an infinite recursion. if (!keyboard_grab_send_keymap(keyboard_grab, keyboard)) { wlr_log(WLR_ERROR, "Failed to send keymap for input-method keyboard grab"); return; } } keyboard_grab_send_repeat_info(keyboard_grab, keyboard); keyboard_grab->keyboard_keymap.notify = handle_keyboard_keymap; wl_signal_add(&keyboard->events.keymap, &keyboard_grab->keyboard_keymap); keyboard_grab->keyboard_repeat_info.notify = handle_keyboard_repeat_info; wl_signal_add(&keyboard->events.repeat_info, &keyboard_grab->keyboard_repeat_info); keyboard_grab->keyboard_destroy.notify = handle_keyboard_destroy; wl_signal_add(&keyboard->base.events.destroy, &keyboard_grab->keyboard_destroy); wlr_input_method_keyboard_grab_v2_send_modifiers(keyboard_grab, &keyboard->modifiers); } keyboard_grab->keyboard = keyboard; }; static void im_grab_keyboard(struct wl_client *client, struct wl_resource *resource, uint32_t keyboard) { struct wlr_input_method_v2 *input_method = input_method_from_resource(resource); if (!input_method) { return; } if (input_method->keyboard_grab) { // Already grabbed return; } struct wlr_input_method_keyboard_grab_v2 *keyboard_grab = calloc(1, sizeof(*keyboard_grab)); if (!keyboard_grab) { wl_client_post_no_memory(client); return; } struct wl_resource *keyboard_grab_resource = wl_resource_create( client, &zwp_input_method_keyboard_grab_v2_interface, wl_resource_get_version(resource), keyboard); if (keyboard_grab_resource == NULL) { free(keyboard_grab); wl_client_post_no_memory(client); return; } wl_resource_set_implementation(keyboard_grab_resource, &keyboard_grab_impl, keyboard_grab, keyboard_grab_resource_destroy); keyboard_grab->resource = keyboard_grab_resource; keyboard_grab->input_method = input_method; input_method->keyboard_grab = keyboard_grab; wl_signal_init(&keyboard_grab->events.destroy); wl_signal_emit_mutable(&input_method->events.grab_keyboard, keyboard_grab); } static const struct zwp_input_method_v2_interface input_method_impl = { .destroy = im_destroy, .commit = im_commit, .commit_string = im_commit_string, .set_preedit_string = im_set_preedit_string, .delete_surrounding_text = im_delete_surrounding_text, .get_input_popup_surface = im_get_input_popup_surface, .grab_keyboard = im_grab_keyboard, }; void wlr_input_method_v2_send_activate( struct wlr_input_method_v2 *input_method) { zwp_input_method_v2_send_activate(input_method->resource); input_method->active = true; } void wlr_input_method_v2_send_deactivate( struct wlr_input_method_v2 *input_method) { zwp_input_method_v2_send_deactivate(input_method->resource); input_method->active = false; } void wlr_input_method_v2_send_surrounding_text( struct wlr_input_method_v2 *input_method, const char *text, uint32_t cursor, uint32_t anchor) { const char *send_text = text; if (!send_text) { send_text = ""; } zwp_input_method_v2_send_surrounding_text(input_method->resource, send_text, cursor, anchor); } void wlr_input_method_v2_send_text_change_cause( struct wlr_input_method_v2 *input_method, uint32_t cause) { zwp_input_method_v2_send_text_change_cause(input_method->resource, cause); } void wlr_input_method_v2_send_content_type( struct wlr_input_method_v2 *input_method, uint32_t hint, uint32_t purpose) { zwp_input_method_v2_send_content_type(input_method->resource, hint, purpose); } void wlr_input_method_v2_send_done(struct wlr_input_method_v2 *input_method) { zwp_input_method_v2_send_done(input_method->resource); input_method->client_active = input_method->active; input_method->current_serial++; struct wlr_input_popup_surface_v2 *popup_surface; wl_list_for_each(popup_surface, &input_method->popup_surfaces, link) { popup_surface_consider_map(popup_surface); } } void wlr_input_method_v2_send_unavailable( struct wlr_input_method_v2 *input_method) { zwp_input_method_v2_send_unavailable(input_method->resource); struct wl_resource *resource = input_method->resource; input_method_destroy(input_method); wl_resource_set_user_data(resource, NULL); } static const struct zwp_input_method_manager_v2_interface input_method_manager_impl; static struct wlr_input_method_manager_v2 *input_method_manager_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &zwp_input_method_manager_v2_interface, &input_method_manager_impl)); return wl_resource_get_user_data(resource); } static void input_method_handle_seat_client_destroy(struct wl_listener *listener, void *data) { struct wlr_input_method_v2 *input_method = wl_container_of(listener, input_method, seat_client_destroy); wlr_input_method_v2_send_unavailable(input_method); } static void manager_get_input_method(struct wl_client *client, struct wl_resource *resource, struct wl_resource *seat, uint32_t input_method_id) { struct wlr_input_method_manager_v2 *im_manager = input_method_manager_from_resource(resource); struct wlr_seat_client *seat_client = wlr_seat_client_from_resource(seat); int version = wl_resource_get_version(resource); struct wl_resource *im_resource = wl_resource_create(client, &zwp_input_method_v2_interface, version, input_method_id); if (im_resource == NULL) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(im_resource, &input_method_impl, NULL, input_method_resource_destroy); wl_list_init(wl_resource_get_link(im_resource)); if (seat_client == NULL) { return; } struct wlr_input_method_v2 *input_method = calloc(1, sizeof(*input_method)); if (!input_method) { wl_client_post_no_memory(client); return; } wl_list_init(&input_method->popup_surfaces); wl_signal_init(&input_method->events.commit); wl_signal_init(&input_method->events.new_popup_surface); wl_signal_init(&input_method->events.grab_keyboard); wl_signal_init(&input_method->events.destroy); input_method->seat_client = seat_client; input_method->seat = input_method->seat_client->seat; wl_signal_add(&input_method->seat_client->events.destroy, &input_method->seat_client_destroy); input_method->seat_client_destroy.notify = input_method_handle_seat_client_destroy; input_method->resource = im_resource; wl_resource_set_user_data(im_resource, input_method); wl_list_insert(&im_manager->input_methods, wl_resource_get_link(input_method->resource)); wl_signal_emit_mutable(&im_manager->events.input_method, input_method); } static void manager_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static const struct zwp_input_method_manager_v2_interface input_method_manager_impl = { .get_input_method = manager_get_input_method, .destroy = manager_destroy, }; static void input_method_manager_bind(struct wl_client *wl_client, void *data, uint32_t version, uint32_t id) { struct wlr_input_method_manager_v2 *im_manager = data; struct wl_resource *bound_resource = wl_resource_create(wl_client, &zwp_input_method_manager_v2_interface, version, id); if (bound_resource == NULL) { wl_client_post_no_memory(wl_client); return; } wl_resource_set_implementation(bound_resource, &input_method_manager_impl, im_manager, NULL); } static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_input_method_manager_v2 *manager = wl_container_of(listener, manager, display_destroy); wl_signal_emit_mutable(&manager->events.destroy, manager); wl_list_remove(&manager->display_destroy.link); wl_global_destroy(manager->global); free(manager); } struct wlr_input_method_manager_v2 *wlr_input_method_manager_v2_create( struct wl_display *display) { struct wlr_input_method_manager_v2 *im_manager = calloc(1, sizeof(*im_manager)); if (!im_manager) { return NULL; } wl_signal_init(&im_manager->events.input_method); wl_signal_init(&im_manager->events.destroy); wl_list_init(&im_manager->input_methods); im_manager->global = wl_global_create(display, &zwp_input_method_manager_v2_interface, 1, im_manager, input_method_manager_bind); if (!im_manager->global) { free(im_manager); return NULL; } im_manager->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &im_manager->display_destroy); return im_manager; } wlroots-0.17.1/types/wlr_keyboard.c000066400000000000000000000203021454110342200172760ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include "interfaces/wlr_input_device.h" #include "types/wlr_keyboard.h" #include "util/set.h" #include "util/shm.h" #include "util/time.h" struct wlr_keyboard *wlr_keyboard_from_input_device( struct wlr_input_device *input_device) { assert(input_device->type == WLR_INPUT_DEVICE_KEYBOARD); return wl_container_of(input_device, (struct wlr_keyboard *)NULL, base); } void keyboard_led_update(struct wlr_keyboard *keyboard) { if (keyboard->xkb_state == NULL) { return; } uint32_t leds = 0; for (uint32_t i = 0; i < WLR_LED_COUNT; ++i) { if (xkb_state_led_index_is_active(keyboard->xkb_state, keyboard->led_indexes[i])) { leds |= (1 << i); } } wlr_keyboard_led_update(keyboard, leds); } /** * Update the modifier state of the wlr-keyboard. Returns true if the modifier * state changed. */ bool keyboard_modifier_update(struct wlr_keyboard *keyboard) { if (keyboard->xkb_state == NULL) { return false; } xkb_mod_mask_t depressed = xkb_state_serialize_mods(keyboard->xkb_state, XKB_STATE_MODS_DEPRESSED); xkb_mod_mask_t latched = xkb_state_serialize_mods(keyboard->xkb_state, XKB_STATE_MODS_LATCHED); xkb_mod_mask_t locked = xkb_state_serialize_mods(keyboard->xkb_state, XKB_STATE_MODS_LOCKED); xkb_mod_mask_t group = xkb_state_serialize_layout(keyboard->xkb_state, XKB_STATE_LAYOUT_EFFECTIVE); if (depressed == keyboard->modifiers.depressed && latched == keyboard->modifiers.latched && locked == keyboard->modifiers.locked && group == keyboard->modifiers.group) { return false; } keyboard->modifiers.depressed = depressed; keyboard->modifiers.latched = latched; keyboard->modifiers.locked = locked; keyboard->modifiers.group = group; return true; } void keyboard_key_update(struct wlr_keyboard *keyboard, struct wlr_keyboard_key_event *event) { if (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { set_add(keyboard->keycodes, &keyboard->num_keycodes, WLR_KEYBOARD_KEYS_CAP, event->keycode); } if (event->state == WL_KEYBOARD_KEY_STATE_RELEASED) { set_remove(keyboard->keycodes, &keyboard->num_keycodes, WLR_KEYBOARD_KEYS_CAP, event->keycode); } assert(keyboard->num_keycodes <= WLR_KEYBOARD_KEYS_CAP); } void wlr_keyboard_notify_modifiers(struct wlr_keyboard *keyboard, uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group) { if (keyboard->xkb_state == NULL) { return; } xkb_state_update_mask(keyboard->xkb_state, mods_depressed, mods_latched, mods_locked, 0, 0, group); bool updated = keyboard_modifier_update(keyboard); if (updated) { wl_signal_emit_mutable(&keyboard->events.modifiers, keyboard); } keyboard_led_update(keyboard); } void wlr_keyboard_notify_key(struct wlr_keyboard *keyboard, struct wlr_keyboard_key_event *event) { keyboard_key_update(keyboard, event); wl_signal_emit_mutable(&keyboard->events.key, event); if (keyboard->xkb_state == NULL) { return; } if (event->update_state) { uint32_t keycode = event->keycode + 8; xkb_state_update_key(keyboard->xkb_state, keycode, event->state == WL_KEYBOARD_KEY_STATE_PRESSED ? XKB_KEY_DOWN : XKB_KEY_UP); } bool updated = keyboard_modifier_update(keyboard); if (updated) { wl_signal_emit_mutable(&keyboard->events.modifiers, keyboard); } keyboard_led_update(keyboard); } void wlr_keyboard_init(struct wlr_keyboard *kb, const struct wlr_keyboard_impl *impl, const char *name) { *kb = (struct wlr_keyboard){ .impl = impl, .keymap_fd = -1, // Sane defaults .repeat_info.rate = 25, .repeat_info.delay = 600, }; wlr_input_device_init(&kb->base, WLR_INPUT_DEVICE_KEYBOARD, name); wl_signal_init(&kb->events.key); wl_signal_init(&kb->events.modifiers); wl_signal_init(&kb->events.keymap); wl_signal_init(&kb->events.repeat_info); } static void keyboard_unset_keymap(struct wlr_keyboard *kb) { xkb_keymap_unref(kb->keymap); kb->keymap = NULL; xkb_state_unref(kb->xkb_state); kb->xkb_state = NULL; free(kb->keymap_string); kb->keymap_string = NULL; kb->keymap_size = 0; if (kb->keymap_fd >= 0) { close(kb->keymap_fd); } kb->keymap_fd = -1; } void wlr_keyboard_finish(struct wlr_keyboard *kb) { /* Release pressed keys */ size_t orig_num_keycodes = kb->num_keycodes; for (size_t i = 0; i < orig_num_keycodes; ++i) { assert(kb->num_keycodes == orig_num_keycodes - i); struct wlr_keyboard_key_event event = { .time_msec = get_current_time_msec(), .keycode = kb->keycodes[orig_num_keycodes - i - 1], .update_state = false, .state = WL_KEYBOARD_KEY_STATE_RELEASED, }; wlr_keyboard_notify_key(kb, &event); // updates num_keycodes } wlr_input_device_finish(&kb->base); keyboard_unset_keymap(kb); } void wlr_keyboard_led_update(struct wlr_keyboard *kb, uint32_t leds) { if (kb->leds == leds) { return; } kb->leds = leds; if (kb->impl && kb->impl->led_update) { kb->impl->led_update(kb, leds); } } bool wlr_keyboard_set_keymap(struct wlr_keyboard *kb, struct xkb_keymap *keymap) { if (keymap == NULL) { keyboard_unset_keymap(kb); wl_signal_emit_mutable(&kb->events.keymap, kb); return true; } struct xkb_state *xkb_state = xkb_state_new(keymap); if (xkb_state == NULL) { wlr_log(WLR_ERROR, "Failed to create XKB state"); return false; } char *keymap_str = xkb_keymap_get_as_string(keymap, XKB_KEYMAP_FORMAT_TEXT_V1); if (keymap_str == NULL) { wlr_log(WLR_ERROR, "Failed to get string version of keymap"); goto error_xkb_state; } size_t keymap_size = strlen(keymap_str) + 1; int rw_fd = -1, ro_fd = -1; if (!allocate_shm_file_pair(keymap_size, &rw_fd, &ro_fd)) { wlr_log(WLR_ERROR, "Failed to allocate shm file for keymap"); goto error_keymap_str; } void *dst = mmap(NULL, keymap_size, PROT_READ | PROT_WRITE, MAP_SHARED, rw_fd, 0); close(rw_fd); if (dst == MAP_FAILED) { wlr_log_errno(WLR_ERROR, "mmap failed"); close(ro_fd); goto error_keymap_str; } memcpy(dst, keymap_str, keymap_size); munmap(dst, keymap_size); keyboard_unset_keymap(kb); kb->keymap = xkb_keymap_ref(keymap); kb->xkb_state = xkb_state; kb->keymap_string = keymap_str; kb->keymap_size = keymap_size; kb->keymap_fd = ro_fd; const char *led_names[WLR_LED_COUNT] = { XKB_LED_NAME_NUM, XKB_LED_NAME_CAPS, XKB_LED_NAME_SCROLL, }; for (size_t i = 0; i < WLR_LED_COUNT; ++i) { kb->led_indexes[i] = xkb_map_led_get_index(kb->keymap, led_names[i]); } const char *mod_names[WLR_MODIFIER_COUNT] = { XKB_MOD_NAME_SHIFT, XKB_MOD_NAME_CAPS, XKB_MOD_NAME_CTRL, // "Control" XKB_MOD_NAME_ALT, // "Mod1" XKB_MOD_NAME_NUM, // "Mod2" "Mod3", XKB_MOD_NAME_LOGO, // "Mod4" "Mod5", }; // TODO: there's also "Ctrl", "Alt"? for (size_t i = 0; i < WLR_MODIFIER_COUNT; ++i) { kb->mod_indexes[i] = xkb_map_mod_get_index(kb->keymap, mod_names[i]); } for (size_t i = 0; i < kb->num_keycodes; ++i) { xkb_keycode_t keycode = kb->keycodes[i] + 8; xkb_state_update_key(kb->xkb_state, keycode, XKB_KEY_DOWN); } keyboard_modifier_update(kb); wl_signal_emit_mutable(&kb->events.keymap, kb); return true; error_keymap_str: free(keymap_str); error_xkb_state: xkb_state_unref(xkb_state); return false; } void wlr_keyboard_set_repeat_info(struct wlr_keyboard *kb, int32_t rate, int32_t delay) { if (kb->repeat_info.rate == rate && kb->repeat_info.delay == delay) { return; } kb->repeat_info.rate = rate; kb->repeat_info.delay = delay; wl_signal_emit_mutable(&kb->events.repeat_info, kb); } uint32_t wlr_keyboard_get_modifiers(struct wlr_keyboard *kb) { xkb_mod_mask_t mask = kb->modifiers.depressed | kb->modifiers.latched; uint32_t modifiers = 0; for (size_t i = 0; i < WLR_MODIFIER_COUNT; ++i) { if (kb->mod_indexes[i] != XKB_MOD_INVALID && (mask & (1 << kb->mod_indexes[i]))) { modifiers |= (1 << i); } } return modifiers; } bool wlr_keyboard_keymaps_match(struct xkb_keymap *km1, struct xkb_keymap *km2) { if (!km1 && !km2) { return true; } if (!km1 || !km2) { return false; } char *km1_str = xkb_keymap_get_as_string(km1, XKB_KEYMAP_FORMAT_TEXT_V1); char *km2_str = xkb_keymap_get_as_string(km2, XKB_KEYMAP_FORMAT_TEXT_V1); bool result = strcmp(km1_str, km2_str) == 0; free(km1_str); free(km2_str); return result; } wlroots-0.17.1/types/wlr_keyboard_group.c000066400000000000000000000240151454110342200205170ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include "types/wlr_keyboard.h" #include "wlr/interfaces/wlr_keyboard.h" #include "wlr/types/wlr_keyboard.h" #include "wlr/types/wlr_keyboard_group.h" #include "wlr/util/log.h" struct keyboard_group_device { struct wlr_keyboard *keyboard; struct wl_listener key; struct wl_listener modifiers; struct wl_listener keymap; struct wl_listener repeat_info; struct wl_listener destroy; struct wl_list link; // wlr_keyboard_group.devices }; struct keyboard_group_key { uint32_t keycode; size_t count; struct wl_list link; // wlr_keyboard_group.keys }; static void keyboard_set_leds(struct wlr_keyboard *kb, uint32_t leds) { struct wlr_keyboard_group *group = wlr_keyboard_group_from_wlr_keyboard(kb); struct keyboard_group_device *device; wl_list_for_each(device, &group->devices, link) { wlr_keyboard_led_update(device->keyboard, leds); } } static const struct wlr_keyboard_impl impl = { .name = "keyboard-group", .led_update = keyboard_set_leds }; struct wlr_keyboard_group *wlr_keyboard_group_create(void) { struct wlr_keyboard_group *group = calloc(1, sizeof(*group)); if (!group) { wlr_log(WLR_ERROR, "Failed to allocate wlr_keyboard_group"); return NULL; } wlr_keyboard_init(&group->keyboard, &impl, "wlr_keyboard_group"); wl_list_init(&group->devices); wl_list_init(&group->keys); wl_signal_init(&group->events.enter); wl_signal_init(&group->events.leave); return group; } struct wlr_keyboard_group *wlr_keyboard_group_from_wlr_keyboard( struct wlr_keyboard *keyboard) { if (keyboard->impl != &impl) { return NULL; } struct wlr_keyboard_group *group = wl_container_of(keyboard, group, keyboard); return group; } static bool process_key(struct keyboard_group_device *group_device, struct wlr_keyboard_key_event *event) { struct wlr_keyboard_group *group = group_device->keyboard->group; struct keyboard_group_key *key, *tmp; wl_list_for_each_safe(key, tmp, &group->keys, link) { if (key->keycode != event->keycode) { continue; } if (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { key->count++; return false; } if (event->state == WL_KEYBOARD_KEY_STATE_RELEASED) { key->count--; if (key->count > 0) { return false; } wl_list_remove(&key->link); free(key); } break; } if (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { struct keyboard_group_key *key = calloc(1, sizeof(*key)); if (!key) { wlr_log(WLR_ERROR, "Failed to allocate keyboard_group_key"); return false; } key->keycode = event->keycode; key->count = 1; wl_list_insert(&group->keys, &key->link); } return true; } static void handle_keyboard_key(struct wl_listener *listener, void *data) { struct keyboard_group_device *group_device = wl_container_of(listener, group_device, key); if (process_key(group_device, data)) { wlr_keyboard_notify_key(&group_device->keyboard->group->keyboard, data); } } static void handle_keyboard_modifiers(struct wl_listener *listener, void *data) { // Sync the effective layout (group modifier) to all keyboards. The rest of // the modifiers will be derived from the wlr_keyboard_group's key state struct keyboard_group_device *group_device = wl_container_of(listener, group_device, modifiers); struct wlr_keyboard_modifiers mods = group_device->keyboard->modifiers; struct keyboard_group_device *device; wl_list_for_each(device, &group_device->keyboard->group->devices, link) { if (mods.depressed != device->keyboard->modifiers.depressed || mods.latched != device->keyboard->modifiers.latched || mods.locked != device->keyboard->modifiers.locked || mods.group != device->keyboard->modifiers.group) { wlr_keyboard_notify_modifiers(device->keyboard, mods.depressed, mods.latched, mods.locked, mods.group); return; } } wlr_keyboard_notify_modifiers(&group_device->keyboard->group->keyboard, mods.depressed, mods.latched, mods.locked, mods.group); } static void handle_keyboard_keymap(struct wl_listener *listener, void *data) { struct keyboard_group_device *group_device = wl_container_of(listener, group_device, keymap); struct wlr_keyboard *keyboard = group_device->keyboard; if (!wlr_keyboard_keymaps_match(keyboard->group->keyboard.keymap, keyboard->keymap)) { struct keyboard_group_device *device; wl_list_for_each(device, &keyboard->group->devices, link) { if (!wlr_keyboard_keymaps_match(keyboard->keymap, device->keyboard->keymap)) { wlr_keyboard_set_keymap(device->keyboard, keyboard->keymap); return; } } } wlr_keyboard_set_keymap(&keyboard->group->keyboard, keyboard->keymap); } static void handle_keyboard_repeat_info(struct wl_listener *listener, void *data) { struct keyboard_group_device *group_device = wl_container_of(listener, group_device, repeat_info); struct wlr_keyboard *keyboard = group_device->keyboard; struct keyboard_group_device *device; wl_list_for_each(device, &keyboard->group->devices, link) { struct wlr_keyboard *devkb = device->keyboard; if (devkb->repeat_info.rate != keyboard->repeat_info.rate || devkb->repeat_info.delay != keyboard->repeat_info.delay) { wlr_keyboard_set_repeat_info(devkb, keyboard->repeat_info.rate, keyboard->repeat_info.delay); return; } } wlr_keyboard_set_repeat_info(&keyboard->group->keyboard, keyboard->repeat_info.rate, keyboard->repeat_info.delay); } static void refresh_state(struct keyboard_group_device *device, enum wl_keyboard_key_state state) { struct wl_array keys; wl_array_init(&keys); for (size_t i = 0; i < device->keyboard->num_keycodes; i++) { struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); struct wlr_keyboard_key_event event = { .time_msec = (int64_t)now.tv_sec * 1000 + now.tv_nsec / 1000000, .keycode = device->keyboard->keycodes[i], .update_state = true, .state = state }; // Update the group's key state and determine whether this is a unique // key that needs to be passed on to the compositor if (process_key(device, &event)) { // Update state for wlr_keyboard_group's keyboard keyboard_key_update(&device->keyboard->group->keyboard, &event); keyboard_modifier_update(&device->keyboard->group->keyboard); keyboard_led_update(&device->keyboard->group->keyboard); // Add the key to the array uint32_t *key = wl_array_add(&keys, sizeof(uint32_t)); *key = event.keycode; } } // If there are any unique keys, emit the enter/leave event if (keys.size > 0) { if (state == WL_KEYBOARD_KEY_STATE_PRESSED) { wl_signal_emit_mutable(&device->keyboard->group->events.enter, &keys); } else { wl_signal_emit_mutable(&device->keyboard->group->events.leave, &keys); } } wl_array_release(&keys); } static void remove_keyboard_group_device(struct keyboard_group_device *device) { refresh_state(device, WL_KEYBOARD_KEY_STATE_RELEASED); device->keyboard->group = NULL; wl_list_remove(&device->link); wl_list_remove(&device->key.link); wl_list_remove(&device->modifiers.link); wl_list_remove(&device->keymap.link); wl_list_remove(&device->repeat_info.link); wl_list_remove(&device->destroy.link); free(device); } static void handle_keyboard_destroy(struct wl_listener *listener, void *data) { struct keyboard_group_device *device = wl_container_of(listener, device, destroy); remove_keyboard_group_device(device); } bool wlr_keyboard_group_add_keyboard(struct wlr_keyboard_group *group, struct wlr_keyboard *keyboard) { if (keyboard->group) { wlr_log(WLR_ERROR, "A wlr_keyboard can only belong to one group"); return false; } if (keyboard->impl == &impl) { wlr_log(WLR_ERROR, "Cannot add a group's keyboard to a group"); return false; } if (!wlr_keyboard_keymaps_match(group->keyboard.keymap, keyboard->keymap)) { wlr_log(WLR_ERROR, "Device keymap does not match keyboard group's"); return false; } struct keyboard_group_device *device = calloc(1, sizeof(*device)); if (!device) { wlr_log(WLR_ERROR, "Failed to allocate keyboard_group_device"); return false; } device->keyboard = keyboard; keyboard->group = group; wl_list_insert(&group->devices, &device->link); wl_signal_add(&keyboard->events.key, &device->key); device->key.notify = handle_keyboard_key; wl_signal_add(&keyboard->events.modifiers, &device->modifiers); device->modifiers.notify = handle_keyboard_modifiers; wl_signal_add(&keyboard->events.keymap, &device->keymap); device->keymap.notify = handle_keyboard_keymap; wl_signal_add(&keyboard->events.repeat_info, &device->repeat_info); device->repeat_info.notify = handle_keyboard_repeat_info; wl_signal_add(&keyboard->base.events.destroy, &device->destroy); device->destroy.notify = handle_keyboard_destroy; struct wlr_keyboard *group_kb = &group->keyboard; if (keyboard->modifiers.group != group_kb->modifiers.group) { wlr_keyboard_notify_modifiers(keyboard, keyboard->modifiers.depressed, keyboard->modifiers.latched, keyboard->modifiers.locked, group_kb->modifiers.group); } if (keyboard->repeat_info.rate != group_kb->repeat_info.rate || keyboard->repeat_info.delay != group_kb->repeat_info.delay) { wlr_keyboard_set_repeat_info(keyboard, group_kb->repeat_info.rate, group_kb->repeat_info.delay); } refresh_state(device, WL_KEYBOARD_KEY_STATE_PRESSED); return true; } void wlr_keyboard_group_remove_keyboard(struct wlr_keyboard_group *group, struct wlr_keyboard *keyboard) { struct keyboard_group_device *device, *tmp; wl_list_for_each_safe(device, tmp, &group->devices, link) { if (device->keyboard == keyboard) { remove_keyboard_group_device(device); return; } } wlr_log(WLR_ERROR, "keyboard not found in group"); } void wlr_keyboard_group_destroy(struct wlr_keyboard_group *group) { struct keyboard_group_device *device, *tmp; wl_list_for_each_safe(device, tmp, &group->devices, link) { wlr_keyboard_group_remove_keyboard(group, device->keyboard); } wlr_keyboard_finish(&group->keyboard); wl_list_remove(&group->events.enter.listener_list); wl_list_remove(&group->events.leave.listener_list); free(group); } wlroots-0.17.1/types/wlr_keyboard_shortcuts_inhibit_v1.c000066400000000000000000000170641454110342200235430ustar00rootroot00000000000000#include #include #include #include #include "keyboard-shortcuts-inhibit-unstable-v1-protocol.h" static const struct zwp_keyboard_shortcuts_inhibit_manager_v1_interface keyboard_shortcuts_inhibit_impl; static const struct zwp_keyboard_shortcuts_inhibitor_v1_interface keyboard_shortcuts_inhibitor_impl; static struct wlr_keyboard_shortcuts_inhibit_manager_v1 * wlr_keyboard_shortcuts_inhibit_manager_v1_from_resource( struct wl_resource *manager_resource) { assert(wl_resource_instance_of(manager_resource, &zwp_keyboard_shortcuts_inhibit_manager_v1_interface, &keyboard_shortcuts_inhibit_impl)); return wl_resource_get_user_data(manager_resource); } static struct wlr_keyboard_shortcuts_inhibitor_v1 * wlr_keyboard_shortcuts_inhibitor_v1_from_resource( struct wl_resource *inhibitor_resource) { assert(wl_resource_instance_of(inhibitor_resource, &zwp_keyboard_shortcuts_inhibitor_v1_interface, &keyboard_shortcuts_inhibitor_impl)); return wl_resource_get_user_data(inhibitor_resource); } static void keyboard_shortcuts_inhibitor_v1_destroy( struct wlr_keyboard_shortcuts_inhibitor_v1 *inhibitor) { if (!inhibitor) { return; } wl_signal_emit_mutable(&inhibitor->events.destroy, inhibitor); wl_resource_set_user_data(inhibitor->resource, NULL); wl_list_remove(&inhibitor->link); wl_list_remove(&inhibitor->surface_destroy.link); wl_list_remove(&inhibitor->seat_destroy.link); free(inhibitor); } static void keyboard_shortcuts_inhibitor_v1_handle_resource_destroy( struct wl_resource *inhibitor_resource) { struct wlr_keyboard_shortcuts_inhibitor_v1 *inhibitor = wlr_keyboard_shortcuts_inhibitor_v1_from_resource( inhibitor_resource); keyboard_shortcuts_inhibitor_v1_destroy(inhibitor); } static void keyboard_shortcuts_inhibitor_handle_surface_destroy( struct wl_listener *listener, void *data) { struct wlr_keyboard_shortcuts_inhibitor_v1 *inhibitor = wl_container_of(listener, inhibitor, surface_destroy); // be gracious and notify client that destruction of a referenced // resource makes inhibitor moot wlr_keyboard_shortcuts_inhibitor_v1_deactivate(inhibitor); keyboard_shortcuts_inhibitor_v1_destroy(inhibitor); } static void keyboard_shortcuts_inhibitor_handle_seat_destroy( struct wl_listener *listener, void *data) { struct wlr_keyboard_shortcuts_inhibitor_v1 *inhibitor = wl_container_of(listener, inhibitor, seat_destroy); wlr_keyboard_shortcuts_inhibitor_v1_deactivate(inhibitor); keyboard_shortcuts_inhibitor_v1_destroy(inhibitor); } static void keyboard_shortcuts_inhibitor_v1_handle_destroy( struct wl_client *client, struct wl_resource *inhibitor_resource) { wl_resource_destroy(inhibitor_resource); } static const struct zwp_keyboard_shortcuts_inhibitor_v1_interface keyboard_shortcuts_inhibitor_impl = { .destroy = keyboard_shortcuts_inhibitor_v1_handle_destroy, }; static void manager_handle_inhibit_shortcuts(struct wl_client *client, struct wl_resource *manager_resource, uint32_t id, struct wl_resource *surface_resource, struct wl_resource *seat_resource) { struct wlr_surface *surface = wlr_surface_from_resource(surface_resource); struct wlr_seat_client *seat_client = wlr_seat_client_from_resource(seat_resource); struct wlr_keyboard_shortcuts_inhibit_manager_v1 *manager = wlr_keyboard_shortcuts_inhibit_manager_v1_from_resource( manager_resource); uint32_t version = wl_resource_get_version(manager_resource); struct wl_resource *inhibitor_resource = wl_resource_create(client, &zwp_keyboard_shortcuts_inhibitor_v1_interface, version, id); if (inhibitor_resource == NULL) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(inhibitor_resource, &keyboard_shortcuts_inhibitor_impl, NULL, keyboard_shortcuts_inhibitor_v1_handle_resource_destroy); if (seat_client == NULL) { return; } struct wlr_seat *seat = seat_client->seat; struct wlr_keyboard_shortcuts_inhibitor_v1 *existing_inhibitor; wl_list_for_each(existing_inhibitor, &manager->inhibitors, link) { if (existing_inhibitor->surface != surface || existing_inhibitor->seat != seat) { continue; } wl_resource_post_error(manager_resource, ZWP_KEYBOARD_SHORTCUTS_INHIBIT_MANAGER_V1_ERROR_ALREADY_INHIBITED, "this surface already has keyboard shortcuts " "inhibited on this seat"); return; } struct wlr_keyboard_shortcuts_inhibitor_v1 *inhibitor = calloc(1, sizeof(*inhibitor)); if (!inhibitor) { wl_client_post_no_memory(client); return; } inhibitor->resource = inhibitor_resource; inhibitor->surface = surface; inhibitor->seat = seat; inhibitor->active = false; wl_signal_init(&inhibitor->events.destroy); inhibitor->surface_destroy.notify = keyboard_shortcuts_inhibitor_handle_surface_destroy; wl_signal_add(&surface->events.destroy, &inhibitor->surface_destroy); inhibitor->seat_destroy.notify = keyboard_shortcuts_inhibitor_handle_seat_destroy; wl_signal_add(&seat->events.destroy, &inhibitor->seat_destroy); wl_resource_set_user_data(inhibitor_resource, inhibitor); wl_list_insert(&manager->inhibitors, &inhibitor->link); wl_signal_emit_mutable(&manager->events.new_inhibitor, inhibitor); } static void manager_handle_destroy(struct wl_client *client, struct wl_resource *manager_resource) { wl_resource_destroy(manager_resource); } static const struct zwp_keyboard_shortcuts_inhibit_manager_v1_interface keyboard_shortcuts_inhibit_impl = { .destroy = manager_handle_destroy, .inhibit_shortcuts = manager_handle_inhibit_shortcuts, }; static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_keyboard_shortcuts_inhibit_manager_v1 *manager = wl_container_of(listener, manager, display_destroy); wl_signal_emit_mutable(&manager->events.destroy, manager); wl_list_remove(&manager->display_destroy.link); wl_global_destroy(manager->global); free(manager); } static void keyboard_shortcuts_inhibit_bind(struct wl_client *wl_client, void *data, uint32_t version, uint32_t id) { struct wlr_keyboard_shortcuts_inhibit_manager_v1 *manager = data; struct wl_resource *manager_resource = wl_resource_create(wl_client, &zwp_keyboard_shortcuts_inhibit_manager_v1_interface, version, id); if (!manager_resource) { wl_client_post_no_memory(wl_client); return; } wl_resource_set_implementation(manager_resource, &keyboard_shortcuts_inhibit_impl, manager, NULL); } struct wlr_keyboard_shortcuts_inhibit_manager_v1 * wlr_keyboard_shortcuts_inhibit_v1_create(struct wl_display *display) { struct wlr_keyboard_shortcuts_inhibit_manager_v1 *manager = calloc(1, sizeof(*manager)); if (!manager) { return NULL; } wl_list_init(&manager->inhibitors); wl_signal_init(&manager->events.new_inhibitor); wl_signal_init(&manager->events.destroy); manager->global = wl_global_create(display, &zwp_keyboard_shortcuts_inhibit_manager_v1_interface, 1, manager, keyboard_shortcuts_inhibit_bind); if (!manager->global) { free(manager); return NULL; } manager->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &manager->display_destroy); return manager; } void wlr_keyboard_shortcuts_inhibitor_v1_activate( struct wlr_keyboard_shortcuts_inhibitor_v1 *inhibitor) { if (!inhibitor->active) { zwp_keyboard_shortcuts_inhibitor_v1_send_active( inhibitor->resource); inhibitor->active = true; } } void wlr_keyboard_shortcuts_inhibitor_v1_deactivate( struct wlr_keyboard_shortcuts_inhibitor_v1 *inhibitor) { if (inhibitor->active) { zwp_keyboard_shortcuts_inhibitor_v1_send_inactive( inhibitor->resource); inhibitor->active = false; } } wlroots-0.17.1/types/wlr_layer_shell_v1.c000066400000000000000000000436741454110342200204300ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include #include #include "wlr-layer-shell-unstable-v1-protocol.h" // Note: zwlr_layer_surface_v1 becomes inert on wlr_layer_surface_v1_destroy() #define LAYER_SHELL_VERSION 4 static void resource_handle_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static const struct zwlr_layer_shell_v1_interface layer_shell_implementation; static const struct zwlr_layer_surface_v1_interface layer_surface_implementation; static void layer_surface_configure_destroy( struct wlr_layer_surface_v1_configure *configure) { if (configure == NULL) { return; } wl_list_remove(&configure->link); free(configure); } static void layer_surface_reset(struct wlr_layer_surface_v1 *surface) { surface->configured = false; surface->initialized = false; struct wlr_xdg_popup *popup, *popup_tmp; wl_list_for_each_safe(popup, popup_tmp, &surface->popups, link) { wlr_xdg_popup_destroy(popup); } struct wlr_layer_surface_v1_configure *configure, *tmp; wl_list_for_each_safe(configure, tmp, &surface->configure_list, link) { layer_surface_configure_destroy(configure); } } static void layer_surface_destroy(struct wlr_layer_surface_v1 *surface) { wlr_surface_unmap(surface->surface); layer_surface_reset(surface); wl_signal_emit_mutable(&surface->events.destroy, surface); wl_resource_set_user_data(surface->resource, NULL); free(surface->namespace); free(surface); } static struct wlr_layer_shell_v1 *layer_shell_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &zwlr_layer_shell_v1_interface, &layer_shell_implementation)); return wl_resource_get_user_data(resource); } struct wlr_layer_surface_v1 *wlr_layer_surface_v1_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &zwlr_layer_surface_v1_interface, &layer_surface_implementation)); return wl_resource_get_user_data(resource); } static const struct wlr_surface_role layer_surface_role; struct wlr_layer_surface_v1 *wlr_layer_surface_v1_try_from_wlr_surface( struct wlr_surface *surface) { if (surface->role != &layer_surface_role || surface->role_resource == NULL) { return NULL; } return wlr_layer_surface_v1_from_resource(surface->role_resource); } static void layer_surface_handle_ack_configure(struct wl_client *client, struct wl_resource *resource, uint32_t serial) { struct wlr_layer_surface_v1 *surface = wlr_layer_surface_v1_from_resource(resource); if (!surface) { return; } // First find the ack'ed configure bool found = false; struct wlr_layer_surface_v1_configure *configure, *tmp; wl_list_for_each(configure, &surface->configure_list, link) { if (configure->serial == serial) { found = true; break; } } if (!found) { wl_resource_post_error(resource, ZWLR_LAYER_SURFACE_V1_ERROR_INVALID_SURFACE_STATE, "wrong configure serial: %" PRIu32, serial); return; } // Then remove old configures from the list wl_list_for_each_safe(configure, tmp, &surface->configure_list, link) { if (configure->serial == serial) { break; } layer_surface_configure_destroy(configure); } surface->pending.configure_serial = configure->serial; surface->pending.actual_width = configure->width; surface->pending.actual_height = configure->height; surface->configured = true; layer_surface_configure_destroy(configure); } static void layer_surface_handle_set_size(struct wl_client *client, struct wl_resource *resource, uint32_t width, uint32_t height) { struct wlr_layer_surface_v1 *surface = wlr_layer_surface_v1_from_resource(resource); if (!surface) { return; } if (surface->current.desired_width == width && surface->current.desired_height == height) { surface->pending.committed &= ~WLR_LAYER_SURFACE_V1_STATE_DESIRED_SIZE; } else { surface->pending.committed |= WLR_LAYER_SURFACE_V1_STATE_DESIRED_SIZE; } surface->pending.desired_width = width; surface->pending.desired_height = height; } static void layer_surface_handle_set_anchor(struct wl_client *client, struct wl_resource *resource, uint32_t anchor) { const uint32_t max_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; if (anchor > max_anchor) { wl_resource_post_error(resource, ZWLR_LAYER_SURFACE_V1_ERROR_INVALID_ANCHOR, "invalid anchor %" PRIu32, anchor); } struct wlr_layer_surface_v1 *surface = wlr_layer_surface_v1_from_resource(resource); if (!surface) { return; } if (surface->current.anchor == anchor) { surface->pending.committed &= ~WLR_LAYER_SURFACE_V1_STATE_ANCHOR; } else { surface->pending.committed |= WLR_LAYER_SURFACE_V1_STATE_ANCHOR; } surface->pending.anchor = anchor; } static void layer_surface_handle_set_exclusive_zone(struct wl_client *client, struct wl_resource *resource, int32_t zone) { struct wlr_layer_surface_v1 *surface = wlr_layer_surface_v1_from_resource(resource); if (!surface) { return; } if (surface->current.exclusive_zone == zone) { surface->pending.committed &= ~WLR_LAYER_SURFACE_V1_STATE_EXCLUSIVE_ZONE; } else { surface->pending.committed |= WLR_LAYER_SURFACE_V1_STATE_EXCLUSIVE_ZONE; } surface->pending.exclusive_zone = zone; } static void layer_surface_handle_set_margin( struct wl_client *client, struct wl_resource *resource, int32_t top, int32_t right, int32_t bottom, int32_t left) { struct wlr_layer_surface_v1 *surface = wlr_layer_surface_v1_from_resource(resource); if (!surface) { return; } if (surface->current.margin.top == top && surface->current.margin.right == right && surface->current.margin.bottom == bottom && surface->current.margin.left == left) { surface->pending.committed &= ~WLR_LAYER_SURFACE_V1_STATE_MARGIN; } else { surface->pending.committed |= WLR_LAYER_SURFACE_V1_STATE_MARGIN; } surface->pending.margin.top = top; surface->pending.margin.right = right; surface->pending.margin.bottom = bottom; surface->pending.margin.left = left; } static void layer_surface_handle_set_keyboard_interactivity( struct wl_client *client, struct wl_resource *resource, uint32_t interactive) { struct wlr_layer_surface_v1 *surface = wlr_layer_surface_v1_from_resource(resource); if (!surface) { return; } surface->pending.committed |= WLR_LAYER_SURFACE_V1_STATE_KEYBOARD_INTERACTIVITY; if (wl_resource_get_version(resource) < ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_ON_DEMAND_SINCE_VERSION) { surface->pending.keyboard_interactive = !!interactive; } else { if (interactive > ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_ON_DEMAND) { wl_resource_post_error(resource, ZWLR_LAYER_SURFACE_V1_ERROR_INVALID_KEYBOARD_INTERACTIVITY, "wrong keyboard interactivity value: %" PRIu32, interactive); } else { surface->pending.keyboard_interactive = interactive; } } } static void layer_surface_handle_get_popup(struct wl_client *client, struct wl_resource *layer_resource, struct wl_resource *popup_resource) { struct wlr_layer_surface_v1 *parent = wlr_layer_surface_v1_from_resource(layer_resource); struct wlr_xdg_popup *popup = wlr_xdg_popup_from_resource(popup_resource); if (!parent) { return; } if (popup->parent != NULL) { wl_resource_post_error(layer_resource, -1, "xdg_popup already has a parent"); return; } popup->parent = parent->surface; wl_list_insert(&parent->popups, &popup->link); wl_signal_emit_mutable(&parent->events.new_popup, popup); } static void layer_surface_set_layer(struct wl_client *client, struct wl_resource *surface_resource, uint32_t layer) { struct wlr_layer_surface_v1 *surface = wlr_layer_surface_v1_from_resource(surface_resource); if (!surface) { return; } if (layer > ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY) { wl_resource_post_error(surface->resource, ZWLR_LAYER_SHELL_V1_ERROR_INVALID_LAYER, "Invalid layer %" PRIu32, layer); return; } if (surface->current.layer == layer) { surface->pending.committed &= ~WLR_LAYER_SURFACE_V1_STATE_LAYER; } else { surface->pending.committed |= WLR_LAYER_SURFACE_V1_STATE_LAYER; } surface->pending.layer = layer; } static const struct zwlr_layer_surface_v1_interface layer_surface_implementation = { .destroy = resource_handle_destroy, .ack_configure = layer_surface_handle_ack_configure, .set_size = layer_surface_handle_set_size, .set_anchor = layer_surface_handle_set_anchor, .set_exclusive_zone = layer_surface_handle_set_exclusive_zone, .set_margin = layer_surface_handle_set_margin, .set_keyboard_interactivity = layer_surface_handle_set_keyboard_interactivity, .get_popup = layer_surface_handle_get_popup, .set_layer = layer_surface_set_layer, }; uint32_t wlr_layer_surface_v1_configure(struct wlr_layer_surface_v1 *surface, uint32_t width, uint32_t height) { if (!surface->initialized) { wlr_log(WLR_ERROR, "A configure is sent to an uninitialized wlr_layer_surface_v1 %p", surface); } struct wl_display *display = wl_client_get_display(wl_resource_get_client(surface->resource)); struct wlr_layer_surface_v1_configure *configure = calloc(1, sizeof(*configure)); if (configure == NULL) { wl_client_post_no_memory(wl_resource_get_client(surface->resource)); return surface->pending.configure_serial; } wl_list_insert(surface->configure_list.prev, &configure->link); configure->width = width; configure->height = height; configure->serial = wl_display_next_serial(display); zwlr_layer_surface_v1_send_configure(surface->resource, configure->serial, configure->width, configure->height); return configure->serial; } void wlr_layer_surface_v1_destroy(struct wlr_layer_surface_v1 *surface) { if (surface == NULL) { return; } zwlr_layer_surface_v1_send_closed(surface->resource); layer_surface_destroy(surface); } static void layer_surface_role_commit(struct wlr_surface *wlr_surface) { struct wlr_layer_surface_v1 *surface = wlr_layer_surface_v1_try_from_wlr_surface(wlr_surface); if (surface == NULL) { return; } if (wlr_surface_has_buffer(surface->surface) && !surface->configured) { wl_resource_post_error(surface->resource, ZWLR_LAYER_SHELL_V1_ERROR_ALREADY_CONSTRUCTED, "layer_surface has never been configured"); return; } const uint32_t horiz = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; if (surface->pending.desired_width == 0 && (surface->pending.anchor & horiz) != horiz) { wl_resource_post_error(surface->resource, ZWLR_LAYER_SURFACE_V1_ERROR_INVALID_SIZE, "width 0 requested without setting left and right anchors"); return; } const uint32_t vert = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; if (surface->pending.desired_height == 0 && (surface->pending.anchor & vert) != vert) { wl_resource_post_error(surface->resource, ZWLR_LAYER_SURFACE_V1_ERROR_INVALID_SIZE, "height 0 requested without setting top and bottom anchors"); return; } if (surface->surface->unmap_commit) { layer_surface_reset(surface); assert(!surface->initialized); surface->initial_commit = false; } else { surface->initial_commit = !surface->initialized; surface->initialized = true; } surface->current = surface->pending; surface->pending.committed = 0; if (!surface->added) { surface->added = true; wl_signal_emit_mutable(&surface->shell->events.new_surface, surface); // Return early here as the compositor may have closed this layer surface // in response to the new_surface event. return; } if (wlr_surface_has_buffer(wlr_surface)) { wlr_surface_map(wlr_surface); } } static void layer_surface_role_destroy(struct wlr_surface *wlr_surface) { struct wlr_layer_surface_v1 *surface = wlr_layer_surface_v1_try_from_wlr_surface(wlr_surface); if (surface == NULL) { return; } layer_surface_destroy(surface); } static const struct wlr_surface_role layer_surface_role = { .name = "zwlr_layer_surface_v1", .commit = layer_surface_role_commit, .destroy = layer_surface_role_destroy, }; static void layer_shell_handle_get_layer_surface(struct wl_client *wl_client, struct wl_resource *client_resource, uint32_t id, struct wl_resource *surface_resource, struct wl_resource *output_resource, uint32_t layer, const char *namespace) { struct wlr_layer_shell_v1 *shell = layer_shell_from_resource(client_resource); struct wlr_surface *wlr_surface = wlr_surface_from_resource(surface_resource); struct wlr_layer_surface_v1 *surface = calloc(1, sizeof(*surface)); if (surface == NULL) { wl_client_post_no_memory(wl_client); return; } if (!wlr_surface_set_role(wlr_surface, &layer_surface_role, client_resource, ZWLR_LAYER_SHELL_V1_ERROR_ROLE)) { free(surface); return; } surface->shell = shell; surface->surface = wlr_surface; if (output_resource) { surface->output = wlr_output_from_resource(output_resource); } surface->current.layer = surface->pending.layer = layer; if (layer > ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY) { free(surface); wl_resource_post_error(client_resource, ZWLR_LAYER_SHELL_V1_ERROR_INVALID_LAYER, "Invalid layer %" PRIu32, layer); return; } surface->namespace = strdup(namespace); if (surface->namespace == NULL) { free(surface); wl_client_post_no_memory(wl_client); return; } surface->resource = wl_resource_create(wl_client, &zwlr_layer_surface_v1_interface, wl_resource_get_version(client_resource), id); if (surface->resource == NULL) { free(surface->namespace); free(surface); wl_client_post_no_memory(wl_client); return; } wl_list_init(&surface->configure_list); wl_list_init(&surface->popups); wl_signal_init(&surface->events.destroy); wl_signal_init(&surface->events.new_popup); wlr_log(WLR_DEBUG, "new layer_surface %p (res %p)", surface, surface->resource); wl_resource_set_implementation(surface->resource, &layer_surface_implementation, surface, NULL); wlr_surface_set_role_object(wlr_surface, surface->resource); } static const struct zwlr_layer_shell_v1_interface layer_shell_implementation = { .get_layer_surface = layer_shell_handle_get_layer_surface, .destroy = resource_handle_destroy, }; static void layer_shell_bind(struct wl_client *wl_client, void *data, uint32_t version, uint32_t id) { struct wlr_layer_shell_v1 *layer_shell = data; struct wl_resource *resource = wl_resource_create( wl_client, &zwlr_layer_shell_v1_interface, version, id); if (resource == NULL) { wl_client_post_no_memory(wl_client); return; } wl_resource_set_implementation(resource, &layer_shell_implementation, layer_shell, NULL); } static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_layer_shell_v1 *layer_shell = wl_container_of(listener, layer_shell, display_destroy); wl_signal_emit_mutable(&layer_shell->events.destroy, layer_shell); wl_list_remove(&layer_shell->display_destroy.link); wl_global_destroy(layer_shell->global); free(layer_shell); } struct wlr_layer_shell_v1 *wlr_layer_shell_v1_create(struct wl_display *display, uint32_t version) { assert(version <= LAYER_SHELL_VERSION); struct wlr_layer_shell_v1 *layer_shell = calloc(1, sizeof(*layer_shell)); if (!layer_shell) { return NULL; } struct wl_global *global = wl_global_create(display, &zwlr_layer_shell_v1_interface, version, layer_shell, layer_shell_bind); if (!global) { free(layer_shell); return NULL; } layer_shell->global = global; wl_signal_init(&layer_shell->events.new_surface); wl_signal_init(&layer_shell->events.destroy); layer_shell->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &layer_shell->display_destroy); return layer_shell; } struct layer_surface_iterator_data { wlr_surface_iterator_func_t user_iterator; void *user_data; int x, y; }; static void layer_surface_iterator(struct wlr_surface *surface, int sx, int sy, void *data) { struct layer_surface_iterator_data *iter_data = data; iter_data->user_iterator(surface, iter_data->x + sx, iter_data->y + sy, iter_data->user_data); } void wlr_layer_surface_v1_for_each_surface(struct wlr_layer_surface_v1 *surface, wlr_surface_iterator_func_t iterator, void *user_data) { wlr_surface_for_each_surface(surface->surface, iterator, user_data); wlr_layer_surface_v1_for_each_popup_surface(surface, iterator, user_data); } void wlr_layer_surface_v1_for_each_popup_surface(struct wlr_layer_surface_v1 *surface, wlr_surface_iterator_func_t iterator, void *user_data) { struct wlr_xdg_popup *popup; wl_list_for_each(popup, &surface->popups, link) { if (!popup->base->surface->mapped) { continue; } double popup_sx, popup_sy; popup_sx = popup->current.geometry.x - popup->base->current.geometry.x; popup_sy = popup->current.geometry.y - popup->base->current.geometry.y; struct layer_surface_iterator_data data = { .user_iterator = iterator, .user_data = user_data, .x = popup_sx, .y = popup_sy, }; wlr_xdg_surface_for_each_surface(popup->base, layer_surface_iterator, &data); } } struct wlr_surface *wlr_layer_surface_v1_surface_at( struct wlr_layer_surface_v1 *surface, double sx, double sy, double *sub_x, double *sub_y) { struct wlr_surface *sub = wlr_layer_surface_v1_popup_surface_at(surface, sx, sy, sub_x, sub_y); if (sub != NULL) { return sub; } return wlr_surface_surface_at(surface->surface, sx, sy, sub_x, sub_y); } struct wlr_surface *wlr_layer_surface_v1_popup_surface_at( struct wlr_layer_surface_v1 *surface, double sx, double sy, double *sub_x, double *sub_y) { struct wlr_xdg_popup *popup; wl_list_for_each(popup, &surface->popups, link) { if (!popup->base->surface->mapped) { continue; } double popup_sx, popup_sy; popup_sx = popup->current.geometry.x - popup->base->current.geometry.x; popup_sy = popup->current.geometry.y - popup->base->current.geometry.y; struct wlr_surface *sub = wlr_xdg_surface_surface_at( popup->base, sx - popup_sx, sy - popup_sy, sub_x, sub_y); if (sub != NULL) { return sub; } } return NULL; } wlroots-0.17.1/types/wlr_linux_dmabuf_v1.c000066400000000000000000001060451454110342200205720ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "linux-dmabuf-unstable-v1-protocol.h" #include "render/drm_format_set.h" #include "util/shm.h" #define LINUX_DMABUF_VERSION 4 struct wlr_linux_buffer_params_v1 { struct wl_resource *resource; struct wlr_linux_dmabuf_v1 *linux_dmabuf; struct wlr_dmabuf_attributes attributes; bool has_modifier; }; struct wlr_linux_dmabuf_feedback_v1_compiled_tranche { dev_t target_device; uint32_t flags; // bitfield of enum zwp_linux_dmabuf_feedback_v1_tranche_flags struct wl_array indices; // uint16_t }; struct wlr_linux_dmabuf_feedback_v1_compiled { dev_t main_device; int table_fd; size_t table_size; size_t tranches_len; struct wlr_linux_dmabuf_feedback_v1_compiled_tranche tranches[]; }; struct wlr_linux_dmabuf_feedback_v1_table_entry { uint32_t format; uint32_t pad; // unused uint64_t modifier; }; // TODO: switch back to static_assert once this fix propagates in stable trees: // https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=255290 _Static_assert(sizeof(struct wlr_linux_dmabuf_feedback_v1_table_entry) == 16, "Expected wlr_linux_dmabuf_feedback_v1_table_entry to be tightly packed"); struct wlr_linux_dmabuf_v1_surface { struct wlr_surface *surface; struct wlr_linux_dmabuf_v1 *linux_dmabuf; struct wl_list link; // wlr_linux_dmabuf_v1.surfaces struct wlr_addon addon; struct wlr_linux_dmabuf_feedback_v1_compiled *feedback; struct wl_list feedback_resources; // wl_resource_get_link }; static void buffer_handle_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static const struct wl_buffer_interface wl_buffer_impl = { .destroy = buffer_handle_destroy, }; static bool buffer_resource_is_instance(struct wl_resource *resource) { return wl_resource_instance_of(resource, &wl_buffer_interface, &wl_buffer_impl); } struct wlr_dmabuf_v1_buffer *wlr_dmabuf_v1_buffer_try_from_buffer_resource( struct wl_resource *resource) { if (!buffer_resource_is_instance(resource) || wl_resource_get_user_data(resource) == NULL) { return NULL; } return wl_resource_get_user_data(resource); } static const struct wlr_buffer_impl buffer_impl; static struct wlr_dmabuf_v1_buffer *dmabuf_v1_buffer_from_buffer( struct wlr_buffer *wlr_buffer) { assert(wlr_buffer->impl == &buffer_impl); struct wlr_dmabuf_v1_buffer *buffer = wl_container_of(wlr_buffer, buffer, base); return buffer; } static void buffer_destroy(struct wlr_buffer *wlr_buffer) { struct wlr_dmabuf_v1_buffer *buffer = dmabuf_v1_buffer_from_buffer(wlr_buffer); if (buffer->resource != NULL) { wl_resource_set_user_data(buffer->resource, NULL); } wlr_dmabuf_attributes_finish(&buffer->attributes); wl_list_remove(&buffer->release.link); free(buffer); } static bool buffer_get_dmabuf(struct wlr_buffer *wlr_buffer, struct wlr_dmabuf_attributes *attribs) { struct wlr_dmabuf_v1_buffer *buffer = dmabuf_v1_buffer_from_buffer(wlr_buffer); *attribs = buffer->attributes; return true; } static const struct wlr_buffer_impl buffer_impl = { .destroy = buffer_destroy, .get_dmabuf = buffer_get_dmabuf, }; static void buffer_handle_release(struct wl_listener *listener, void *data) { struct wlr_dmabuf_v1_buffer *buffer = wl_container_of(listener, buffer, release); if (buffer->resource != NULL) { wl_buffer_send_release(buffer->resource); } } static const struct zwp_linux_buffer_params_v1_interface buffer_params_impl; static struct wlr_linux_buffer_params_v1 *params_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &zwp_linux_buffer_params_v1_interface, &buffer_params_impl)); return wl_resource_get_user_data(resource); } static void params_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static void params_add(struct wl_client *client, struct wl_resource *params_resource, int32_t fd, uint32_t plane_idx, uint32_t offset, uint32_t stride, uint32_t modifier_hi, uint32_t modifier_lo) { struct wlr_linux_buffer_params_v1 *params = params_from_resource(params_resource); if (!params) { wl_resource_post_error(params_resource, ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ALREADY_USED, "params was already used to create a wl_buffer"); close(fd); return; } if (plane_idx >= WLR_DMABUF_MAX_PLANES) { wl_resource_post_error(params_resource, ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_IDX, "plane index %u > %u", plane_idx, WLR_DMABUF_MAX_PLANES); close(fd); return; } if (params->attributes.fd[plane_idx] != -1) { wl_resource_post_error(params_resource, ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_SET, "a dmabuf with FD %d has already been added for plane %u", params->attributes.fd[plane_idx], plane_idx); close(fd); return; } uint64_t modifier = ((uint64_t)modifier_hi << 32) | modifier_lo; if (params->has_modifier && modifier != params->attributes.modifier) { wl_resource_post_error(params_resource, ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_FORMAT, "sent modifier %" PRIu64 " for plane %u, expected" " modifier %" PRIu64 " like other planes", modifier, plane_idx, params->attributes.modifier); close(fd); return; } params->attributes.modifier = modifier; params->has_modifier = true; params->attributes.fd[plane_idx] = fd; params->attributes.offset[plane_idx] = offset; params->attributes.stride[plane_idx] = stride; params->attributes.n_planes++; } static void buffer_handle_resource_destroy(struct wl_resource *buffer_resource) { struct wlr_dmabuf_v1_buffer *buffer = wlr_dmabuf_v1_buffer_try_from_buffer_resource(buffer_resource); assert(buffer != NULL); buffer->resource = NULL; wlr_buffer_drop(&buffer->base); } static bool check_import_dmabuf(struct wlr_linux_dmabuf_v1 *linux_dmabuf, struct wlr_dmabuf_attributes *attribs) { if (linux_dmabuf->main_device_fd < 0) { return true; } // TODO: check number of planes for (int i = 0; i < attribs->n_planes; i++) { uint32_t handle = 0; if (drmPrimeFDToHandle(linux_dmabuf->main_device_fd, attribs->fd[i], &handle) != 0) { wlr_log_errno(WLR_DEBUG, "Failed to import DMA-BUF FD"); return false; } if (drmCloseBufferHandle(linux_dmabuf->main_device_fd, handle) != 0) { wlr_log_errno(WLR_ERROR, "Failed to close buffer handle"); return false; } } return true; } static void params_create_common(struct wl_resource *params_resource, uint32_t buffer_id, int32_t width, int32_t height, uint32_t format, uint32_t flags) { struct wlr_linux_buffer_params_v1 *params = params_from_resource(params_resource); if (!params) { wl_resource_post_error(params_resource, ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ALREADY_USED, "params was already used to create a wl_buffer"); return; } struct wlr_dmabuf_attributes attribs = params->attributes; struct wlr_linux_dmabuf_v1 *linux_dmabuf = params->linux_dmabuf; // Make the params resource inert wl_resource_set_user_data(params_resource, NULL); free(params); if (!attribs.n_planes) { wl_resource_post_error(params_resource, ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE, "no dmabuf has been added to the params"); goto err_out; } if (attribs.fd[0] == -1) { wl_resource_post_error(params_resource, ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE, "no dmabuf has been added for plane 0"); goto err_out; } if ((attribs.fd[3] >= 0 || attribs.fd[2] >= 0) && (attribs.fd[2] == -1 || attribs.fd[1] == -1)) { wl_resource_post_error(params_resource, ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE, "gap in dmabuf planes"); goto err_out; } /* reject unknown flags */ uint32_t all_flags = ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_Y_INVERT | ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_INTERLACED | ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_BOTTOM_FIRST; if (flags & ~all_flags) { wl_resource_post_error(params_resource, ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_FORMAT, "Unknown dmabuf flags %"PRIu32, flags); goto err_out; } if (flags != 0) { wlr_log(WLR_ERROR, "dmabuf flags aren't supported"); goto err_failed; } attribs.width = width; attribs.height = height; attribs.format = format; if (width < 1 || height < 1) { wl_resource_post_error(params_resource, ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_DIMENSIONS, "invalid width %d or height %d", width, height); goto err_out; } for (int i = 0; i < attribs.n_planes; i++) { if ((uint64_t)attribs.offset[i] + attribs.stride[i] > UINT32_MAX) { wl_resource_post_error(params_resource, ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS, "size overflow for plane %d", i); goto err_out; } if ((uint64_t)attribs.offset[i] + (uint64_t)attribs.stride[i] * height > UINT32_MAX) { wl_resource_post_error(params_resource, ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS, "size overflow for plane %d", i); goto err_out; } off_t size = lseek(attribs.fd[i], 0, SEEK_END); if (size == -1) { // Skip checks if kernel does no support seek on buffer continue; } if (attribs.offset[i] > size) { wl_resource_post_error(params_resource, ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS, "invalid offset %" PRIu32 " for plane %d", attribs.offset[i], i); goto err_out; } if (attribs.offset[i] + attribs.stride[i] > size || attribs.stride[i] == 0) { wl_resource_post_error(params_resource, ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS, "invalid stride %" PRIu32 " for plane %d", attribs.stride[i], i); goto err_out; } // planes > 0 might be subsampled according to fourcc format if (i == 0 && attribs.offset[i] + attribs.stride[i] * height > size) { wl_resource_post_error(params_resource, ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS, "invalid buffer stride or height for plane %d", i); goto err_out; } } /* Check if dmabuf is usable */ if (!check_import_dmabuf(linux_dmabuf, &attribs)) { goto err_failed; } struct wlr_dmabuf_v1_buffer *buffer = calloc(1, sizeof(*buffer)); if (!buffer) { wl_resource_post_no_memory(params_resource); goto err_failed; } wlr_buffer_init(&buffer->base, &buffer_impl, attribs.width, attribs.height); struct wl_client *client = wl_resource_get_client(params_resource); buffer->resource = wl_resource_create(client, &wl_buffer_interface, 1, buffer_id); if (!buffer->resource) { wl_resource_post_no_memory(params_resource); free(buffer); goto err_failed; } wl_resource_set_implementation(buffer->resource, &wl_buffer_impl, buffer, buffer_handle_resource_destroy); buffer->attributes = attribs; buffer->release.notify = buffer_handle_release; wl_signal_add(&buffer->base.events.release, &buffer->release); /* send 'created' event when the request is not for an immediate * import, that is buffer_id is zero */ if (buffer_id == 0) { zwp_linux_buffer_params_v1_send_created(params_resource, buffer->resource); } return; err_failed: if (buffer_id == 0) { zwp_linux_buffer_params_v1_send_failed(params_resource); } else { /* since the behavior is left implementation defined by the * protocol in case of create_immed failure due to an unknown cause, * we choose to treat it as a fatal error and immediately kill the * client instead of creating an invalid handle and waiting for it * to be used. */ wl_resource_post_error(params_resource, ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_WL_BUFFER, "importing the supplied dmabufs failed"); } err_out: wlr_dmabuf_attributes_finish(&attribs); } static void params_create(struct wl_client *client, struct wl_resource *params_resource, int32_t width, int32_t height, uint32_t format, uint32_t flags) { params_create_common(params_resource, 0, width, height, format, flags); } static void params_create_immed(struct wl_client *client, struct wl_resource *params_resource, uint32_t buffer_id, int32_t width, int32_t height, uint32_t format, uint32_t flags) { params_create_common(params_resource, buffer_id, width, height, format, flags); } static const struct zwp_linux_buffer_params_v1_interface buffer_params_impl = { .destroy = params_destroy, .add = params_add, .create = params_create, .create_immed = params_create_immed, }; static void params_handle_resource_destroy(struct wl_resource *resource) { struct wlr_linux_buffer_params_v1 *params = params_from_resource(resource); if (!params) { return; } wlr_dmabuf_attributes_finish(¶ms->attributes); free(params); } static const struct zwp_linux_dmabuf_v1_interface linux_dmabuf_impl; static struct wlr_linux_dmabuf_v1 *linux_dmabuf_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &zwp_linux_dmabuf_v1_interface, &linux_dmabuf_impl)); struct wlr_linux_dmabuf_v1 *dmabuf = wl_resource_get_user_data(resource); assert(dmabuf); return dmabuf; } static void linux_dmabuf_create_params(struct wl_client *client, struct wl_resource *linux_dmabuf_resource, uint32_t params_id) { struct wlr_linux_dmabuf_v1 *linux_dmabuf = linux_dmabuf_from_resource(linux_dmabuf_resource); struct wlr_linux_buffer_params_v1 *params = calloc(1, sizeof(*params)); if (!params) { wl_resource_post_no_memory(linux_dmabuf_resource); return; } for (int i = 0; i < WLR_DMABUF_MAX_PLANES; i++) { params->attributes.fd[i] = -1; } params->linux_dmabuf = linux_dmabuf; uint32_t version = wl_resource_get_version(linux_dmabuf_resource); params->resource = wl_resource_create(client, &zwp_linux_buffer_params_v1_interface, version, params_id); if (!params->resource) { free(params); wl_resource_post_no_memory(linux_dmabuf_resource); return; } wl_resource_set_implementation(params->resource, &buffer_params_impl, params, params_handle_resource_destroy); } static void linux_dmabuf_feedback_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static const struct zwp_linux_dmabuf_feedback_v1_interface linux_dmabuf_feedback_impl = { .destroy = linux_dmabuf_feedback_destroy, }; static ssize_t get_drm_format_set_index(const struct wlr_drm_format_set *set, uint32_t format, uint64_t modifier) { bool format_found = false; const struct wlr_drm_format *fmt; size_t idx = 0; for (size_t i = 0; i < set->len; i++) { fmt = &set->formats[i]; if (fmt->format == format) { format_found = true; break; } idx += fmt->len; } if (!format_found) { return -1; } for (size_t i = 0; i < fmt->len; i++) { if (fmt->modifiers[i] == modifier) { return idx; } idx++; } return -1; } static struct wlr_linux_dmabuf_feedback_v1_compiled *feedback_compile( const struct wlr_linux_dmabuf_feedback_v1 *feedback) { const struct wlr_linux_dmabuf_feedback_v1_tranche *tranches = feedback->tranches.data; size_t tranches_len = feedback->tranches.size / sizeof(struct wlr_linux_dmabuf_feedback_v1_tranche); assert(tranches_len > 0); // Make one big format set that contains all formats across all tranches so that we // can build an index struct wlr_drm_format_set all_formats = {0}; for (size_t i = 0; i < tranches_len; i++) { const struct wlr_linux_dmabuf_feedback_v1_tranche *tranche = &tranches[i]; if (!wlr_drm_format_set_union(&all_formats, &all_formats, &tranche->formats)) { wlr_log(WLR_ERROR, "Failed to union scanout formats into one tranche"); goto err_all_formats; } } size_t table_len = 0; for (size_t i = 0; i < all_formats.len; i++) { const struct wlr_drm_format *fmt = &all_formats.formats[i]; table_len += fmt->len; } assert(table_len > 0); size_t table_size = table_len * sizeof(struct wlr_linux_dmabuf_feedback_v1_table_entry); int rw_fd, ro_fd; if (!allocate_shm_file_pair(table_size, &rw_fd, &ro_fd)) { wlr_log(WLR_ERROR, "Failed to allocate shm file for format table"); return NULL; } struct wlr_linux_dmabuf_feedback_v1_table_entry *table = mmap(NULL, table_size, PROT_READ | PROT_WRITE, MAP_SHARED, rw_fd, 0); if (table == MAP_FAILED) { wlr_log_errno(WLR_ERROR, "mmap failed"); close(rw_fd); close(ro_fd); goto err_all_formats; } close(rw_fd); size_t n = 0; for (size_t i = 0; i < all_formats.len; i++) { const struct wlr_drm_format *fmt = &all_formats.formats[i]; for (size_t k = 0; k < fmt->len; k++) { table[n] = (struct wlr_linux_dmabuf_feedback_v1_table_entry){ .format = fmt->format, .modifier = fmt->modifiers[k], }; n++; } } assert(n == table_len); munmap(table, table_size); struct wlr_linux_dmabuf_feedback_v1_compiled *compiled = calloc(1, sizeof(*compiled) + tranches_len * sizeof(struct wlr_linux_dmabuf_feedback_v1_compiled_tranche)); if (compiled == NULL) { close(ro_fd); goto err_all_formats; } compiled->main_device = feedback->main_device; compiled->tranches_len = tranches_len; compiled->table_fd = ro_fd; compiled->table_size = table_size; // Build the indices lists for all tranches for (size_t i = 0; i < tranches_len; i++) { const struct wlr_linux_dmabuf_feedback_v1_tranche *tranche = &tranches[i]; struct wlr_linux_dmabuf_feedback_v1_compiled_tranche *compiled_tranche = &compiled->tranches[i]; compiled_tranche->target_device = tranche->target_device; compiled_tranche->flags = tranche->flags; wl_array_init(&compiled_tranche->indices); if (!wl_array_add(&compiled_tranche->indices, table_len * sizeof(uint16_t))) { wlr_log(WLR_ERROR, "Failed to allocate tranche indices array"); goto error_compiled; } n = 0; uint16_t *indices = compiled_tranche->indices.data; for (size_t j = 0; j < tranche->formats.len; j++) { const struct wlr_drm_format *fmt = &tranche->formats.formats[j]; for (size_t k = 0; k < fmt->len; k++) { ssize_t index = get_drm_format_set_index( &all_formats, fmt->format, fmt->modifiers[k]); if (index < 0) { wlr_log(WLR_ERROR, "Format 0x%" PRIX32 " and modifier " "0x%" PRIX64 " are in tranche #%zu but are missing " "from the fallback tranche", fmt->format, fmt->modifiers[k], i); goto error_compiled; } indices[n] = index; n++; } } compiled_tranche->indices.size = n * sizeof(uint16_t); } wlr_drm_format_set_finish(&all_formats); return compiled; error_compiled: close(compiled->table_fd); free(compiled); err_all_formats: wlr_drm_format_set_finish(&all_formats); return NULL; } static void compiled_feedback_destroy( struct wlr_linux_dmabuf_feedback_v1_compiled *feedback) { if (feedback == NULL) { return; } for (size_t i = 0; i < feedback->tranches_len; i++) { wl_array_release(&feedback->tranches[i].indices); } close(feedback->table_fd); free(feedback); } static void feedback_tranche_send( const struct wlr_linux_dmabuf_feedback_v1_compiled_tranche *tranche, struct wl_resource *resource) { struct wl_array dev_array = { .size = sizeof(tranche->target_device), .data = (void *)&tranche->target_device, }; zwp_linux_dmabuf_feedback_v1_send_tranche_target_device(resource, &dev_array); zwp_linux_dmabuf_feedback_v1_send_tranche_flags(resource, tranche->flags); zwp_linux_dmabuf_feedback_v1_send_tranche_formats(resource, (struct wl_array *)&tranche->indices); zwp_linux_dmabuf_feedback_v1_send_tranche_done(resource); } static void feedback_send(const struct wlr_linux_dmabuf_feedback_v1_compiled *feedback, struct wl_resource *resource) { struct wl_array dev_array = { .size = sizeof(feedback->main_device), .data = (void *)&feedback->main_device, }; zwp_linux_dmabuf_feedback_v1_send_main_device(resource, &dev_array); zwp_linux_dmabuf_feedback_v1_send_format_table(resource, feedback->table_fd, feedback->table_size); for (size_t i = 0; i < feedback->tranches_len; i++) { feedback_tranche_send(&feedback->tranches[i], resource); } zwp_linux_dmabuf_feedback_v1_send_done(resource); } static void linux_dmabuf_get_default_feedback(struct wl_client *client, struct wl_resource *resource, uint32_t id) { struct wlr_linux_dmabuf_v1 *linux_dmabuf = linux_dmabuf_from_resource(resource); uint32_t version = wl_resource_get_version(resource); struct wl_resource *feedback_resource = wl_resource_create(client, &zwp_linux_dmabuf_feedback_v1_interface, version, id); if (feedback_resource == NULL) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(feedback_resource, &linux_dmabuf_feedback_impl, NULL, NULL); feedback_send(linux_dmabuf->default_feedback, feedback_resource); } static void surface_destroy(struct wlr_linux_dmabuf_v1_surface *surface) { struct wl_resource *resource, *resource_tmp; wl_resource_for_each_safe(resource, resource_tmp, &surface->feedback_resources) { struct wl_list *link = wl_resource_get_link(resource); wl_list_remove(link); wl_list_init(link); } compiled_feedback_destroy(surface->feedback); wlr_addon_finish(&surface->addon); wl_list_remove(&surface->link); free(surface); } static void surface_addon_destroy(struct wlr_addon *addon) { struct wlr_linux_dmabuf_v1_surface *surface = wl_container_of(addon, surface, addon); surface_destroy(surface); } static const struct wlr_addon_interface surface_addon_impl = { .name = "wlr_linux_dmabuf_v1_surface", .destroy = surface_addon_destroy, }; static struct wlr_linux_dmabuf_v1_surface *surface_get_or_create( struct wlr_linux_dmabuf_v1 *linux_dmabuf, struct wlr_surface *wlr_surface) { struct wlr_addon *addon = wlr_addon_find(&wlr_surface->addons, linux_dmabuf, &surface_addon_impl); if (addon != NULL) { struct wlr_linux_dmabuf_v1_surface *surface = wl_container_of(addon, surface, addon); return surface; } struct wlr_linux_dmabuf_v1_surface *surface = calloc(1, sizeof(*surface)); if (surface == NULL) { return NULL; } surface->surface = wlr_surface; surface->linux_dmabuf = linux_dmabuf; wl_list_init(&surface->feedback_resources); wlr_addon_init(&surface->addon, &wlr_surface->addons, linux_dmabuf, &surface_addon_impl); wl_list_insert(&linux_dmabuf->surfaces, &surface->link); return surface; } static const struct wlr_linux_dmabuf_feedback_v1_compiled *surface_get_feedback( struct wlr_linux_dmabuf_v1_surface *surface) { if (surface->feedback != NULL) { return surface->feedback; } return surface->linux_dmabuf->default_feedback; } static void surface_feedback_handle_resource_destroy(struct wl_resource *resource) { wl_list_remove(wl_resource_get_link(resource)); } static void linux_dmabuf_get_surface_feedback(struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *surface_resource) { struct wlr_linux_dmabuf_v1 *linux_dmabuf = linux_dmabuf_from_resource(resource); struct wlr_surface *wlr_surface = wlr_surface_from_resource(surface_resource); struct wlr_linux_dmabuf_v1_surface *surface = surface_get_or_create(linux_dmabuf, wlr_surface); if (surface == NULL) { wl_client_post_no_memory(client); return; } uint32_t version = wl_resource_get_version(resource); struct wl_resource *feedback_resource = wl_resource_create(client, &zwp_linux_dmabuf_feedback_v1_interface, version, id); if (feedback_resource == NULL) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(feedback_resource, &linux_dmabuf_feedback_impl, NULL, surface_feedback_handle_resource_destroy); wl_list_insert(&surface->feedback_resources, wl_resource_get_link(feedback_resource)); feedback_send(surface_get_feedback(surface), feedback_resource); } static void linux_dmabuf_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static const struct zwp_linux_dmabuf_v1_interface linux_dmabuf_impl = { .destroy = linux_dmabuf_destroy, .create_params = linux_dmabuf_create_params, .get_default_feedback = linux_dmabuf_get_default_feedback, .get_surface_feedback = linux_dmabuf_get_surface_feedback, }; static void linux_dmabuf_send_modifiers(struct wl_resource *resource, const struct wlr_drm_format *fmt) { if (wl_resource_get_version(resource) < ZWP_LINUX_DMABUF_V1_MODIFIER_SINCE_VERSION) { if (wlr_drm_format_has(fmt, DRM_FORMAT_MOD_INVALID)) { zwp_linux_dmabuf_v1_send_format(resource, fmt->format); } return; } // In case only INVALID and LINEAR are advertised, send INVALID only due to XWayland: // https://gitlab.freedesktop.org/xorg/xserver/-/issues/1166 if (fmt->len == 2 && wlr_drm_format_has(fmt, DRM_FORMAT_MOD_INVALID) && wlr_drm_format_has(fmt, DRM_FORMAT_MOD_LINEAR)) { uint64_t mod = DRM_FORMAT_MOD_INVALID; zwp_linux_dmabuf_v1_send_modifier(resource, fmt->format, mod >> 32, mod & 0xFFFFFFFF); return; } for (size_t i = 0; i < fmt->len; i++) { uint64_t mod = fmt->modifiers[i]; zwp_linux_dmabuf_v1_send_modifier(resource, fmt->format, mod >> 32, mod & 0xFFFFFFFF); } } static void linux_dmabuf_send_formats(struct wlr_linux_dmabuf_v1 *linux_dmabuf, struct wl_resource *resource) { for (size_t i = 0; i < linux_dmabuf->default_formats.len; i++) { const struct wlr_drm_format *fmt = &linux_dmabuf->default_formats.formats[i]; linux_dmabuf_send_modifiers(resource, fmt); } } static void linux_dmabuf_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id) { struct wlr_linux_dmabuf_v1 *linux_dmabuf = data; struct wl_resource *resource = wl_resource_create(client, &zwp_linux_dmabuf_v1_interface, version, id); if (resource == NULL) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(resource, &linux_dmabuf_impl, linux_dmabuf, NULL); if (version < ZWP_LINUX_DMABUF_V1_GET_DEFAULT_FEEDBACK_SINCE_VERSION) { linux_dmabuf_send_formats(linux_dmabuf, resource); } } static struct wlr_buffer *buffer_from_resource(struct wl_resource *resource) { struct wlr_dmabuf_v1_buffer *buffer = wlr_dmabuf_v1_buffer_try_from_buffer_resource(resource); assert(buffer != NULL); return &buffer->base; } static const struct wlr_buffer_resource_interface buffer_resource_interface = { .name = "wlr_dmabuf_v1_buffer", .is_instance = buffer_resource_is_instance, .from_resource = buffer_from_resource, }; static void linux_dmabuf_v1_destroy(struct wlr_linux_dmabuf_v1 *linux_dmabuf) { wl_signal_emit_mutable(&linux_dmabuf->events.destroy, linux_dmabuf); struct wlr_linux_dmabuf_v1_surface *surface, *surface_tmp; wl_list_for_each_safe(surface, surface_tmp, &linux_dmabuf->surfaces, link) { surface_destroy(surface); } compiled_feedback_destroy(linux_dmabuf->default_feedback); wlr_drm_format_set_finish(&linux_dmabuf->default_formats); if (linux_dmabuf->main_device_fd >= 0) { close(linux_dmabuf->main_device_fd); } wl_list_remove(&linux_dmabuf->display_destroy.link); wl_global_destroy(linux_dmabuf->global); free(linux_dmabuf); } static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_linux_dmabuf_v1 *linux_dmabuf = wl_container_of(listener, linux_dmabuf, display_destroy); linux_dmabuf_v1_destroy(linux_dmabuf); } static bool set_default_feedback(struct wlr_linux_dmabuf_v1 *linux_dmabuf, const struct wlr_linux_dmabuf_feedback_v1 *feedback) { struct wlr_linux_dmabuf_feedback_v1_compiled *compiled = feedback_compile(feedback); if (compiled == NULL) { return false; } drmDevice *device = NULL; if (drmGetDeviceFromDevId(feedback->main_device, 0, &device) != 0) { wlr_log_errno(WLR_ERROR, "drmGetDeviceFromDevId failed"); goto error_compiled; } int main_device_fd = -1; if (device->available_nodes & (1 << DRM_NODE_RENDER)) { const char *name = device->nodes[DRM_NODE_RENDER]; main_device_fd = open(name, O_RDWR | O_CLOEXEC); drmFreeDevice(&device); if (main_device_fd < 0) { wlr_log_errno(WLR_ERROR, "Failed to open DRM device %s", name); goto error_compiled; } } else { // Likely a split display/render setup. Unfortunately we have no way to // get back the proper render node used by the renderer under-the-hood. // TODO: drop once mesa!24825 is widespread assert(device->available_nodes & (1 << DRM_NODE_PRIMARY)); wlr_log(WLR_DEBUG, "DRM device %s has no render node, " "skipping DMA-BUF import checks", device->nodes[DRM_NODE_PRIMARY]); drmFreeDevice(&device); } size_t tranches_len = feedback->tranches.size / sizeof(struct wlr_linux_dmabuf_feedback_v1_tranche); const struct wlr_linux_dmabuf_feedback_v1_tranche *tranches = feedback->tranches.data; struct wlr_drm_format_set formats = {0}; for (size_t i = 0; i < tranches_len; i++) { const struct wlr_linux_dmabuf_feedback_v1_tranche *tranche = &tranches[i]; if (!wlr_drm_format_set_union(&formats, &formats, &tranche->formats)) { goto error_formats; } } compiled_feedback_destroy(linux_dmabuf->default_feedback); linux_dmabuf->default_feedback = compiled; if (linux_dmabuf->main_device_fd >= 0) { close(linux_dmabuf->main_device_fd); } linux_dmabuf->main_device_fd = main_device_fd; wlr_drm_format_set_finish(&linux_dmabuf->default_formats); linux_dmabuf->default_formats = formats; return true; error_formats: wlr_drm_format_set_finish(&formats); error_compiled: compiled_feedback_destroy(compiled); return false; } struct wlr_linux_dmabuf_v1 *wlr_linux_dmabuf_v1_create(struct wl_display *display, uint32_t version, const struct wlr_linux_dmabuf_feedback_v1 *default_feedback) { assert(version <= LINUX_DMABUF_VERSION); struct wlr_linux_dmabuf_v1 *linux_dmabuf = calloc(1, sizeof(*linux_dmabuf)); if (linux_dmabuf == NULL) { wlr_log(WLR_ERROR, "could not create simple dmabuf manager"); return NULL; } linux_dmabuf->main_device_fd = -1; wl_list_init(&linux_dmabuf->surfaces); wl_signal_init(&linux_dmabuf->events.destroy); linux_dmabuf->global = wl_global_create(display, &zwp_linux_dmabuf_v1_interface, version, linux_dmabuf, linux_dmabuf_bind); if (!linux_dmabuf->global) { wlr_log(WLR_ERROR, "could not create linux dmabuf v1 wl global"); goto error_linux_dmabuf; } if (!set_default_feedback(linux_dmabuf, default_feedback)) { goto error_global; } linux_dmabuf->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &linux_dmabuf->display_destroy); wlr_buffer_register_resource_interface(&buffer_resource_interface); return linux_dmabuf; error_global: wl_global_destroy(linux_dmabuf->global); error_linux_dmabuf: free(linux_dmabuf); return NULL; } struct wlr_linux_dmabuf_v1 *wlr_linux_dmabuf_v1_create_with_renderer(struct wl_display *display, uint32_t version, struct wlr_renderer *renderer) { const struct wlr_linux_dmabuf_feedback_v1_init_options options = { .main_renderer = renderer, }; struct wlr_linux_dmabuf_feedback_v1 feedback = {0}; if (!wlr_linux_dmabuf_feedback_v1_init_with_options(&feedback, &options)) { return NULL; } struct wlr_linux_dmabuf_v1 *linux_dmabuf = wlr_linux_dmabuf_v1_create(display, version, &feedback); wlr_linux_dmabuf_feedback_v1_finish(&feedback); return linux_dmabuf; } bool wlr_linux_dmabuf_v1_set_surface_feedback( struct wlr_linux_dmabuf_v1 *linux_dmabuf, struct wlr_surface *wlr_surface, const struct wlr_linux_dmabuf_feedback_v1 *feedback) { struct wlr_linux_dmabuf_v1_surface *surface = surface_get_or_create(linux_dmabuf, wlr_surface); if (surface == NULL) { return false; } struct wlr_linux_dmabuf_feedback_v1_compiled *compiled = NULL; if (feedback != NULL) { compiled = feedback_compile(feedback); if (compiled == NULL) { return false; } } compiled_feedback_destroy(surface->feedback); surface->feedback = compiled; struct wl_resource *resource; wl_resource_for_each(resource, &surface->feedback_resources) { feedback_send(surface_get_feedback(surface), resource); } return true; } struct wlr_linux_dmabuf_feedback_v1_tranche *wlr_linux_dmabuf_feedback_add_tranche( struct wlr_linux_dmabuf_feedback_v1 *feedback) { struct wlr_linux_dmabuf_feedback_v1_tranche *tranche = wl_array_add(&feedback->tranches, sizeof(*tranche)); if (tranche == NULL) { wlr_log_errno(WLR_ERROR, "Allocation failed"); return NULL; } *tranche = (struct wlr_linux_dmabuf_feedback_v1_tranche){0}; return tranche; } void wlr_linux_dmabuf_feedback_v1_finish(struct wlr_linux_dmabuf_feedback_v1 *feedback) { struct wlr_linux_dmabuf_feedback_v1_tranche *tranche; wl_array_for_each(tranche, &feedback->tranches) { wlr_drm_format_set_finish(&tranche->formats); } wl_array_release(&feedback->tranches); } static bool devid_from_fd(int fd, dev_t *devid) { struct stat stat; if (fstat(fd, &stat) != 0) { wlr_log_errno(WLR_ERROR, "fstat failed"); return false; } *devid = stat.st_rdev; return true; } bool wlr_linux_dmabuf_feedback_v1_init_with_options(struct wlr_linux_dmabuf_feedback_v1 *feedback, const struct wlr_linux_dmabuf_feedback_v1_init_options *options) { assert(options->main_renderer != NULL); assert(options->scanout_primary_output == NULL || options->output_layer_feedback_event == NULL); *feedback = (struct wlr_linux_dmabuf_feedback_v1){0}; int renderer_drm_fd = wlr_renderer_get_drm_fd(options->main_renderer); if (renderer_drm_fd < 0) { wlr_log(WLR_ERROR, "Failed to get renderer DRM FD"); goto error; } dev_t renderer_dev; if (!devid_from_fd(renderer_drm_fd, &renderer_dev)) { goto error; } feedback->main_device = renderer_dev; const struct wlr_drm_format_set *renderer_formats = wlr_renderer_get_dmabuf_texture_formats(options->main_renderer); if (renderer_formats == NULL) { wlr_log(WLR_ERROR, "Failed to get renderer DMA-BUF texture formats"); goto error; } if (options->output_layer_feedback_event != NULL) { const struct wlr_output_layer_feedback_event *event = options->output_layer_feedback_event; struct wlr_linux_dmabuf_feedback_v1_tranche *tranche = wlr_linux_dmabuf_feedback_add_tranche(feedback); if (tranche == NULL) { goto error; } tranche->target_device = event->target_device; tranche->flags = ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_FLAGS_SCANOUT; if (!wlr_drm_format_set_intersect(&tranche->formats, event->formats, renderer_formats)) { wlr_log(WLR_ERROR, "Failed to intersect renderer and scanout formats"); goto error; } } else if (options->scanout_primary_output != NULL) { int backend_drm_fd = wlr_backend_get_drm_fd(options->scanout_primary_output->backend); if (backend_drm_fd < 0) { wlr_log(WLR_ERROR, "Failed to get backend DRM FD"); goto error; } dev_t backend_dev; if (!devid_from_fd(backend_drm_fd, &backend_dev)) { goto error; } const struct wlr_drm_format_set *scanout_formats = wlr_output_get_primary_formats(options->scanout_primary_output, WLR_BUFFER_CAP_DMABUF); if (scanout_formats == NULL) { wlr_log(WLR_ERROR, "Failed to get output primary DMA-BUF formats"); goto error; } struct wlr_linux_dmabuf_feedback_v1_tranche *tranche = wlr_linux_dmabuf_feedback_add_tranche(feedback); if (tranche == NULL) { goto error; } tranche->target_device = backend_dev; tranche->flags = ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_FLAGS_SCANOUT; if (!wlr_drm_format_set_intersect(&tranche->formats, scanout_formats, renderer_formats)) { wlr_log(WLR_ERROR, "Failed to intersect renderer and scanout formats"); goto error; } } struct wlr_linux_dmabuf_feedback_v1_tranche *tranche = wlr_linux_dmabuf_feedback_add_tranche(feedback); if (tranche == NULL) { goto error; } tranche->target_device = renderer_dev; if (!wlr_drm_format_set_copy(&tranche->formats, renderer_formats)) { goto error; } return true; error: wlr_linux_dmabuf_feedback_v1_finish(feedback); return false; } wlroots-0.17.1/types/wlr_matrix.c000066400000000000000000000076631454110342200170210ustar00rootroot00000000000000#include #include #include #include #include #include #include "types/wlr_matrix.h" void wlr_matrix_identity(float mat[static 9]) { static const float identity[9] = { 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, }; memcpy(mat, identity, sizeof(identity)); } void wlr_matrix_multiply(float mat[static 9], const float a[static 9], const float b[static 9]) { float product[9]; product[0] = a[0]*b[0] + a[1]*b[3] + a[2]*b[6]; product[1] = a[0]*b[1] + a[1]*b[4] + a[2]*b[7]; product[2] = a[0]*b[2] + a[1]*b[5] + a[2]*b[8]; product[3] = a[3]*b[0] + a[4]*b[3] + a[5]*b[6]; product[4] = a[3]*b[1] + a[4]*b[4] + a[5]*b[7]; product[5] = a[3]*b[2] + a[4]*b[5] + a[5]*b[8]; product[6] = a[6]*b[0] + a[7]*b[3] + a[8]*b[6]; product[7] = a[6]*b[1] + a[7]*b[4] + a[8]*b[7]; product[8] = a[6]*b[2] + a[7]*b[5] + a[8]*b[8]; memcpy(mat, product, sizeof(product)); } void wlr_matrix_transpose(float mat[static 9], const float a[static 9]) { float transposition[9] = { a[0], a[3], a[6], a[1], a[4], a[7], a[2], a[5], a[8], }; memcpy(mat, transposition, sizeof(transposition)); } void wlr_matrix_translate(float mat[static 9], float x, float y) { float translate[9] = { 1.0f, 0.0f, x, 0.0f, 1.0f, y, 0.0f, 0.0f, 1.0f, }; wlr_matrix_multiply(mat, mat, translate); } void wlr_matrix_scale(float mat[static 9], float x, float y) { float scale[9] = { x, 0.0f, 0.0f, 0.0f, y, 0.0f, 0.0f, 0.0f, 1.0f, }; wlr_matrix_multiply(mat, mat, scale); } void wlr_matrix_rotate(float mat[static 9], float rad) { float rotate[9] = { cos(rad), -sin(rad), 0.0f, sin(rad), cos(rad), 0.0f, 0.0f, 0.0f, 1.0f, }; wlr_matrix_multiply(mat, mat, rotate); } static const float transforms[][9] = { [WL_OUTPUT_TRANSFORM_NORMAL] = { 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, }, [WL_OUTPUT_TRANSFORM_90] = { 0.0f, 1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, }, [WL_OUTPUT_TRANSFORM_180] = { -1.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, }, [WL_OUTPUT_TRANSFORM_270] = { 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, }, [WL_OUTPUT_TRANSFORM_FLIPPED] = { -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, }, [WL_OUTPUT_TRANSFORM_FLIPPED_90] = { 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, }, [WL_OUTPUT_TRANSFORM_FLIPPED_180] = { 1.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, }, [WL_OUTPUT_TRANSFORM_FLIPPED_270] = { 0.0f, -1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, }, }; void wlr_matrix_transform(float mat[static 9], enum wl_output_transform transform) { wlr_matrix_multiply(mat, mat, transforms[transform]); } void matrix_projection(float mat[static 9], int width, int height, enum wl_output_transform transform) { memset(mat, 0, sizeof(*mat) * 9); const float *t = transforms[transform]; float x = 2.0f / width; float y = 2.0f / height; // Rotation + reflection mat[0] = x * t[0]; mat[1] = x * t[1]; mat[3] = y * -t[3]; mat[4] = y * -t[4]; // Translation mat[2] = -copysign(1.0f, mat[0] + mat[1]); mat[5] = -copysign(1.0f, mat[3] + mat[4]); // Identity mat[8] = 1.0f; } void wlr_matrix_project_box(float mat[static 9], const struct wlr_box *box, enum wl_output_transform transform, float rotation, const float projection[static 9]) { int x = box->x; int y = box->y; int width = box->width; int height = box->height; wlr_matrix_identity(mat); wlr_matrix_translate(mat, x, y); if (rotation != 0) { wlr_matrix_translate(mat, width/2, height/2); wlr_matrix_rotate(mat, rotation); wlr_matrix_translate(mat, -width/2, -height/2); } wlr_matrix_scale(mat, width, height); if (transform != WL_OUTPUT_TRANSFORM_NORMAL) { wlr_matrix_translate(mat, 0.5, 0.5); wlr_matrix_transform(mat, transform); wlr_matrix_translate(mat, -0.5, -0.5); } wlr_matrix_multiply(mat, projection, mat); } wlroots-0.17.1/types/wlr_output_layer.c000066400000000000000000000010751454110342200202400ustar00rootroot00000000000000#include #include struct wlr_output_layer *wlr_output_layer_create(struct wlr_output *output) { struct wlr_output_layer *layer = calloc(1, sizeof(*layer)); if (layer == NULL) { return NULL; } wl_list_insert(&output->layers, &layer->link); wlr_addon_set_init(&layer->addons); wl_signal_init(&layer->events.feedback); return layer; } void wlr_output_layer_destroy(struct wlr_output_layer *layer) { if (layer == NULL) { return; } wlr_addon_set_finish(&layer->addons); wl_list_remove(&layer->link); free(layer); } wlroots-0.17.1/types/wlr_output_layout.c000066400000000000000000000324441454110342200204450ustar00rootroot00000000000000#include #include #include #include #include #include #include #include static const struct wlr_addon_interface addon_impl; struct wlr_output_layout *wlr_output_layout_create(void) { struct wlr_output_layout *layout = calloc(1, sizeof(*layout)); if (layout == NULL) { return NULL; } wl_list_init(&layout->outputs); wl_signal_init(&layout->events.add); wl_signal_init(&layout->events.change); wl_signal_init(&layout->events.destroy); return layout; } static void output_layout_output_destroy( struct wlr_output_layout_output *l_output) { wl_signal_emit_mutable(&l_output->events.destroy, l_output); wlr_output_destroy_global(l_output->output); wl_list_remove(&l_output->commit.link); wl_list_remove(&l_output->link); wlr_addon_finish(&l_output->addon); free(l_output); } void wlr_output_layout_destroy(struct wlr_output_layout *layout) { if (!layout) { return; } wl_signal_emit_mutable(&layout->events.destroy, layout); struct wlr_output_layout_output *l_output, *temp; wl_list_for_each_safe(l_output, temp, &layout->outputs, link) { output_layout_output_destroy(l_output); } free(layout); } static void output_layout_output_get_box( struct wlr_output_layout_output *l_output, struct wlr_box *box) { box->x = l_output->x; box->y = l_output->y; wlr_output_effective_resolution(l_output->output, &box->width, &box->height); } /** * This must be called whenever the layout changes to reconfigure the auto * configured outputs and emit the `changed` event. * * Auto configured outputs are placed to the right of the north east corner of * the rightmost output in the layout in a horizontal line. */ static void output_layout_reconfigure(struct wlr_output_layout *layout) { int max_x = INT_MIN; int max_x_y = INT_MIN; // y value for the max_x output // find the rightmost x coordinate occupied by a manually configured output // in the layout struct wlr_output_layout_output *l_output; struct wlr_box output_box; wl_list_for_each(l_output, &layout->outputs, link) { if (l_output->auto_configured) { continue; } output_layout_output_get_box(l_output, &output_box); if (output_box.x + output_box.width > max_x) { max_x = output_box.x + output_box.width; max_x_y = output_box.y; } } if (max_x == INT_MIN) { // there are no manually configured outputs max_x = 0; max_x_y = 0; } wl_list_for_each(l_output, &layout->outputs, link) { if (!l_output->auto_configured) { continue; } output_layout_output_get_box(l_output, &output_box); l_output->x = max_x; l_output->y = max_x_y; max_x += output_box.width; } wl_signal_emit_mutable(&layout->events.change, layout); } static void output_update_global(struct wlr_output *output) { // Don't expose the output if it doesn't have a current mode if (output->width > 0 && output->height > 0) { wlr_output_create_global(output); } else { wlr_output_destroy_global(output); } } static void handle_output_commit(struct wl_listener *listener, void *data) { struct wlr_output_layout_output *l_output = wl_container_of(listener, l_output, commit); struct wlr_output_event_commit *event = data; if (event->state->committed & (WLR_OUTPUT_STATE_SCALE | WLR_OUTPUT_STATE_TRANSFORM | WLR_OUTPUT_STATE_MODE)) { output_layout_reconfigure(l_output->layout); output_update_global(l_output->output); } } static void addon_destroy(struct wlr_addon *addon) { assert(addon->impl == &addon_impl); struct wlr_output_layout_output *l_output = wl_container_of(addon, l_output, addon); struct wlr_output_layout *layout = l_output->layout; output_layout_output_destroy(l_output); output_layout_reconfigure(layout); } static const struct wlr_addon_interface addon_impl = { .name = "wlr_output_layout_output", .destroy = addon_destroy, }; static struct wlr_output_layout_output *output_layout_output_create( struct wlr_output_layout *layout, struct wlr_output *output) { struct wlr_output_layout_output *l_output = calloc(1, sizeof(*l_output)); if (l_output == NULL) { return NULL; } l_output->layout = layout; l_output->output = output; wl_signal_init(&l_output->events.destroy); /* * Insert at the end of the list so that auto-configuring the * new output doesn't change the layout of other outputs */ wl_list_insert(layout->outputs.prev, &l_output->link); wl_signal_add(&output->events.commit, &l_output->commit); l_output->commit.notify = handle_output_commit; wlr_addon_init(&l_output->addon, &output->addons, layout, &addon_impl); return l_output; } static struct wlr_output_layout_output *output_layout_add(struct wlr_output_layout *layout, struct wlr_output *output, int lx, int ly, bool auto_configured) { struct wlr_output_layout_output *l_output = wlr_output_layout_get(layout, output); bool is_new = l_output == NULL; if (is_new) { l_output = output_layout_output_create(layout, output); if (l_output == NULL) { return NULL; } } l_output->x = lx; l_output->y = ly; l_output->auto_configured = auto_configured; output_layout_reconfigure(layout); output_update_global(output); if (is_new) { wl_signal_emit_mutable(&layout->events.add, l_output); } return l_output; } struct wlr_output_layout_output *wlr_output_layout_add(struct wlr_output_layout *layout, struct wlr_output *output, int lx, int ly) { return output_layout_add(layout, output, lx, ly, false); } struct wlr_output_layout_output *wlr_output_layout_add_auto(struct wlr_output_layout *layout, struct wlr_output *output) { return output_layout_add(layout, output, 0, 0, true); } void wlr_output_layout_remove(struct wlr_output_layout *layout, struct wlr_output *output) { struct wlr_output_layout_output *l_output = wlr_output_layout_get(layout, output); if (l_output != NULL) { output_layout_output_destroy(l_output); output_layout_reconfigure(layout); } } struct wlr_output_layout_output *wlr_output_layout_get( struct wlr_output_layout *layout, struct wlr_output *reference) { struct wlr_output_layout_output *l_output = NULL; struct wlr_addon *addon = wlr_addon_find(&reference->addons, layout, &addon_impl); if (addon) { l_output = wl_container_of(addon, l_output, addon); } return l_output; } bool wlr_output_layout_contains_point(struct wlr_output_layout *layout, struct wlr_output *reference, int lx, int ly) { if (reference) { struct wlr_output_layout_output *l_output = wlr_output_layout_get(layout, reference); if (!l_output) { return false; } struct wlr_box output_box; output_layout_output_get_box(l_output, &output_box); return wlr_box_contains_point(&output_box, lx, ly); } else { return !!wlr_output_layout_output_at(layout, lx, ly); } } bool wlr_output_layout_intersects(struct wlr_output_layout *layout, struct wlr_output *reference, const struct wlr_box *target_lbox) { struct wlr_box out_box; if (reference == NULL) { struct wlr_output_layout_output *l_output; wl_list_for_each(l_output, &layout->outputs, link) { struct wlr_box output_box; output_layout_output_get_box(l_output, &output_box); if (wlr_box_intersection(&out_box, &output_box, target_lbox)) { return true; } } return false; } else { struct wlr_output_layout_output *l_output = wlr_output_layout_get(layout, reference); if (!l_output) { return false; } struct wlr_box output_box; output_layout_output_get_box(l_output, &output_box); return wlr_box_intersection(&out_box, &output_box, target_lbox); } } struct wlr_output *wlr_output_layout_output_at(struct wlr_output_layout *layout, double lx, double ly) { struct wlr_output_layout_output *l_output; wl_list_for_each(l_output, &layout->outputs, link) { struct wlr_box output_box; output_layout_output_get_box(l_output, &output_box); if (wlr_box_contains_point(&output_box, lx, ly)) { return l_output->output; } } return NULL; } void wlr_output_layout_output_coords(struct wlr_output_layout *layout, struct wlr_output *reference, double *lx, double *ly) { assert(layout && reference); double src_x = *lx; double src_y = *ly; struct wlr_output_layout_output *l_output; wl_list_for_each(l_output, &layout->outputs, link) { if (l_output->output == reference) { *lx = src_x - (double)l_output->x; *ly = src_y - (double)l_output->y; return; } } } void wlr_output_layout_closest_point(struct wlr_output_layout *layout, struct wlr_output *reference, double lx, double ly, double *dest_lx, double *dest_ly) { if (dest_lx == NULL && dest_ly == NULL) { return; } double min_x = lx, min_y = ly, min_distance = DBL_MAX; struct wlr_output_layout_output *l_output; wl_list_for_each(l_output, &layout->outputs, link) { if (reference != NULL && reference != l_output->output) { continue; } double output_x, output_y, output_distance; struct wlr_box output_box; output_layout_output_get_box(l_output, &output_box); wlr_box_closest_point(&output_box, lx, ly, &output_x, &output_y); // calculate squared distance suitable for comparison output_distance = (lx - output_x) * (lx - output_x) + (ly - output_y) * (ly - output_y); if (!isfinite(output_distance)) { output_distance = DBL_MAX; } if (output_distance < min_distance) { min_x = output_x; min_y = output_y; min_distance = output_distance; } } if (dest_lx) { *dest_lx = min_x; } if (dest_ly) { *dest_ly = min_y; } } void wlr_output_layout_get_box(struct wlr_output_layout *layout, struct wlr_output *reference, struct wlr_box *dest_box) { *dest_box = (struct wlr_box){0}; struct wlr_output_layout_output *l_output; if (reference) { // output extents l_output = wlr_output_layout_get(layout, reference); if (l_output) { output_layout_output_get_box(l_output, dest_box); } } else { // layout extents int min_x = 0, max_x = 0, min_y = 0, max_y = 0; if (!wl_list_empty(&layout->outputs)) { min_x = min_y = INT_MAX; max_x = max_y = INT_MIN; wl_list_for_each(l_output, &layout->outputs, link) { struct wlr_box output_box; output_layout_output_get_box(l_output, &output_box); if (output_box.x < min_x) { min_x = output_box.x; } if (output_box.y < min_y) { min_y = output_box.y; } if (output_box.x + output_box.width > max_x) { max_x = output_box.x + output_box.width; } if (output_box.y + output_box.height > max_y) { max_y = output_box.y + output_box.height; } } } dest_box->x = min_x; dest_box->y = min_y; dest_box->width = max_x - min_x; dest_box->height = max_y - min_y; } } struct wlr_output *wlr_output_layout_get_center_output( struct wlr_output_layout *layout) { if (wl_list_empty(&layout->outputs)) { return NULL; } struct wlr_box extents; wlr_output_layout_get_box(layout, NULL, &extents); double center_x = extents.width / 2. + extents.x; double center_y = extents.height / 2. + extents.y; double dest_x = 0, dest_y = 0; wlr_output_layout_closest_point(layout, NULL, center_x, center_y, &dest_x, &dest_y); return wlr_output_layout_output_at(layout, dest_x, dest_y); } enum distance_selection_method { NEAREST, FARTHEST }; static struct wlr_output *wlr_output_layout_output_in_direction( struct wlr_output_layout *layout, enum wlr_direction direction, struct wlr_output *reference, double ref_lx, double ref_ly, enum distance_selection_method distance_method) { assert(reference); struct wlr_box ref_box; wlr_output_layout_get_box(layout, reference, &ref_box); if (wlr_box_empty(&ref_box)) { // The output doesn't belong to the layout return NULL; } double min_distance = (distance_method == NEAREST) ? DBL_MAX : DBL_MIN; struct wlr_output *closest_output = NULL; struct wlr_output_layout_output *l_output; wl_list_for_each(l_output, &layout->outputs, link) { if (reference != NULL && reference == l_output->output) { continue; } struct wlr_box box; output_layout_output_get_box(l_output, &box); bool match = false; // test to make sure this output is in the given direction if (direction & WLR_DIRECTION_LEFT) { match = box.x + box.width <= ref_box.x || match; } if (direction & WLR_DIRECTION_RIGHT) { match = box.x >= ref_box.x + ref_box.width || match; } if (direction & WLR_DIRECTION_UP) { match = box.y + box.height <= ref_box.y || match; } if (direction & WLR_DIRECTION_DOWN) { match = box.y >= ref_box.y + ref_box.height || match; } if (!match) { continue; } // calculate distance from the given reference point double x, y; wlr_output_layout_closest_point(layout, l_output->output, ref_lx, ref_ly, &x, &y); double distance = (x - ref_lx) * (x - ref_lx) + (y - ref_ly) * (y - ref_ly); if ((distance_method == NEAREST) ? distance < min_distance : distance > min_distance) { min_distance = distance; closest_output = l_output->output; } } return closest_output; } struct wlr_output *wlr_output_layout_adjacent_output( struct wlr_output_layout *layout, enum wlr_direction direction, struct wlr_output *reference, double ref_lx, double ref_ly) { return wlr_output_layout_output_in_direction(layout, direction, reference, ref_lx, ref_ly, NEAREST); } struct wlr_output *wlr_output_layout_farthest_output( struct wlr_output_layout *layout, enum wlr_direction direction, struct wlr_output *reference, double ref_lx, double ref_ly) { return wlr_output_layout_output_in_direction(layout, direction, reference, ref_lx, ref_ly, FARTHEST); } wlroots-0.17.1/types/wlr_output_management_v1.c000066400000000000000000000772421454110342200216570ustar00rootroot00000000000000#include #include #include #include #include #include "wlr-output-management-unstable-v1-protocol.h" #define OUTPUT_MANAGER_VERSION 4 enum { HEAD_STATE_ENABLED = 1 << 0, HEAD_STATE_MODE = 1 << 1, HEAD_STATE_POSITION = 1 << 2, HEAD_STATE_TRANSFORM = 1 << 3, HEAD_STATE_SCALE = 1 << 4, HEAD_STATE_ADAPTIVE_SYNC = 1 << 5, }; static const uint32_t HEAD_STATE_ALL = HEAD_STATE_ENABLED | HEAD_STATE_MODE | HEAD_STATE_POSITION | HEAD_STATE_TRANSFORM | HEAD_STATE_SCALE | HEAD_STATE_ADAPTIVE_SYNC; static const struct zwlr_output_head_v1_interface head_impl; // Can return NULL if the head is inert static struct wlr_output_head_v1 *head_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &zwlr_output_head_v1_interface, &head_impl)); return wl_resource_get_user_data(resource); } static const struct zwlr_output_mode_v1_interface output_mode_impl; // Can return NULL if the mode is custom or inert static struct wlr_output_mode *mode_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &zwlr_output_mode_v1_interface, &output_mode_impl)); return wl_resource_get_user_data(resource); } static void head_destroy(struct wlr_output_head_v1 *head) { if (head == NULL) { return; } struct wl_resource *resource, *tmp; wl_resource_for_each_safe(resource, tmp, &head->mode_resources) { zwlr_output_mode_v1_send_finished(resource); wl_list_remove(wl_resource_get_link(resource)); wl_list_init(wl_resource_get_link(resource)); wl_resource_set_user_data(resource, NULL); } wl_resource_for_each_safe(resource, tmp, &head->resources) { zwlr_output_head_v1_send_finished(resource); wl_list_remove(wl_resource_get_link(resource)); wl_list_init(wl_resource_get_link(resource)); wl_resource_set_user_data(resource, NULL); } wl_list_remove(&head->link); wl_list_remove(&head->output_destroy.link); free(head); } static void head_handle_output_destroy(struct wl_listener *listener, void *data) { struct wlr_output_head_v1 *head = wl_container_of(listener, head, output_destroy); head->manager->current_configuration_dirty = true; head_destroy(head); } static struct wlr_output_head_v1 *head_create( struct wlr_output_manager_v1 *manager, struct wlr_output *output) { struct wlr_output_head_v1 *head = calloc(1, sizeof(*head)); if (head == NULL) { return NULL; } head->manager = manager; head->state.output = output; wl_list_init(&head->resources); wl_list_init(&head->mode_resources); wl_list_insert(&manager->heads, &head->link); head->output_destroy.notify = head_handle_output_destroy; wl_signal_add(&output->events.destroy, &head->output_destroy); return head; } static void head_destroy_custom_mode_resources(struct wlr_output_head_v1 *head) { struct wl_resource *resource, *tmp; wl_resource_for_each_safe(resource, tmp, &head->mode_resources) { if (wl_resource_get_user_data(resource) != NULL) { continue; } zwlr_output_mode_v1_send_finished(resource); wl_list_remove(wl_resource_get_link(resource)); wl_list_init(wl_resource_get_link(resource)); } } static bool head_has_custom_mode_resources(const struct wlr_output_head_v1 *head) { struct wl_resource *resource; wl_resource_for_each(resource, &head->mode_resources) { if (wl_resource_get_user_data(resource) == NULL) { return true; } } return false; } static void config_head_destroy( struct wlr_output_configuration_head_v1 *config_head) { if (config_head == NULL) { return; } if (config_head->resource != NULL) { wl_resource_set_user_data(config_head->resource, NULL); // make inert } wl_list_remove(&config_head->link); wl_list_remove(&config_head->output_destroy.link); free(config_head); } static void config_head_handle_output_destroy(struct wl_listener *listener, void *data) { struct wlr_output_configuration_head_v1 *config_head = wl_container_of(listener, config_head, output_destroy); config_head_destroy(config_head); } static struct wlr_output_configuration_head_v1 *config_head_create( struct wlr_output_configuration_v1 *config, struct wlr_output *output) { struct wlr_output_configuration_head_v1 *config_head = calloc(1, sizeof(*config_head)); if (config_head == NULL) { return NULL; } config_head->config = config; config_head->state.output = output; wl_list_insert(&config->heads, &config_head->link); config_head->output_destroy.notify = config_head_handle_output_destroy; wl_signal_add(&output->events.destroy, &config_head->output_destroy); return config_head; } struct wlr_output_configuration_head_v1 * wlr_output_configuration_head_v1_create( struct wlr_output_configuration_v1 *config, struct wlr_output *output) { struct wlr_output_configuration_head_v1 *config_head = config_head_create(config, output); if (config_head == NULL) { return NULL; } config_head->state.enabled = output->enabled; config_head->state.mode = output->current_mode; config_head->state.custom_mode.width = output->width; config_head->state.custom_mode.height = output->height; config_head->state.custom_mode.refresh = output->refresh; config_head->state.transform = output->transform; config_head->state.scale = output->scale; config_head->state.adaptive_sync_enabled = output->adaptive_sync_status == WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED; return config_head; } static const struct zwlr_output_configuration_head_v1_interface config_head_impl; // Can return NULL if the configuration head is inert static struct wlr_output_configuration_head_v1 *config_head_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &zwlr_output_configuration_head_v1_interface, &config_head_impl)); return wl_resource_get_user_data(resource); } static void config_head_handle_set_mode(struct wl_client *client, struct wl_resource *config_head_resource, struct wl_resource *mode_resource) { struct wlr_output_configuration_head_v1 *config_head = config_head_from_resource(config_head_resource); if (config_head == NULL) { return; } // Mode can be NULL if the output uses a custom mode (in which case we // expose a virtual mode with user data set to NULL). struct wlr_output_mode *mode = mode_from_resource(mode_resource); struct wlr_output *output = config_head->state.output; bool found = mode == NULL; struct wlr_output_mode *m; wl_list_for_each(m, &output->modes, link) { if (mode == m) { found = true; break; } } if (!found) { wl_resource_post_error(config_head_resource, ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_INVALID_MODE, "mode doesn't belong to head"); return; } config_head->state.mode = mode; if (mode != NULL) { config_head->state.custom_mode.width = 0; config_head->state.custom_mode.height = 0; config_head->state.custom_mode.refresh = 0; } } static void config_head_handle_set_custom_mode(struct wl_client *client, struct wl_resource *config_head_resource, int32_t width, int32_t height, int32_t refresh) { struct wlr_output_configuration_head_v1 *config_head = config_head_from_resource(config_head_resource); if (config_head == NULL) { return; } if (width <= 0 || height <= 0 || refresh < 0) { wl_resource_post_error(config_head_resource, ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_INVALID_CUSTOM_MODE, "invalid custom mode"); return; } config_head->state.mode = NULL; config_head->state.custom_mode.width = width; config_head->state.custom_mode.height = height; config_head->state.custom_mode.refresh = refresh; } static void config_head_handle_set_position(struct wl_client *client, struct wl_resource *config_head_resource, int32_t x, int32_t y) { struct wlr_output_configuration_head_v1 *config_head = config_head_from_resource(config_head_resource); if (config_head == NULL) { return; } config_head->state.x = x; config_head->state.y = y; } static void config_head_handle_set_transform(struct wl_client *client, struct wl_resource *config_head_resource, int32_t transform) { struct wlr_output_configuration_head_v1 *config_head = config_head_from_resource(config_head_resource); if (config_head == NULL) { return; } if (transform < WL_OUTPUT_TRANSFORM_NORMAL || transform > WL_OUTPUT_TRANSFORM_FLIPPED_270) { wl_resource_post_error(config_head_resource, ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_INVALID_TRANSFORM, "invalid transform"); return; } config_head->state.transform = transform; } static void config_head_handle_set_scale(struct wl_client *client, struct wl_resource *config_head_resource, wl_fixed_t scale_fixed) { struct wlr_output_configuration_head_v1 *config_head = config_head_from_resource(config_head_resource); if (config_head == NULL) { return; } float scale = wl_fixed_to_double(scale_fixed); if (scale <= 0) { wl_resource_post_error(config_head_resource, ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_INVALID_SCALE, "invalid scale"); return; } config_head->state.scale = scale; } static void config_head_handle_set_adaptive_sync(struct wl_client *client, struct wl_resource *config_head_resource, uint32_t state) { struct wlr_output_configuration_head_v1 *config_head = config_head_from_resource(config_head_resource); if (config_head == NULL) { return; } switch (state) { case ZWLR_OUTPUT_HEAD_V1_ADAPTIVE_SYNC_STATE_ENABLED: config_head->state.adaptive_sync_enabled = true; break; case ZWLR_OUTPUT_HEAD_V1_ADAPTIVE_SYNC_STATE_DISABLED: config_head->state.adaptive_sync_enabled = false; break; default: wl_resource_post_error(config_head_resource, ZWLR_OUTPUT_CONFIGURATION_HEAD_V1_ERROR_INVALID_ADAPTIVE_SYNC_STATE, "client requested invalid adaptive sync state %ul", state); break; } } static const struct zwlr_output_configuration_head_v1_interface config_head_impl = { .set_mode = config_head_handle_set_mode, .set_custom_mode = config_head_handle_set_custom_mode, .set_position = config_head_handle_set_position, .set_transform = config_head_handle_set_transform, .set_scale = config_head_handle_set_scale, .set_adaptive_sync = config_head_handle_set_adaptive_sync, }; static void config_head_handle_resource_destroy(struct wl_resource *resource) { struct wlr_output_configuration_head_v1 *config_head = config_head_from_resource(resource); config_head_destroy(config_head); } static const struct zwlr_output_configuration_v1_interface config_impl; // Can return NULL if the config has been used static struct wlr_output_configuration_v1 *config_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &zwlr_output_configuration_v1_interface, &config_impl)); return wl_resource_get_user_data(resource); } // Checks that the head is unconfigured (ie. no enable_head/disable_head request // has yet been sent for this head), if not sends a protocol error. static bool config_check_head_is_unconfigured( struct wlr_output_configuration_v1 *config, struct wlr_output *output) { struct wlr_output_configuration_head_v1 *head; wl_list_for_each(head, &config->heads, link) { if (head->state.output == output) { wl_resource_post_error(config->resource, ZWLR_OUTPUT_CONFIGURATION_V1_ERROR_ALREADY_CONFIGURED_HEAD, "head has already been configured"); return false; } } return true; } static void config_handle_enable_head(struct wl_client *client, struct wl_resource *config_resource, uint32_t id, struct wl_resource *head_resource) { struct wlr_output_configuration_v1 *config = config_from_resource(config_resource); if (config == NULL || config->finalized) { wl_resource_post_error(config_resource, ZWLR_OUTPUT_CONFIGURATION_V1_ERROR_ALREADY_USED, "configuration object has already been used"); return; } struct wlr_output_head_v1 *head = head_from_resource(head_resource); // Create an inert resource if the head no longer exists struct wlr_output_configuration_head_v1 *config_head = NULL; if (head != NULL) { if (!config_check_head_is_unconfigured(config, head->state.output)) { return; } config_head = config_head_create(config, head->state.output); if (config_head == NULL) { wl_resource_post_no_memory(config_resource); return; } config_head->state = head->state; } uint32_t version = wl_resource_get_version(config_resource); struct wl_resource *resource = wl_resource_create(client, &zwlr_output_configuration_head_v1_interface, version, id); if (resource == NULL) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(resource, &config_head_impl, config_head, config_head_handle_resource_destroy); if (config_head != NULL) { config_head->resource = resource; config_head->state.enabled = true; } } static void config_handle_disable_head(struct wl_client *client, struct wl_resource *config_resource, struct wl_resource *head_resource) { struct wlr_output_configuration_v1 *config = config_from_resource(config_resource); if (config == NULL || config->finalized) { wl_resource_post_error(config_resource, ZWLR_OUTPUT_CONFIGURATION_V1_ERROR_ALREADY_USED, "configuration object has already been used"); return; } struct wlr_output_head_v1 *head = head_from_resource(head_resource); if (head == NULL) { return; } if (!config_check_head_is_unconfigured(config, head->state.output)) { return; } struct wlr_output_configuration_head_v1 *config_head = config_head_create(config, head->state.output); if (config_head == NULL) { wl_resource_post_no_memory(config_resource); return; } config_head->state.enabled = false; } // Finalizes a configuration. This prevents the same config from being used // multiple times. static void config_finalize(struct wlr_output_configuration_v1 *config) { if (config->finalized) { return; } // Destroy config head resources now, the client is forbidden to use them at // this point anyway struct wlr_output_configuration_head_v1 *config_head, *tmp; wl_list_for_each_safe(config_head, tmp, &config->heads, link) { // Resource is NULL if head has been disabled if (config_head->resource != NULL) { wl_resource_set_user_data(config_head->resource, NULL); wl_resource_destroy(config_head->resource); config_head->resource = NULL; } } config->finalized = true; } // Destroys the config if serial is invalid static bool config_validate_serial(struct wlr_output_configuration_v1 *config) { if (config->serial != config->manager->serial) { wlr_log(WLR_DEBUG, "Ignored configuration request: invalid serial"); zwlr_output_configuration_v1_send_cancelled(config->resource); wlr_output_configuration_v1_destroy(config); return false; } return true; } static void config_handle_apply(struct wl_client *client, struct wl_resource *config_resource) { struct wlr_output_configuration_v1 *config = config_from_resource(config_resource); if (config == NULL || config->finalized) { wl_resource_post_error(config_resource, ZWLR_OUTPUT_CONFIGURATION_V1_ERROR_ALREADY_USED, "configuration object has already been used"); return; } config_finalize(config); if (!config_validate_serial(config)) { return; } wl_signal_emit_mutable(&config->manager->events.apply, config); } static void config_handle_test(struct wl_client *client, struct wl_resource *config_resource) { struct wlr_output_configuration_v1 *config = config_from_resource(config_resource); if (config == NULL || config->finalized) { wl_resource_post_error(config_resource, ZWLR_OUTPUT_CONFIGURATION_V1_ERROR_ALREADY_USED, "configuration object has already been used"); return; } config_finalize(config); if (!config_validate_serial(config)) { return; } wl_signal_emit_mutable(&config->manager->events.test, config); } static void config_handle_destroy(struct wl_client *client, struct wl_resource *config_resource) { wl_resource_destroy(config_resource); } static const struct zwlr_output_configuration_v1_interface config_impl = { .enable_head = config_handle_enable_head, .disable_head = config_handle_disable_head, .apply = config_handle_apply, .test = config_handle_test, .destroy = config_handle_destroy, }; static struct wlr_output_configuration_v1 *config_create(bool finalized) { struct wlr_output_configuration_v1 *config = calloc(1, sizeof(*config)); if (config == NULL) { return NULL; } wl_list_init(&config->heads); config->finalized = finalized; return config; } struct wlr_output_configuration_v1 *wlr_output_configuration_v1_create(void) { return config_create(true); } void wlr_output_configuration_v1_destroy( struct wlr_output_configuration_v1 *config) { if (config == NULL) { return; } config_finalize(config); if (config->resource != NULL) { wl_resource_set_user_data(config->resource, NULL); // make inert } struct wlr_output_configuration_head_v1 *config_head, *tmp; wl_list_for_each_safe(config_head, tmp, &config->heads, link) { config_head_destroy(config_head); } free(config); } static void config_handle_resource_destroy(struct wl_resource *resource) { struct wlr_output_configuration_v1 *config = config_from_resource(resource); if (config == NULL) { return; } if (config->finalized) { config->resource = NULL; // we no longer own the config } else { wlr_output_configuration_v1_destroy(config); } } void wlr_output_configuration_v1_send_succeeded( struct wlr_output_configuration_v1 *config) { assert(!config->finished); if (config->resource == NULL) { return; // client destroyed the resource early } zwlr_output_configuration_v1_send_succeeded(config->resource); config->finished = true; } void wlr_output_configuration_v1_send_failed( struct wlr_output_configuration_v1 *config) { assert(!config->finished); if (config->resource == NULL) { return; // client destroyed the resource early } zwlr_output_configuration_v1_send_failed(config->resource); config->finished = true; } static const struct zwlr_output_manager_v1_interface manager_impl; static struct wlr_output_manager_v1 *manager_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &zwlr_output_manager_v1_interface, &manager_impl)); return wl_resource_get_user_data(resource); } static void manager_handle_create_configuration(struct wl_client *client, struct wl_resource *manager_resource, uint32_t id, uint32_t serial) { struct wlr_output_manager_v1 *manager = manager_from_resource(manager_resource); struct wlr_output_configuration_v1 *config = config_create(false); if (config == NULL) { wl_resource_post_no_memory(manager_resource); return; } config->manager = manager; config->serial = serial; uint32_t version = wl_resource_get_version(manager_resource); config->resource = wl_resource_create(client, &zwlr_output_configuration_v1_interface, version, id); if (config->resource == NULL) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(config->resource, &config_impl, config, config_handle_resource_destroy); } static void manager_handle_stop(struct wl_client *client, struct wl_resource *manager_resource) { zwlr_output_manager_v1_send_finished(manager_resource); wl_resource_destroy(manager_resource); } static const struct zwlr_output_manager_v1_interface manager_impl = { .create_configuration = manager_handle_create_configuration, .stop = manager_handle_stop, }; static void manager_handle_resource_destroy(struct wl_resource *resource) { wl_list_remove(wl_resource_get_link(resource)); } static void manager_send_head(struct wlr_output_manager_v1 *manager, struct wlr_output_head_v1 *head, struct wl_resource *manager_resource); static void manager_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id) { struct wlr_output_manager_v1 *manager = data; struct wl_resource *resource = wl_resource_create(client, &zwlr_output_manager_v1_interface, version, id); if (resource == NULL) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(resource, &manager_impl, manager, manager_handle_resource_destroy); wl_list_insert(&manager->resources, wl_resource_get_link(resource)); struct wlr_output_head_v1 *head; wl_list_for_each(head, &manager->heads, link) { manager_send_head(manager, head, resource); } zwlr_output_manager_v1_send_done(resource, manager->serial); } static void manager_handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_output_manager_v1 *manager = wl_container_of(listener, manager, display_destroy); wl_signal_emit_mutable(&manager->events.destroy, manager); wl_list_remove(&manager->display_destroy.link); struct wlr_output_head_v1 *head, *tmp; wl_list_for_each_safe(head, tmp, &manager->heads, link) { head_destroy(head); } wl_global_destroy(manager->global); free(manager); } struct wlr_output_manager_v1 *wlr_output_manager_v1_create( struct wl_display *display) { struct wlr_output_manager_v1 *manager = calloc(1, sizeof(*manager)); if (manager == NULL) { return NULL; } manager->display = display; wl_list_init(&manager->resources); wl_list_init(&manager->heads); wl_signal_init(&manager->events.destroy); wl_signal_init(&manager->events.apply); wl_signal_init(&manager->events.test); manager->global = wl_global_create(display, &zwlr_output_manager_v1_interface, OUTPUT_MANAGER_VERSION, manager, manager_bind); if (manager->global == NULL) { free(manager); return NULL; } manager->display_destroy.notify = manager_handle_display_destroy; wl_display_add_destroy_listener(display, &manager->display_destroy); return manager; } static struct wlr_output_configuration_head_v1 *configuration_get_head( struct wlr_output_configuration_v1 *config, struct wlr_output *output) { struct wlr_output_configuration_head_v1 *head; wl_list_for_each(head, &config->heads, link) { if (head->state.output == output) { return head; } } return NULL; } static void send_mode_state(struct wl_resource *mode_resource, struct wlr_output_mode *mode) { zwlr_output_mode_v1_send_size(mode_resource, mode->width, mode->height); if (mode->refresh > 0) { zwlr_output_mode_v1_send_refresh(mode_resource, mode->refresh); } if (mode->preferred) { zwlr_output_mode_v1_send_preferred(mode_resource); } } static void output_mode_handle_resource_destroy(struct wl_resource *resource) { wl_list_remove(wl_resource_get_link(resource)); } static void output_mode_handle_release(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static const struct zwlr_output_mode_v1_interface output_mode_impl = { .release = output_mode_handle_release, }; static struct wl_resource *head_send_mode(struct wlr_output_head_v1 *head, struct wl_resource *head_resource, struct wlr_output_mode *mode) { struct wl_client *client = wl_resource_get_client(head_resource); uint32_t version = wl_resource_get_version(head_resource); struct wl_resource *mode_resource = wl_resource_create(client, &zwlr_output_mode_v1_interface, version, 0); if (mode_resource == NULL) { wl_resource_post_no_memory(head_resource); return NULL; } wl_resource_set_implementation(mode_resource, &output_mode_impl, mode, output_mode_handle_resource_destroy); wl_list_insert(&head->mode_resources, wl_resource_get_link(mode_resource)); zwlr_output_head_v1_send_mode(head_resource, mode_resource); if (mode != NULL) { send_mode_state(mode_resource, mode); } return mode_resource; } // Sends new head state to a client. static void head_send_state(struct wlr_output_head_v1 *head, struct wl_resource *head_resource, uint32_t state) { struct wl_client *client = wl_resource_get_client(head_resource); if (state & HEAD_STATE_ENABLED) { zwlr_output_head_v1_send_enabled(head_resource, head->state.enabled); // On enabling we send all current data since clients have not been // notified about potential data changes while the head was disabled. state = HEAD_STATE_ALL; } if (!head->state.enabled) { return; } if (state & HEAD_STATE_MODE) { bool found = false; struct wl_resource *mode_resource; wl_resource_for_each(mode_resource, &head->mode_resources) { if (wl_resource_get_client(mode_resource) == client && mode_from_resource(mode_resource) == head->state.mode) { found = true; break; } } assert(found); if (head->state.mode == NULL) { // Fake a single output mode if output doesn't support modes struct wlr_output_mode virtual_mode = { .width = head->state.custom_mode.width, .height = head->state.custom_mode.height, .refresh = head->state.custom_mode.refresh, }; send_mode_state(mode_resource, &virtual_mode); } zwlr_output_head_v1_send_current_mode(head_resource, mode_resource); } if (state & HEAD_STATE_POSITION) { zwlr_output_head_v1_send_position(head_resource, head->state.x, head->state.y); } if (state & HEAD_STATE_TRANSFORM) { zwlr_output_head_v1_send_transform(head_resource, head->state.transform); } if (state & HEAD_STATE_SCALE) { zwlr_output_head_v1_send_scale(head_resource, wl_fixed_from_double(head->state.scale)); } if ((state & HEAD_STATE_ADAPTIVE_SYNC) && wl_resource_get_version(head_resource) >= ZWLR_OUTPUT_HEAD_V1_ADAPTIVE_SYNC_SINCE_VERSION) { if (head->state.adaptive_sync_enabled) { zwlr_output_head_v1_send_adaptive_sync(head_resource, ZWLR_OUTPUT_HEAD_V1_ADAPTIVE_SYNC_STATE_ENABLED); } else { zwlr_output_head_v1_send_adaptive_sync(head_resource, ZWLR_OUTPUT_HEAD_V1_ADAPTIVE_SYNC_STATE_DISABLED); } } } static void head_handle_resource_destroy(struct wl_resource *resource) { wl_list_remove(wl_resource_get_link(resource)); } static void head_handle_release(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static const struct zwlr_output_head_v1_interface head_impl = { .release = head_handle_release, }; static void manager_send_head(struct wlr_output_manager_v1 *manager, struct wlr_output_head_v1 *head, struct wl_resource *manager_resource) { struct wlr_output *output = head->state.output; struct wl_client *client = wl_resource_get_client(manager_resource); uint32_t version = wl_resource_get_version(manager_resource); struct wl_resource *head_resource = wl_resource_create(client, &zwlr_output_head_v1_interface, version, 0); if (head_resource == NULL) { wl_resource_post_no_memory(manager_resource); return; } wl_resource_set_implementation(head_resource, &head_impl, head, head_handle_resource_destroy); wl_list_insert(&head->resources, wl_resource_get_link(head_resource)); zwlr_output_manager_v1_send_head(manager_resource, head_resource); zwlr_output_head_v1_send_name(head_resource, output->name); zwlr_output_head_v1_send_description(head_resource, output->description ? output->description : "Unknown"); if (output->phys_width > 0 && output->phys_height > 0) { zwlr_output_head_v1_send_physical_size(head_resource, output->phys_width, output->phys_height); } if (version >= ZWLR_OUTPUT_HEAD_V1_MAKE_SINCE_VERSION && output->make != NULL) { zwlr_output_head_v1_send_make(head_resource, output->make); } if (version >= ZWLR_OUTPUT_HEAD_V1_MODEL_SINCE_VERSION && output->model != NULL) { zwlr_output_head_v1_send_model(head_resource, output->model); } if (version >= ZWLR_OUTPUT_HEAD_V1_SERIAL_NUMBER_SINCE_VERSION && output->serial != NULL) { zwlr_output_head_v1_send_serial_number(head_resource, output->serial); } struct wlr_output_mode *mode; wl_list_for_each(mode, &output->modes, link) { head_send_mode(head, head_resource, mode); } if (output->current_mode == NULL) { // Output doesn't have a fixed mode set. Send a virtual one. head_send_mode(head, head_resource, NULL); } head_send_state(head, head_resource, HEAD_STATE_ALL); } // Compute state that has changed and sends it to all clients. Then writes the // new state to the head. static bool manager_update_head(struct wlr_output_manager_v1 *manager, struct wlr_output_head_v1 *head, struct wlr_output_head_v1_state *next) { struct wlr_output_head_v1_state *current = &head->state; uint32_t state = 0; if (current->enabled != next->enabled) { state |= HEAD_STATE_ENABLED; } if (current->mode != next->mode) { state |= HEAD_STATE_MODE; } if (current->custom_mode.width != next->custom_mode.width || current->custom_mode.height != next->custom_mode.height || current->custom_mode.refresh != next->custom_mode.refresh) { state |= HEAD_STATE_MODE; } if (current->x != next->x || current->y != next->y) { state |= HEAD_STATE_POSITION; } if (current->transform != next->transform) { state |= HEAD_STATE_TRANSFORM; } if (current->scale != next->scale) { state |= HEAD_STATE_SCALE; } if (current->adaptive_sync_enabled != next->adaptive_sync_enabled) { state |= HEAD_STATE_ADAPTIVE_SYNC; } // If a mode was added to wlr_output.modes we need to add the new mode // to the wlr_output_head struct wlr_output_mode *mode; wl_list_for_each(mode, &head->state.output->modes, link) { bool found = false; struct wl_resource *mode_resource; wl_resource_for_each(mode_resource, &head->mode_resources) { if (mode_from_resource(mode_resource) == mode) { found = true; break; } } if (!found) { struct wl_resource *resource; wl_resource_for_each(resource, &head->resources) { head_send_mode(head, resource, mode); } } } if (next->mode == NULL && !head_has_custom_mode_resources(head)) { struct wl_resource *resource; wl_resource_for_each(resource, &head->resources) { head_send_mode(head, resource, NULL); } } else if (next->mode != NULL) { head_destroy_custom_mode_resources(head); } if (state != 0) { *current = *next; struct wl_resource *resource; wl_resource_for_each(resource, &head->resources) { head_send_state(head, resource, state); } } return state != 0; } void wlr_output_manager_v1_set_configuration( struct wlr_output_manager_v1 *manager, struct wlr_output_configuration_v1 *config) { bool changed = manager->current_configuration_dirty; // Either update or destroy existing heads struct wlr_output_head_v1 *existing_head, *head_tmp; wl_list_for_each_safe(existing_head, head_tmp, &manager->heads, link) { struct wlr_output_configuration_head_v1 *updated_head = configuration_get_head(config, existing_head->state.output); if (updated_head != NULL) { changed |= manager_update_head(manager, existing_head, &updated_head->state); config_head_destroy(updated_head); } else { head_destroy(existing_head); changed = true; } } // Heads remaining in `config` are new heads // Move new heads to current config struct wlr_output_configuration_head_v1 *config_head, *config_head_tmp; wl_list_for_each_safe(config_head, config_head_tmp, &config->heads, link) { struct wlr_output_head_v1 *head = head_create(manager, config_head->state.output); if (head == NULL) { wlr_log_errno(WLR_ERROR, "Allocation failed"); continue; } head->state = config_head->state; struct wl_resource *manager_resource; wl_resource_for_each(manager_resource, &manager->resources) { manager_send_head(manager, head, manager_resource); } changed = true; } wlr_output_configuration_v1_destroy(config); if (!changed) { return; } manager->serial = wl_display_next_serial(manager->display); struct wl_resource *manager_resource; wl_resource_for_each(manager_resource, &manager->resources) { zwlr_output_manager_v1_send_done(manager_resource, manager->serial); } manager->current_configuration_dirty = false; } void wlr_output_head_v1_state_apply( const struct wlr_output_head_v1_state *head_state, struct wlr_output_state *output_state) { wlr_output_state_set_enabled(output_state, head_state->enabled); if (!head_state->enabled) { return; } if (head_state->mode != NULL) { wlr_output_state_set_mode(output_state, head_state->mode); } else { wlr_output_state_set_custom_mode(output_state, head_state->custom_mode.width, head_state->custom_mode.height, head_state->custom_mode.refresh); } wlr_output_state_set_scale(output_state, head_state->scale); wlr_output_state_set_transform(output_state, head_state->transform); wlr_output_state_set_adaptive_sync_enabled(output_state, head_state->adaptive_sync_enabled); } wlroots-0.17.1/types/wlr_output_power_management_v1.c000066400000000000000000000161341454110342200230640ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include "wlr-output-power-management-unstable-v1-protocol.h" #define OUTPUT_POWER_MANAGER_V1_VERSION 1 static void output_power_handle_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static void output_power_destroy(struct wlr_output_power_v1 *output_power) { if (output_power == NULL) { return; } wl_resource_set_user_data(output_power->resource, NULL); wl_list_remove(&output_power->output_destroy_listener.link); wl_list_remove(&output_power->output_commit_listener.link); wl_list_remove(&output_power->link); free(output_power); } static const struct zwlr_output_power_v1_interface output_power_impl; static struct wlr_output_power_v1 *output_power_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &zwlr_output_power_v1_interface, &output_power_impl)); return wl_resource_get_user_data(resource); } static void output_power_handle_resource_destroy(struct wl_resource *resource) { struct wlr_output_power_v1 *output_power = output_power_from_resource(resource); output_power_destroy(output_power); } static void output_power_handle_output_destroy(struct wl_listener *listener, void *data) { struct wlr_output_power_v1 *output_power = wl_container_of(listener, output_power, output_destroy_listener); output_power_destroy(output_power); } static void output_power_v1_send_mode(struct wlr_output_power_v1 *output_power) { enum zwlr_output_power_v1_mode mode; mode = output_power->output->enabled ? ZWLR_OUTPUT_POWER_V1_MODE_ON : ZWLR_OUTPUT_POWER_V1_MODE_OFF; zwlr_output_power_v1_send_mode(output_power->resource, mode); } static void output_power_handle_output_commit(struct wl_listener *listener, void *data) { struct wlr_output_power_v1 *output_power = wl_container_of(listener, output_power, output_commit_listener); struct wlr_output_event_commit *event = data; if (event->state->committed & WLR_OUTPUT_STATE_ENABLED) { output_power_v1_send_mode(output_power); } } static void output_power_handle_set_mode(struct wl_client *client, struct wl_resource *output_power_resource, enum zwlr_output_power_v1_mode mode) { struct wlr_output_power_v1 *output_power = output_power_from_resource(output_power_resource); if (output_power == NULL) { return; } switch (mode) { case ZWLR_OUTPUT_POWER_V1_MODE_OFF: case ZWLR_OUTPUT_POWER_V1_MODE_ON: break; default: wlr_log(WLR_ERROR, "Invalid power mode %d", mode); wl_resource_post_error(output_power_resource, ZWLR_OUTPUT_POWER_V1_ERROR_INVALID_MODE, "Invalid power mode"); return; } struct wlr_output_power_v1_set_mode_event event = { .output = output_power->output, .mode = mode, }; wl_signal_emit_mutable(&output_power->manager->events.set_mode, &event); } static const struct zwlr_output_power_v1_interface output_power_impl = { .destroy = output_power_handle_destroy, .set_mode = output_power_handle_set_mode, }; static const struct zwlr_output_power_manager_v1_interface output_power_manager_impl; static struct wlr_output_power_manager_v1 *output_power_manager_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &zwlr_output_power_manager_v1_interface, &output_power_manager_impl)); return wl_resource_get_user_data(resource); } static void output_power_manager_get_output_power(struct wl_client *client, struct wl_resource *manager_resource, uint32_t id, struct wl_resource *output_resource) { struct wlr_output_power_manager_v1 *manager = output_power_manager_from_resource(manager_resource); struct wlr_output *output = wlr_output_from_resource(output_resource); struct wlr_output_power_v1 *output_power = calloc(1, sizeof(*output_power)); if (output_power == NULL) { wl_client_post_no_memory(client); return; } output_power->output = output; output_power->manager = manager; wl_list_init(&output_power->link); uint32_t version = wl_resource_get_version(manager_resource); output_power->resource = wl_resource_create(client, &zwlr_output_power_v1_interface, version, id); if (output_power->resource == NULL) { free(output_power); wl_client_post_no_memory(client); return; } wl_resource_set_implementation(output_power->resource, &output_power_impl, output_power, output_power_handle_resource_destroy); if (!output) { wl_resource_set_user_data(output_power->resource, NULL); zwlr_output_power_v1_send_failed(output_power->resource); free(output_power); return; } wl_signal_add(&output->events.destroy, &output_power->output_destroy_listener); output_power->output_destroy_listener.notify = output_power_handle_output_destroy; wl_signal_add(&output->events.commit, &output_power->output_commit_listener); output_power->output_commit_listener.notify = output_power_handle_output_commit; struct wlr_output_power_v1 *mgmt; wl_list_for_each(mgmt, &manager->output_powers, link) { if (mgmt->output == output) { zwlr_output_power_v1_send_failed(output_power->resource); output_power_destroy(output_power); return; } } wl_list_insert(&manager->output_powers, &output_power->link); output_power_v1_send_mode(output_power); } static void output_power_manager_destroy(struct wl_client *client, struct wl_resource *manager_resource) { wl_resource_destroy(manager_resource); } static const struct zwlr_output_power_manager_v1_interface output_power_manager_impl = { .get_output_power = output_power_manager_get_output_power, .destroy = output_power_manager_destroy, }; static void output_power_manager_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id) { struct wlr_output_power_manager_v1 *manager = data; struct wl_resource *resource = wl_resource_create(client, &zwlr_output_power_manager_v1_interface, version, id); if (resource == NULL) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(resource, &output_power_manager_impl, manager, NULL); } static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_output_power_manager_v1 *manager = wl_container_of(listener, manager, display_destroy); wl_signal_emit_mutable(&manager->events.destroy, manager); wl_global_destroy(manager->global); free(manager); } struct wlr_output_power_manager_v1 *wlr_output_power_manager_v1_create( struct wl_display *display) { struct wlr_output_power_manager_v1 *manager = calloc(1, sizeof(*manager)); if (!manager) { return NULL; } manager->global = wl_global_create(display, &zwlr_output_power_manager_v1_interface, OUTPUT_POWER_MANAGER_V1_VERSION, manager, output_power_manager_bind); if (manager->global == NULL) { free(manager); return NULL; } wl_signal_init(&manager->events.set_mode); wl_signal_init(&manager->events.destroy); wl_list_init(&manager->output_powers); manager->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &manager->display_destroy); return manager; } wlroots-0.17.1/types/wlr_pointer.c000066400000000000000000000025611454110342200171650ustar00rootroot00000000000000#include #include #include #include #include #include #include "interfaces/wlr_input_device.h" struct wlr_pointer *wlr_pointer_from_input_device( struct wlr_input_device *input_device) { assert(input_device->type == WLR_INPUT_DEVICE_POINTER); return wl_container_of(input_device, (struct wlr_pointer *)NULL, base); } void wlr_pointer_init(struct wlr_pointer *pointer, const struct wlr_pointer_impl *impl, const char *name) { *pointer = (struct wlr_pointer){ .impl = impl, }; wlr_input_device_init(&pointer->base, WLR_INPUT_DEVICE_POINTER, name); wl_signal_init(&pointer->events.motion); wl_signal_init(&pointer->events.motion_absolute); wl_signal_init(&pointer->events.button); wl_signal_init(&pointer->events.axis); wl_signal_init(&pointer->events.frame); wl_signal_init(&pointer->events.swipe_begin); wl_signal_init(&pointer->events.swipe_update); wl_signal_init(&pointer->events.swipe_end); wl_signal_init(&pointer->events.pinch_begin); wl_signal_init(&pointer->events.pinch_update); wl_signal_init(&pointer->events.pinch_end); wl_signal_init(&pointer->events.hold_begin); wl_signal_init(&pointer->events.hold_end); } void wlr_pointer_finish(struct wlr_pointer *pointer) { wlr_input_device_finish(&pointer->base); free(pointer->output_name); } wlroots-0.17.1/types/wlr_pointer_constraints_v1.c000066400000000000000000000304131454110342200222170ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include static const struct zwp_locked_pointer_v1_interface locked_pointer_impl; static const struct zwp_confined_pointer_v1_interface confined_pointer_impl; static const struct zwp_pointer_constraints_v1_interface pointer_constraints_impl; static void pointer_constraint_destroy(struct wlr_pointer_constraint_v1 *constraint); static struct wlr_pointer_constraint_v1 *pointer_constraint_from_resource( struct wl_resource *resource) { assert( wl_resource_instance_of( resource, &zwp_confined_pointer_v1_interface, &confined_pointer_impl) || wl_resource_instance_of( resource, &zwp_locked_pointer_v1_interface, &locked_pointer_impl)); return wl_resource_get_user_data(resource); } static struct wlr_pointer_constraints_v1 *pointer_constraints_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &zwp_pointer_constraints_v1_interface, &pointer_constraints_impl)); return wl_resource_get_user_data(resource); } static void resource_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static void pointer_constraint_destroy(struct wlr_pointer_constraint_v1 *constraint) { if (constraint == NULL) { return; } wlr_log(WLR_DEBUG, "destroying constraint %p", constraint); wl_signal_emit_mutable(&constraint->events.destroy, constraint); wl_resource_set_user_data(constraint->resource, NULL); wl_list_remove(&constraint->link); wl_list_remove(&constraint->surface_commit.link); wl_list_remove(&constraint->surface_destroy.link); wl_list_remove(&constraint->seat_destroy.link); pixman_region32_fini(&constraint->current.region); pixman_region32_fini(&constraint->pending.region); pixman_region32_fini(&constraint->region); free(constraint); } static void pointer_constraint_destroy_resource(struct wl_resource *resource) { struct wlr_pointer_constraint_v1 *constraint = pointer_constraint_from_resource(resource); pointer_constraint_destroy(constraint); } static void pointer_constraint_set_region( struct wlr_pointer_constraint_v1 *constraint, struct wl_resource *region_resource) { pixman_region32_clear(&constraint->pending.region); if (region_resource) { const pixman_region32_t *region = wlr_region_from_resource(region_resource); pixman_region32_copy(&constraint->pending.region, region); } constraint->pending.committed |= WLR_POINTER_CONSTRAINT_V1_STATE_REGION; } static void pointer_constraint_handle_set_region(struct wl_client *client, struct wl_resource *resource, struct wl_resource *region_resource) { struct wlr_pointer_constraint_v1 *constraint = pointer_constraint_from_resource(resource); if (constraint == NULL) { return; } pointer_constraint_set_region(constraint, region_resource); } static void pointer_constraint_set_cursor_position_hint(struct wl_client *client, struct wl_resource *resource, wl_fixed_t x, wl_fixed_t y) { struct wlr_pointer_constraint_v1 *constraint = pointer_constraint_from_resource(resource); if (constraint == NULL) { return; } constraint->pending.cursor_hint.x = wl_fixed_to_double(x); constraint->pending.cursor_hint.y = wl_fixed_to_double(y); constraint->pending.committed |= WLR_POINTER_CONSTRAINT_V1_STATE_CURSOR_HINT; } static void pointer_constraint_commit( struct wlr_pointer_constraint_v1 *constraint) { if (constraint->pending.committed & WLR_POINTER_CONSTRAINT_V1_STATE_REGION) { pixman_region32_copy(&constraint->current.region, &constraint->pending.region); } if (constraint->pending.committed & WLR_POINTER_CONSTRAINT_V1_STATE_CURSOR_HINT) { constraint->current.cursor_hint = constraint->pending.cursor_hint; } constraint->current.committed |= constraint->pending.committed; bool updated_region = !!constraint->pending.committed; constraint->pending.committed = 0; pixman_region32_clear(&constraint->region); if (pixman_region32_not_empty(&constraint->current.region)) { pixman_region32_intersect(&constraint->region, &constraint->surface->input_region, &constraint->current.region); } else { pixman_region32_copy(&constraint->region, &constraint->surface->input_region); } if (updated_region) { wl_signal_emit_mutable(&constraint->events.set_region, NULL); } } static void handle_surface_commit(struct wl_listener *listener, void *data) { struct wlr_pointer_constraint_v1 *constraint = wl_container_of(listener, constraint, surface_commit); pointer_constraint_commit(constraint); } static void handle_surface_destroy(struct wl_listener *listener, void *data) { struct wlr_pointer_constraint_v1 *constraint = wl_container_of(listener, constraint, surface_destroy); pointer_constraint_destroy(constraint); } static void handle_seat_destroy(struct wl_listener *listener, void *data) { struct wlr_pointer_constraint_v1 *constraint = wl_container_of(listener, constraint, seat_destroy); pointer_constraint_destroy(constraint); } static const struct zwp_confined_pointer_v1_interface confined_pointer_impl = { .destroy = resource_destroy, .set_region = pointer_constraint_handle_set_region, }; static const struct zwp_locked_pointer_v1_interface locked_pointer_impl = { .destroy = resource_destroy, .set_region = pointer_constraint_handle_set_region, .set_cursor_position_hint = pointer_constraint_set_cursor_position_hint, }; static void pointer_constraint_create(struct wl_client *client, struct wl_resource *pointer_constraints_resource, uint32_t id, struct wl_resource *surface_resource, struct wl_resource *pointer_resource, struct wl_resource *region_resource, enum zwp_pointer_constraints_v1_lifetime lifetime, enum wlr_pointer_constraint_v1_type type) { struct wlr_pointer_constraints_v1 *pointer_constraints = pointer_constraints_from_resource(pointer_constraints_resource); struct wlr_surface *surface = wlr_surface_from_resource(surface_resource); struct wlr_seat *seat = wlr_seat_client_from_pointer_resource(pointer_resource)->seat; if (wlr_pointer_constraints_v1_constraint_for_surface(pointer_constraints, surface, seat)) { wl_resource_post_error(pointer_constraints_resource, ZWP_POINTER_CONSTRAINTS_V1_ERROR_ALREADY_CONSTRAINED, "a pointer constraint with a wl_pointer of the same wl_seat" " is already on this surface"); return; } uint32_t version = wl_resource_get_version(pointer_constraints_resource); bool locked_pointer = type == WLR_POINTER_CONSTRAINT_V1_LOCKED; struct wl_resource *resource = locked_pointer ? wl_resource_create(client, &zwp_locked_pointer_v1_interface, version, id) : wl_resource_create(client, &zwp_confined_pointer_v1_interface, version, id); if (resource == NULL) { wl_client_post_no_memory(client); return; } struct wlr_pointer_constraint_v1 *constraint = calloc(1, sizeof(*constraint)); if (constraint == NULL) { wl_resource_destroy(resource); wl_client_post_no_memory(client); return; } constraint->resource = resource; constraint->surface = surface; constraint->seat = seat; constraint->lifetime = lifetime; constraint->type = type; constraint->pointer_constraints = pointer_constraints; wl_signal_init(&constraint->events.set_region); wl_signal_init(&constraint->events.destroy); pixman_region32_init(&constraint->region); pixman_region32_init(&constraint->pending.region); pixman_region32_init(&constraint->current.region); pointer_constraint_set_region(constraint, region_resource); pointer_constraint_commit(constraint); constraint->surface_commit.notify = handle_surface_commit; wl_signal_add(&surface->events.commit, &constraint->surface_commit); constraint->surface_destroy.notify = handle_surface_destroy; wl_signal_add(&surface->events.destroy, &constraint->surface_destroy); constraint->seat_destroy.notify = handle_seat_destroy; wl_signal_add(&seat->events.destroy, &constraint->seat_destroy); void *impl = locked_pointer ? (void *)&locked_pointer_impl : (void *)&confined_pointer_impl; wl_resource_set_implementation(constraint->resource, impl, constraint, pointer_constraint_destroy_resource); wlr_log(WLR_DEBUG, "new %s_pointer %p (res %p)", locked_pointer ? "locked" : "confined", constraint, constraint->resource); wl_list_insert(&pointer_constraints->constraints, &constraint->link); wl_signal_emit_mutable(&pointer_constraints->events.new_constraint, constraint); } static void pointer_constraints_lock_pointer(struct wl_client *client, struct wl_resource *cons_resource, uint32_t id, struct wl_resource *surface, struct wl_resource *pointer, struct wl_resource *region, enum zwp_pointer_constraints_v1_lifetime lifetime) { pointer_constraint_create(client, cons_resource, id, surface, pointer, region, lifetime, WLR_POINTER_CONSTRAINT_V1_LOCKED); } static void pointer_constraints_confine_pointer(struct wl_client *client, struct wl_resource *cons_resource, uint32_t id, struct wl_resource *surface, struct wl_resource *pointer, struct wl_resource *region, enum zwp_pointer_constraints_v1_lifetime lifetime) { pointer_constraint_create(client, cons_resource, id, surface, pointer, region, lifetime, WLR_POINTER_CONSTRAINT_V1_CONFINED); } static const struct zwp_pointer_constraints_v1_interface pointer_constraints_impl = { .destroy = resource_destroy, .lock_pointer = pointer_constraints_lock_pointer, .confine_pointer = pointer_constraints_confine_pointer, }; static void pointer_constraints_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id) { struct wlr_pointer_constraints_v1 *pointer_constraints = data; struct wl_resource *resource = wl_resource_create(client, &zwp_pointer_constraints_v1_interface, version, id); if (resource == NULL) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(resource, &pointer_constraints_impl, pointer_constraints, NULL); } static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_pointer_constraints_v1 *pointer_constraints = wl_container_of(listener, pointer_constraints, display_destroy); wl_list_remove(&pointer_constraints->display_destroy.link); wl_global_destroy(pointer_constraints->global); free(pointer_constraints); } struct wlr_pointer_constraints_v1 *wlr_pointer_constraints_v1_create( struct wl_display *display) { struct wlr_pointer_constraints_v1 *pointer_constraints = calloc(1, sizeof(*pointer_constraints)); if (!pointer_constraints) { return NULL; } struct wl_global *wl_global = wl_global_create(display, &zwp_pointer_constraints_v1_interface, 1, pointer_constraints, pointer_constraints_bind); if (!wl_global) { free(pointer_constraints); return NULL; } pointer_constraints->global = wl_global; wl_list_init(&pointer_constraints->constraints); wl_signal_init(&pointer_constraints->events.new_constraint); pointer_constraints->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &pointer_constraints->display_destroy); return pointer_constraints; } struct wlr_pointer_constraint_v1 * wlr_pointer_constraints_v1_constraint_for_surface( struct wlr_pointer_constraints_v1 *pointer_constraints, struct wlr_surface *surface, struct wlr_seat *seat) { struct wlr_pointer_constraint_v1 *constraint; wl_list_for_each(constraint, &pointer_constraints->constraints, link) { if (constraint->surface == surface && constraint->seat == seat) { return constraint; } } return NULL; } void wlr_pointer_constraint_v1_send_activated( struct wlr_pointer_constraint_v1 *constraint) { wlr_log(WLR_DEBUG, "constrained %p", constraint); if (constraint->type == WLR_POINTER_CONSTRAINT_V1_LOCKED) { zwp_locked_pointer_v1_send_locked(constraint->resource); } else { zwp_confined_pointer_v1_send_confined(constraint->resource); } } void wlr_pointer_constraint_v1_send_deactivated( struct wlr_pointer_constraint_v1 *constraint) { wlr_log(WLR_DEBUG, "unconstrained %p", constraint); if (constraint->type == WLR_POINTER_CONSTRAINT_V1_LOCKED) { zwp_locked_pointer_v1_send_unlocked(constraint->resource); } else { zwp_confined_pointer_v1_send_unconfined(constraint->resource); } if (constraint->lifetime == ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_ONESHOT) { pointer_constraint_destroy(constraint); } } wlroots-0.17.1/types/wlr_pointer_gestures_v1.c000066400000000000000000000326571454110342200215250ustar00rootroot00000000000000#ifndef _POSIX_C_SOURCE #define _POSIX_C_SOURCE 200809L #endif #include #include #include #include #include #include #include #include "pointer-gestures-unstable-v1-protocol.h" #define POINTER_GESTURES_VERSION 3 static void resource_handle_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static void resource_remove_from_list(struct wl_resource *resource) { wl_list_remove(wl_resource_get_link(resource)); } static const struct zwp_pointer_gestures_v1_interface gestures_impl; static const struct zwp_pointer_gesture_swipe_v1_interface swipe_impl; static const struct zwp_pointer_gesture_pinch_v1_interface pinch_impl; static const struct zwp_pointer_gesture_hold_v1_interface hold_impl; static struct wlr_pointer_gestures_v1 *pointer_gestures_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &zwp_pointer_gestures_v1_interface, &gestures_impl)); return wl_resource_get_user_data(resource); } static struct wlr_seat *seat_from_pointer_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &zwp_pointer_gesture_swipe_v1_interface, &swipe_impl) || wl_resource_instance_of(resource, &zwp_pointer_gesture_pinch_v1_interface, &pinch_impl) || wl_resource_instance_of(resource, &zwp_pointer_gesture_hold_v1_interface, &hold_impl)); return wl_resource_get_user_data(resource); } void wlr_pointer_gestures_v1_send_swipe_begin( struct wlr_pointer_gestures_v1 *gestures, struct wlr_seat *seat, uint32_t time_msec, uint32_t fingers) { struct wlr_surface *focus = seat->pointer_state.focused_surface; struct wlr_seat_client *focus_seat_client = seat->pointer_state.focused_client; if (focus == NULL || focus_seat_client == NULL) { return; } struct wl_client *focus_client = focus_seat_client->client; uint32_t serial = wlr_seat_client_next_serial(focus_seat_client); struct wl_resource *gesture; wl_resource_for_each(gesture, &gestures->swipes) { struct wlr_seat *gesture_seat = seat_from_pointer_resource(gesture); struct wl_client *gesture_client = wl_resource_get_client(gesture); if (gesture_seat != seat || gesture_client != focus_client) { continue; } zwp_pointer_gesture_swipe_v1_send_begin(gesture, serial, time_msec, focus->resource, fingers); } } void wlr_pointer_gestures_v1_send_swipe_update( struct wlr_pointer_gestures_v1 *gestures, struct wlr_seat *seat, uint32_t time_msec, double dx, double dy) { struct wlr_surface *focus = seat->pointer_state.focused_surface; struct wlr_seat_client *focus_seat_client = seat->pointer_state.focused_client; if (focus == NULL || focus_seat_client == NULL) { return; } struct wl_client *focus_client = focus_seat_client->client; struct wl_resource *gesture; wl_resource_for_each(gesture, &gestures->swipes) { struct wlr_seat *gesture_seat = seat_from_pointer_resource(gesture); struct wl_client *gesture_client = wl_resource_get_client(gesture); if (gesture_seat != seat || gesture_client != focus_client) { continue; } zwp_pointer_gesture_swipe_v1_send_update(gesture, time_msec, wl_fixed_from_double(dx), wl_fixed_from_double(dy)); } } void wlr_pointer_gestures_v1_send_swipe_end( struct wlr_pointer_gestures_v1 *gestures, struct wlr_seat *seat, uint32_t time_msec, bool cancelled) { struct wlr_surface *focus = seat->pointer_state.focused_surface; struct wlr_seat_client *focus_seat_client = seat->pointer_state.focused_client; if (focus == NULL || focus_seat_client == NULL) { return; } struct wl_client *focus_client = focus_seat_client->client; uint32_t serial = wlr_seat_client_next_serial(focus_seat_client); struct wl_resource *gesture; wl_resource_for_each(gesture, &gestures->swipes) { struct wlr_seat *gesture_seat = seat_from_pointer_resource(gesture); struct wl_client *gesture_client = wl_resource_get_client(gesture); if (gesture_seat != seat || gesture_client != focus_client) { continue; } zwp_pointer_gesture_swipe_v1_send_end(gesture, serial, time_msec, cancelled); } } static const struct zwp_pointer_gesture_swipe_v1_interface swipe_impl = { .destroy = resource_handle_destroy, }; static void get_swipe_gesture(struct wl_client *client, struct wl_resource *gestures_resource, uint32_t id, struct wl_resource *pointer_resource) { struct wlr_seat_client *seat_client = wlr_seat_client_from_pointer_resource(pointer_resource); struct wlr_seat *seat = NULL; if (seat_client != NULL) { seat = seat_client->seat; } // Otherwise, the resource will be inert // (NULL seat, so all seat comparisons will fail) struct wlr_pointer_gestures_v1 *gestures = pointer_gestures_from_resource(gestures_resource); struct wl_resource *gesture = wl_resource_create(client, &zwp_pointer_gesture_swipe_v1_interface, wl_resource_get_version(gestures_resource), id); if (gesture == NULL) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(gesture, &swipe_impl, seat, resource_remove_from_list); wl_list_insert(&gestures->swipes, wl_resource_get_link(gesture)); } void wlr_pointer_gestures_v1_send_pinch_begin( struct wlr_pointer_gestures_v1 *gestures, struct wlr_seat *seat, uint32_t time_msec, uint32_t fingers) { struct wlr_surface *focus = seat->pointer_state.focused_surface; struct wlr_seat_client *focus_seat_client = seat->pointer_state.focused_client; if (focus == NULL || focus_seat_client == NULL) { return; } struct wl_client *focus_client = focus_seat_client->client; uint32_t serial = wlr_seat_client_next_serial(focus_seat_client); struct wl_resource *gesture; wl_resource_for_each(gesture, &gestures->pinches) { struct wlr_seat *gesture_seat = seat_from_pointer_resource(gesture); struct wl_client *gesture_client = wl_resource_get_client(gesture); if (gesture_seat != seat || gesture_client != focus_client) { continue; } zwp_pointer_gesture_pinch_v1_send_begin(gesture, serial, time_msec, focus->resource, fingers); } } void wlr_pointer_gestures_v1_send_pinch_update( struct wlr_pointer_gestures_v1 *gestures, struct wlr_seat *seat, uint32_t time_msec, double dx, double dy, double scale, double rotation) { struct wlr_surface *focus = seat->pointer_state.focused_surface; struct wlr_seat_client *focus_seat_client = seat->pointer_state.focused_client; if (focus == NULL || focus_seat_client == NULL) { return; } struct wl_client *focus_client = focus_seat_client->client; struct wl_resource *gesture; wl_resource_for_each(gesture, &gestures->pinches) { struct wlr_seat *gesture_seat = seat_from_pointer_resource(gesture); struct wl_client *gesture_client = wl_resource_get_client(gesture); if (gesture_seat != seat || gesture_client != focus_client) { continue; } zwp_pointer_gesture_pinch_v1_send_update(gesture, time_msec, wl_fixed_from_double(dx), wl_fixed_from_double(dy), wl_fixed_from_double(scale), wl_fixed_from_double(rotation)); } } void wlr_pointer_gestures_v1_send_pinch_end( struct wlr_pointer_gestures_v1 *gestures, struct wlr_seat *seat, uint32_t time_msec, bool cancelled) { struct wlr_surface *focus = seat->pointer_state.focused_surface; struct wlr_seat_client *focus_seat_client = seat->pointer_state.focused_client; if (focus == NULL || focus_seat_client == NULL) { return; } struct wl_client *focus_client = focus_seat_client->client; uint32_t serial = wlr_seat_client_next_serial(focus_seat_client); struct wl_resource *gesture; wl_resource_for_each(gesture, &gestures->pinches) { struct wlr_seat *gesture_seat = seat_from_pointer_resource(gesture); struct wl_client *gesture_client = wl_resource_get_client(gesture); if (gesture_seat != seat || gesture_client != focus_client) { continue; } zwp_pointer_gesture_pinch_v1_send_end(gesture, serial, time_msec, cancelled); } } static const struct zwp_pointer_gesture_pinch_v1_interface pinch_impl = { .destroy = resource_handle_destroy, }; static void get_pinch_gesture(struct wl_client *client, struct wl_resource *gestures_resource, uint32_t id, struct wl_resource *pointer_resource) { struct wlr_seat_client *seat_client = wlr_seat_client_from_pointer_resource(pointer_resource); struct wlr_seat *seat = NULL; if (seat_client != NULL) { seat = seat_client->seat; } // Otherwise, the resource will be inert // (NULL seat, so all seat comparisons will fail) struct wlr_pointer_gestures_v1 *gestures = pointer_gestures_from_resource(gestures_resource); struct wl_resource *gesture = wl_resource_create(client, &zwp_pointer_gesture_pinch_v1_interface, wl_resource_get_version(gestures_resource), id); if (gesture == NULL) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(gesture, &pinch_impl, seat, resource_remove_from_list); wl_list_insert(&gestures->pinches, wl_resource_get_link(gesture)); } static void pointer_gestures_release(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } void wlr_pointer_gestures_v1_send_hold_begin( struct wlr_pointer_gestures_v1 *gestures, struct wlr_seat *seat, uint32_t time_msec, uint32_t fingers) { struct wlr_surface *focus = seat->pointer_state.focused_surface; struct wlr_seat_client *focus_seat_client = seat->pointer_state.focused_client; if (focus == NULL || focus_seat_client == NULL) { return; } struct wl_client *focus_client = focus_seat_client->client; uint32_t serial = wlr_seat_client_next_serial(focus_seat_client); struct wl_resource *gesture; wl_resource_for_each(gesture, &gestures->holds) { struct wlr_seat *gesture_seat = seat_from_pointer_resource(gesture); struct wl_client *gesture_client = wl_resource_get_client(gesture); if (gesture_seat != seat || gesture_client != focus_client) { continue; } zwp_pointer_gesture_hold_v1_send_begin(gesture, serial, time_msec, focus->resource, fingers); } } void wlr_pointer_gestures_v1_send_hold_end( struct wlr_pointer_gestures_v1 *gestures, struct wlr_seat *seat, uint32_t time_msec, bool cancelled) { struct wlr_surface *focus = seat->pointer_state.focused_surface; struct wlr_seat_client *focus_seat_client = seat->pointer_state.focused_client; if (focus == NULL || focus_seat_client == NULL) { return; } struct wl_client *focus_client = focus_seat_client->client; uint32_t serial = wlr_seat_client_next_serial(focus_seat_client); struct wl_resource *gesture; wl_resource_for_each(gesture, &gestures->holds) { struct wlr_seat *gesture_seat = seat_from_pointer_resource(gesture); struct wl_client *gesture_client = wl_resource_get_client(gesture); if (gesture_seat != seat || gesture_client != focus_client) { continue; } zwp_pointer_gesture_hold_v1_send_end(gesture, serial, time_msec, cancelled); } } static const struct zwp_pointer_gesture_hold_v1_interface hold_impl = { .destroy = resource_handle_destroy, }; static void get_hold_gesture(struct wl_client *client, struct wl_resource *gestures_resource, uint32_t id, struct wl_resource *pointer_resource) { struct wlr_seat_client *seat_client = wlr_seat_client_from_pointer_resource(pointer_resource); struct wlr_seat *seat = NULL; if (seat_client != NULL) { seat = seat_client->seat; } // Otherwise, the resource will be inert // (NULL seat, so all seat comparisons will fail) struct wlr_pointer_gestures_v1 *gestures = pointer_gestures_from_resource(gestures_resource); struct wl_resource *gesture = wl_resource_create(client, &zwp_pointer_gesture_hold_v1_interface, wl_resource_get_version(gestures_resource), id); if (gesture == NULL) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(gesture, &hold_impl, seat, resource_remove_from_list); wl_list_insert(&gestures->holds, wl_resource_get_link(gesture)); } static const struct zwp_pointer_gestures_v1_interface gestures_impl = { .get_swipe_gesture = get_swipe_gesture, .get_pinch_gesture = get_pinch_gesture, .release = pointer_gestures_release, .get_hold_gesture = get_hold_gesture, }; static void pointer_gestures_v1_bind(struct wl_client *wl_client, void *data, uint32_t version, uint32_t id) { struct wlr_pointer_gestures_v1 *gestures = data; struct wl_resource *resource = wl_resource_create(wl_client, &zwp_pointer_gestures_v1_interface, version, id); if (resource == NULL) { wl_client_post_no_memory(wl_client); return; } wl_resource_set_implementation(resource, &gestures_impl, gestures, NULL); } static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_pointer_gestures_v1 *gestures = wl_container_of(listener, gestures, display_destroy); wl_list_remove(&gestures->display_destroy.link); wl_global_destroy(gestures->global); free(gestures); } struct wlr_pointer_gestures_v1 *wlr_pointer_gestures_v1_create( struct wl_display *display) { struct wlr_pointer_gestures_v1 *gestures = calloc(1, sizeof(*gestures)); if (!gestures) { return NULL; } wl_list_init(&gestures->swipes); wl_list_init(&gestures->pinches); wl_list_init(&gestures->holds); gestures->global = wl_global_create(display, &zwp_pointer_gestures_v1_interface, POINTER_GESTURES_VERSION, gestures, pointer_gestures_v1_bind); if (gestures->global == NULL) { free(gestures); return NULL; } gestures->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &gestures->display_destroy); return gestures; } wlroots-0.17.1/types/wlr_presentation_time.c000066400000000000000000000255771454110342200212520ustar00rootroot00000000000000#define _POSIX_C_SOURCE 199309L #include #include #include #include #include #include #include #include "presentation-time-protocol.h" #define PRESENTATION_VERSION 1 struct wlr_presentation_surface_state { struct wlr_presentation_feedback *feedback; }; struct wlr_presentation_surface { struct wlr_presentation_surface_state current, pending; struct wlr_addon addon; // wlr_surface.addons struct wl_listener surface_commit; }; static void feedback_handle_resource_destroy(struct wl_resource *resource) { wl_list_remove(wl_resource_get_link(resource)); } static void feedback_resource_send_presented( struct wl_resource *feedback_resource, const struct wlr_presentation_event *event) { struct wl_client *client = wl_resource_get_client(feedback_resource); struct wl_resource *output_resource; wl_resource_for_each(output_resource, &event->output->resources) { if (wl_resource_get_client(output_resource) == client) { wp_presentation_feedback_send_sync_output(feedback_resource, output_resource); } } uint32_t tv_sec_hi = event->tv_sec >> 32; uint32_t tv_sec_lo = event->tv_sec & 0xFFFFFFFF; uint32_t seq_hi = event->seq >> 32; uint32_t seq_lo = event->seq & 0xFFFFFFFF; wp_presentation_feedback_send_presented(feedback_resource, tv_sec_hi, tv_sec_lo, event->tv_nsec, event->refresh, seq_hi, seq_lo, event->flags); wl_resource_destroy(feedback_resource); } static void feedback_resource_send_discarded( struct wl_resource *feedback_resource) { wp_presentation_feedback_send_discarded(feedback_resource); wl_resource_destroy(feedback_resource); } static const struct wlr_addon_interface presentation_surface_addon_impl; static void presentation_surface_addon_destroy(struct wlr_addon *addon) { struct wlr_presentation_surface *p_surface = wl_container_of(addon, p_surface, addon); wlr_addon_finish(addon); wlr_presentation_feedback_destroy(p_surface->current.feedback); wlr_presentation_feedback_destroy(p_surface->pending.feedback); wl_list_remove(&p_surface->surface_commit.link); free(p_surface); } static const struct wlr_addon_interface presentation_surface_addon_impl = { .name = "wlr_presentation_surface", .destroy = presentation_surface_addon_destroy, }; static void presentation_surface_handle_surface_commit( struct wl_listener *listener, void *data) { struct wlr_presentation_surface *p_surface = wl_container_of(listener, p_surface, surface_commit); wlr_presentation_feedback_destroy(p_surface->current.feedback); p_surface->current.feedback = p_surface->pending.feedback; p_surface->pending.feedback = NULL; } static const struct wp_presentation_interface presentation_impl; static struct wlr_presentation *presentation_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &wp_presentation_interface, &presentation_impl)); return wl_resource_get_user_data(resource); } static void presentation_handle_feedback(struct wl_client *client, struct wl_resource *presentation_resource, struct wl_resource *surface_resource, uint32_t id) { struct wlr_presentation *presentation = presentation_from_resource(presentation_resource); struct wlr_surface *surface = wlr_surface_from_resource(surface_resource); struct wlr_addon *addon = wlr_addon_find(&surface->addons, presentation, &presentation_surface_addon_impl); struct wlr_presentation_surface *p_surface = NULL; if (addon != NULL) { p_surface = wl_container_of(addon, p_surface, addon); } else { p_surface = calloc(1, sizeof(*p_surface)); if (p_surface == NULL) { wl_client_post_no_memory(client); return; } wlr_addon_init(&p_surface->addon, &surface->addons, presentation, &presentation_surface_addon_impl); p_surface->surface_commit.notify = presentation_surface_handle_surface_commit; wl_signal_add(&surface->events.commit, &p_surface->surface_commit); } struct wlr_presentation_feedback *feedback = p_surface->pending.feedback; if (feedback == NULL) { feedback = calloc(1, sizeof(*feedback)); if (feedback == NULL) { wl_client_post_no_memory(client); return; } wl_list_init(&feedback->resources); p_surface->pending.feedback = feedback; } uint32_t version = wl_resource_get_version(presentation_resource); struct wl_resource *resource = wl_resource_create(client, &wp_presentation_feedback_interface, version, id); if (resource == NULL) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(resource, NULL, feedback, feedback_handle_resource_destroy); wl_list_insert(&feedback->resources, wl_resource_get_link(resource)); } static void presentation_handle_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static const struct wp_presentation_interface presentation_impl = { .feedback = presentation_handle_feedback, .destroy = presentation_handle_destroy, }; static void presentation_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id) { struct wlr_presentation *presentation = data; struct wl_resource *resource = wl_resource_create(client, &wp_presentation_interface, version, id); if (resource == NULL) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(resource, &presentation_impl, presentation, NULL); wp_presentation_send_clock_id(resource, CLOCK_MONOTONIC); } static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_presentation *presentation = wl_container_of(listener, presentation, display_destroy); wl_signal_emit_mutable(&presentation->events.destroy, presentation); wl_list_remove(&presentation->display_destroy.link); wl_global_destroy(presentation->global); free(presentation); } struct wlr_presentation *wlr_presentation_create(struct wl_display *display, struct wlr_backend *backend) { struct wlr_presentation *presentation = calloc(1, sizeof(*presentation)); if (presentation == NULL) { return NULL; } presentation->global = wl_global_create(display, &wp_presentation_interface, PRESENTATION_VERSION, presentation, presentation_bind); if (presentation->global == NULL) { free(presentation); return NULL; } wl_signal_init(&presentation->events.destroy); presentation->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &presentation->display_destroy); return presentation; } void wlr_presentation_feedback_send_presented( struct wlr_presentation_feedback *feedback, const struct wlr_presentation_event *event) { struct wl_resource *resource, *tmp; wl_resource_for_each_safe(resource, tmp, &feedback->resources) { feedback_resource_send_presented(resource, event); } } struct wlr_presentation_feedback *wlr_presentation_surface_sampled( struct wlr_presentation *presentation, struct wlr_surface *surface) { struct wlr_addon *addon = wlr_addon_find(&surface->addons, presentation, &presentation_surface_addon_impl); if (addon != NULL) { struct wlr_presentation_surface *p_surface = wl_container_of(addon, p_surface, addon); struct wlr_presentation_feedback *sampled = p_surface->current.feedback; p_surface->current.feedback = NULL; return sampled; } return NULL; } static void feedback_unset_output(struct wlr_presentation_feedback *feedback); void wlr_presentation_feedback_destroy( struct wlr_presentation_feedback *feedback) { if (feedback == NULL) { return; } struct wl_resource *resource, *tmp; wl_resource_for_each_safe(resource, tmp, &feedback->resources) { feedback_resource_send_discarded(resource); } assert(wl_list_empty(&feedback->resources)); feedback_unset_output(feedback); free(feedback); } void wlr_presentation_event_from_output(struct wlr_presentation_event *event, const struct wlr_output_event_present *output_event) { *event = (struct wlr_presentation_event){ .output = output_event->output, .tv_sec = (uint64_t)output_event->when->tv_sec, .tv_nsec = (uint32_t)output_event->when->tv_nsec, .refresh = (uint32_t)output_event->refresh, .seq = (uint64_t)output_event->seq, .flags = output_event->flags, }; } static void feedback_unset_output(struct wlr_presentation_feedback *feedback) { if (feedback->output == NULL) { return; } feedback->output = NULL; wl_list_remove(&feedback->output_commit.link); wl_list_remove(&feedback->output_present.link); wl_list_remove(&feedback->output_destroy.link); } static void feedback_handle_output_commit(struct wl_listener *listener, void *data) { struct wlr_presentation_feedback *feedback = wl_container_of(listener, feedback, output_commit); if (feedback->output_committed) { return; } feedback->output_committed = true; feedback->output_commit_seq = feedback->output->commit_seq; } static void feedback_handle_output_present(struct wl_listener *listener, void *data) { struct wlr_presentation_feedback *feedback = wl_container_of(listener, feedback, output_present); struct wlr_output_event_present *output_event = data; if (!feedback->output_committed || output_event->commit_seq != feedback->output_commit_seq) { return; } if (output_event->presented) { struct wlr_presentation_event event = {0}; wlr_presentation_event_from_output(&event, output_event); if (!feedback->zero_copy) { event.flags &= ~WP_PRESENTATION_FEEDBACK_KIND_ZERO_COPY; } wlr_presentation_feedback_send_presented(feedback, &event); } wlr_presentation_feedback_destroy(feedback); } static void feedback_handle_output_destroy(struct wl_listener *listener, void *data) { struct wlr_presentation_feedback *feedback = wl_container_of(listener, feedback, output_destroy); wlr_presentation_feedback_destroy(feedback); } static void presentation_surface_queued_on_output( struct wlr_presentation *presentation, struct wlr_surface *surface, struct wlr_output *output, bool zero_copy) { struct wlr_presentation_feedback *feedback = wlr_presentation_surface_sampled(presentation, surface); if (feedback == NULL) { return; } assert(feedback->output == NULL); feedback->output = output; feedback->zero_copy = zero_copy; feedback->output_commit.notify = feedback_handle_output_commit; wl_signal_add(&output->events.commit, &feedback->output_commit); feedback->output_present.notify = feedback_handle_output_present; wl_signal_add(&output->events.present, &feedback->output_present); feedback->output_destroy.notify = feedback_handle_output_destroy; wl_signal_add(&output->events.destroy, &feedback->output_destroy); } void wlr_presentation_surface_textured_on_output( struct wlr_presentation *presentation, struct wlr_surface *surface, struct wlr_output *output) { return presentation_surface_queued_on_output(presentation, surface, output, false); } void wlr_presentation_surface_scanned_out_on_output( struct wlr_presentation *presentation, struct wlr_surface *surface, struct wlr_output *output) { return presentation_surface_queued_on_output(presentation, surface, output, true); } wlroots-0.17.1/types/wlr_primary_selection.c000066400000000000000000000057451454110342200212440ustar00rootroot00000000000000#include #include #include #include void wlr_primary_selection_source_init( struct wlr_primary_selection_source *source, const struct wlr_primary_selection_source_impl *impl) { assert(impl->send); *source = (struct wlr_primary_selection_source){ .impl = impl, }; wl_array_init(&source->mime_types); wl_signal_init(&source->events.destroy); } void wlr_primary_selection_source_destroy( struct wlr_primary_selection_source *source) { if (source == NULL) { return; } wl_signal_emit_mutable(&source->events.destroy, source); char **p; wl_array_for_each(p, &source->mime_types) { free(*p); } wl_array_release(&source->mime_types); if (source->impl->destroy) { source->impl->destroy(source); } else { free(source); } } void wlr_primary_selection_source_send( struct wlr_primary_selection_source *source, const char *mime_type, int32_t fd) { source->impl->send(source, mime_type, fd); } void wlr_seat_request_set_primary_selection(struct wlr_seat *seat, struct wlr_seat_client *client, struct wlr_primary_selection_source *source, uint32_t serial) { if (client && !wlr_seat_client_validate_event_serial(client, serial)) { wlr_log(WLR_DEBUG, "Rejecting set_primary_selection request, " "serial %"PRIu32" was never given to client", serial); return; } if (seat->primary_selection_source && serial - seat->primary_selection_serial > UINT32_MAX / 2) { wlr_log(WLR_DEBUG, "Rejecting set_primary_selection request, " "serial indicates superseded (%"PRIu32" < %"PRIu32")", serial, seat->primary_selection_serial); return; } struct wlr_seat_request_set_primary_selection_event event = { .source = source, .serial = serial, }; wl_signal_emit_mutable(&seat->events.request_set_primary_selection, &event); } static void seat_handle_primary_selection_source_destroy( struct wl_listener *listener, void *data) { struct wlr_seat *seat = wl_container_of(listener, seat, primary_selection_source_destroy); wl_list_remove(&seat->primary_selection_source_destroy.link); seat->primary_selection_source = NULL; wl_signal_emit_mutable(&seat->events.set_primary_selection, seat); } void wlr_seat_set_primary_selection(struct wlr_seat *seat, struct wlr_primary_selection_source *source, uint32_t serial) { if (seat->primary_selection_source == source) { seat->primary_selection_serial = serial; return; } if (seat->primary_selection_source != NULL) { wl_list_remove(&seat->primary_selection_source_destroy.link); wlr_primary_selection_source_destroy(seat->primary_selection_source); seat->primary_selection_source = NULL; } seat->primary_selection_source = source; seat->primary_selection_serial = serial; if (source != NULL) { seat->primary_selection_source_destroy.notify = seat_handle_primary_selection_source_destroy; wl_signal_add(&source->events.destroy, &seat->primary_selection_source_destroy); } wl_signal_emit_mutable(&seat->events.set_primary_selection, seat); } wlroots-0.17.1/types/wlr_primary_selection_v1.c000066400000000000000000000354451454110342200216520ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include #include #include "primary-selection-unstable-v1-protocol.h" #define DEVICE_MANAGER_VERSION 1 static const struct zwp_primary_selection_offer_v1_interface offer_impl; static struct wlr_primary_selection_v1_device *device_from_offer_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &zwp_primary_selection_offer_v1_interface, &offer_impl)); return wl_resource_get_user_data(resource); } static void offer_handle_receive(struct wl_client *client, struct wl_resource *resource, const char *mime_type, int32_t fd) { struct wlr_primary_selection_v1_device *device = device_from_offer_resource(resource); if (device == NULL || device->seat->primary_selection_source == NULL) { close(fd); return; } wlr_primary_selection_source_send(device->seat->primary_selection_source, mime_type, fd); } static void offer_handle_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static const struct zwp_primary_selection_offer_v1_interface offer_impl = { .receive = offer_handle_receive, .destroy = offer_handle_destroy, }; static void offer_handle_resource_destroy(struct wl_resource *resource) { wl_list_remove(wl_resource_get_link(resource)); } static struct wlr_primary_selection_v1_device *device_from_resource( struct wl_resource *resource); static void create_offer(struct wl_resource *device_resource, struct wlr_primary_selection_source *source) { struct wlr_primary_selection_v1_device *device = device_from_resource(device_resource); assert(device != NULL); struct wl_client *client = wl_resource_get_client(device_resource); uint32_t version = wl_resource_get_version(device_resource); struct wl_resource *resource = wl_resource_create(client, &zwp_primary_selection_offer_v1_interface, version, 0); if (resource == NULL) { wl_resource_post_no_memory(device_resource); return; } wl_resource_set_implementation(resource, &offer_impl, device, offer_handle_resource_destroy); wl_list_insert(&device->offers, wl_resource_get_link(resource)); zwp_primary_selection_device_v1_send_data_offer(device_resource, resource); char **p; wl_array_for_each(p, &source->mime_types) { zwp_primary_selection_offer_v1_send_offer(resource, *p); } zwp_primary_selection_device_v1_send_selection(device_resource, resource); } static void destroy_offer(struct wl_resource *resource) { if (device_from_offer_resource(resource) == NULL) { return; } // Make the offer inert wl_resource_set_user_data(resource, NULL); struct wl_list *link = wl_resource_get_link(resource); wl_list_remove(link); wl_list_init(link); } struct client_data_source { struct wlr_primary_selection_source source; struct wl_resource *resource; bool finalized; }; static void client_source_send( struct wlr_primary_selection_source *wlr_source, const char *mime_type, int fd) { struct client_data_source *source = wl_container_of(wlr_source, source, source); zwp_primary_selection_source_v1_send_send(source->resource, mime_type, fd); close(fd); } static void client_source_destroy( struct wlr_primary_selection_source *wlr_source) { struct client_data_source *source = wl_container_of(wlr_source, source, source); zwp_primary_selection_source_v1_send_cancelled(source->resource); // Make the source resource inert wl_resource_set_user_data(source->resource, NULL); free(source); } static const struct wlr_primary_selection_source_impl client_source_impl = { .send = client_source_send, .destroy = client_source_destroy, }; static const struct zwp_primary_selection_source_v1_interface source_impl; static struct client_data_source *client_data_source_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &zwp_primary_selection_source_v1_interface, &source_impl)); return wl_resource_get_user_data(resource); } static void source_handle_offer(struct wl_client *client, struct wl_resource *resource, const char *mime_type) { struct client_data_source *source = client_data_source_from_resource(resource); if (source == NULL) { return; } if (source->finalized) { wlr_log(WLR_DEBUG, "Offering additional MIME type after set_selection"); } const char **mime_type_ptr; wl_array_for_each(mime_type_ptr, &source->source.mime_types) { if (strcmp(*mime_type_ptr, mime_type) == 0) { wlr_log(WLR_DEBUG, "Ignoring duplicate MIME type offer %s", mime_type); return; } } char *dup_mime_type = strdup(mime_type); if (dup_mime_type == NULL) { wl_resource_post_no_memory(resource); return; } char **p = wl_array_add(&source->source.mime_types, sizeof(*p)); if (p == NULL) { free(dup_mime_type); wl_resource_post_no_memory(resource); return; } *p = dup_mime_type; } static void source_handle_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static const struct zwp_primary_selection_source_v1_interface source_impl = { .offer = source_handle_offer, .destroy = source_handle_destroy, }; static void source_resource_handle_destroy(struct wl_resource *resource) { struct client_data_source *source = client_data_source_from_resource(resource); if (source == NULL) { return; } wlr_primary_selection_source_destroy(&source->source); } static const struct zwp_primary_selection_device_v1_interface device_impl; static struct wlr_primary_selection_v1_device *device_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &zwp_primary_selection_device_v1_interface, &device_impl)); return wl_resource_get_user_data(resource); } static void device_handle_set_selection(struct wl_client *client, struct wl_resource *resource, struct wl_resource *source_resource, uint32_t serial) { struct wlr_primary_selection_v1_device *device = device_from_resource(resource); if (device == NULL) { return; } struct client_data_source *client_source = NULL; if (source_resource != NULL) { client_source = client_data_source_from_resource(source_resource); } struct wlr_primary_selection_source *source = NULL; if (client_source != NULL) { client_source->finalized = true; source = &client_source->source; } struct wlr_seat_client *seat_client = wlr_seat_client_for_wl_client(device->seat, client); wlr_seat_request_set_primary_selection(device->seat, seat_client, source, serial); } static void device_handle_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static const struct zwp_primary_selection_device_v1_interface device_impl = { .set_selection = device_handle_set_selection, .destroy = device_handle_destroy, }; static void device_handle_resource_destroy(struct wl_resource *resource) { wl_list_remove(wl_resource_get_link(resource)); } static void device_resource_send_selection(struct wl_resource *resource, struct wlr_primary_selection_source *source) { assert(device_from_resource(resource) != NULL); if (source != NULL) { create_offer(resource, source); } else { zwp_primary_selection_device_v1_send_selection(resource, NULL); } } static void device_send_selection( struct wlr_primary_selection_v1_device *device) { struct wlr_seat_client *seat_client = device->seat->keyboard_state.focused_client; if (seat_client == NULL) { return; } struct wl_resource *resource; wl_resource_for_each(resource, &device->resources) { if (wl_resource_get_client(resource) == seat_client->client) { device_resource_send_selection(resource, device->seat->primary_selection_source); } } } static void device_destroy(struct wlr_primary_selection_v1_device *device); static void device_handle_seat_destroy(struct wl_listener *listener, void *data) { struct wlr_primary_selection_v1_device *device = wl_container_of(listener, device, seat_destroy); device_destroy(device); } static void device_handle_seat_focus_change(struct wl_listener *listener, void *data) { struct wlr_primary_selection_v1_device *device = wl_container_of(listener, device, seat_focus_change); // TODO: maybe make previous offers inert, or set a NULL selection for // previous client? device_send_selection(device); } static void device_handle_seat_set_primary_selection( struct wl_listener *listener, void *data) { struct wlr_primary_selection_v1_device *device = wl_container_of(listener, device, seat_set_primary_selection); struct wl_resource *resource, *tmp; wl_resource_for_each_safe(resource, tmp, &device->offers) { destroy_offer(resource); } device_send_selection(device); } static struct wlr_primary_selection_v1_device *get_or_create_device( struct wlr_primary_selection_v1_device_manager *manager, struct wlr_seat *seat) { struct wlr_primary_selection_v1_device *device; wl_list_for_each(device, &manager->devices, link) { if (device->seat == seat) { return device; } } device = calloc(1, sizeof(*device)); if (device == NULL) { return NULL; } device->manager = manager; device->seat = seat; wl_list_init(&device->resources); wl_list_insert(&manager->devices, &device->link); wl_list_init(&device->offers); device->seat_destroy.notify = device_handle_seat_destroy; wl_signal_add(&seat->events.destroy, &device->seat_destroy); device->seat_focus_change.notify = device_handle_seat_focus_change; wl_signal_add(&seat->keyboard_state.events.focus_change, &device->seat_focus_change); device->seat_set_primary_selection.notify = device_handle_seat_set_primary_selection; wl_signal_add(&seat->events.set_primary_selection, &device->seat_set_primary_selection); return device; } static void device_destroy(struct wlr_primary_selection_v1_device *device) { if (device == NULL) { return; } wl_list_remove(&device->link); wl_list_remove(&device->seat_destroy.link); wl_list_remove(&device->seat_focus_change.link); wl_list_remove(&device->seat_set_primary_selection.link); struct wl_resource *resource, *resource_tmp; wl_resource_for_each_safe(resource, resource_tmp, &device->offers) { destroy_offer(resource); } wl_resource_for_each_safe(resource, resource_tmp, &device->resources) { // Make the resource inert wl_resource_set_user_data(resource, NULL); struct wl_list *link = wl_resource_get_link(resource); wl_list_remove(link); wl_list_init(link); } free(device); } static const struct zwp_primary_selection_device_manager_v1_interface device_manager_impl; static struct wlr_primary_selection_v1_device_manager *manager_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &zwp_primary_selection_device_manager_v1_interface, &device_manager_impl)); return wl_resource_get_user_data(resource); } static void device_manager_handle_create_source(struct wl_client *client, struct wl_resource *manager_resource, uint32_t id) { struct client_data_source *source = calloc(1, sizeof(*source)); if (source == NULL) { wl_client_post_no_memory(client); return; } wlr_primary_selection_source_init(&source->source, &client_source_impl); uint32_t version = wl_resource_get_version(manager_resource); source->resource = wl_resource_create(client, &zwp_primary_selection_source_v1_interface, version, id); if (source->resource == NULL) { free(source); wl_client_post_no_memory(client); return; } wl_resource_set_implementation(source->resource, &source_impl, source, source_resource_handle_destroy); } static void device_manager_handle_get_device(struct wl_client *client, struct wl_resource *manager_resource, uint32_t id, struct wl_resource *seat_resource) { struct wlr_seat_client *seat_client = wlr_seat_client_from_resource(seat_resource); struct wlr_primary_selection_v1_device_manager *manager = manager_from_resource(manager_resource); uint32_t version = wl_resource_get_version(manager_resource); struct wl_resource *resource = wl_resource_create(client, &zwp_primary_selection_device_v1_interface, version, id); if (resource == NULL) { wl_resource_post_no_memory(manager_resource); return; } wl_resource_set_implementation(resource, &device_impl, NULL, device_handle_resource_destroy); wl_list_init(wl_resource_get_link(resource)); if (seat_client == NULL) { return; } struct wlr_primary_selection_v1_device *device = get_or_create_device(manager, seat_client->seat); if (device == NULL) { wl_resource_destroy(resource); wl_resource_post_no_memory(manager_resource); return; } wl_resource_set_user_data(resource, device); wl_list_insert(&device->resources, wl_resource_get_link(resource)); if (device->seat->keyboard_state.focused_client == seat_client) { device_resource_send_selection(resource, device->seat->primary_selection_source); } } static void device_manager_handle_destroy(struct wl_client *client, struct wl_resource *manager_resource) { wl_resource_destroy(manager_resource); } static const struct zwp_primary_selection_device_manager_v1_interface device_manager_impl = { .create_source = device_manager_handle_create_source, .get_device = device_manager_handle_get_device, .destroy = device_manager_handle_destroy, }; static void primary_selection_device_manager_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id) { struct wlr_primary_selection_v1_device_manager *manager = data; struct wl_resource *resource = wl_resource_create(client, &zwp_primary_selection_device_manager_v1_interface, version, id); if (resource == NULL) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(resource, &device_manager_impl, manager, NULL); } static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_primary_selection_v1_device_manager *manager = wl_container_of(listener, manager, display_destroy); struct wlr_primary_selection_v1_device *device, *tmp; wl_list_for_each_safe(device, tmp, &manager->devices, link) { device_destroy(device); } wl_signal_emit_mutable(&manager->events.destroy, manager); wl_list_remove(&manager->display_destroy.link); wl_global_destroy(manager->global); free(manager); } struct wlr_primary_selection_v1_device_manager * wlr_primary_selection_v1_device_manager_create( struct wl_display *display) { struct wlr_primary_selection_v1_device_manager *manager = calloc(1, sizeof(*manager)); if (manager == NULL) { return NULL; } manager->global = wl_global_create(display, &zwp_primary_selection_device_manager_v1_interface, DEVICE_MANAGER_VERSION, manager, primary_selection_device_manager_bind); if (manager->global == NULL) { free(manager); return NULL; } wl_list_init(&manager->devices); wl_signal_init(&manager->events.destroy); manager->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &manager->display_destroy); return manager; } wlroots-0.17.1/types/wlr_region.c000066400000000000000000000044001454110342200167620ustar00rootroot00000000000000#include #include #include #include #include #include #include "types/wlr_region.h" static const struct wl_region_interface region_impl; static pixman_region32_t *region_from_resource(struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &wl_region_interface, ®ion_impl)); return wl_resource_get_user_data(resource); } const pixman_region32_t *wlr_region_from_resource(struct wl_resource *resource) { return region_from_resource(resource); } static void region_add(struct wl_client *client, struct wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) { pixman_region32_t *region = region_from_resource(resource); pixman_region32_union_rect(region, region, x, y, width, height); } static void region_subtract(struct wl_client *client, struct wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) { pixman_region32_t *region = region_from_resource(resource); pixman_region32_union_rect(region, region, x, y, width, height); pixman_region32_t rect; pixman_region32_init_rect(&rect, x, y, width, height); pixman_region32_subtract(region, region, &rect); pixman_region32_fini(&rect); } static void region_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static const struct wl_region_interface region_impl = { .destroy = region_destroy, .add = region_add, .subtract = region_subtract, }; static void region_handle_resource_destroy(struct wl_resource *resource) { pixman_region32_t *reg = region_from_resource(resource); pixman_region32_fini(reg); free(reg); } struct wl_resource *region_create(struct wl_client *client, uint32_t version, uint32_t id) { pixman_region32_t *region = calloc(1, sizeof(*region)); if (region == NULL) { wl_client_post_no_memory(client); return NULL; } pixman_region32_init(region); struct wl_resource *region_resource = wl_resource_create(client, &wl_region_interface, version, id); if (region_resource == NULL) { free(region); wl_client_post_no_memory(client); return NULL; } wl_resource_set_implementation(region_resource, ®ion_impl, region, region_handle_resource_destroy); return region_resource; } wlroots-0.17.1/types/wlr_relative_pointer_v1.c000066400000000000000000000161521454110342200214670ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include "relative-pointer-unstable-v1-protocol.h" #define RELATIVE_POINTER_MANAGER_VERSION 1 static const struct zwp_relative_pointer_manager_v1_interface relative_pointer_manager_v1_impl; static const struct zwp_relative_pointer_v1_interface relative_pointer_v1_impl; struct wlr_relative_pointer_v1 *wlr_relative_pointer_v1_from_resource(struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &zwp_relative_pointer_v1_interface, &relative_pointer_v1_impl)); return wl_resource_get_user_data(resource); } static struct wlr_relative_pointer_manager_v1 *relative_pointer_manager_from_resource(struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &zwp_relative_pointer_manager_v1_interface, &relative_pointer_manager_v1_impl)); return wl_resource_get_user_data(resource); } static void relative_pointer_destroy(struct wlr_relative_pointer_v1 *relative_pointer) { wl_signal_emit_mutable(&relative_pointer->events.destroy, relative_pointer); wl_list_remove(&relative_pointer->link); wl_list_remove(&relative_pointer->seat_destroy.link); wl_list_remove(&relative_pointer->pointer_destroy.link); wl_resource_set_user_data(relative_pointer->resource, NULL); free(relative_pointer); } static void relative_pointer_v1_handle_resource_destroy(struct wl_resource *resource) { struct wlr_relative_pointer_v1 *relative_pointer = wlr_relative_pointer_v1_from_resource(resource); if (relative_pointer == NULL) { return; } relative_pointer_destroy(relative_pointer); } static void relative_pointer_v1_handle_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static void relative_pointer_handle_seat_destroy(struct wl_listener *listener, void *data) { struct wlr_relative_pointer_v1 *relative_pointer = wl_container_of(listener, relative_pointer, seat_destroy); relative_pointer_destroy(relative_pointer); } static void relative_pointer_handle_pointer_destroy(struct wl_listener *listener, void *data) { struct wlr_relative_pointer_v1 *relative_pointer = wl_container_of(listener, relative_pointer, pointer_destroy); relative_pointer_destroy(relative_pointer); } static void relative_pointer_manager_v1_handle_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static void relative_pointer_manager_v1_handle_get_relative_pointer(struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *pointer) { struct wlr_relative_pointer_manager_v1 *manager = relative_pointer_manager_from_resource(resource); struct wlr_seat_client *seat_client = wlr_seat_client_from_pointer_resource(pointer); struct wl_resource *relative_pointer_resource = wl_resource_create(client, &zwp_relative_pointer_v1_interface, wl_resource_get_version(resource), id); if (relative_pointer_resource == NULL) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(relative_pointer_resource, &relative_pointer_v1_impl, NULL, relative_pointer_v1_handle_resource_destroy); if (seat_client == NULL) { // Leave the resource inert return; } struct wlr_relative_pointer_v1 *relative_pointer = calloc(1, sizeof(*relative_pointer)); if (relative_pointer == NULL) { wl_client_post_no_memory(client); return; } relative_pointer->resource = relative_pointer_resource; relative_pointer->pointer_resource = pointer; relative_pointer->seat = seat_client->seat; wl_signal_add(&relative_pointer->seat->events.destroy, &relative_pointer->seat_destroy); relative_pointer->seat_destroy.notify = relative_pointer_handle_seat_destroy; wl_signal_init(&relative_pointer->events.destroy); wl_resource_set_user_data(relative_pointer_resource, relative_pointer); wl_list_insert(&manager->relative_pointers, &relative_pointer->link); wl_resource_add_destroy_listener(relative_pointer->pointer_resource, &relative_pointer->pointer_destroy); relative_pointer->pointer_destroy.notify = relative_pointer_handle_pointer_destroy; wl_signal_emit_mutable(&manager->events.new_relative_pointer, relative_pointer); } static void relative_pointer_manager_v1_bind(struct wl_client *wl_client, void *data, uint32_t version, uint32_t id) { struct wlr_relative_pointer_manager_v1 *manager = data; struct wl_resource *manager_resource = wl_resource_create(wl_client, &zwp_relative_pointer_manager_v1_interface, version, id); if (manager_resource == NULL) { wl_client_post_no_memory(wl_client); return; } wl_resource_set_implementation(manager_resource, &relative_pointer_manager_v1_impl, manager, NULL); } static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_relative_pointer_manager_v1 *manager = wl_container_of(listener, manager, display_destroy_listener); wl_signal_emit_mutable(&manager->events.destroy, manager); wl_list_remove(&manager->display_destroy_listener.link); wl_global_destroy(manager->global); free(manager); } static const struct zwp_relative_pointer_manager_v1_interface relative_pointer_manager_v1_impl = { .destroy = relative_pointer_manager_v1_handle_destroy, .get_relative_pointer = relative_pointer_manager_v1_handle_get_relative_pointer, }; static const struct zwp_relative_pointer_v1_interface relative_pointer_v1_impl = { .destroy = relative_pointer_v1_handle_destroy, }; struct wlr_relative_pointer_manager_v1 *wlr_relative_pointer_manager_v1_create(struct wl_display *display) { struct wlr_relative_pointer_manager_v1 *manager = calloc(1, sizeof(*manager)); if (manager == NULL) { return NULL; } wl_list_init(&manager->relative_pointers); manager->global = wl_global_create(display, &zwp_relative_pointer_manager_v1_interface, RELATIVE_POINTER_MANAGER_VERSION, manager, relative_pointer_manager_v1_bind); if (manager->global == NULL) { free(manager); return NULL; } wl_signal_init(&manager->events.destroy); wl_signal_init(&manager->events.new_relative_pointer); manager->display_destroy_listener.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &manager->display_destroy_listener); return manager; } void wlr_relative_pointer_manager_v1_send_relative_motion( struct wlr_relative_pointer_manager_v1 *manager, struct wlr_seat *seat, uint64_t time_usec, double dx, double dy, double dx_unaccel, double dy_unaccel) { struct wlr_seat_client *focused = seat->pointer_state.focused_client; if (focused == NULL) { return; } struct wlr_relative_pointer_v1 *pointer; wl_list_for_each(pointer, &manager->relative_pointers, link) { struct wlr_seat_client *seat_client = wlr_seat_client_from_pointer_resource(pointer->pointer_resource); if (!pointer->seat || seat != pointer->seat || focused != seat_client) { continue; } zwp_relative_pointer_v1_send_relative_motion(pointer->resource, (uint32_t)(time_usec >> 32), (uint32_t)time_usec, wl_fixed_from_double(dx), wl_fixed_from_double(dy), wl_fixed_from_double(dx_unaccel), wl_fixed_from_double(dy_unaccel)); } } wlroots-0.17.1/types/wlr_screencopy_v1.c000066400000000000000000000472151454110342200202720ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include "wlr-screencopy-unstable-v1-protocol.h" #include "render/pixel_format.h" #include "render/wlr_renderer.h" #define SCREENCOPY_MANAGER_VERSION 3 struct screencopy_damage { struct wl_list link; struct wlr_output *output; struct pixman_region32 damage; struct wl_listener output_precommit; struct wl_listener output_destroy; }; static const struct zwlr_screencopy_frame_v1_interface frame_impl; static struct screencopy_damage *screencopy_damage_find( struct wlr_screencopy_v1_client *client, struct wlr_output *output) { struct screencopy_damage *damage; wl_list_for_each(damage, &client->damages, link) { if (damage->output == output) { return damage; } } return NULL; } static void screencopy_damage_accumulate(struct screencopy_damage *damage, const struct wlr_output_state *state) { struct pixman_region32 *region = &damage->damage; struct wlr_output *output = damage->output; if (state->committed & WLR_OUTPUT_STATE_DAMAGE) { // If the compositor submitted damage, copy it over pixman_region32_union(region, region, &state->damage); pixman_region32_intersect_rect(region, region, 0, 0, output->width, output->height); } else if (state->committed & WLR_OUTPUT_STATE_BUFFER) { // If the compositor did not submit damage but did submit a buffer // damage everything pixman_region32_union_rect(region, region, 0, 0, output->width, output->height); } } static void screencopy_damage_handle_output_precommit( struct wl_listener *listener, void *data) { struct screencopy_damage *damage = wl_container_of(listener, damage, output_precommit); const struct wlr_output_event_precommit *event = data; screencopy_damage_accumulate(damage, event->state); } static void screencopy_damage_destroy(struct screencopy_damage *damage) { wl_list_remove(&damage->output_destroy.link); wl_list_remove(&damage->output_precommit.link); wl_list_remove(&damage->link); pixman_region32_fini(&damage->damage); free(damage); } static void screencopy_damage_handle_output_destroy( struct wl_listener *listener, void *data) { struct screencopy_damage *damage = wl_container_of(listener, damage, output_destroy); screencopy_damage_destroy(damage); } static struct screencopy_damage *screencopy_damage_create( struct wlr_screencopy_v1_client *client, struct wlr_output *output) { struct screencopy_damage *damage = calloc(1, sizeof(*damage)); if (!damage) { return NULL; } damage->output = output; pixman_region32_init_rect(&damage->damage, 0, 0, output->width, output->height); wl_list_insert(&client->damages, &damage->link); wl_signal_add(&output->events.precommit, &damage->output_precommit); damage->output_precommit.notify = screencopy_damage_handle_output_precommit; wl_signal_add(&output->events.destroy, &damage->output_destroy); damage->output_destroy.notify = screencopy_damage_handle_output_destroy; return damage; } static struct screencopy_damage *screencopy_damage_get_or_create( struct wlr_screencopy_v1_client *client, struct wlr_output *output) { struct screencopy_damage *damage = screencopy_damage_find(client, output); return damage ? damage : screencopy_damage_create(client, output); } static void client_unref(struct wlr_screencopy_v1_client *client) { assert(client->ref > 0); if (--client->ref != 0) { return; } struct screencopy_damage *damage, *tmp_damage; wl_list_for_each_safe(damage, tmp_damage, &client->damages, link) { screencopy_damage_destroy(damage); } free(client); } static struct wlr_screencopy_frame_v1 *frame_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &zwlr_screencopy_frame_v1_interface, &frame_impl)); return wl_resource_get_user_data(resource); } static void frame_destroy(struct wlr_screencopy_frame_v1 *frame) { if (frame == NULL) { return; } if (frame->output != NULL && frame->buffer != NULL) { wlr_output_lock_attach_render(frame->output, false); if (frame->cursor_locked) { wlr_output_lock_software_cursors(frame->output, false); } } wl_list_remove(&frame->link); wl_list_remove(&frame->output_commit.link); wl_list_remove(&frame->output_destroy.link); wl_list_remove(&frame->output_enable.link); // Make the frame resource inert wl_resource_set_user_data(frame->resource, NULL); wlr_buffer_unlock(frame->buffer); client_unref(frame->client); free(frame); } static void frame_send_damage(struct wlr_screencopy_frame_v1 *frame) { if (!frame->with_damage) { return; } struct screencopy_damage *damage = screencopy_damage_get_or_create(frame->client, frame->output); if (damage == NULL) { return; } // TODO: send fine-grained damage events struct pixman_box32 *damage_box = pixman_region32_extents(&damage->damage); int damage_x = damage_box->x1; int damage_y = damage_box->y1; int damage_width = damage_box->x2 - damage_box->x1; int damage_height = damage_box->y2 - damage_box->y1; zwlr_screencopy_frame_v1_send_damage(frame->resource, damage_x, damage_y, damage_width, damage_height); pixman_region32_clear(&damage->damage); } static void frame_send_ready(struct wlr_screencopy_frame_v1 *frame, struct timespec *when) { time_t tv_sec = when->tv_sec; uint32_t tv_sec_hi = (sizeof(tv_sec) > 4) ? tv_sec >> 32 : 0; uint32_t tv_sec_lo = tv_sec & 0xFFFFFFFF; zwlr_screencopy_frame_v1_send_ready(frame->resource, tv_sec_hi, tv_sec_lo, when->tv_nsec); } static bool frame_shm_copy(struct wlr_screencopy_frame_v1 *frame, struct wlr_buffer *src_buffer) { struct wlr_output *output = frame->output; struct wlr_renderer *renderer = output->renderer; assert(renderer); int x = frame->box.x; int y = frame->box.y; int width = frame->box.width; int height = frame->box.height; void *data; uint32_t format; size_t stride; if (!wlr_buffer_begin_data_ptr_access(frame->buffer, WLR_BUFFER_DATA_PTR_ACCESS_WRITE, &data, &format, &stride)) { return false; } bool ok = false; if (!renderer_bind_buffer(renderer, src_buffer)) { goto out; } ok = wlr_renderer_read_pixels(renderer, format, stride, width, height, x, y, 0, 0, data); renderer_bind_buffer(renderer, NULL); out: wlr_buffer_end_data_ptr_access(frame->buffer); return ok; } static bool frame_dma_copy(struct wlr_screencopy_frame_v1 *frame, struct wlr_buffer *src_buffer) { struct wlr_buffer *dst_buffer = frame->buffer; struct wlr_output *output = frame->output; struct wlr_renderer *renderer = output->renderer; assert(renderer); struct wlr_texture *src_tex = wlr_texture_from_buffer(renderer, src_buffer); if (src_tex == NULL) { return false; } bool ok = false; struct wlr_render_pass *pass = wlr_renderer_begin_buffer_pass(renderer, dst_buffer, NULL); if (!pass) { goto out; } wlr_render_pass_add_texture(pass, &(struct wlr_render_texture_options) { .texture = src_tex, .blend_mode = WLR_RENDER_BLEND_MODE_NONE, .dst_box = (struct wlr_box){ .width = dst_buffer->width, .height = dst_buffer->height, }, .src_box = (struct wlr_fbox){ .x = frame->box.x, .y = frame->box.y, .width = frame->box.width, .height = frame->box.height, }, }); ok = wlr_render_pass_submit(pass); out: wlr_texture_destroy(src_tex); return ok; } static void frame_handle_output_commit(struct wl_listener *listener, void *data) { struct wlr_screencopy_frame_v1 *frame = wl_container_of(listener, frame, output_commit); struct wlr_output_event_commit *event = data; struct wlr_output *output = frame->output; struct wlr_renderer *renderer = output->renderer; assert(renderer); if (!(event->state->committed & WLR_OUTPUT_STATE_BUFFER)) { return; } if (!frame->buffer) { return; } if (frame->with_damage) { struct screencopy_damage *damage = screencopy_damage_get_or_create(frame->client, output); if (damage && !pixman_region32_not_empty(&damage->damage)) { return; } } wl_list_remove(&frame->output_commit.link); wl_list_init(&frame->output_commit.link); struct wlr_buffer *src_buffer = event->state->buffer; if (frame->box.x < 0 || frame->box.y < 0 || frame->box.x + frame->box.width > src_buffer->width || frame->box.y + frame->box.height > src_buffer->height) { goto err; } switch (frame->buffer_cap) { case WLR_BUFFER_CAP_DMABUF: if (!frame_dma_copy(frame, src_buffer)) { goto err; } break; case WLR_BUFFER_CAP_DATA_PTR: if (!frame_shm_copy(frame, src_buffer)) { goto err; } break; default: abort(); // unreachable } zwlr_screencopy_frame_v1_send_flags(frame->resource, 0); frame_send_damage(frame); frame_send_ready(frame, event->when); frame_destroy(frame); return; err: zwlr_screencopy_frame_v1_send_failed(frame->resource); frame_destroy(frame); } static void frame_handle_output_enable(struct wl_listener *listener, void *data) { struct wlr_screencopy_frame_v1 *frame = wl_container_of(listener, frame, output_enable); if (!frame->output->enabled) { zwlr_screencopy_frame_v1_send_failed(frame->resource); frame_destroy(frame); } } static void frame_handle_output_destroy(struct wl_listener *listener, void *data) { struct wlr_screencopy_frame_v1 *frame = wl_container_of(listener, frame, output_destroy); zwlr_screencopy_frame_v1_send_failed(frame->resource); frame_destroy(frame); } static void frame_handle_copy(struct wl_client *wl_client, struct wl_resource *frame_resource, struct wl_resource *buffer_resource) { struct wlr_screencopy_frame_v1 *frame = frame_from_resource(frame_resource); if (frame == NULL) { return; } struct wlr_output *output = frame->output; if (!output->enabled) { zwlr_screencopy_frame_v1_send_failed(frame->resource); frame_destroy(frame); return; } struct wlr_buffer *buffer = wlr_buffer_try_from_resource(buffer_resource); if (buffer == NULL) { wl_resource_post_error(frame->resource, ZWLR_SCREENCOPY_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer"); return; } if (buffer->width != frame->box.width || buffer->height != frame->box.height) { wl_resource_post_error(frame->resource, ZWLR_SCREENCOPY_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer dimensions"); return; } if (frame->buffer != NULL) { wl_resource_post_error(frame->resource, ZWLR_SCREENCOPY_FRAME_V1_ERROR_ALREADY_USED, "frame already used"); return; } enum wlr_buffer_cap cap; struct wlr_dmabuf_attributes dmabuf; void *data; uint32_t format; size_t stride; if (wlr_buffer_get_dmabuf(buffer, &dmabuf)) { cap = WLR_BUFFER_CAP_DMABUF; if (dmabuf.format != frame->dmabuf_format) { wl_resource_post_error(frame->resource, ZWLR_SCREENCOPY_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer format"); return; } } else if (wlr_buffer_begin_data_ptr_access(buffer, WLR_BUFFER_DATA_PTR_ACCESS_WRITE, &data, &format, &stride)) { wlr_buffer_end_data_ptr_access(buffer); cap = WLR_BUFFER_CAP_DATA_PTR; if (format != frame->shm_format) { wl_resource_post_error(frame->resource, ZWLR_SCREENCOPY_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer format"); return; } if (stride != (size_t)frame->shm_stride) { wl_resource_post_error(frame->resource, ZWLR_SCREENCOPY_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer stride"); return; } } else { wl_resource_post_error(frame->resource, ZWLR_SCREENCOPY_FRAME_V1_ERROR_INVALID_BUFFER, "unsupported buffer type"); return; } frame->buffer = buffer; frame->buffer_cap = cap; wl_signal_add(&output->events.commit, &frame->output_commit); frame->output_commit.notify = frame_handle_output_commit; wl_signal_add(&output->events.destroy, &frame->output_enable); frame->output_enable.notify = frame_handle_output_enable; // Request a frame because we can't assume that the current front buffer is still usable. It may // have been released already, and we shouldn't lock it here because compositors want to render // into the least damaged buffer. wlr_output_update_needs_frame(output); wlr_output_lock_attach_render(output, true); if (frame->overlay_cursor) { wlr_output_lock_software_cursors(output, true); frame->cursor_locked = true; } } static void frame_handle_copy_with_damage(struct wl_client *wl_client, struct wl_resource *frame_resource, struct wl_resource *buffer_resource) { struct wlr_screencopy_frame_v1 *frame = frame_from_resource(frame_resource); if (frame == NULL) { return; } frame->with_damage = true; frame_handle_copy(wl_client, frame_resource, buffer_resource); } static void frame_handle_destroy(struct wl_client *wl_client, struct wl_resource *frame_resource) { wl_resource_destroy(frame_resource); } static const struct zwlr_screencopy_frame_v1_interface frame_impl = { .copy = frame_handle_copy, .destroy = frame_handle_destroy, .copy_with_damage = frame_handle_copy_with_damage, }; static void frame_handle_resource_destroy(struct wl_resource *frame_resource) { struct wlr_screencopy_frame_v1 *frame = frame_from_resource(frame_resource); frame_destroy(frame); } static const struct zwlr_screencopy_manager_v1_interface manager_impl; static struct wlr_screencopy_v1_client *client_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &zwlr_screencopy_manager_v1_interface, &manager_impl)); return wl_resource_get_user_data(resource); } static void capture_output(struct wl_client *wl_client, struct wlr_screencopy_v1_client *client, uint32_t version, uint32_t id, int32_t overlay_cursor, struct wlr_output *output, const struct wlr_box *box) { struct wlr_screencopy_frame_v1 *frame = calloc(1, sizeof(*frame)); if (frame == NULL) { wl_client_post_no_memory(wl_client); return; } frame->output = output; frame->overlay_cursor = !!overlay_cursor; frame->resource = wl_resource_create(wl_client, &zwlr_screencopy_frame_v1_interface, version, id); if (frame->resource == NULL) { free(frame); wl_client_post_no_memory(wl_client); return; } wl_resource_set_implementation(frame->resource, &frame_impl, frame, frame_handle_resource_destroy); if (output == NULL) { wl_resource_set_user_data(frame->resource, NULL); zwlr_screencopy_frame_v1_send_failed(frame->resource); free(frame); return; } frame->client = client; client->ref++; wl_list_insert(&client->manager->frames, &frame->link); wl_list_init(&frame->output_commit.link); wl_list_init(&frame->output_enable.link); wl_signal_add(&output->events.destroy, &frame->output_destroy); frame->output_destroy.notify = frame_handle_output_destroy; if (output == NULL || !output->enabled) { goto error; } struct wlr_renderer *renderer = output->renderer; assert(renderer); frame->shm_format = wlr_output_preferred_read_format(frame->output); if (frame->shm_format == DRM_FORMAT_INVALID) { wlr_log(WLR_ERROR, "Failed to capture output: no read format supported by renderer"); goto error; } const struct wlr_pixel_format_info *shm_info = drm_get_pixel_format_info(frame->shm_format); if (!shm_info) { wlr_log(WLR_ERROR, "Failed to capture output: no pixel format info matching read format"); goto error; } if (output->allocator && (output->allocator->buffer_caps & WLR_BUFFER_CAP_DMABUF)) { frame->dmabuf_format = output->render_format; } else { frame->dmabuf_format = DRM_FORMAT_INVALID; } struct wlr_box buffer_box = {0}; if (box == NULL) { buffer_box.width = output->width; buffer_box.height = output->height; } else { int ow, oh; wlr_output_effective_resolution(output, &ow, &oh); buffer_box = *box; wlr_box_transform(&buffer_box, &buffer_box, wlr_output_transform_invert(output->transform), ow, oh); buffer_box.x *= output->scale; buffer_box.y *= output->scale; buffer_box.width *= output->scale; buffer_box.height *= output->scale; } frame->box = buffer_box; frame->shm_stride = pixel_format_info_min_stride(shm_info, buffer_box.width); zwlr_screencopy_frame_v1_send_buffer(frame->resource, convert_drm_format_to_wl_shm(frame->shm_format), buffer_box.width, buffer_box.height, frame->shm_stride); if (version >= 3) { if (frame->dmabuf_format != DRM_FORMAT_INVALID) { zwlr_screencopy_frame_v1_send_linux_dmabuf( frame->resource, frame->dmabuf_format, buffer_box.width, buffer_box.height); } zwlr_screencopy_frame_v1_send_buffer_done(frame->resource); } return; error: zwlr_screencopy_frame_v1_send_failed(frame->resource); frame_destroy(frame); } static void manager_handle_capture_output(struct wl_client *wl_client, struct wl_resource *manager_resource, uint32_t id, int32_t overlay_cursor, struct wl_resource *output_resource) { struct wlr_screencopy_v1_client *client = client_from_resource(manager_resource); uint32_t version = wl_resource_get_version(manager_resource); struct wlr_output *output = wlr_output_from_resource(output_resource); capture_output(wl_client, client, version, id, overlay_cursor, output, NULL); } static void manager_handle_capture_output_region(struct wl_client *wl_client, struct wl_resource *manager_resource, uint32_t id, int32_t overlay_cursor, struct wl_resource *output_resource, int32_t x, int32_t y, int32_t width, int32_t height) { struct wlr_screencopy_v1_client *client = client_from_resource(manager_resource); uint32_t version = wl_resource_get_version(manager_resource); struct wlr_output *output = wlr_output_from_resource(output_resource); struct wlr_box box = { .x = x, .y = y, .width = width, .height = height, }; capture_output(wl_client, client, version, id, overlay_cursor, output, &box); } static void manager_handle_destroy(struct wl_client *wl_client, struct wl_resource *manager_resource) { wl_resource_destroy(manager_resource); } static const struct zwlr_screencopy_manager_v1_interface manager_impl = { .capture_output = manager_handle_capture_output, .capture_output_region = manager_handle_capture_output_region, .destroy = manager_handle_destroy, }; static void manager_handle_resource_destroy(struct wl_resource *resource) { struct wlr_screencopy_v1_client *client = client_from_resource(resource); client_unref(client); } static void manager_bind(struct wl_client *wl_client, void *data, uint32_t version, uint32_t id) { struct wlr_screencopy_manager_v1 *manager = data; struct wlr_screencopy_v1_client *client = calloc(1, sizeof(*client)); if (client == NULL) { goto failure; } struct wl_resource *resource = wl_resource_create(wl_client, &zwlr_screencopy_manager_v1_interface, version, id); if (resource == NULL) { goto failure; } client->ref = 1; client->manager = manager; wl_list_init(&client->damages); wl_resource_set_implementation(resource, &manager_impl, client, manager_handle_resource_destroy); return; failure: free(client); wl_client_post_no_memory(wl_client); } static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_screencopy_manager_v1 *manager = wl_container_of(listener, manager, display_destroy); wl_signal_emit_mutable(&manager->events.destroy, manager); wl_list_remove(&manager->display_destroy.link); wl_global_destroy(manager->global); free(manager); } struct wlr_screencopy_manager_v1 *wlr_screencopy_manager_v1_create( struct wl_display *display) { struct wlr_screencopy_manager_v1 *manager = calloc(1, sizeof(*manager)); if (manager == NULL) { return NULL; } manager->global = wl_global_create(display, &zwlr_screencopy_manager_v1_interface, SCREENCOPY_MANAGER_VERSION, manager, manager_bind); if (manager->global == NULL) { free(manager); return NULL; } wl_list_init(&manager->frames); wl_signal_init(&manager->events.destroy); manager->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &manager->display_destroy); return manager; } wlroots-0.17.1/types/wlr_security_context_v1.c000066400000000000000000000331471454110342200215320ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include #include "security-context-v1-protocol.h" #define SECURITY_CONTEXT_MANAGER_V1_VERSION 1 struct wlr_security_context_v1 { struct wlr_security_context_manager_v1 *manager; struct wlr_security_context_v1_state state; struct wl_list link; // wlr_security_context_manager_v1.contexts int listen_fd, close_fd; struct wl_event_source *listen_source, *close_source; }; struct wlr_security_context_v1_client { struct wlr_security_context_v1_state state; struct wl_listener destroy; }; static void resource_handle_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static const struct wp_security_context_manager_v1_interface manager_impl; static const struct wp_security_context_v1_interface security_context_impl; static struct wlr_security_context_manager_v1 *manager_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &wp_security_context_manager_v1_interface, &manager_impl)); struct wlr_security_context_manager_v1 *manager = wl_resource_get_user_data(resource); assert(manager != NULL); return manager; } /** * Get a struct wlr_security_context_v1 from a struct wl_resource. * * NULL is returned if the security context has been committed. */ static struct wlr_security_context_v1 *security_context_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &wp_security_context_v1_interface, &security_context_impl)); return wl_resource_get_user_data(resource); } static void security_context_state_finish(struct wlr_security_context_v1_state *state) { free(state->app_id); free(state->sandbox_engine); free(state->instance_id); } static bool copy_state_field(char **dst, const char *src) { if (src == NULL) { return true; } *dst = strdup(src); return *dst != NULL; } static bool security_context_state_copy(struct wlr_security_context_v1_state *dst, const struct wlr_security_context_v1_state *src) { bool ok = copy_state_field(&dst->app_id, src->app_id) && copy_state_field(&dst->sandbox_engine, src->sandbox_engine) && copy_state_field(&dst->instance_id, src->instance_id); if (!ok) { security_context_state_finish(dst); } return ok; } static void security_context_destroy( struct wlr_security_context_v1 *security_context) { if (security_context == NULL) { return; } if (security_context->listen_source != NULL) { wl_event_source_remove(security_context->listen_source); } if (security_context->close_source != NULL) { wl_event_source_remove(security_context->close_source); } close(security_context->listen_fd); close(security_context->close_fd); security_context_state_finish(&security_context->state); wl_list_remove(&security_context->link); free(security_context); } static void security_context_client_handle_destroy(struct wl_listener *listener, void *data) { struct wlr_security_context_v1_client *security_context_client = wl_container_of(listener, security_context_client, destroy); wl_list_remove(&security_context_client->destroy.link); security_context_state_finish(&security_context_client->state); free(security_context_client); } static int security_context_handle_listen_fd_event(int listen_fd, uint32_t mask, void *data) { struct wlr_security_context_v1 *security_context = data; if (mask & (WL_EVENT_HANGUP | WL_EVENT_ERROR)) { security_context_destroy(security_context); return 0; } if (mask & WL_EVENT_READABLE) { int client_fd = accept(listen_fd, NULL, NULL); if (client_fd < 0) { wlr_log_errno(WLR_ERROR, "accept failed"); return 0; } struct wlr_security_context_v1_client *security_context_client = calloc(1, sizeof(*security_context_client)); if (security_context_client == NULL) { wlr_log_errno(WLR_ERROR, "Allocation failed"); close(client_fd); return 0; } struct wl_display *display = wl_global_get_display(security_context->manager->global); struct wl_client *client = wl_client_create(display, client_fd); if (client == NULL) { wlr_log(WLR_ERROR, "wl_client_create failed"); close(client_fd); return 0; } if (!security_context_state_copy(&security_context_client->state, &security_context->state)) { wl_client_post_no_memory(client); return 0; } security_context_client->destroy.notify = security_context_client_handle_destroy; wl_client_add_destroy_listener(client, &security_context_client->destroy); } return 0; } static int security_context_handle_close_fd_event(int fd, uint32_t mask, void *data) { struct wlr_security_context_v1 *security_context = data; if (mask & (WL_EVENT_HANGUP | WL_EVENT_ERROR)) { security_context_destroy(security_context); } return 0; } static void security_context_handle_commit(struct wl_client *client, struct wl_resource *resource) { struct wlr_security_context_v1 *security_context = security_context_from_resource(resource); if (security_context == NULL) { wl_resource_post_error(resource, WP_SECURITY_CONTEXT_V1_ERROR_ALREADY_USED, "Security context has already been committed"); return; } // In theory the compositor should prevent this with a global filter, but // let's make sure it doesn't happen. if (wlr_security_context_manager_v1_lookup_client(security_context->manager, client) != NULL) { wl_resource_post_error(resource, WP_SECURITY_CONTEXT_MANAGER_V1_ERROR_NESTED, "Nested security contexts are forbidden"); return; } struct wl_display *display = wl_client_get_display(client); struct wl_event_loop *loop = wl_display_get_event_loop(display); security_context->listen_source = wl_event_loop_add_fd(loop, security_context->listen_fd, WL_EVENT_READABLE, security_context_handle_listen_fd_event, security_context); if (security_context->listen_source == NULL) { wl_resource_post_no_memory(resource); return; } security_context->close_source = wl_event_loop_add_fd(loop, security_context->close_fd, 0, security_context_handle_close_fd_event, security_context); if (security_context->close_source == NULL) { wl_resource_post_no_memory(resource); return; } wl_resource_set_user_data(resource, NULL); struct wlr_security_context_v1_commit_event event = { .state = &security_context->state, .parent_client = client, }; wl_signal_emit_mutable(&security_context->manager->events.commit, &event); } static void security_context_handle_set_sandbox_engine(struct wl_client *client, struct wl_resource *resource, const char *sandbox_engine) { struct wlr_security_context_v1 *security_context = security_context_from_resource(resource); if (security_context == NULL) { wl_resource_post_error(resource, WP_SECURITY_CONTEXT_V1_ERROR_ALREADY_USED, "Security context has already been committed"); return; } if (security_context->state.sandbox_engine != NULL) { wl_resource_post_error(resource, WP_SECURITY_CONTEXT_V1_ERROR_ALREADY_SET, "Sandbox engine has already been set"); return; } security_context->state.sandbox_engine = strdup(sandbox_engine); if (security_context->state.sandbox_engine == NULL) { wl_resource_post_no_memory(resource); } } static void security_context_handle_set_app_id(struct wl_client *client, struct wl_resource *resource, const char *app_id) { struct wlr_security_context_v1 *security_context = security_context_from_resource(resource); if (security_context == NULL) { wl_resource_post_error(resource, WP_SECURITY_CONTEXT_V1_ERROR_ALREADY_USED, "Security context has already been committed"); return; } if (security_context->state.app_id != NULL) { wl_resource_post_error(resource, WP_SECURITY_CONTEXT_V1_ERROR_ALREADY_SET, "App ID has already been set"); return; } security_context->state.app_id = strdup(app_id); if (security_context->state.app_id == NULL) { wl_resource_post_no_memory(resource); } } static void security_context_handle_set_instance_id(struct wl_client *client, struct wl_resource *resource, const char *instance_id) { struct wlr_security_context_v1 *security_context = security_context_from_resource(resource); if (security_context == NULL) { wl_resource_post_error(resource, WP_SECURITY_CONTEXT_V1_ERROR_ALREADY_USED, "Security context has already been committed"); return; } if (security_context->state.instance_id != NULL) { wl_resource_post_error(resource, WP_SECURITY_CONTEXT_V1_ERROR_ALREADY_SET, "Instance ID has already been set"); return; } security_context->state.instance_id = strdup(instance_id); if (security_context->state.instance_id == NULL) { wl_resource_post_no_memory(resource); } } static const struct wp_security_context_v1_interface security_context_impl = { .destroy = resource_handle_destroy, .commit = security_context_handle_commit, .set_sandbox_engine = security_context_handle_set_sandbox_engine, .set_app_id = security_context_handle_set_app_id, .set_instance_id = security_context_handle_set_instance_id, }; static void security_context_resource_destroy(struct wl_resource *resource) { struct wlr_security_context_v1 *security_context = security_context_from_resource(resource); security_context_destroy(security_context); } static void manager_handle_create_listener(struct wl_client *client, struct wl_resource *manager_resource, uint32_t id, int listen_fd, int close_fd) { struct wlr_security_context_manager_v1 *manager = manager_from_resource(manager_resource); struct stat stat_buf = {0}; if (fstat(listen_fd, &stat_buf) != 0) { wlr_log_errno(WLR_ERROR, "fstat failed on listen FD"); wl_resource_post_error(manager_resource, WP_SECURITY_CONTEXT_MANAGER_V1_ERROR_INVALID_LISTEN_FD, "Invalid listen_fd"); return; } else if (!S_ISSOCK(stat_buf.st_mode)) { wl_resource_post_error(manager_resource, WP_SECURITY_CONTEXT_MANAGER_V1_ERROR_INVALID_LISTEN_FD, "listen_fd is not a socket"); return; } int accept_conn = 0; socklen_t accept_conn_size = sizeof(accept_conn); if (getsockopt(listen_fd, SOL_SOCKET, SO_ACCEPTCONN, &accept_conn, &accept_conn_size) != 0) { wlr_log_errno(WLR_ERROR, "getsockopt failed on listen FD"); wl_resource_post_error(manager_resource, WP_SECURITY_CONTEXT_MANAGER_V1_ERROR_INVALID_LISTEN_FD, "Invalid listen_fd"); return; } else if (accept_conn == 0) { wl_resource_post_error(manager_resource, WP_SECURITY_CONTEXT_MANAGER_V1_ERROR_INVALID_LISTEN_FD, "listen_fd is not a listening socket"); return; } struct wlr_security_context_v1 *security_context = calloc(1, sizeof(*security_context)); if (security_context == NULL) { wl_resource_post_no_memory(manager_resource); return; } security_context->manager = manager; security_context->listen_fd = listen_fd; security_context->close_fd = close_fd; uint32_t version = wl_resource_get_version(manager_resource); struct wl_resource *resource = wl_resource_create(client, &wp_security_context_v1_interface, version, id); if (resource == NULL) { free(security_context); wl_resource_post_no_memory(manager_resource); return; } wl_resource_set_implementation(resource, &security_context_impl, security_context, security_context_resource_destroy); wl_list_insert(&manager->contexts, &security_context->link); } static const struct wp_security_context_manager_v1_interface manager_impl = { .destroy = resource_handle_destroy, .create_listener = manager_handle_create_listener, }; static void manager_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id) { struct wlr_security_context_manager_v1 *manager = data; struct wl_resource *resource = wl_resource_create(client, &wp_security_context_manager_v1_interface, version, id); if (resource == NULL) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(resource, &manager_impl, manager, NULL); } static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_security_context_manager_v1 *manager = wl_container_of(listener, manager, display_destroy); wl_signal_emit_mutable(&manager->events.destroy, manager); assert(wl_list_empty(&manager->events.destroy.listener_list)); assert(wl_list_empty(&manager->events.commit.listener_list)); struct wlr_security_context_v1 *security_context, *tmp; wl_list_for_each_safe(security_context, tmp, &manager->contexts, link) { security_context_destroy(security_context); } wl_global_destroy(manager->global); wl_list_remove(&manager->display_destroy.link); free(manager); } struct wlr_security_context_manager_v1 *wlr_security_context_manager_v1_create( struct wl_display *display) { struct wlr_security_context_manager_v1 *manager = calloc(1, sizeof(*manager)); if (manager == NULL) { return NULL; } manager->global = wl_global_create(display, &wp_security_context_manager_v1_interface, SECURITY_CONTEXT_MANAGER_V1_VERSION, manager, manager_bind); if (manager->global == NULL) { free(manager); return NULL; } wl_list_init(&manager->contexts); wl_signal_init(&manager->events.destroy); wl_signal_init(&manager->events.commit); manager->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &manager->display_destroy); return manager; } const struct wlr_security_context_v1_state *wlr_security_context_manager_v1_lookup_client( struct wlr_security_context_manager_v1 *manager, struct wl_client *client) { struct wl_listener *listener = wl_client_get_destroy_listener(client, security_context_client_handle_destroy); if (listener == NULL) { return NULL; } struct wlr_security_context_v1_client *security_context_client = wl_container_of(listener, security_context_client, destroy); return &security_context_client->state; } wlroots-0.17.1/types/wlr_server_decoration.c000066400000000000000000000146771454110342200212350ustar00rootroot00000000000000#include #include #include #include #include #include "server-decoration-protocol.h" static const struct org_kde_kwin_server_decoration_interface server_decoration_impl; static struct wlr_server_decoration *decoration_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &org_kde_kwin_server_decoration_interface, &server_decoration_impl)); return wl_resource_get_user_data(resource); } static void server_decoration_handle_release(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static void server_decoration_handle_request_mode(struct wl_client *client, struct wl_resource *resource, uint32_t mode) { struct wlr_server_decoration *decoration = decoration_from_resource(resource); if (decoration == NULL || decoration->mode == mode) { return; } decoration->mode = mode; wl_signal_emit_mutable(&decoration->events.mode, decoration); org_kde_kwin_server_decoration_send_mode(decoration->resource, decoration->mode); } static void server_decoration_destroy( struct wlr_server_decoration *decoration) { wl_signal_emit_mutable(&decoration->events.destroy, decoration); wl_list_remove(&decoration->surface_destroy_listener.link); wl_resource_set_user_data(decoration->resource, NULL); wl_list_remove(&decoration->link); free(decoration); } static void server_decoration_destroy_resource(struct wl_resource *resource) { struct wlr_server_decoration *decoration = decoration_from_resource(resource); if (decoration != NULL) { server_decoration_destroy(decoration); } } static void server_decoration_handle_surface_destroy( struct wl_listener *listener, void *data) { struct wlr_server_decoration *decoration = wl_container_of(listener, decoration, surface_destroy_listener); server_decoration_destroy(decoration); } static const struct org_kde_kwin_server_decoration_interface server_decoration_impl = { .release = server_decoration_handle_release, .request_mode = server_decoration_handle_request_mode, }; static const struct org_kde_kwin_server_decoration_manager_interface server_decoration_manager_impl; static struct wlr_server_decoration_manager *manager_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &org_kde_kwin_server_decoration_manager_interface, &server_decoration_manager_impl)); return wl_resource_get_user_data(resource); } static void server_decoration_manager_handle_create(struct wl_client *client, struct wl_resource *manager_resource, uint32_t id, struct wl_resource *surface_resource) { struct wlr_server_decoration_manager *manager = manager_from_resource(manager_resource); struct wlr_surface *surface = wlr_surface_from_resource(surface_resource); struct wlr_server_decoration *decoration = calloc(1, sizeof(*decoration)); if (decoration == NULL) { wl_client_post_no_memory(client); return; } decoration->surface = surface; decoration->mode = manager->default_mode; int version = wl_resource_get_version(manager_resource); decoration->resource = wl_resource_create(client, &org_kde_kwin_server_decoration_interface, version, id); if (decoration->resource == NULL) { free(decoration); wl_client_post_no_memory(client); return; } wl_resource_set_implementation(decoration->resource, &server_decoration_impl, decoration, server_decoration_destroy_resource); wlr_log(WLR_DEBUG, "new server_decoration %p (res %p)", decoration, decoration->resource); wl_signal_init(&decoration->events.destroy); wl_signal_init(&decoration->events.mode); wl_signal_add(&surface->events.destroy, &decoration->surface_destroy_listener); decoration->surface_destroy_listener.notify = server_decoration_handle_surface_destroy; wl_list_insert(&manager->decorations, &decoration->link); org_kde_kwin_server_decoration_send_mode(decoration->resource, decoration->mode); wl_signal_emit_mutable(&manager->events.new_decoration, decoration); } static const struct org_kde_kwin_server_decoration_manager_interface server_decoration_manager_impl = { .create = server_decoration_manager_handle_create, }; void wlr_server_decoration_manager_set_default_mode( struct wlr_server_decoration_manager *manager, uint32_t default_mode) { manager->default_mode = default_mode; struct wl_resource *resource; wl_resource_for_each(resource, &manager->resources) { org_kde_kwin_server_decoration_manager_send_default_mode(resource, manager->default_mode); } } static void server_decoration_manager_destroy_resource( struct wl_resource *resource) { wl_list_remove(wl_resource_get_link(resource)); } static void server_decoration_manager_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id) { struct wlr_server_decoration_manager *manager = data; struct wl_resource *resource = wl_resource_create(client, &org_kde_kwin_server_decoration_manager_interface, version, id); if (resource == NULL) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(resource, &server_decoration_manager_impl, manager, server_decoration_manager_destroy_resource); wl_list_insert(&manager->resources, wl_resource_get_link(resource)); org_kde_kwin_server_decoration_manager_send_default_mode(resource, manager->default_mode); } static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_server_decoration_manager *manager = wl_container_of(listener, manager, display_destroy); wl_signal_emit_mutable(&manager->events.destroy, manager); wl_list_remove(&manager->display_destroy.link); wl_global_destroy(manager->global); free(manager); } struct wlr_server_decoration_manager *wlr_server_decoration_manager_create( struct wl_display *display) { struct wlr_server_decoration_manager *manager = calloc(1, sizeof(*manager)); if (manager == NULL) { return NULL; } manager->global = wl_global_create(display, &org_kde_kwin_server_decoration_manager_interface, 1, manager, server_decoration_manager_bind); if (manager->global == NULL) { free(manager); return NULL; } manager->default_mode = ORG_KDE_KWIN_SERVER_DECORATION_MANAGER_MODE_NONE; wl_list_init(&manager->resources); wl_list_init(&manager->decorations); wl_signal_init(&manager->events.new_decoration); wl_signal_init(&manager->events.destroy); manager->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &manager->display_destroy); return manager; } wlroots-0.17.1/types/wlr_session_lock_v1.c000066400000000000000000000346531454110342200206150ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include "ext-session-lock-v1-protocol.h" // Note: ext_session_lock_surface_v1 objects become inert // when the corresponding ext_session_lock_v1 is destroyed #define SESSION_LOCK_VERSION 1 static void resource_handle_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static const struct ext_session_lock_manager_v1_interface lock_manager_implementation; static const struct ext_session_lock_v1_interface lock_implementation; static const struct ext_session_lock_surface_v1_interface lock_surface_implementation; static void lock_surface_destroy(struct wlr_session_lock_surface_v1 *lock_surface) { wlr_surface_unmap(lock_surface->surface); wl_signal_emit_mutable(&lock_surface->events.destroy, NULL); wl_list_remove(&lock_surface->link); struct wlr_session_lock_surface_v1_configure *configure, *tmp; wl_list_for_each_safe(configure, tmp, &lock_surface->configure_list, link) { wl_list_remove(&configure->link); free(configure); } assert(wl_list_empty(&lock_surface->events.destroy.listener_list)); wl_list_remove(&lock_surface->output_destroy.link); wl_resource_set_user_data(lock_surface->resource, NULL); free(lock_surface); } static struct wlr_session_lock_manager_v1 *lock_manager_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &ext_session_lock_manager_v1_interface, &lock_manager_implementation)); struct wlr_session_lock_manager_v1 *lock_manager = wl_resource_get_user_data(resource); assert(lock_manager != NULL); return lock_manager; } static struct wlr_session_lock_v1 *lock_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &ext_session_lock_v1_interface, &lock_implementation)); return wl_resource_get_user_data(resource); } static struct wlr_session_lock_surface_v1 *lock_surface_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &ext_session_lock_surface_v1_interface, &lock_surface_implementation)); return wl_resource_get_user_data(resource); } static const struct wlr_surface_role lock_surface_role; struct wlr_session_lock_surface_v1 *wlr_session_lock_surface_v1_try_from_wlr_surface( struct wlr_surface *surface) { if (surface->role != &lock_surface_role || surface->role_resource == NULL) { return NULL; } return lock_surface_from_resource(surface->role_resource); } uint32_t wlr_session_lock_surface_v1_configure( struct wlr_session_lock_surface_v1 *lock_surface, uint32_t width, uint32_t height) { struct wlr_session_lock_surface_v1_configure *configure = calloc(1, sizeof(*configure)); if (configure == NULL) { wl_resource_post_no_memory(lock_surface->resource); return lock_surface->pending.configure_serial; } struct wl_display *display = wl_client_get_display(wl_resource_get_client(lock_surface->resource)); configure->width = width; configure->height = height; configure->serial = wl_display_next_serial(display); wl_list_insert(lock_surface->configure_list.prev, &configure->link); ext_session_lock_surface_v1_send_configure(lock_surface->resource, configure->serial, configure->width, configure->height); return configure->serial; } static void lock_surface_handle_ack_configure(struct wl_client *client, struct wl_resource *resource, uint32_t serial) { struct wlr_session_lock_surface_v1 *lock_surface = lock_surface_from_resource(resource); if (lock_surface == NULL) { return; } // First find the ack'ed configure bool found = false; struct wlr_session_lock_surface_v1_configure *configure, *tmp; wl_list_for_each(configure, &lock_surface->configure_list, link) { if (configure->serial == serial) { found = true; break; } } if (!found) { wl_resource_post_error(resource, EXT_SESSION_LOCK_SURFACE_V1_ERROR_INVALID_SERIAL, "ack_configure serial %" PRIu32 " does not match any configure serial", serial); return; } // Then remove old configures from the list wl_list_for_each_safe(configure, tmp, &lock_surface->configure_list, link) { if (configure->serial == serial) { break; } wl_list_remove(&configure->link); free(configure); } lock_surface->pending.configure_serial = configure->serial; lock_surface->pending.width = configure->width; lock_surface->pending.height = configure->height; lock_surface->configured = true; wl_list_remove(&configure->link); free(configure); } static const struct ext_session_lock_surface_v1_interface lock_surface_implementation = { .destroy = resource_handle_destroy, .ack_configure = lock_surface_handle_ack_configure, }; static void lock_surface_role_commit(struct wlr_surface *surface) { struct wlr_session_lock_surface_v1 *lock_surface = wlr_session_lock_surface_v1_try_from_wlr_surface(surface); if (lock_surface == NULL) { return; } if (!wlr_surface_has_buffer(surface)) { wl_resource_post_error(lock_surface->resource, EXT_SESSION_LOCK_SURFACE_V1_ERROR_NULL_BUFFER, "session lock surface is committed with a null buffer"); return; } if (!lock_surface->configured) { wl_resource_post_error(lock_surface->resource, EXT_SESSION_LOCK_SURFACE_V1_ERROR_COMMIT_BEFORE_FIRST_ACK, "session lock surface has never been configured"); return; } if (surface->current.width < 0 || surface->current.height < 0 || (uint32_t)surface->current.width != lock_surface->pending.width || (uint32_t)surface->current.height != lock_surface->pending.height) { wl_resource_post_error(lock_surface->resource, EXT_SESSION_LOCK_SURFACE_V1_ERROR_DIMENSIONS_MISMATCH, "committed surface dimensions do not match last acked configure"); return; } lock_surface->current = lock_surface->pending; wlr_surface_map(surface); } static void lock_surface_role_destroy(struct wlr_surface *surface) { struct wlr_session_lock_surface_v1 *lock_surface = wlr_session_lock_surface_v1_try_from_wlr_surface(surface); if (lock_surface == NULL) { return; } lock_surface_destroy(lock_surface); } static const struct wlr_surface_role lock_surface_role = { .name = "ext_session_lock_surface_v1", .commit = lock_surface_role_commit, .destroy = lock_surface_role_destroy, }; static void lock_surface_handle_output_destroy(struct wl_listener *listener, void *data) { struct wlr_session_lock_surface_v1 *lock_surface = wl_container_of(listener, lock_surface, output_destroy); lock_surface_destroy(lock_surface); } static void lock_handle_get_lock_surface(struct wl_client *client, struct wl_resource *lock_resource, uint32_t id, struct wl_resource *surface_resource, struct wl_resource *output_resource) { // We always need to create a lock surface resource to stay in sync // with the client, even if the lock resource or output resource is // inert. For example, if the compositor denies the lock and immediately // calls wlr_session_lock_v1_destroy() the client may have already sent // get_lock_surface requests. struct wl_resource *lock_surface_resource = wl_resource_create( client, &ext_session_lock_surface_v1_interface, wl_resource_get_version(lock_resource), id); if (lock_surface_resource == NULL) { wl_client_post_no_memory(client); return; } // Leave the lock surface resource inert for now, we will set the // user data at the end of this function if everything is successful. wl_resource_set_implementation(lock_surface_resource, &lock_surface_implementation, NULL, NULL); struct wlr_session_lock_v1 *lock = lock_from_resource(lock_resource); if (lock == NULL) { return; } struct wlr_output *output = wlr_output_from_resource(output_resource); if (output == NULL) { return; } struct wlr_session_lock_surface_v1 *other; wl_list_for_each(other, &lock->surfaces, link) { if (other->output == output) { wl_resource_post_error(lock_resource, EXT_SESSION_LOCK_V1_ERROR_DUPLICATE_OUTPUT, "session lock surface already created for the given output"); return; } } struct wlr_surface *surface = wlr_surface_from_resource(surface_resource); if (wlr_surface_has_buffer(surface)) { wl_resource_post_error(lock_resource, EXT_SESSION_LOCK_V1_ERROR_ALREADY_CONSTRUCTED, "surface already has a buffer attached"); return; } struct wlr_session_lock_surface_v1 *lock_surface = calloc(1, sizeof(*lock_surface)); if (lock_surface == NULL) { wl_client_post_no_memory(client); return; } if (!wlr_surface_set_role(surface, &lock_surface_role, lock_resource, EXT_SESSION_LOCK_V1_ERROR_ROLE)) { free(lock_surface); return; } lock_surface->resource = lock_surface_resource; wl_resource_set_user_data(lock_surface_resource, lock_surface); wlr_surface_set_role_object(surface, lock_surface_resource); wl_list_insert(&lock->surfaces, &lock_surface->link); lock_surface->output = output; lock_surface->surface = surface; wl_list_init(&lock_surface->configure_list); wl_signal_init(&lock_surface->events.destroy); wl_signal_add(&output->events.destroy, &lock_surface->output_destroy); lock_surface->output_destroy.notify = lock_surface_handle_output_destroy; wl_signal_emit_mutable(&lock->events.new_surface, lock_surface); } static void lock_handle_unlock_and_destroy(struct wl_client *client, struct wl_resource *lock_resource) { struct wlr_session_lock_v1 *lock = lock_from_resource(lock_resource); if (lock == NULL) { // This can happen if the compositor sent the locked event and // later the finished event as the lock is destroyed when the // finished event is sent. wl_resource_destroy(lock_resource); return; } if (!lock->locked_sent) { wl_resource_post_error(lock_resource, EXT_SESSION_LOCK_V1_ERROR_INVALID_UNLOCK, "the locked event was never sent"); return; } wl_signal_emit_mutable(&lock->events.unlock, NULL); wl_resource_destroy(lock_resource); } static void lock_handle_destroy(struct wl_client *client, struct wl_resource *lock_resource) { struct wlr_session_lock_v1 *lock = lock_from_resource(lock_resource); if (lock == NULL) { // The compositor sent the finished event and destroyed the lock. wl_resource_destroy(lock_resource); return; } if (lock->locked_sent) { wl_resource_post_error(lock_resource, EXT_SESSION_LOCK_V1_ERROR_INVALID_DESTROY, "the session lock may not be destroyed while locked"); } else { wl_resource_post_error(lock_resource, EXT_SESSION_LOCK_V1_ERROR_INVALID_DESTROY, "the finished event was never sent"); } } static const struct ext_session_lock_v1_interface lock_implementation = { .destroy = lock_handle_destroy, .get_lock_surface = lock_handle_get_lock_surface, .unlock_and_destroy = lock_handle_unlock_and_destroy, }; void wlr_session_lock_v1_send_locked(struct wlr_session_lock_v1 *lock) { assert(!lock->locked_sent); lock->locked_sent = true; ext_session_lock_v1_send_locked(lock->resource); } static void lock_destroy(struct wlr_session_lock_v1 *lock) { struct wlr_session_lock_surface_v1 *lock_surface, *tmp; wl_list_for_each_safe(lock_surface, tmp, &lock->surfaces, link) { lock_surface_destroy(lock_surface); } assert(wl_list_empty(&lock->surfaces)); wl_signal_emit_mutable(&lock->events.destroy, NULL); assert(wl_list_empty(&lock->events.new_surface.listener_list)); assert(wl_list_empty(&lock->events.unlock.listener_list)); assert(wl_list_empty(&lock->events.destroy.listener_list)); wl_resource_set_user_data(lock->resource, NULL); free(lock); } void wlr_session_lock_v1_destroy(struct wlr_session_lock_v1 *lock) { ext_session_lock_v1_send_finished(lock->resource); lock_destroy(lock); } static void lock_resource_destroy(struct wl_resource *lock_resource) { struct wlr_session_lock_v1 *lock = lock_from_resource(lock_resource); if (lock != NULL) { lock_destroy(lock); } } static void lock_manager_handle_lock(struct wl_client *client, struct wl_resource *manager_resource, uint32_t id) { struct wlr_session_lock_manager_v1 *lock_manager = lock_manager_from_resource(manager_resource); struct wlr_session_lock_v1 *lock = calloc(1, sizeof(*lock)); if (lock == NULL) { wl_client_post_no_memory(client); return; } lock->resource = wl_resource_create(client, &ext_session_lock_v1_interface, wl_resource_get_version(manager_resource), id); if (lock->resource == NULL) { free(lock); wl_client_post_no_memory(client); return; } wl_list_init(&lock->surfaces); wl_signal_init(&lock->events.new_surface); wl_signal_init(&lock->events.unlock); wl_signal_init(&lock->events.destroy); wl_resource_set_implementation(lock->resource, &lock_implementation, lock, lock_resource_destroy); wl_signal_emit_mutable(&lock_manager->events.new_lock, lock); } static const struct ext_session_lock_manager_v1_interface lock_manager_implementation = { .destroy = resource_handle_destroy, .lock = lock_manager_handle_lock, }; static void lock_manager_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id) { struct wlr_session_lock_manager_v1 *lock_manager = data; struct wl_resource *resource = wl_resource_create( client, &ext_session_lock_manager_v1_interface, version, id); if (resource == NULL) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(resource, &lock_manager_implementation, lock_manager, NULL); } static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_session_lock_manager_v1 *lock_manager = wl_container_of(listener, lock_manager, display_destroy); wl_signal_emit_mutable(&lock_manager->events.destroy, NULL); wl_list_remove(&lock_manager->display_destroy.link); wl_global_destroy(lock_manager->global); assert(wl_list_empty(&lock_manager->events.new_lock.listener_list)); assert(wl_list_empty(&lock_manager->events.destroy.listener_list)); free(lock_manager); } struct wlr_session_lock_manager_v1 *wlr_session_lock_manager_v1_create(struct wl_display *display) { struct wlr_session_lock_manager_v1 *lock_manager = calloc(1, sizeof(*lock_manager)); if (lock_manager == NULL) { return NULL; } struct wl_global *global = wl_global_create(display, &ext_session_lock_manager_v1_interface, SESSION_LOCK_VERSION, lock_manager, lock_manager_bind); if (global == NULL) { free(lock_manager); return NULL; } lock_manager->global = global; wl_signal_init(&lock_manager->events.new_lock); wl_signal_init(&lock_manager->events.destroy); lock_manager->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &lock_manager->display_destroy); return lock_manager; } wlroots-0.17.1/types/wlr_shm.c000066400000000000000000000374161454110342200163030ustar00rootroot00000000000000#define _DEFAULT_SOURCE // for MAP_ANONYMOUS #include #include #include #include #include #include #include #include #include #include #include #include #include "render/pixel_format.h" #ifdef __STDC_NO_ATOMICS__ #error "C11 atomics are required" #endif #if ATOMIC_POINTER_LOCK_FREE == 0 #error "Lock-free C11 atomic pointers are required" #endif #define SHM_VERSION 1 struct wlr_shm { struct wl_global *global; uint32_t *formats; size_t formats_len; struct wl_listener display_destroy; }; struct wlr_shm_pool { struct wl_resource *resource; // may be NULL struct wlr_shm *shm; struct wl_list buffers; // wlr_shm_buffer.link int fd; struct wlr_shm_mapping *mapping; }; /** * A mapped space to a client-owned file. * * Clients may resize pools via wl_shm_pool.resize, in which case we need to * re-map the FD with a larger size. However we might be at the same time still * accessing the old mapping (via wlr_buffer_begin_data_ptr_access()). We need * to keep the old mapping alive in that case. */ struct wlr_shm_mapping { void *data; size_t size; bool dropped; // false while a wlr_shm_pool references this mapping }; struct wlr_shm_sigbus_data { struct wlr_shm_mapping *mapping; struct sigaction prev_action; struct wlr_shm_sigbus_data *_Atomic next; }; struct wlr_shm_buffer { struct wlr_buffer base; struct wlr_shm_pool *pool; uint32_t drm_format; int32_t stride; off_t offset; struct wl_list link; // wlr_shm_pool.buffers struct wl_resource *resource; // may be NULL struct wl_listener release; struct wlr_shm_sigbus_data sigbus_data; }; // Needs to be a lock-free atomic because it's accessed from a signal handler static struct wlr_shm_sigbus_data *_Atomic sigbus_data = NULL; static const struct wl_buffer_interface wl_buffer_impl; static const struct wl_shm_pool_interface pool_impl; static const struct wl_shm_interface shm_impl; static bool buffer_resource_is_instance(struct wl_resource *resource) { return wl_resource_instance_of(resource, &wl_buffer_interface, &wl_buffer_impl); } static struct wlr_shm_buffer *buffer_from_resource(struct wl_resource *resource) { assert(buffer_resource_is_instance(resource)); return wl_resource_get_user_data(resource); } static struct wlr_buffer *buffer_base_from_resource(struct wl_resource *resource) { return &buffer_from_resource(resource)->base; } static struct wlr_shm_pool *pool_from_resource(struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &wl_shm_pool_interface, &pool_impl)); return wl_resource_get_user_data(resource); } static struct wlr_shm *shm_from_resource(struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &wl_shm_interface, &shm_impl)); return wl_resource_get_user_data(resource); } static struct wlr_shm_mapping *mapping_create(int fd, size_t size) { void *data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (data == MAP_FAILED) { wlr_log_errno(WLR_DEBUG, "mmap failed"); return NULL; } struct wlr_shm_mapping *mapping = calloc(1, sizeof(*mapping)); if (mapping == NULL) { munmap(data, size); return NULL; } mapping->data = data; mapping->size = size; return mapping; } static void mapping_consider_destroy(struct wlr_shm_mapping *mapping) { if (!mapping->dropped) { return; } for (struct wlr_shm_sigbus_data *cur = sigbus_data; cur != NULL; cur = cur->next) { if (cur->mapping == mapping) { return; } } munmap(mapping->data, mapping->size); free(mapping); } /** * Indicate that this mapping is no longer used by its wlr_shm_pool owner. * * May destroy the mapping. */ static void mapping_drop(struct wlr_shm_mapping *mapping) { if (mapping == NULL) { return; } mapping->dropped = true; mapping_consider_destroy(mapping); } static const struct wlr_buffer_resource_interface buffer_resource_interface = { .name = "wl_shm", .is_instance = buffer_resource_is_instance, .from_resource = buffer_base_from_resource, }; static void pool_consider_destroy(struct wlr_shm_pool *pool); static void buffer_destroy(struct wlr_buffer *wlr_buffer) { struct wlr_shm_buffer *buffer = wl_container_of(wlr_buffer, buffer, base); assert(buffer->resource == NULL); wl_list_remove(&buffer->release.link); wl_list_remove(&buffer->link); pool_consider_destroy(buffer->pool); free(buffer); } static bool buffer_get_shm(struct wlr_buffer *wlr_buffer, struct wlr_shm_attributes *attrs) { struct wlr_shm_buffer *buffer = wl_container_of(wlr_buffer, buffer, base); *attrs = (struct wlr_shm_attributes){ .fd = buffer->pool->fd, .format = buffer->drm_format, .width = buffer->base.width, .height = buffer->base.height, .stride = buffer->stride, .offset = buffer->offset, }; return true; } static void handle_sigbus(int sig, siginfo_t *info, void *context) { assert(sigbus_data != NULL); struct sigaction prev_action = sigbus_data->prev_action; // Check whether the offending address is inside of the wl_shm_pool's mapped // space uintptr_t addr = (uintptr_t)info->si_addr; struct wlr_shm_mapping *mapping = NULL; for (struct wlr_shm_sigbus_data *data = sigbus_data; data != NULL; data = data->next) { uintptr_t mapping_start = (uintptr_t)data->mapping->data; size_t mapping_size = data->mapping->size; if (addr >= mapping_start && addr < mapping_start + mapping_size) { mapping = data->mapping; break; } } if (mapping == NULL) { goto reraise; } // Replace the mapping with a new one which won't cause SIGBUS (instead, it // will read as zeroes). Technically mmap() isn't part of the // async-signal-safe functions... if (mmap(mapping->data, mapping->size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS, -1, 0) == MAP_FAILED) { goto reraise; } return; reraise: if (prev_action.sa_flags & SA_SIGINFO) { prev_action.sa_sigaction(sig, info, context); } else { prev_action.sa_handler(sig); } } static bool buffer_begin_data_ptr_access(struct wlr_buffer *wlr_buffer, uint32_t flags, void **data, uint32_t *format, size_t *stride) { struct wlr_shm_buffer *buffer = wl_container_of(wlr_buffer, buffer, base); if (!atomic_is_lock_free(&sigbus_data)) { wlr_log(WLR_ERROR, "Lock-free atomic pointers are required"); return false; } // Install a SIGBUS handler. SIGBUS is triggered if the client shrinks the // backing file, and then we try to access the mapping. struct sigaction prev_action; if (sigbus_data == NULL) { struct sigaction new_action = { .sa_sigaction = handle_sigbus, .sa_flags = SA_SIGINFO | SA_NODEFER, }; if (sigaction(SIGBUS, &new_action, &prev_action) != 0) { wlr_log_errno(WLR_ERROR, "sigaction failed"); return false; } } else { prev_action = sigbus_data->prev_action; } struct wlr_shm_mapping *mapping = buffer->pool->mapping; buffer->sigbus_data = (struct wlr_shm_sigbus_data){ .mapping = mapping, .prev_action = prev_action, .next = sigbus_data, }; sigbus_data = &buffer->sigbus_data; *data = (char *)mapping->data + buffer->offset; *format = buffer->drm_format; *stride = buffer->stride; return true; } static void buffer_end_data_ptr_access(struct wlr_buffer *wlr_buffer) { struct wlr_shm_buffer *buffer = wl_container_of(wlr_buffer, buffer, base); if (sigbus_data == &buffer->sigbus_data) { sigbus_data = buffer->sigbus_data.next; } else { for (struct wlr_shm_sigbus_data *cur = sigbus_data; cur != NULL; cur = cur->next) { if (cur->next == &buffer->sigbus_data) { cur->next = buffer->sigbus_data.next; break; } } } if (sigbus_data == NULL) { if (sigaction(SIGBUS, &buffer->sigbus_data.prev_action, NULL) != 0) { wlr_log_errno(WLR_ERROR, "sigaction failed"); } } mapping_consider_destroy(buffer->sigbus_data.mapping); } static const struct wlr_buffer_impl buffer_impl = { .destroy = buffer_destroy, .get_shm = buffer_get_shm, .begin_data_ptr_access = buffer_begin_data_ptr_access, .end_data_ptr_access = buffer_end_data_ptr_access, }; static void destroy_resource(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static const struct wl_buffer_interface wl_buffer_impl = { .destroy = destroy_resource, }; static void buffer_handle_release(struct wl_listener *listener, void *data) { struct wlr_shm_buffer *buffer = wl_container_of(listener, buffer, release); if (buffer->resource != NULL) { wl_buffer_send_release(buffer->resource); } } static void buffer_handle_resource_destroy(struct wl_resource *resource) { struct wlr_shm_buffer *buffer = buffer_from_resource(resource); buffer->resource = NULL; wlr_buffer_drop(&buffer->base); } static bool shm_has_format(struct wlr_shm *shm, uint32_t shm_format); static void pool_handle_create_buffer(struct wl_client *client, struct wl_resource *pool_resource, uint32_t id, int32_t offset, int32_t width, int32_t height, int32_t stride, uint32_t shm_format) { struct wlr_shm_pool *pool = pool_from_resource(pool_resource); // Convert to uint64_t to avoid integer overflow if (offset < 0 || width <= 0 || height <= 0 || stride < width || offset + (uint64_t)stride * height > pool->mapping->size) { wl_resource_post_error(pool_resource, WL_SHM_ERROR_INVALID_STRIDE, "Invalid width, height or stride (%dx%d, %d)", width, height, stride); return; } if (!shm_has_format(pool->shm, shm_format)) { wl_resource_post_error(pool_resource, WL_SHM_ERROR_INVALID_FORMAT, "Unsupported format"); return; } uint32_t drm_format = convert_wl_shm_format_to_drm(shm_format); const struct wlr_pixel_format_info *format_info = drm_get_pixel_format_info(drm_format); if (format_info == NULL) { wl_resource_post_error(pool_resource, WL_SHM_ERROR_INVALID_FORMAT, "Unknown format"); return; } if (!pixel_format_info_check_stride(format_info, stride, width)) { wl_resource_post_error(pool_resource, WL_SHM_ERROR_INVALID_STRIDE, "Invalid stride (%d)", stride); return; } struct wlr_shm_buffer *buffer = calloc(1, sizeof(*buffer)); if (buffer == NULL) { wl_resource_post_no_memory(pool_resource); return; } buffer->resource = wl_resource_create(client, &wl_buffer_interface, 1, id); if (buffer->resource == NULL) { free(buffer); wl_resource_post_no_memory(pool_resource); return; } buffer->pool = pool; buffer->offset = offset; buffer->stride = stride; buffer->drm_format = drm_format; wlr_buffer_init(&buffer->base, &buffer_impl, width, height); wl_resource_set_implementation(buffer->resource, &wl_buffer_impl, buffer, buffer_handle_resource_destroy); wl_list_insert(&pool->buffers, &buffer->link); buffer->release.notify = buffer_handle_release; wl_signal_add(&buffer->base.events.release, &buffer->release); } static void pool_handle_resize(struct wl_client *client, struct wl_resource *pool_resource, int32_t size) { struct wlr_shm_pool *pool = pool_from_resource(pool_resource); if (size <= 0 || (size_t)size < pool->mapping->size) { wl_resource_post_error(pool_resource, WL_SHM_ERROR_INVALID_STRIDE, "Shrinking a pool (%zu to %d) is forbidden", pool->mapping->size, size); return; } struct wlr_shm_mapping *mapping = mapping_create(pool->fd, size); if (mapping == NULL) { wl_resource_post_error(pool_resource, WL_SHM_ERROR_INVALID_FD, "Failed to create memory mapping"); return; } mapping_drop(pool->mapping); pool->mapping = mapping; } static const struct wl_shm_pool_interface pool_impl = { .create_buffer = pool_handle_create_buffer, .destroy = destroy_resource, .resize = pool_handle_resize, }; static void pool_consider_destroy(struct wlr_shm_pool *pool) { if (pool->resource != NULL || !wl_list_empty(&pool->buffers)) { return; } mapping_drop(pool->mapping); close(pool->fd); free(pool); } static void pool_handle_resource_destroy(struct wl_resource *resource) { struct wlr_shm_pool *pool = pool_from_resource(resource); pool->resource = NULL; pool_consider_destroy(pool); } static void shm_handle_create_pool(struct wl_client *client, struct wl_resource *shm_resource, uint32_t id, int fd, int32_t size) { struct wlr_shm *shm = shm_from_resource(shm_resource); if (size <= 0) { wl_resource_post_error(shm_resource, WL_SHM_ERROR_INVALID_STRIDE, "Invalid size (%d)", size); goto error_fd; } struct wlr_shm_mapping *mapping = mapping_create(fd, size); if (mapping == NULL) { wl_resource_post_error(shm_resource, WL_SHM_ERROR_INVALID_FD, "Failed to create memory mapping"); goto error_fd; } struct wlr_shm_pool *pool = calloc(1, sizeof(*pool)); if (pool == NULL) { wl_resource_post_no_memory(shm_resource); goto error_mapping; } uint32_t version = wl_resource_get_version(shm_resource); pool->resource = wl_resource_create(client, &wl_shm_pool_interface, version, id); if (pool->resource == NULL) { wl_resource_post_no_memory(shm_resource); goto error_pool; } wl_resource_set_implementation(pool->resource, &pool_impl, pool, pool_handle_resource_destroy); pool->mapping = mapping; pool->shm = shm; pool->fd = fd; wl_list_init(&pool->buffers); return; error_pool: free(pool); error_mapping: mapping_drop(mapping); error_fd: close(fd); } static const struct wl_shm_interface shm_impl = { .create_pool = shm_handle_create_pool, }; static void shm_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id) { struct wlr_shm *shm = data; struct wl_resource *resource = wl_resource_create(client, &wl_shm_interface, version, id); if (resource == NULL) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(resource, &shm_impl, shm, NULL); for (size_t i = 0; i < shm->formats_len; i++) { wl_shm_send_format(resource, shm->formats[i]); } } static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_shm *shm = wl_container_of(listener, shm, display_destroy); wl_list_remove(&shm->display_destroy.link); wl_global_destroy(shm->global); free(shm->formats); free(shm); } struct wlr_shm *wlr_shm_create(struct wl_display *display, uint32_t version, const uint32_t *formats, size_t formats_len) { // ARGB8888 and XRGB8888 must be supported per the wl_shm spec bool has_argb8888 = false, has_xrgb8888 = false; for (size_t i = 0; i < formats_len; i++) { switch (formats[i]) { case DRM_FORMAT_ARGB8888: has_argb8888 = true; break; case DRM_FORMAT_XRGB8888: has_xrgb8888 = true; break; } } assert(has_argb8888 && has_xrgb8888); struct wlr_shm *shm = calloc(1, sizeof(*shm)); if (shm == NULL) { wlr_log(WLR_ERROR, "Allocation failed"); return NULL; } shm->formats_len = formats_len; shm->formats = malloc(formats_len * sizeof(uint32_t)); if (shm->formats == NULL) { wlr_log(WLR_ERROR, "Allocation failed"); free(shm); return NULL; } for (size_t i = 0; i < formats_len; i++) { shm->formats[i] = convert_drm_format_to_wl_shm(formats[i]); } shm->global = wl_global_create(display, &wl_shm_interface, SHM_VERSION, shm, shm_bind); if (shm->global == NULL) { wlr_log(WLR_ERROR, "wl_global_create failed"); free(shm->formats); free(shm); return NULL; } shm->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &shm->display_destroy); wlr_buffer_register_resource_interface(&buffer_resource_interface); return shm; } struct wlr_shm *wlr_shm_create_with_renderer(struct wl_display *display, uint32_t version, struct wlr_renderer *renderer) { size_t formats_len; const uint32_t *formats = wlr_renderer_get_shm_texture_formats(renderer, &formats_len); if (formats == NULL) { wlr_log(WLR_ERROR, "Failed to initialize wl_shm: " "cannot get renderer formats"); return NULL; } return wlr_shm_create(display, version, formats, formats_len); } static bool shm_has_format(struct wlr_shm *shm, uint32_t shm_format) { for (size_t i = 0; i < shm->formats_len; i++) { if (shm->formats[i] == shm_format) { return true; } } return false; } wlroots-0.17.1/types/wlr_single_pixel_buffer_v1.c000066400000000000000000000122111454110342200221170ustar00rootroot00000000000000#include #include #include #include #include #include #include "single-pixel-buffer-v1-protocol.h" #define SINGLE_PIXEL_MANAGER_VERSION 1 struct wlr_single_pixel_buffer_manager_v1 { struct wl_global *global; struct wl_listener display_destroy; }; struct wlr_single_pixel_buffer_v1 { struct wlr_buffer base; struct wl_resource *resource; uint32_t r, g, b, a; uint8_t argb8888[4]; // packed little-endian DRM_FORMAT_ARGB8888 }; static void destroy_resource(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static const struct wl_buffer_interface wl_buffer_impl = { .destroy = destroy_resource, }; static const struct wlr_buffer_impl buffer_impl; static bool buffer_resource_is_instance(struct wl_resource *resource) { return wl_resource_instance_of(resource, &wl_buffer_interface, &wl_buffer_impl); } static struct wlr_single_pixel_buffer_v1 *single_pixel_buffer_v1_from_resource( struct wl_resource *resource) { assert(buffer_resource_is_instance(resource)); return wl_resource_get_user_data(resource); } static struct wlr_buffer *buffer_from_resource( struct wl_resource *resource) { return &single_pixel_buffer_v1_from_resource(resource)->base; } static const struct wlr_buffer_resource_interface buffer_resource_interface = { .name = "single_pixel_buffer_v1", .is_instance = buffer_resource_is_instance, .from_resource = buffer_from_resource, }; static void buffer_destroy(struct wlr_buffer *wlr_buffer) { struct wlr_single_pixel_buffer_v1 *buffer = wl_container_of(wlr_buffer, buffer, base); if (buffer->resource != NULL) { wl_resource_set_user_data(buffer->resource, NULL); } free(buffer); } static bool buffer_begin_data_ptr_access(struct wlr_buffer *wlr_buffer, uint32_t flags, void **data, uint32_t *format, size_t *stride) { struct wlr_single_pixel_buffer_v1 *buffer = wl_container_of(wlr_buffer, buffer, base); if (flags & ~WLR_BUFFER_DATA_PTR_ACCESS_READ) { return false; // the buffer is read-only } *data = &buffer->argb8888; *format = DRM_FORMAT_ARGB8888; *stride = sizeof(buffer->argb8888); return true; } static void buffer_end_data_ptr_access(struct wlr_buffer *wlr_buffer) { // This space is intentionally left blank } static const struct wlr_buffer_impl buffer_impl = { .destroy = buffer_destroy, .begin_data_ptr_access = buffer_begin_data_ptr_access, .end_data_ptr_access = buffer_end_data_ptr_access, }; static void buffer_handle_resource_destroy(struct wl_resource *resource) { struct wlr_single_pixel_buffer_v1 *buffer = single_pixel_buffer_v1_from_resource(resource); buffer->resource = NULL; wlr_buffer_drop(&buffer->base); } static void manager_handle_create_u32_rgba_buffer(struct wl_client *client, struct wl_resource *resource, uint32_t id, uint32_t r, uint32_t g, uint32_t b, uint32_t a) { struct wlr_single_pixel_buffer_v1 *buffer = calloc(1, sizeof(*buffer)); if (buffer == NULL) { wl_client_post_no_memory(client); return; } buffer->resource = wl_resource_create(client, &wl_buffer_interface, 1, id); if (buffer->resource == NULL) { wl_client_post_no_memory(client); free(buffer); return; } wlr_buffer_init(&buffer->base, &buffer_impl, 1, 1); wl_resource_set_implementation(buffer->resource, &wl_buffer_impl, buffer, buffer_handle_resource_destroy); buffer->r = r; buffer->g = g; buffer->b = b; buffer->a = a; double f = (double)0xFF / 0xFFFFFFFF; buffer->argb8888[0] = (uint8_t)((double)buffer->b * f); buffer->argb8888[1] = (uint8_t)((double)buffer->g * f); buffer->argb8888[2] = (uint8_t)((double)buffer->r * f); buffer->argb8888[3] = (uint8_t)((double)buffer->a * f); } static const struct wp_single_pixel_buffer_manager_v1_interface manager_impl = { .destroy = destroy_resource, .create_u32_rgba_buffer = manager_handle_create_u32_rgba_buffer, }; static void manager_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id) { struct wl_resource *resource = wl_resource_create(client, &wp_single_pixel_buffer_manager_v1_interface, version, id); if (resource == NULL) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(resource, &manager_impl, NULL, NULL); } static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_single_pixel_buffer_manager_v1 *manager = wl_container_of(listener, manager, display_destroy); wl_global_destroy(manager->global); free(manager); } struct wlr_single_pixel_buffer_manager_v1 *wlr_single_pixel_buffer_manager_v1_create( struct wl_display *display) { struct wlr_single_pixel_buffer_manager_v1 *manager = calloc(1, sizeof(*manager)); if (manager == NULL) { return NULL; } manager->global = wl_global_create(display, &wp_single_pixel_buffer_manager_v1_interface, SINGLE_PIXEL_MANAGER_VERSION, NULL, manager_bind); if (manager->global == NULL) { free(manager); return NULL; } manager->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &manager->display_destroy); wlr_buffer_register_resource_interface(&buffer_resource_interface); return manager; } wlroots-0.17.1/types/wlr_subcompositor.c000066400000000000000000000307511454110342200204170ustar00rootroot00000000000000#include #include #include #include #include #include "types/wlr_region.h" #include "types/wlr_subcompositor.h" // Note: wl_subsurface becomes inert on parent surface destroy #define SUBCOMPOSITOR_VERSION 1 static bool subsurface_is_synchronized(struct wlr_subsurface *subsurface) { while (subsurface != NULL) { if (subsurface->synchronized) { return true; } subsurface = wlr_subsurface_try_from_wlr_surface(subsurface->parent); } return false; } static const struct wl_subsurface_interface subsurface_implementation; static void subsurface_destroy(struct wlr_subsurface *subsurface) { if (subsurface->has_cache) { wlr_surface_unlock_cached(subsurface->surface, subsurface->cached_seq); } wlr_surface_unmap(subsurface->surface); wl_signal_emit_mutable(&subsurface->events.destroy, subsurface); wl_list_remove(&subsurface->surface_client_commit.link); wl_list_remove(&subsurface->current.link); wl_list_remove(&subsurface->pending.link); wl_list_remove(&subsurface->parent_destroy.link); wl_resource_set_user_data(subsurface->resource, NULL); free(subsurface); } /** * Get a wlr_subsurface from a wl_subsurface resource. * * Returns NULL if the subsurface is inert (e.g. the wl_surface object or the * parent surface got destroyed). */ static struct wlr_subsurface *subsurface_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &wl_subsurface_interface, &subsurface_implementation)); return wl_resource_get_user_data(resource); } static void subsurface_handle_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static void subsurface_handle_set_position(struct wl_client *client, struct wl_resource *resource, int32_t x, int32_t y) { struct wlr_subsurface *subsurface = subsurface_from_resource(resource); if (subsurface == NULL) { return; } subsurface->pending.x = x; subsurface->pending.y = y; } static struct wlr_subsurface *subsurface_find_sibling( struct wlr_subsurface *subsurface, struct wlr_surface *surface) { struct wlr_surface *parent = subsurface->parent; struct wlr_subsurface *sibling; wl_list_for_each(sibling, &parent->pending.subsurfaces_below, pending.link) { if (sibling->surface == surface && sibling != subsurface) { return sibling; } } wl_list_for_each(sibling, &parent->pending.subsurfaces_above, pending.link) { if (sibling->surface == surface && sibling != subsurface) { return sibling; } } return NULL; } static void subsurface_handle_place_above(struct wl_client *client, struct wl_resource *resource, struct wl_resource *sibling_resource) { struct wlr_subsurface *subsurface = subsurface_from_resource(resource); if (subsurface == NULL) { return; } struct wlr_surface *sibling_surface = wlr_surface_from_resource(sibling_resource); struct wl_list *node; if (sibling_surface == subsurface->parent) { node = &subsurface->parent->pending.subsurfaces_above; } else { struct wlr_subsurface *sibling = subsurface_find_sibling(subsurface, sibling_surface); if (!sibling) { wl_resource_post_error(subsurface->resource, WL_SUBSURFACE_ERROR_BAD_SURFACE, "%s: wl_surface@%" PRIu32 "is not a parent or sibling", "place_above", wl_resource_get_id(sibling_resource)); return; } node = &sibling->pending.link; } wl_list_remove(&subsurface->pending.link); wl_list_insert(node, &subsurface->pending.link); subsurface->reordered = true; } static void subsurface_handle_place_below(struct wl_client *client, struct wl_resource *resource, struct wl_resource *sibling_resource) { struct wlr_subsurface *subsurface = subsurface_from_resource(resource); if (subsurface == NULL) { return; } struct wlr_surface *sibling_surface = wlr_surface_from_resource(sibling_resource); struct wl_list *node; if (sibling_surface == subsurface->parent) { node = &subsurface->parent->pending.subsurfaces_below; } else { struct wlr_subsurface *sibling = subsurface_find_sibling(subsurface, sibling_surface); if (!sibling) { wl_resource_post_error(subsurface->resource, WL_SUBSURFACE_ERROR_BAD_SURFACE, "%s: wl_surface@%" PRIu32 " is not a parent or sibling", "place_below", wl_resource_get_id(sibling_resource)); return; } node = &sibling->pending.link; } wl_list_remove(&subsurface->pending.link); wl_list_insert(node->prev, &subsurface->pending.link); subsurface->reordered = true; } static void subsurface_handle_set_sync(struct wl_client *client, struct wl_resource *resource) { struct wlr_subsurface *subsurface = subsurface_from_resource(resource); if (subsurface == NULL) { return; } subsurface->synchronized = true; } static void subsurface_handle_set_desync(struct wl_client *client, struct wl_resource *resource) { struct wlr_subsurface *subsurface = subsurface_from_resource(resource); if (subsurface == NULL) { return; } if (subsurface->synchronized) { subsurface->synchronized = false; if (!subsurface_is_synchronized(subsurface) && subsurface->has_cache) { wlr_surface_unlock_cached(subsurface->surface, subsurface->cached_seq); subsurface->has_cache = false; } } } static const struct wl_subsurface_interface subsurface_implementation = { .destroy = subsurface_handle_destroy, .set_position = subsurface_handle_set_position, .place_above = subsurface_handle_place_above, .place_below = subsurface_handle_place_below, .set_sync = subsurface_handle_set_sync, .set_desync = subsurface_handle_set_desync, }; const struct wlr_surface_role subsurface_role; void subsurface_consider_map(struct wlr_subsurface *subsurface) { if (subsurface->parent->mapped && wlr_surface_has_buffer(subsurface->surface)) { wlr_surface_map(subsurface->surface); } } static void subsurface_role_commit(struct wlr_surface *surface) { struct wlr_subsurface *subsurface = wlr_subsurface_try_from_wlr_surface(surface); if (subsurface == NULL) { return; } subsurface_consider_map(subsurface); } static void subsurface_role_destroy(struct wlr_surface *surface) { struct wlr_subsurface *subsurface = wlr_subsurface_try_from_wlr_surface(surface); if (subsurface == NULL) { return; } subsurface_destroy(subsurface); } const struct wlr_surface_role subsurface_role = { .name = "wl_subsurface", .commit = subsurface_role_commit, .destroy = subsurface_role_destroy, }; static void subsurface_handle_parent_destroy(struct wl_listener *listener, void *data) { struct wlr_subsurface *subsurface = wl_container_of(listener, subsurface, parent_destroy); // Once the parent is destroyed, the client has no way to use the // wl_subsurface object anymore, so we can destroy it. subsurface_destroy(subsurface); } static void subsurface_handle_surface_client_commit( struct wl_listener *listener, void *data) { struct wlr_subsurface *subsurface = wl_container_of(listener, subsurface, surface_client_commit); struct wlr_surface *surface = subsurface->surface; if (subsurface_is_synchronized(subsurface)) { if (subsurface->has_cache) { // We already lock a previous commit. The prevents any future // commit to be applied before we release the previous commit. return; } subsurface->has_cache = true; subsurface->cached_seq = wlr_surface_lock_pending(surface); } else if (subsurface->has_cache) { wlr_surface_unlock_cached(surface, subsurface->cached_seq); subsurface->has_cache = false; } } static void collect_damage_iter(struct wlr_surface *surface, int sx, int sy, void *data) { struct wlr_subsurface *subsurface = data; pixman_region32_t *damage = &subsurface->parent->external_damage; pixman_region32_union_rect(damage, damage, subsurface->current.x + sx, subsurface->current.y + sy, surface->current.width, surface->current.height); } void subsurface_handle_parent_commit(struct wlr_subsurface *subsurface) { struct wlr_surface *surface = subsurface->surface; bool moved = subsurface->current.x != subsurface->pending.x || subsurface->current.y != subsurface->pending.y; if (subsurface->surface->mapped && moved) { wlr_surface_for_each_surface(surface, collect_damage_iter, subsurface); } if (subsurface->synchronized && subsurface->has_cache) { wlr_surface_unlock_cached(surface, subsurface->cached_seq); subsurface->has_cache = false; } subsurface->current.x = subsurface->pending.x; subsurface->current.y = subsurface->pending.y; if (subsurface->surface->mapped && (moved || subsurface->reordered)) { subsurface->reordered = false; wlr_surface_for_each_surface(surface, collect_damage_iter, subsurface); } if (!subsurface->added) { subsurface->added = true; wl_signal_emit_mutable(&subsurface->parent->events.new_subsurface, subsurface); subsurface_consider_map(subsurface); } } struct wlr_subsurface *wlr_subsurface_try_from_wlr_surface(struct wlr_surface *surface) { if (surface->role != &subsurface_role || surface->role_resource == NULL) { return NULL; } return subsurface_from_resource(surface->role_resource); } static void subcompositor_handle_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static void subcompositor_handle_get_subsurface(struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *surface_resource, struct wl_resource *parent_resource) { struct wlr_surface *surface = wlr_surface_from_resource(surface_resource); struct wlr_surface *parent = wlr_surface_from_resource(parent_resource); struct wlr_subsurface *subsurface = calloc(1, sizeof(*subsurface)); if (!subsurface) { wl_client_post_no_memory(client); return; } if (!wlr_surface_set_role(surface, &subsurface_role, resource, WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE)) { free(subsurface); return; } if (wlr_surface_get_root_surface(parent) == surface) { wl_resource_post_error(resource, WL_SUBCOMPOSITOR_ERROR_BAD_PARENT, "wl_subsurface@%" PRIu32 " cannot be a parent of itself or its ancestor", id); free(subsurface); return; } subsurface->synchronized = true; subsurface->surface = surface; subsurface->resource = wl_resource_create(client, &wl_subsurface_interface, wl_resource_get_version(resource), id); if (subsurface->resource == NULL) { free(subsurface); wl_client_post_no_memory(client); return; } wl_resource_set_implementation(subsurface->resource, &subsurface_implementation, subsurface, NULL); wlr_surface_set_role_object(surface, subsurface->resource); wl_signal_init(&subsurface->events.destroy); wl_signal_add(&surface->events.client_commit, &subsurface->surface_client_commit); subsurface->surface_client_commit.notify = subsurface_handle_surface_client_commit; // link parent subsurface->parent = parent; wl_signal_add(&parent->events.destroy, &subsurface->parent_destroy); subsurface->parent_destroy.notify = subsurface_handle_parent_destroy; wl_list_init(&subsurface->current.link); wl_list_insert(parent->pending.subsurfaces_above.prev, &subsurface->pending.link); } static const struct wl_subcompositor_interface subcompositor_impl = { .destroy = subcompositor_handle_destroy, .get_subsurface = subcompositor_handle_get_subsurface, }; static void subcompositor_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id) { struct wlr_subcompositor *subcompositor = data; struct wl_resource *resource = wl_resource_create(client, &wl_subcompositor_interface, 1, id); if (resource == NULL) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(resource, &subcompositor_impl, subcompositor, NULL); } static void subcompositor_handle_display_destroy( struct wl_listener *listener, void *data) { struct wlr_subcompositor *subcompositor = wl_container_of(listener, subcompositor, display_destroy); wl_signal_emit_mutable(&subcompositor->events.destroy, NULL); wl_list_remove(&subcompositor->display_destroy.link); wl_global_destroy(subcompositor->global); free(subcompositor); } struct wlr_subcompositor *wlr_subcompositor_create(struct wl_display *display) { struct wlr_subcompositor *subcompositor = calloc(1, sizeof(*subcompositor)); if (!subcompositor) { return NULL; } subcompositor->global = wl_global_create(display, &wl_subcompositor_interface, SUBCOMPOSITOR_VERSION, subcompositor, subcompositor_bind); if (!subcompositor->global) { free(subcompositor); return NULL; } wl_signal_init(&subcompositor->events.destroy); subcompositor->display_destroy.notify = subcompositor_handle_display_destroy; wl_display_add_destroy_listener(display, &subcompositor->display_destroy); return subcompositor; } wlroots-0.17.1/types/wlr_switch.c000066400000000000000000000015121454110342200170010ustar00rootroot00000000000000#include #include #include #include #include #include #include "interfaces/wlr_input_device.h" struct wlr_switch *wlr_switch_from_input_device( struct wlr_input_device *input_device) { assert(input_device->type == WLR_INPUT_DEVICE_SWITCH); return wl_container_of(input_device, (struct wlr_switch *)NULL, base); } void wlr_switch_init(struct wlr_switch *switch_device, const struct wlr_switch_impl *impl, const char *name) { *switch_device = (struct wlr_switch){ .impl = impl, }; wlr_input_device_init(&switch_device->base, WLR_INPUT_DEVICE_SWITCH, name); wl_signal_init(&switch_device->events.toggle); } void wlr_switch_finish(struct wlr_switch *switch_device) { wlr_input_device_finish(&switch_device->base); } wlroots-0.17.1/types/wlr_tablet_pad.c000066400000000000000000000024471454110342200176070ustar00rootroot00000000000000#include #include #include #include #include #include #include #include "interfaces/wlr_input_device.h" struct wlr_tablet_pad *wlr_tablet_pad_from_input_device( struct wlr_input_device *input_device) { assert(input_device->type == WLR_INPUT_DEVICE_TABLET_PAD); return wl_container_of(input_device, (struct wlr_tablet_pad *)NULL, base); } void wlr_tablet_pad_init(struct wlr_tablet_pad *pad, const struct wlr_tablet_pad_impl *impl, const char *name) { *pad = (struct wlr_tablet_pad){ .impl = impl, }; wlr_input_device_init(&pad->base, WLR_INPUT_DEVICE_TABLET_PAD, name); wl_signal_init(&pad->events.button); wl_signal_init(&pad->events.ring); wl_signal_init(&pad->events.strip); wl_signal_init(&pad->events.attach_tablet); wl_list_init(&pad->groups); wl_array_init(&pad->paths); } void wlr_tablet_pad_finish(struct wlr_tablet_pad *pad) { wlr_input_device_finish(&pad->base); char **path_ptr; wl_array_for_each(path_ptr, &pad->paths) { free(*path_ptr); } wl_array_release(&pad->paths); /* TODO: wlr_tablet_pad should own its wlr_tablet_pad_group */ if (!wl_list_empty(&pad->groups)) { wlr_log(WLR_ERROR, "wlr_tablet_pad groups is not empty"); } } wlroots-0.17.1/types/wlr_tablet_tool.c000066400000000000000000000021101454110342200200030ustar00rootroot00000000000000#include #include #include #include #include #include #include "interfaces/wlr_input_device.h" struct wlr_tablet *wlr_tablet_from_input_device( struct wlr_input_device *input_device) { assert(input_device->type == WLR_INPUT_DEVICE_TABLET_TOOL); return wl_container_of(input_device, (struct wlr_tablet *)NULL, base); } void wlr_tablet_init(struct wlr_tablet *tablet, const struct wlr_tablet_impl *impl, const char *name) { *tablet = (struct wlr_tablet){ .impl = impl, }; wlr_input_device_init(&tablet->base, WLR_INPUT_DEVICE_TABLET_TOOL, name); wl_signal_init(&tablet->events.axis); wl_signal_init(&tablet->events.proximity); wl_signal_init(&tablet->events.tip); wl_signal_init(&tablet->events.button); wl_array_init(&tablet->paths); } void wlr_tablet_finish(struct wlr_tablet *tablet) { wlr_input_device_finish(&tablet->base); char **path_ptr; wl_array_for_each(path_ptr, &tablet->paths) { free(*path_ptr); } wl_array_release(&tablet->paths); } wlroots-0.17.1/types/wlr_tearing_control_v1.c000066400000000000000000000143051454110342200213030ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include "tearing-control-v1-protocol.h" #define TEARING_CONTROL_MANAGER_VERSION 1 static const struct wp_tearing_control_manager_v1_interface tearing_impl; static const struct wp_tearing_control_v1_interface tearing_control_impl; static struct wlr_tearing_control_manager_v1 *tearing_manager_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &wp_tearing_control_manager_v1_interface, &tearing_impl)); return wl_resource_get_user_data(resource); } static struct wlr_tearing_control_v1 *tearing_surface_hint_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &wp_tearing_control_v1_interface, &tearing_control_impl)); return wl_resource_get_user_data(resource); } static void destroy_tearing_hint(struct wlr_tearing_control_v1 *hint) { if (hint == NULL) { return; } wl_signal_emit_mutable(&hint->events.destroy, NULL); wl_list_remove(&hint->link); wl_resource_set_user_data(hint->resource, NULL); wlr_addon_finish(&hint->addon); free(hint); } static void surface_addon_destroy(struct wlr_addon *addon) { struct wlr_tearing_control_v1 *hint = wl_container_of(addon, hint, addon); destroy_tearing_hint(hint); } static const struct wlr_addon_interface surface_addon_impl = { .name = "wp_tearing_control_v1", .destroy = surface_addon_destroy, }; static void resource_handle_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static void destroy_tearing_resource_impl(struct wl_resource *resource) { struct wlr_tearing_control_v1 *hint = tearing_surface_hint_from_resource(resource); destroy_tearing_hint(hint); } static void tearing_control_handle_presentation_hint(struct wl_client *client, struct wl_resource *resource, uint32_t hint) { struct wlr_tearing_control_v1 *surface_hint = tearing_surface_hint_from_resource(resource); surface_hint->hint = hint; wl_signal_emit_mutable(&surface_hint->events.set_hint, NULL); } static const struct wp_tearing_control_v1_interface tearing_control_impl = { .destroy = resource_handle_destroy, .set_presentation_hint = tearing_control_handle_presentation_hint }; static void tearing_control_manager_handle_get_tearing_control( struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *surface_resource) { struct wlr_tearing_control_manager_v1 *manager = tearing_manager_from_resource(resource); struct wlr_surface *surface = wlr_surface_from_resource(surface_resource); if (wlr_addon_find(&surface->addons, manager, &surface_addon_impl) != NULL) { wl_resource_post_error(resource, WP_TEARING_CONTROL_MANAGER_V1_ERROR_TEARING_CONTROL_EXISTS, "Tearing control object already exists!"); return; } struct wlr_tearing_control_v1 *hint = calloc(1, sizeof(*hint)); if (!hint) { wl_client_post_no_memory(client); return; } struct wl_resource *created_resource = wl_resource_create(client, &wp_tearing_control_v1_interface, wl_resource_get_version(resource), id); if (created_resource == NULL) { wl_resource_post_no_memory(resource); return; } wl_resource_set_implementation(created_resource, &tearing_control_impl, hint, destroy_tearing_resource_impl); hint->hint = WP_TEARING_CONTROL_V1_PRESENTATION_HINT_VSYNC; hint->client = client; hint->resource = created_resource; hint->surface = surface; wlr_addon_init(&hint->addon, &hint->surface->addons, manager, &surface_addon_impl); wl_signal_init(&hint->events.set_hint); wl_signal_init(&hint->events.destroy); wl_list_insert(&manager->surface_hints, &hint->link); wl_signal_emit_mutable(&manager->events.new_object, hint); } static const struct wp_tearing_control_manager_v1_interface tearing_impl = { .destroy = resource_handle_destroy, .get_tearing_control = tearing_control_manager_handle_get_tearing_control, }; static void tearing_bind(struct wl_client *wl_client, void *data, uint32_t version, uint32_t id) { struct wlr_tearing_control_manager_v1 *manager = data; struct wl_resource *wl_resource = wl_resource_create(wl_client, &wp_tearing_control_manager_v1_interface, version, id); if (wl_resource == NULL) { wl_client_post_no_memory(wl_client); return; } wl_resource_set_implementation(wl_resource, &tearing_impl, manager, NULL); } static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_tearing_control_manager_v1 *manager = wl_container_of(listener, manager, display_destroy); wl_signal_emit_mutable(&manager->events.destroy, NULL); struct wlr_tearing_control_v1 *hint, *tmp; wl_list_for_each_safe(hint, tmp, &manager->surface_hints, link) { destroy_tearing_hint(hint); } wl_list_remove(&manager->display_destroy.link); wl_global_destroy(manager->global); free(manager); } struct wlr_tearing_control_manager_v1 *wlr_tearing_control_manager_v1_create( struct wl_display *display, uint32_t version) { assert(version <= TEARING_CONTROL_MANAGER_VERSION); struct wlr_tearing_control_manager_v1 *manager = calloc(1, sizeof(*manager)); if (!manager) { wlr_log_errno(WLR_ERROR, "Allocation failed"); return NULL; } wl_signal_init(&manager->events.new_object); wl_signal_init(&manager->events.destroy); wl_list_init(&manager->surface_hints); manager->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &manager->display_destroy); manager->global = wl_global_create(display, &wp_tearing_control_manager_v1_interface, version, manager, tearing_bind); if (manager->global == NULL) { wl_list_remove(&manager->display_destroy.link); free(manager); return NULL; } return manager; } enum wp_tearing_control_v1_presentation_hint wlr_tearing_control_manager_v1_surface_hint_from_surface(struct wlr_tearing_control_manager_v1 *manager, struct wlr_surface *surface) { struct wlr_addon *addon = wlr_addon_find(&surface->addons, manager, &surface_addon_impl); if (addon == NULL) { return WP_TEARING_CONTROL_V1_PRESENTATION_HINT_VSYNC; } struct wlr_tearing_control_v1 *hint = wl_container_of(addon, hint, addon); return hint->hint; } wlroots-0.17.1/types/wlr_text_input_v3.c000066400000000000000000000264111454110342200203200ustar00rootroot00000000000000#ifndef _POSIX_C_SOURCE #define _POSIX_C_SOURCE 200809L #endif #include #include #include #include #include #include #include "text-input-unstable-v3-protocol.h" static void text_input_clear_focused_surface(struct wlr_text_input_v3 *text_input) { wl_list_remove(&text_input->surface_destroy.link); wl_list_init(&text_input->surface_destroy.link); text_input->focused_surface = NULL; } static const struct zwp_text_input_v3_interface text_input_impl; static struct wlr_text_input_v3 *text_input_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &zwp_text_input_v3_interface, &text_input_impl)); return wl_resource_get_user_data(resource); } void wlr_text_input_v3_send_enter(struct wlr_text_input_v3 *text_input, struct wlr_surface *surface) { assert(wl_resource_get_client(text_input->resource) == wl_resource_get_client(surface->resource)); text_input->focused_surface = surface; wl_signal_add(&text_input->focused_surface->events.destroy, &text_input->surface_destroy); zwp_text_input_v3_send_enter(text_input->resource, text_input->focused_surface->resource); } void wlr_text_input_v3_send_leave(struct wlr_text_input_v3 *text_input) { zwp_text_input_v3_send_leave(text_input->resource, text_input->focused_surface->resource); text_input_clear_focused_surface(text_input); } void wlr_text_input_v3_send_preedit_string(struct wlr_text_input_v3 *text_input, const char *text, int32_t cursor_begin, int32_t cursor_end) { zwp_text_input_v3_send_preedit_string(text_input->resource, text, cursor_begin, cursor_end); } void wlr_text_input_v3_send_commit_string(struct wlr_text_input_v3 *text_input, const char *text) { zwp_text_input_v3_send_commit_string(text_input->resource, text); } void wlr_text_input_v3_send_delete_surrounding_text( struct wlr_text_input_v3 *text_input, uint32_t before_length, uint32_t after_length) { zwp_text_input_v3_send_delete_surrounding_text(text_input->resource, before_length, after_length); } void wlr_text_input_v3_send_done(struct wlr_text_input_v3 *text_input) { zwp_text_input_v3_send_done(text_input->resource, text_input->current_serial); } static void wlr_text_input_destroy(struct wlr_text_input_v3 *text_input) { wl_signal_emit_mutable(&text_input->events.destroy, text_input); text_input_clear_focused_surface(text_input); wl_list_remove(&text_input->seat_destroy.link); // remove from manager.text_inputs wl_list_remove(&text_input->link); free(text_input->current.surrounding.text); free(text_input->pending.surrounding.text); free(text_input); } static void text_input_resource_destroy(struct wl_resource *resource) { struct wlr_text_input_v3 *text_input = text_input_from_resource(resource); if (!text_input) { return; } wlr_text_input_destroy(text_input); } static void text_input_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static void text_input_enable(struct wl_client *client, struct wl_resource *resource) { struct wlr_text_input_v3 *text_input = text_input_from_resource(resource); if (!text_input) { return; } struct wlr_text_input_v3_state defaults = {0}; free(text_input->pending.surrounding.text); text_input->pending = defaults; text_input->pending_enabled = true; } static void text_input_disable(struct wl_client *client, struct wl_resource *resource) { struct wlr_text_input_v3 *text_input = text_input_from_resource(resource); if (!text_input) { return; } text_input->pending_enabled = false; } static void text_input_set_surrounding_text(struct wl_client *client, struct wl_resource *resource, const char *text, int32_t cursor, int32_t anchor) { struct wlr_text_input_v3 *text_input = text_input_from_resource(resource); if (!text_input) { return; } free(text_input->pending.surrounding.text); text_input->pending.surrounding.text = strdup(text); if (!text_input->pending.surrounding.text) { wl_client_post_no_memory(client); } text_input->pending.features |= WLR_TEXT_INPUT_V3_FEATURE_SURROUNDING_TEXT; text_input->pending.surrounding.cursor = cursor; text_input->pending.surrounding.anchor = anchor; } static void text_input_set_text_change_cause(struct wl_client *client, struct wl_resource *resource, uint32_t cause) { struct wlr_text_input_v3 *text_input = text_input_from_resource(resource); if (!text_input) { return; } text_input->pending.text_change_cause = cause; } static void text_input_set_content_type(struct wl_client *client, struct wl_resource *resource, uint32_t hint, uint32_t purpose) { struct wlr_text_input_v3 *text_input = text_input_from_resource(resource); if (!text_input) { return; } text_input->pending.features |= WLR_TEXT_INPUT_V3_FEATURE_CONTENT_TYPE; text_input->pending.content_type.hint = hint; text_input->pending.content_type.purpose = purpose; } static void text_input_set_cursor_rectangle(struct wl_client *client, struct wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) { struct wlr_text_input_v3 *text_input = text_input_from_resource(resource); if (!text_input) { return; } text_input->pending.features |= WLR_TEXT_INPUT_V3_FEATURE_CURSOR_RECTANGLE; text_input->pending.cursor_rectangle.x = x; text_input->pending.cursor_rectangle.y = y; text_input->pending.cursor_rectangle.width = width; text_input->pending.cursor_rectangle.height = height; } static void text_input_commit(struct wl_client *client, struct wl_resource *resource) { struct wlr_text_input_v3 *text_input = text_input_from_resource(resource); if (!text_input) { return; } free(text_input->current.surrounding.text); text_input->current = text_input->pending; if (text_input->pending.surrounding.text) { text_input->current.surrounding.text = strdup(text_input->pending.surrounding.text); if (text_input->current.surrounding.text == NULL) { wl_client_post_no_memory(client); return; } } bool old_enabled = text_input->current_enabled; text_input->current_enabled = text_input->pending_enabled; text_input->current_serial++; if (text_input->focused_surface == NULL) { wlr_log(WLR_DEBUG, "Text input commit received without focus"); } if (!old_enabled && text_input->current_enabled) { text_input->active_features = text_input->current.features; wl_signal_emit_mutable(&text_input->events.enable, text_input); } else if (old_enabled && !text_input->current_enabled) { text_input->active_features = 0; wl_signal_emit_mutable(&text_input->events.disable, text_input); } else { // including never enabled wl_signal_emit_mutable(&text_input->events.commit, text_input); } } static const struct zwp_text_input_v3_interface text_input_impl = { .destroy = text_input_destroy, .enable = text_input_enable, .disable = text_input_disable, .set_surrounding_text = text_input_set_surrounding_text, .set_text_change_cause = text_input_set_text_change_cause, .set_content_type = text_input_set_content_type, .set_cursor_rectangle = text_input_set_cursor_rectangle, .commit = text_input_commit, }; static const struct zwp_text_input_manager_v3_interface text_input_manager_impl; static struct wlr_text_input_manager_v3 *text_input_manager_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &zwp_text_input_manager_v3_interface, &text_input_manager_impl)); return wl_resource_get_user_data(resource); } static void text_input_manager_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static void text_input_handle_seat_destroy(struct wl_listener *listener, void *data) { struct wlr_text_input_v3 *text_input = wl_container_of(listener, text_input, seat_destroy); struct wl_resource *resource = text_input->resource; wlr_text_input_destroy(text_input); wl_resource_set_user_data(resource, NULL); } static void text_input_handle_focused_surface_destroy( struct wl_listener *listener, void *data) { struct wlr_text_input_v3 *text_input = wl_container_of(listener, text_input, surface_destroy); text_input_clear_focused_surface(text_input); } static void text_input_manager_get_text_input(struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *seat) { int version = wl_resource_get_version(resource); struct wl_resource *text_input_resource = wl_resource_create(client, &zwp_text_input_v3_interface, version, id); if (text_input_resource == NULL) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(text_input_resource, &text_input_impl, NULL, text_input_resource_destroy); struct wlr_seat_client *seat_client = wlr_seat_client_from_resource(seat); if (seat_client == NULL) { return; } struct wlr_text_input_v3 *text_input = calloc(1, sizeof(*text_input)); if (text_input == NULL) { wl_client_post_no_memory(client); return; } wl_signal_init(&text_input->events.enable); wl_signal_init(&text_input->events.commit); wl_signal_init(&text_input->events.disable); wl_signal_init(&text_input->events.destroy); text_input->resource = text_input_resource; wl_resource_set_user_data(text_input_resource, text_input); struct wlr_seat *wlr_seat = seat_client->seat; text_input->seat = wlr_seat; wl_signal_add(&seat_client->events.destroy, &text_input->seat_destroy); text_input->seat_destroy.notify = text_input_handle_seat_destroy; text_input->surface_destroy.notify = text_input_handle_focused_surface_destroy; wl_list_init(&text_input->surface_destroy.link); struct wlr_text_input_manager_v3 *manager = text_input_manager_from_resource(resource); wl_list_insert(&manager->text_inputs, &text_input->link); wl_signal_emit_mutable(&manager->events.text_input, text_input); } static const struct zwp_text_input_manager_v3_interface text_input_manager_impl = { .destroy = text_input_manager_destroy, .get_text_input = text_input_manager_get_text_input, }; static void text_input_manager_bind(struct wl_client *wl_client, void *data, uint32_t version, uint32_t id) { struct wlr_text_input_manager_v3 *manager = data; struct wl_resource *resource = wl_resource_create(wl_client, &zwp_text_input_manager_v3_interface, version, id); if (resource == NULL) { wl_client_post_no_memory(wl_client); return; } wl_resource_set_implementation(resource, &text_input_manager_impl, manager, NULL); } static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_text_input_manager_v3 *manager = wl_container_of(listener, manager, display_destroy); wl_signal_emit_mutable(&manager->events.destroy, manager); wl_list_remove(&manager->display_destroy.link); wl_global_destroy(manager->global); free(manager); } struct wlr_text_input_manager_v3 *wlr_text_input_manager_v3_create( struct wl_display *display) { struct wlr_text_input_manager_v3 *manager = calloc(1, sizeof(*manager)); if (!manager) { return NULL; } wl_list_init(&manager->text_inputs); wl_signal_init(&manager->events.text_input); wl_signal_init(&manager->events.destroy); manager->global = wl_global_create(display, &zwp_text_input_manager_v3_interface, 1, manager, text_input_manager_bind); if (!manager->global) { free(manager); return NULL; } manager->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &manager->display_destroy); return manager; } wlroots-0.17.1/types/wlr_touch.c000066400000000000000000000017021454110342200166230ustar00rootroot00000000000000#include #include #include #include #include #include #include "interfaces/wlr_input_device.h" struct wlr_touch *wlr_touch_from_input_device( struct wlr_input_device *input_device) { assert(input_device->type == WLR_INPUT_DEVICE_TOUCH); return wl_container_of(input_device, (struct wlr_touch *)NULL, base); } void wlr_touch_init(struct wlr_touch *touch, const struct wlr_touch_impl *impl, const char *name) { *touch = (struct wlr_touch){ .impl = impl, }; wlr_input_device_init(&touch->base, WLR_INPUT_DEVICE_TOUCH, name); wl_signal_init(&touch->events.down); wl_signal_init(&touch->events.up); wl_signal_init(&touch->events.motion); wl_signal_init(&touch->events.cancel); wl_signal_init(&touch->events.frame); } void wlr_touch_finish(struct wlr_touch *touch) { wlr_input_device_finish(&touch->base); free(touch->output_name); } wlroots-0.17.1/types/wlr_viewporter.c000066400000000000000000000176451454110342200177240ustar00rootroot00000000000000#include #include #include #include #include #include "viewporter-protocol.h" #define VIEWPORTER_VERSION 1 struct wlr_viewport { struct wl_resource *resource; struct wlr_surface *surface; struct wlr_addon addon; struct wl_listener surface_client_commit; }; static const struct wp_viewport_interface viewport_impl; // Returns NULL if the viewport is inert static struct wlr_viewport *viewport_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &wp_viewport_interface, &viewport_impl)); return wl_resource_get_user_data(resource); } static void viewport_handle_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static void viewport_handle_set_source(struct wl_client *client, struct wl_resource *resource, wl_fixed_t x_fixed, wl_fixed_t y_fixed, wl_fixed_t width_fixed, wl_fixed_t height_fixed) { struct wlr_viewport *viewport = viewport_from_resource(resource); if (viewport == NULL) { wl_resource_post_error(resource, WP_VIEWPORT_ERROR_NO_SURFACE, "wp_viewport.set_source sent after wl_surface has been destroyed"); return; } struct wlr_surface_state *pending = &viewport->surface->pending; double x = wl_fixed_to_double(x_fixed); double y = wl_fixed_to_double(y_fixed); double width = wl_fixed_to_double(width_fixed); double height = wl_fixed_to_double(height_fixed); if (x == -1.0 && y == -1.0 && width == -1.0 && height == -1.0) { pending->viewport.has_src = false; } else if (x < 0 || y < 0 || width <= 0 || height <= 0) { wl_resource_post_error(resource, WP_VIEWPORT_ERROR_BAD_VALUE, "wl_viewport.set_source sent with invalid values"); return; } else { pending->viewport.has_src = true; } pending->viewport.src.x = x; pending->viewport.src.y = y; pending->viewport.src.width = width; pending->viewport.src.height = height; pending->committed |= WLR_SURFACE_STATE_VIEWPORT; } static void viewport_handle_set_destination(struct wl_client *client, struct wl_resource *resource, int32_t width, int32_t height) { struct wlr_viewport *viewport = viewport_from_resource(resource); if (viewport == NULL) { wl_resource_post_error(resource, WP_VIEWPORT_ERROR_NO_SURFACE, "wp_viewport.set_destination sent after wl_surface has been destroyed"); return; } struct wlr_surface_state *pending = &viewport->surface->pending; if (width == -1 && height == -1) { pending->viewport.has_dst = false; } else if (width <= 0 || height <= 0) { wl_resource_post_error(resource, WP_VIEWPORT_ERROR_BAD_VALUE, "wl_viewport.set_destination sent with invalid values"); return; } else { pending->viewport.has_dst = true; } pending->viewport.dst_width = width; pending->viewport.dst_height = height; pending->committed |= WLR_SURFACE_STATE_VIEWPORT; } static const struct wp_viewport_interface viewport_impl = { .destroy = viewport_handle_destroy, .set_source = viewport_handle_set_source, .set_destination = viewport_handle_set_destination, }; static void viewport_destroy(struct wlr_viewport *viewport) { if (viewport == NULL) { return; } struct wlr_surface_state *pending = &viewport->surface->pending; pending->viewport.has_src = false; pending->viewport.has_dst = false; pending->committed |= WLR_SURFACE_STATE_VIEWPORT; wlr_addon_finish(&viewport->addon); wl_resource_set_user_data(viewport->resource, NULL); wl_list_remove(&viewport->surface_client_commit.link); free(viewport); } static void surface_addon_destroy(struct wlr_addon *addon) { struct wlr_viewport *viewport = wl_container_of(addon, viewport, addon); viewport_destroy(viewport); } static const struct wlr_addon_interface surface_addon_impl = { .name = "wlr_viewport", .destroy = surface_addon_destroy, }; static void viewport_handle_resource_destroy(struct wl_resource *resource) { struct wlr_viewport *viewport = viewport_from_resource(resource); viewport_destroy(viewport); } static bool check_src_buffer_bounds(const struct wlr_surface_state *state) { int width = state->buffer_width / state->scale; int height = state->buffer_height / state->scale; if (state->transform & WL_OUTPUT_TRANSFORM_90) { int tmp = width; width = height; height = tmp; } struct wlr_fbox box = state->viewport.src; return box.x + box.width <= width && box.y + box.height <= height; } static void viewport_handle_surface_client_commit(struct wl_listener *listener, void *data) { struct wlr_viewport *viewport = wl_container_of(listener, viewport, surface_client_commit); struct wlr_surface_state *state = &viewport->surface->pending; if (!state->viewport.has_dst && (floor(state->viewport.src.width) != state->viewport.src.width || floor(state->viewport.src.height) != state->viewport.src.height)) { wl_resource_post_error(viewport->resource, WP_VIEWPORT_ERROR_BAD_SIZE, "wl_viewport.set_source width and height must be integers " "when the destination rectangle is unset"); return; } if (state->viewport.has_src && state->buffer != NULL && !check_src_buffer_bounds(state)) { wl_resource_post_error(viewport->resource, WP_VIEWPORT_ERROR_OUT_OF_BUFFER, "source rectangle out of buffer bounds"); return; } } static void viewporter_handle_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static void viewporter_handle_get_viewport(struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *surface_resource) { struct wlr_surface *surface = wlr_surface_from_resource(surface_resource); if (wlr_addon_find(&surface->addons, NULL, &surface_addon_impl) != NULL) { wl_resource_post_error(resource, WP_VIEWPORTER_ERROR_VIEWPORT_EXISTS, "wp_viewport for this surface already exists"); return; } struct wlr_viewport *viewport = calloc(1, sizeof(*viewport)); if (viewport == NULL) { wl_client_post_no_memory(client); return; } uint32_t version = wl_resource_get_version(resource); viewport->resource = wl_resource_create(client, &wp_viewport_interface, version, id); if (viewport->resource == NULL) { wl_client_post_no_memory(client); free(viewport); return; } wl_resource_set_implementation(viewport->resource, &viewport_impl, viewport, viewport_handle_resource_destroy); viewport->surface = surface; wlr_addon_init(&viewport->addon, &surface->addons, NULL, &surface_addon_impl); viewport->surface_client_commit.notify = viewport_handle_surface_client_commit; wl_signal_add(&surface->events.client_commit, &viewport->surface_client_commit); } static const struct wp_viewporter_interface viewporter_impl = { .destroy = viewporter_handle_destroy, .get_viewport = viewporter_handle_get_viewport, }; static void viewporter_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id) { struct wlr_viewporter *viewporter = data; struct wl_resource *resource = wl_resource_create(client, &wp_viewporter_interface, version, id); if (resource == NULL) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(resource, &viewporter_impl, viewporter, NULL); } static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_viewporter *viewporter = wl_container_of(listener, viewporter, display_destroy); wl_signal_emit_mutable(&viewporter->events.destroy, NULL); wl_global_destroy(viewporter->global); free(viewporter); } struct wlr_viewporter *wlr_viewporter_create(struct wl_display *display) { struct wlr_viewporter *viewporter = calloc(1, sizeof(*viewporter)); if (viewporter == NULL) { return NULL; } viewporter->global = wl_global_create(display, &wp_viewporter_interface, VIEWPORTER_VERSION, viewporter, viewporter_bind); if (viewporter->global == NULL) { free(viewporter); return NULL; } wl_signal_init(&viewporter->events.destroy); viewporter->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &viewporter->display_destroy); return viewporter; } wlroots-0.17.1/types/wlr_virtual_keyboard_v1.c000066400000000000000000000163331454110342200214630ustar00rootroot00000000000000#define _POSIX_C_SOURCE 199309L #include #include #include #include #include #include #include #include #include #include "virtual-keyboard-unstable-v1-protocol.h" static const struct wlr_keyboard_impl keyboard_impl = { .name = "virtual-keyboard", }; static const struct zwp_virtual_keyboard_v1_interface virtual_keyboard_impl; static struct wlr_virtual_keyboard_v1 *virtual_keyboard_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &zwp_virtual_keyboard_v1_interface, &virtual_keyboard_impl)); return wl_resource_get_user_data(resource); } struct wlr_virtual_keyboard_v1 *wlr_input_device_get_virtual_keyboard( struct wlr_input_device *wlr_dev) { if (wlr_dev->type != WLR_INPUT_DEVICE_KEYBOARD) { return NULL; } struct wlr_keyboard *wlr_keyboard = wlr_keyboard_from_input_device(wlr_dev); if (wlr_keyboard->impl != &keyboard_impl) { return NULL; } return wl_container_of(wlr_keyboard, (struct wlr_virtual_keyboard_v1 *)NULL, keyboard); } static void virtual_keyboard_keymap(struct wl_client *client, struct wl_resource *resource, uint32_t format, int32_t fd, uint32_t size) { struct wlr_virtual_keyboard_v1 *keyboard = virtual_keyboard_from_resource(resource); if (keyboard == NULL) { return; } struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); if (!context) { goto context_fail; } void *data = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); if (data == MAP_FAILED) { goto fd_fail; } struct xkb_keymap *keymap = xkb_keymap_new_from_string(context, data, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS); munmap(data, size); if (!keymap) { goto keymap_fail; } wlr_keyboard_set_keymap(&keyboard->keyboard, keymap); keyboard->has_keymap = true; xkb_keymap_unref(keymap); xkb_context_unref(context); close(fd); return; keymap_fail: fd_fail: xkb_context_unref(context); context_fail: wl_client_post_no_memory(client); close(fd); } static void virtual_keyboard_key(struct wl_client *client, struct wl_resource *resource, uint32_t time, uint32_t key, uint32_t state) { struct wlr_virtual_keyboard_v1 *keyboard = virtual_keyboard_from_resource(resource); if (keyboard == NULL) { return; } if (!keyboard->has_keymap) { wl_resource_post_error(resource, ZWP_VIRTUAL_KEYBOARD_V1_ERROR_NO_KEYMAP, "Cannot send a keypress before defining a keymap"); return; } struct wlr_keyboard_key_event event = { .time_msec = time, .keycode = key, .update_state = false, .state = state, }; wlr_keyboard_notify_key(&keyboard->keyboard, &event); } static void virtual_keyboard_modifiers(struct wl_client *client, struct wl_resource *resource, uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group) { struct wlr_virtual_keyboard_v1 *keyboard = virtual_keyboard_from_resource(resource); if (keyboard == NULL) { return; } if (!keyboard->has_keymap) { wl_resource_post_error(resource, ZWP_VIRTUAL_KEYBOARD_V1_ERROR_NO_KEYMAP, "Cannot send a modifier state before defining a keymap"); return; } wlr_keyboard_notify_modifiers(&keyboard->keyboard, mods_depressed, mods_latched, mods_locked, group); } static void virtual_keyboard_destroy_resource(struct wl_resource *resource) { struct wlr_virtual_keyboard_v1 *keyboard = virtual_keyboard_from_resource(resource); if (keyboard == NULL) { return; } wlr_keyboard_finish(&keyboard->keyboard); wl_resource_set_user_data(keyboard->resource, NULL); wl_list_remove(&keyboard->link); free(keyboard); } static void virtual_keyboard_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static const struct zwp_virtual_keyboard_v1_interface virtual_keyboard_impl = { .keymap = virtual_keyboard_keymap, .key = virtual_keyboard_key, .modifiers = virtual_keyboard_modifiers, .destroy = virtual_keyboard_destroy, }; static const struct zwp_virtual_keyboard_manager_v1_interface manager_impl; static struct wlr_virtual_keyboard_manager_v1 *manager_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &zwp_virtual_keyboard_manager_v1_interface, &manager_impl)); return wl_resource_get_user_data(resource); } static void virtual_keyboard_manager_create_virtual_keyboard( struct wl_client *client, struct wl_resource *resource, struct wl_resource *seat, uint32_t id) { struct wlr_virtual_keyboard_manager_v1 *manager = manager_from_resource(resource); struct wlr_seat_client *seat_client = wlr_seat_client_from_resource(seat); struct wl_resource *keyboard_resource = wl_resource_create(client, &zwp_virtual_keyboard_v1_interface, wl_resource_get_version(resource), id); if (!keyboard_resource) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(keyboard_resource, &virtual_keyboard_impl, NULL, virtual_keyboard_destroy_resource); if (seat_client == NULL) { return; } struct wlr_virtual_keyboard_v1 *virtual_keyboard = calloc(1, sizeof(*virtual_keyboard)); if (!virtual_keyboard) { wl_client_post_no_memory(client); return; } wlr_keyboard_init(&virtual_keyboard->keyboard, &keyboard_impl, "wlr_virtual_keyboard_v1"); virtual_keyboard->resource = keyboard_resource; virtual_keyboard->seat = seat_client->seat; wl_resource_set_user_data(keyboard_resource, virtual_keyboard); wl_list_insert(&manager->virtual_keyboards, &virtual_keyboard->link); wl_signal_emit_mutable(&manager->events.new_virtual_keyboard, virtual_keyboard); } static const struct zwp_virtual_keyboard_manager_v1_interface manager_impl = { .create_virtual_keyboard = virtual_keyboard_manager_create_virtual_keyboard, }; static void virtual_keyboard_manager_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id) { struct wlr_virtual_keyboard_manager_v1 *manager = data; struct wl_resource *resource = wl_resource_create(client, &zwp_virtual_keyboard_manager_v1_interface, version, id); if (!resource) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(resource, &manager_impl, manager, NULL); } static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_virtual_keyboard_manager_v1 *manager = wl_container_of(listener, manager, display_destroy); wl_signal_emit_mutable(&manager->events.destroy, manager); wl_list_remove(&manager->display_destroy.link); wl_global_destroy(manager->global); free(manager); } struct wlr_virtual_keyboard_manager_v1* wlr_virtual_keyboard_manager_v1_create( struct wl_display *display) { struct wlr_virtual_keyboard_manager_v1 *manager = calloc(1, sizeof(*manager)); if (!manager) { return NULL; } manager->global = wl_global_create(display, &zwp_virtual_keyboard_manager_v1_interface, 1, manager, virtual_keyboard_manager_bind); if (!manager->global) { free(manager); return NULL; } manager->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &manager->display_destroy); wl_list_init(&manager->virtual_keyboards); wl_signal_init(&manager->events.new_virtual_keyboard); wl_signal_init(&manager->events.destroy); return manager; } wlroots-0.17.1/types/wlr_virtual_pointer_v1.c000066400000000000000000000257261454110342200213510ustar00rootroot00000000000000#include #include #include #include #include #include #include #include "wlr-virtual-pointer-unstable-v1-protocol.h" static const struct wlr_pointer_impl pointer_impl = { .name = "virtual-pointer", }; static const struct zwlr_virtual_pointer_v1_interface virtual_pointer_impl; static struct wlr_virtual_pointer_v1 *virtual_pointer_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &zwlr_virtual_pointer_v1_interface, &virtual_pointer_impl)); return wl_resource_get_user_data(resource); } static void virtual_pointer_motion(struct wl_client *client, struct wl_resource *resource, uint32_t time, wl_fixed_t dx, wl_fixed_t dy) { struct wlr_virtual_pointer_v1 *pointer = virtual_pointer_from_resource(resource); if (pointer == NULL) { return; } struct wlr_pointer_motion_event event = { .pointer = &pointer->pointer, .time_msec = time, .delta_x = wl_fixed_to_double(dx), .delta_y = wl_fixed_to_double(dy), .unaccel_dx = wl_fixed_to_double(dx), .unaccel_dy = wl_fixed_to_double(dy), }; wl_signal_emit_mutable(&pointer->pointer.events.motion, &event); } static void virtual_pointer_motion_absolute(struct wl_client *client, struct wl_resource *resource, uint32_t time, uint32_t x, uint32_t y, uint32_t x_extent, uint32_t y_extent) { struct wlr_virtual_pointer_v1 *pointer = virtual_pointer_from_resource(resource); if (pointer == NULL) { return; } if (x_extent == 0 || y_extent == 0) { return; } struct wlr_pointer_motion_absolute_event event = { .pointer = &pointer->pointer, .time_msec = time, .x = (double)x / x_extent, .y = (double)y / y_extent, }; wl_signal_emit_mutable(&pointer->pointer.events.motion_absolute, &event); } static void virtual_pointer_button(struct wl_client *client, struct wl_resource *resource, uint32_t time, uint32_t button, uint32_t state) { struct wlr_virtual_pointer_v1 *pointer = virtual_pointer_from_resource(resource); if (pointer == NULL) { return; } struct wlr_pointer_button_event event = { .pointer = &pointer->pointer, .time_msec = time, .button = button, .state = state ? WLR_BUTTON_PRESSED : WLR_BUTTON_RELEASED }; wl_signal_emit_mutable(&pointer->pointer.events.button, &event); } static void virtual_pointer_axis(struct wl_client *client, struct wl_resource *resource, uint32_t time, uint32_t axis, wl_fixed_t value) { if (axis > WL_POINTER_AXIS_HORIZONTAL_SCROLL) { wl_resource_post_error(resource, ZWLR_VIRTUAL_POINTER_V1_ERROR_INVALID_AXIS, "Invalid enumeration value %" PRIu32, axis); return; } struct wlr_virtual_pointer_v1 *pointer = virtual_pointer_from_resource(resource); if (pointer == NULL) { return; } pointer->axis = axis; pointer->axis_valid[pointer->axis] = true; pointer->axis_event[pointer->axis].pointer = &pointer->pointer; pointer->axis_event[pointer->axis].time_msec = time; pointer->axis_event[pointer->axis].orientation = axis; pointer->axis_event[pointer->axis].delta = wl_fixed_to_double(value); } static void virtual_pointer_frame(struct wl_client *client, struct wl_resource *resource) { struct wlr_virtual_pointer_v1 *pointer = virtual_pointer_from_resource(resource); if (pointer == NULL) { return; } for (size_t i = 0; i < sizeof(pointer->axis_valid) / sizeof(pointer->axis_valid[0]); ++i) { if (pointer->axis_valid[i]) { /* Deliver pending axis event */ wl_signal_emit_mutable(&pointer->pointer.events.axis, &pointer->axis_event[i]); pointer->axis_event[i] = (struct wlr_pointer_axis_event){0}; pointer->axis_valid[i] = false; } } wl_signal_emit_mutable(&pointer->pointer.events.frame, &pointer->pointer); } static void virtual_pointer_axis_source(struct wl_client *client, struct wl_resource *resource, uint32_t source) { if (source > WL_POINTER_AXIS_SOURCE_WHEEL_TILT) { wl_resource_post_error(resource, ZWLR_VIRTUAL_POINTER_V1_ERROR_INVALID_AXIS_SOURCE, "Invalid enumeration value %" PRIu32, source); return; } struct wlr_virtual_pointer_v1 *pointer = virtual_pointer_from_resource(resource); if (pointer == NULL) { return; } pointer->axis_event[pointer->axis].pointer = &pointer->pointer; pointer->axis_event[pointer->axis].source = source; } static void virtual_pointer_axis_stop(struct wl_client *client, struct wl_resource *resource, uint32_t time, uint32_t axis) { if (axis > WL_POINTER_AXIS_HORIZONTAL_SCROLL) { wl_resource_post_error(resource, ZWLR_VIRTUAL_POINTER_V1_ERROR_INVALID_AXIS, "Invalid enumeration value %" PRIu32, axis); return; } struct wlr_virtual_pointer_v1 *pointer = virtual_pointer_from_resource(resource); if (pointer == NULL) { return; } pointer->axis = axis; pointer->axis_valid[pointer->axis] = true; pointer->axis_event[pointer->axis].pointer = &pointer->pointer; pointer->axis_event[pointer->axis].time_msec = time; pointer->axis_event[pointer->axis].orientation = axis; pointer->axis_event[pointer->axis].delta = 0; pointer->axis_event[pointer->axis].delta_discrete = 0; } static void virtual_pointer_axis_discrete(struct wl_client *client, struct wl_resource *resource, uint32_t time, uint32_t axis, wl_fixed_t value, int32_t discrete) { if (axis > WL_POINTER_AXIS_HORIZONTAL_SCROLL) { wl_resource_post_error(resource, ZWLR_VIRTUAL_POINTER_V1_ERROR_INVALID_AXIS, "Invalid enumeration value %" PRIu32, axis); return; } struct wlr_virtual_pointer_v1 *pointer = virtual_pointer_from_resource(resource); if (pointer == NULL) { return; } pointer->axis = axis; pointer->axis_valid[pointer->axis] = true; pointer->axis_event[pointer->axis].pointer = &pointer->pointer; pointer->axis_event[pointer->axis].time_msec = time; pointer->axis_event[pointer->axis].orientation = axis; pointer->axis_event[pointer->axis].delta = wl_fixed_to_double(value); pointer->axis_event[pointer->axis].delta_discrete = discrete * WLR_POINTER_AXIS_DISCRETE_STEP; } static void virtual_pointer_destroy_resource(struct wl_resource *resource) { struct wlr_virtual_pointer_v1 *pointer = virtual_pointer_from_resource(resource); if (pointer == NULL) { return; } wlr_pointer_finish(&pointer->pointer); wl_resource_set_user_data(pointer->resource, NULL); wl_list_remove(&pointer->link); free(pointer); } static void virtual_pointer_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static const struct zwlr_virtual_pointer_v1_interface virtual_pointer_impl = { .motion = virtual_pointer_motion, .motion_absolute = virtual_pointer_motion_absolute, .button = virtual_pointer_button, .axis = virtual_pointer_axis, .frame = virtual_pointer_frame, .axis_source = virtual_pointer_axis_source, .axis_stop = virtual_pointer_axis_stop, .axis_discrete = virtual_pointer_axis_discrete, .destroy = virtual_pointer_destroy, }; static const struct zwlr_virtual_pointer_manager_v1_interface manager_impl; static struct wlr_virtual_pointer_manager_v1 *manager_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &zwlr_virtual_pointer_manager_v1_interface, &manager_impl)); return wl_resource_get_user_data(resource); } static void virtual_pointer_manager_create_virtual_pointer_with_output( struct wl_client *client, struct wl_resource *resource, struct wl_resource *seat, struct wl_resource *output, uint32_t id) { struct wlr_virtual_pointer_manager_v1 *manager = manager_from_resource(resource); struct wlr_virtual_pointer_v1 *virtual_pointer = calloc(1, sizeof(*virtual_pointer)); if (!virtual_pointer) { wl_client_post_no_memory(client); return; } wlr_pointer_init(&virtual_pointer->pointer, &pointer_impl, "wlr_virtual_pointer_v1"); struct wl_resource *pointer_resource = wl_resource_create(client, &zwlr_virtual_pointer_v1_interface, wl_resource_get_version(resource), id); if (!pointer_resource) { free(virtual_pointer); wl_client_post_no_memory(client); return; } wl_resource_set_implementation(pointer_resource, &virtual_pointer_impl, virtual_pointer, virtual_pointer_destroy_resource); struct wlr_virtual_pointer_v1_new_pointer_event event = { .new_pointer = virtual_pointer, }; if (seat) { struct wlr_seat_client *seat_client = wlr_seat_client_from_resource(seat); event.suggested_seat = seat_client != NULL ? seat_client->seat : NULL; } if (output) { struct wlr_output *wlr_output = wlr_output_from_resource(output); event.suggested_output = wlr_output; } virtual_pointer->resource = pointer_resource; wl_list_insert(&manager->virtual_pointers, &virtual_pointer->link); wl_signal_emit_mutable(&manager->events.new_virtual_pointer, &event); } static void virtual_pointer_manager_create_virtual_pointer( struct wl_client *client, struct wl_resource *resource, struct wl_resource *seat, uint32_t id) { virtual_pointer_manager_create_virtual_pointer_with_output(client, resource, seat, NULL, id); } static void virtual_pointer_manager_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static const struct zwlr_virtual_pointer_manager_v1_interface manager_impl = { .create_virtual_pointer = virtual_pointer_manager_create_virtual_pointer, .create_virtual_pointer_with_output = virtual_pointer_manager_create_virtual_pointer_with_output, .destroy = virtual_pointer_manager_destroy, }; static void virtual_pointer_manager_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id) { struct wlr_virtual_pointer_manager_v1 *manager = data; struct wl_resource *resource = wl_resource_create(client, &zwlr_virtual_pointer_manager_v1_interface, version, id); if (!resource) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(resource, &manager_impl, manager, NULL); } static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_virtual_pointer_manager_v1 *manager = wl_container_of(listener, manager, display_destroy); wl_signal_emit_mutable(&manager->events.destroy, manager); wl_list_remove(&manager->display_destroy.link); wl_global_destroy(manager->global); struct wlr_virtual_pointer_v1 *pointer, *pointer_tmp; wl_list_for_each_safe(pointer, pointer_tmp, &manager->virtual_pointers, link) { wl_resource_destroy(pointer->resource); } free(manager); } struct wlr_virtual_pointer_manager_v1* wlr_virtual_pointer_manager_v1_create( struct wl_display *display) { struct wlr_virtual_pointer_manager_v1 *manager = calloc(1, sizeof(*manager)); if (!manager) { return NULL; } wl_list_init(&manager->virtual_pointers); wl_signal_init(&manager->events.new_virtual_pointer); wl_signal_init(&manager->events.destroy); manager->global = wl_global_create(display, &zwlr_virtual_pointer_manager_v1_interface, 2, manager, virtual_pointer_manager_bind); if (!manager->global) { free(manager); return NULL; } manager->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &manager->display_destroy); return manager; } wlroots-0.17.1/types/wlr_xcursor_manager.c000066400000000000000000000032541454110342200207040ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200809L #include #include #include struct wlr_xcursor_manager *wlr_xcursor_manager_create(const char *name, uint32_t size) { struct wlr_xcursor_manager *manager = calloc(1, sizeof(*manager)); if (manager == NULL) { return NULL; } if (name != NULL) { manager->name = strdup(name); } manager->size = size; wl_list_init(&manager->scaled_themes); return manager; } void wlr_xcursor_manager_destroy(struct wlr_xcursor_manager *manager) { if (manager == NULL) { return; } struct wlr_xcursor_manager_theme *theme, *tmp; wl_list_for_each_safe(theme, tmp, &manager->scaled_themes, link) { wl_list_remove(&theme->link); wlr_xcursor_theme_destroy(theme->theme); free(theme); } free(manager->name); free(manager); } bool wlr_xcursor_manager_load(struct wlr_xcursor_manager *manager, float scale) { struct wlr_xcursor_manager_theme *theme; wl_list_for_each(theme, &manager->scaled_themes, link) { if (theme->scale == scale) { return true; } } theme = calloc(1, sizeof(*theme)); if (theme == NULL) { return false; } theme->scale = scale; theme->theme = wlr_xcursor_theme_load(manager->name, manager->size * scale); if (theme->theme == NULL) { free(theme); return false; } wl_list_insert(&manager->scaled_themes, &theme->link); return true; } struct wlr_xcursor *wlr_xcursor_manager_get_xcursor( struct wlr_xcursor_manager *manager, const char *name, float scale) { struct wlr_xcursor_manager_theme *theme; wl_list_for_each(theme, &manager->scaled_themes, link) { if (theme->scale == scale) { return wlr_xcursor_theme_get_cursor(theme->theme, name); } } return NULL; } wlroots-0.17.1/types/wlr_xdg_activation_v1.c000066400000000000000000000311151454110342200211130ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include "util/token.h" #include "xdg-activation-v1-protocol.h" #define XDG_ACTIVATION_V1_VERSION 1 static const struct xdg_activation_token_v1_interface token_impl; static struct wlr_xdg_activation_token_v1 *token_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &xdg_activation_token_v1_interface, &token_impl)); return wl_resource_get_user_data(resource); } void wlr_xdg_activation_token_v1_destroy( struct wlr_xdg_activation_token_v1 *token) { if (token == NULL) { return; } if (token->resource != NULL) { wl_resource_set_user_data(token->resource, NULL); // make inert } if (token->timeout != NULL) { wl_event_source_remove(token->timeout); } wl_signal_emit_mutable(&token->events.destroy, NULL); wl_list_remove(&token->link); wl_list_remove(&token->seat_destroy.link); wl_list_remove(&token->surface_destroy.link); free(token->app_id); free(token->token); free(token); } static int token_handle_timeout(void *data) { struct wlr_xdg_activation_token_v1 *token = data; wlr_log(WLR_DEBUG, "Activation token '%s' has expired", token->token); wlr_xdg_activation_token_v1_destroy(token); return 0; } static void token_handle_resource_destroy(struct wl_resource *resource) { struct wlr_xdg_activation_token_v1 *token = token_from_resource(resource); wlr_xdg_activation_token_v1_destroy(token); } static void token_handle_destroy(struct wl_client *client, struct wl_resource *token_resource) { wl_resource_destroy(token_resource); } static bool token_init( struct wlr_xdg_activation_token_v1 *token) { char token_str[TOKEN_SIZE] = {0}; if (!generate_token(token_str)) { return false; } token->token = strdup(token_str); if (token->token == NULL) { return false; } if (token->activation->token_timeout_msec > 0) { // Needs wayland > 1.19 // struct wl_display *display = wl_global_get_display(activation->global); struct wl_display *display = token->activation->display; struct wl_event_loop *loop = wl_display_get_event_loop(display); token->timeout = wl_event_loop_add_timer(loop, token_handle_timeout, token); if (token->timeout == NULL) { return false; } wl_event_source_timer_update(token->timeout, token->activation->token_timeout_msec); } assert(wl_list_empty(&token->link)); wl_list_insert(&token->activation->tokens, &token->link); return true; } static void token_handle_commit(struct wl_client *client, struct wl_resource *token_resource) { struct wlr_xdg_activation_token_v1 *token = token_from_resource(token_resource); if (token == NULL) { wl_resource_post_error(token_resource, XDG_ACTIVATION_TOKEN_V1_ERROR_ALREADY_USED, "The activation token has already been used"); return; } // Make the token resource inert wl_resource_set_user_data(token->resource, NULL); token->resource = NULL; if (token->seat != NULL) { struct wlr_seat_client *seat_client = wlr_seat_client_for_wl_client(token->seat, client); if (seat_client == NULL || !wlr_seat_client_validate_event_serial(seat_client, token->serial)) { wlr_log(WLR_DEBUG, "Rejecting token commit request: " "serial %"PRIu32" was never given to client", token->serial); goto error; } if (token->surface != NULL && token->surface != token->seat->keyboard_state.focused_surface && token->surface != token->seat->pointer_state.focused_surface) { wlr_log(WLR_DEBUG, "Rejecting token commit request: " "surface doesn't have focus"); goto error; } } if (!token_init(token)) { wl_client_post_no_memory(client); return; } wl_signal_emit_mutable(&token->activation->events.new_token, token); xdg_activation_token_v1_send_done(token_resource, token->token); return; error:; // Here we send a generated token, but it's invalid and can't be used to // request activation. char token_str[TOKEN_SIZE] = {0}; if (!generate_token(token_str)) { wl_client_post_no_memory(client); return; } xdg_activation_token_v1_send_done(token_resource, token_str); wlr_xdg_activation_token_v1_destroy(token); } static void token_handle_set_app_id(struct wl_client *client, struct wl_resource *token_resource, const char *app_id) { struct wlr_xdg_activation_token_v1 *token = token_from_resource(token_resource); if (token == NULL) { wl_resource_post_error(token_resource, XDG_ACTIVATION_TOKEN_V1_ERROR_ALREADY_USED, "The activation token has already been used"); return; } free(token->app_id); token->app_id = strdup(app_id); } static void token_handle_seat_destroy(struct wl_listener *listener, void *data) { struct wlr_xdg_activation_token_v1 *token = wl_container_of(listener, token, seat_destroy); wl_list_remove(&token->seat_destroy.link); wl_list_init(&token->seat_destroy.link); token->serial = 0; token->seat = NULL; } static void token_handle_set_serial(struct wl_client *client, struct wl_resource *token_resource, uint32_t serial, struct wl_resource *seat_resource) { struct wlr_xdg_activation_token_v1 *token = token_from_resource(token_resource); if (token == NULL) { wl_resource_post_error(token_resource, XDG_ACTIVATION_TOKEN_V1_ERROR_ALREADY_USED, "The activation token has already been used"); return; } struct wlr_seat_client *seat_client = wlr_seat_client_from_resource(seat_resource); if (seat_client == NULL) { wlr_log(WLR_DEBUG, "Rejecting token set_serial request: seat is inert"); return; } token->seat = seat_client->seat; token->serial = serial; token->seat_destroy.notify = token_handle_seat_destroy; wl_list_remove(&token->seat_destroy.link); wl_signal_add(&token->seat->events.destroy, &token->seat_destroy); } static void token_handle_surface_destroy(struct wl_listener *listener, void *data) { struct wlr_xdg_activation_token_v1 *token = wl_container_of(listener, token, surface_destroy); wl_list_remove(&token->surface_destroy.link); wl_list_init(&token->surface_destroy.link); token->surface = NULL; } static void token_handle_set_surface(struct wl_client *client, struct wl_resource *token_resource, struct wl_resource *surface_resource) { struct wlr_xdg_activation_token_v1 *token = token_from_resource(token_resource); struct wlr_surface *surface = wlr_surface_from_resource(surface_resource); if (token == NULL) { wl_resource_post_error(token_resource, XDG_ACTIVATION_TOKEN_V1_ERROR_ALREADY_USED, "The activation token has already been used"); return; } token->surface = surface; token->surface_destroy.notify = token_handle_surface_destroy; wl_list_remove(&token->surface_destroy.link); wl_signal_add(&surface->events.destroy, &token->surface_destroy); } static const struct xdg_activation_token_v1_interface token_impl = { .destroy = token_handle_destroy, .commit = token_handle_commit, .set_app_id = token_handle_set_app_id, .set_serial = token_handle_set_serial, .set_surface = token_handle_set_surface, }; static const struct xdg_activation_v1_interface activation_impl; static struct wlr_xdg_activation_v1 *activation_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &xdg_activation_v1_interface, &activation_impl)); return wl_resource_get_user_data(resource); } static void activation_handle_destroy(struct wl_client *client, struct wl_resource *activation_resource) { wl_resource_destroy(activation_resource); } static struct wlr_xdg_activation_token_v1 *activation_token_create( struct wlr_xdg_activation_v1 *activation) { struct wlr_xdg_activation_token_v1 *token = calloc(1, sizeof(*token)); if (token == NULL) { return NULL; } wl_list_init(&token->link); wl_list_init(&token->seat_destroy.link); wl_list_init(&token->surface_destroy.link); wl_signal_init(&token->events.destroy); token->activation = activation; return token; } static void activation_handle_get_activation_token(struct wl_client *client, struct wl_resource *activation_resource, uint32_t id) { struct wlr_xdg_activation_v1 *activation = activation_from_resource(activation_resource); struct wlr_xdg_activation_token_v1 *token = activation_token_create(activation); if (token == NULL) { wl_client_post_no_memory(client); return; } uint32_t version = wl_resource_get_version(activation_resource); token->resource = wl_resource_create(client, &xdg_activation_token_v1_interface, version, id); if (token->resource == NULL) { free(token); wl_client_post_no_memory(client); return; } wl_resource_set_implementation(token->resource, &token_impl, token, token_handle_resource_destroy); } static void activation_handle_activate(struct wl_client *client, struct wl_resource *activation_resource, const char *token_str, struct wl_resource *surface_resource) { struct wlr_xdg_activation_v1 *activation = activation_from_resource(activation_resource); struct wlr_surface *surface = wlr_surface_from_resource(surface_resource); struct wlr_xdg_activation_token_v1 *token; bool found = false; wl_list_for_each(token, &activation->tokens, link) { if (strcmp(token_str, token->token) == 0) { found = true; break; } } if (!found) { wlr_log(WLR_DEBUG, "Rejecting activate request: unknown token"); return; } struct wlr_xdg_activation_v1_request_activate_event event = { .activation = activation, .token = token, .surface = surface, }; wl_signal_emit_mutable(&activation->events.request_activate, &event); wlr_xdg_activation_token_v1_destroy(token); } static const struct xdg_activation_v1_interface activation_impl = { .destroy = activation_handle_destroy, .get_activation_token = activation_handle_get_activation_token, .activate = activation_handle_activate, }; static void activation_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id) { struct wlr_xdg_activation_v1 *activation = data; struct wl_resource *resource = wl_resource_create(client, &xdg_activation_v1_interface, version, id); if (resource == NULL) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(resource, &activation_impl, activation, NULL); } static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_xdg_activation_v1 *activation = wl_container_of(listener, activation, display_destroy); wl_signal_emit_mutable(&activation->events.destroy, NULL); struct wlr_xdg_activation_token_v1 *token, *token_tmp; wl_list_for_each_safe(token, token_tmp, &activation->tokens, link) { wlr_xdg_activation_token_v1_destroy(token); } wl_list_remove(&activation->display_destroy.link); wl_global_destroy(activation->global); free(activation); } struct wlr_xdg_activation_v1 *wlr_xdg_activation_v1_create( struct wl_display *display) { struct wlr_xdg_activation_v1 *activation = calloc(1, sizeof(*activation)); if (activation == NULL) { return NULL; } activation->token_timeout_msec = 30000; // 30s wl_list_init(&activation->tokens); wl_signal_init(&activation->events.destroy); wl_signal_init(&activation->events.request_activate); wl_signal_init(&activation->events.new_token); activation->global = wl_global_create(display, &xdg_activation_v1_interface, XDG_ACTIVATION_V1_VERSION, activation, activation_bind); if (activation->global == NULL) { free(activation); return NULL; } activation->display = display; activation->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &activation->display_destroy); return activation; } struct wlr_xdg_activation_token_v1 *wlr_xdg_activation_token_v1_create( struct wlr_xdg_activation_v1 *activation) { struct wlr_xdg_activation_token_v1 *token = activation_token_create(activation); if (token == NULL) { return NULL; } if (!token_init(token)) { wlr_xdg_activation_token_v1_destroy(token); return NULL; } return token; } struct wlr_xdg_activation_token_v1 *wlr_xdg_activation_v1_find_token( struct wlr_xdg_activation_v1 *activation, const char *token_str) { struct wlr_xdg_activation_token_v1 *token; wl_list_for_each(token, &activation->tokens, link) { if (strcmp(token_str, token->token) == 0) { return token; } } return NULL; } const char *wlr_xdg_activation_token_v1_get_name( struct wlr_xdg_activation_token_v1 *token) { return token->token; } struct wlr_xdg_activation_token_v1 *wlr_xdg_activation_v1_add_token( struct wlr_xdg_activation_v1 *activation, const char *token_str) { assert(token_str); struct wlr_xdg_activation_token_v1 *token = activation_token_create(activation); if (token == NULL) { return NULL; } token->token = strdup(token_str); wl_list_insert(&activation->tokens, &token->link); return token; } wlroots-0.17.1/types/wlr_xdg_decoration_v1.c000066400000000000000000000241771454110342200211130ustar00rootroot00000000000000#include #include #include #include #include #include "xdg-decoration-unstable-v1-protocol.h" #define DECORATION_MANAGER_VERSION 1 static const struct zxdg_toplevel_decoration_v1_interface toplevel_decoration_impl; static struct wlr_xdg_toplevel_decoration_v1 *toplevel_decoration_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &zxdg_toplevel_decoration_v1_interface, &toplevel_decoration_impl)); return wl_resource_get_user_data(resource); } static void toplevel_decoration_handle_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static void toplevel_decoration_handle_set_mode(struct wl_client *client, struct wl_resource *resource, enum zxdg_toplevel_decoration_v1_mode mode) { struct wlr_xdg_toplevel_decoration_v1 *decoration = toplevel_decoration_from_resource(resource); decoration->requested_mode = (enum wlr_xdg_toplevel_decoration_v1_mode)mode; wl_signal_emit_mutable(&decoration->events.request_mode, decoration); } static void toplevel_decoration_handle_unset_mode(struct wl_client *client, struct wl_resource *resource) { struct wlr_xdg_toplevel_decoration_v1 *decoration = toplevel_decoration_from_resource(resource); decoration->requested_mode = WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_NONE; wl_signal_emit_mutable(&decoration->events.request_mode, decoration); } static const struct zxdg_toplevel_decoration_v1_interface toplevel_decoration_impl = { .destroy = toplevel_decoration_handle_destroy, .set_mode = toplevel_decoration_handle_set_mode, .unset_mode = toplevel_decoration_handle_unset_mode, }; uint32_t wlr_xdg_toplevel_decoration_v1_set_mode( struct wlr_xdg_toplevel_decoration_v1 *decoration, enum wlr_xdg_toplevel_decoration_v1_mode mode) { assert(mode != WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_NONE); decoration->scheduled_mode = mode; return wlr_xdg_surface_schedule_configure(decoration->toplevel->base); } static void toplevel_decoration_handle_resource_destroy( struct wl_resource *resource) { struct wlr_xdg_toplevel_decoration_v1 *decoration = toplevel_decoration_from_resource(resource); wl_signal_emit_mutable(&decoration->events.destroy, decoration); wl_list_remove(&decoration->surface_commit.link); wl_list_remove(&decoration->surface_destroy.link); wl_list_remove(&decoration->surface_configure.link); wl_list_remove(&decoration->surface_ack_configure.link); struct wlr_xdg_toplevel_decoration_v1_configure *configure, *tmp; wl_list_for_each_safe(configure, tmp, &decoration->configure_list, link) { free(configure); } wl_list_remove(&decoration->link); free(decoration); } static void toplevel_decoration_handle_surface_destroy( struct wl_listener *listener, void *data) { struct wlr_xdg_toplevel_decoration_v1 *decoration = wl_container_of(listener, decoration, surface_destroy); wl_resource_post_error(decoration->resource, ZXDG_TOPLEVEL_DECORATION_V1_ERROR_ORPHANED, "xdg_toplevel destroyed before xdg_toplevel_decoration"); wl_resource_destroy(decoration->resource); } static void toplevel_decoration_handle_surface_configure( struct wl_listener *listener, void *data) { struct wlr_xdg_toplevel_decoration_v1 *decoration = wl_container_of(listener, decoration, surface_configure); struct wlr_xdg_surface_configure *surface_configure = data; if (decoration->pending.mode == decoration->scheduled_mode) { return; } struct wlr_xdg_toplevel_decoration_v1_configure *configure = calloc(1, sizeof(*configure)); if (configure == NULL) { return; } configure->surface_configure = surface_configure; configure->mode = decoration->scheduled_mode; wl_list_insert(decoration->configure_list.prev, &configure->link); zxdg_toplevel_decoration_v1_send_configure(decoration->resource, configure->mode); } static void toplevel_decoration_handle_surface_ack_configure( struct wl_listener *listener, void *data) { struct wlr_xdg_toplevel_decoration_v1 *decoration = wl_container_of(listener, decoration, surface_ack_configure); struct wlr_xdg_surface_configure *surface_configure = data; // First find the ack'ed configure bool found = false; struct wlr_xdg_toplevel_decoration_v1_configure *configure, *tmp; wl_list_for_each(configure, &decoration->configure_list, link) { if (configure->surface_configure == surface_configure) { found = true; break; } } if (!found) { return; } // Then remove old configures from the list wl_list_for_each_safe(configure, tmp, &decoration->configure_list, link) { if (configure->surface_configure == surface_configure) { break; } wl_list_remove(&configure->link); free(configure); } decoration->pending.mode = configure->mode; wl_list_remove(&configure->link); free(configure); } static void toplevel_decoration_handle_surface_commit( struct wl_listener *listener, void *data) { struct wlr_xdg_toplevel_decoration_v1 *decoration = wl_container_of(listener, decoration, surface_commit); struct wlr_xdg_decoration_manager_v1 *manager = decoration->manager; decoration->current = decoration->pending; if (decoration->toplevel->base->added && !decoration->added) { decoration->added = true; wl_signal_emit_mutable(&manager->events.new_toplevel_decoration, decoration); } } static const struct zxdg_decoration_manager_v1_interface decoration_manager_impl; static struct wlr_xdg_decoration_manager_v1 * decoration_manager_from_resource(struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &zxdg_decoration_manager_v1_interface, &decoration_manager_impl)); return wl_resource_get_user_data(resource); } static void decoration_manager_handle_destroy( struct wl_client *client, struct wl_resource *manager_resource) { wl_resource_destroy(manager_resource); } static void decoration_manager_handle_get_toplevel_decoration( struct wl_client *client, struct wl_resource *manager_resource, uint32_t id, struct wl_resource *toplevel_resource) { struct wlr_xdg_decoration_manager_v1 *manager = decoration_manager_from_resource(manager_resource); struct wlr_xdg_toplevel *toplevel = wlr_xdg_toplevel_from_resource(toplevel_resource); if (wlr_surface_has_buffer(toplevel->base->surface)) { wl_resource_post_error(manager_resource, ZXDG_TOPLEVEL_DECORATION_V1_ERROR_UNCONFIGURED_BUFFER, "xdg_toplevel_decoration must not have a buffer at creation"); return; } struct wlr_xdg_toplevel_decoration_v1 *existing; wl_list_for_each(existing, &manager->decorations, link) { if (existing->toplevel == toplevel) { wl_resource_post_error(manager_resource, ZXDG_TOPLEVEL_DECORATION_V1_ERROR_ALREADY_CONSTRUCTED, "xdg_toplevel already has a decoration object"); return; } } struct wlr_xdg_toplevel_decoration_v1 *decoration = calloc(1, sizeof(*decoration)); if (decoration == NULL) { wl_client_post_no_memory(client); return; } decoration->manager = manager; decoration->toplevel = toplevel; uint32_t version = wl_resource_get_version(manager_resource); decoration->resource = wl_resource_create(client, &zxdg_toplevel_decoration_v1_interface, version, id); if (decoration->resource == NULL) { free(decoration); wl_client_post_no_memory(client); return; } wl_resource_set_implementation(decoration->resource, &toplevel_decoration_impl, decoration, toplevel_decoration_handle_resource_destroy); wlr_log(WLR_DEBUG, "new xdg_toplevel_decoration %p (res %p)", decoration, decoration->resource); wl_list_init(&decoration->configure_list); wl_signal_init(&decoration->events.destroy); wl_signal_init(&decoration->events.request_mode); wl_signal_add(&toplevel->base->events.destroy, &decoration->surface_destroy); decoration->surface_destroy.notify = toplevel_decoration_handle_surface_destroy; wl_signal_add(&toplevel->base->events.configure, &decoration->surface_configure); decoration->surface_configure.notify = toplevel_decoration_handle_surface_configure; wl_signal_add(&toplevel->base->events.ack_configure, &decoration->surface_ack_configure); decoration->surface_ack_configure.notify = toplevel_decoration_handle_surface_ack_configure; wl_signal_add(&toplevel->base->surface->events.commit, &decoration->surface_commit); decoration->surface_commit.notify = toplevel_decoration_handle_surface_commit; wl_list_insert(&manager->decorations, &decoration->link); if (toplevel->base->added) { decoration->added = true; wl_signal_emit_mutable(&manager->events.new_toplevel_decoration, decoration); } } static const struct zxdg_decoration_manager_v1_interface decoration_manager_impl = { .destroy = decoration_manager_handle_destroy, .get_toplevel_decoration = decoration_manager_handle_get_toplevel_decoration, }; static void decoration_manager_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id) { struct wlr_xdg_decoration_manager_v1 *manager = data; struct wl_resource *resource = wl_resource_create(client, &zxdg_decoration_manager_v1_interface, version, id); if (resource == NULL) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(resource, &decoration_manager_impl, manager, NULL); } static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_xdg_decoration_manager_v1 *manager = wl_container_of(listener, manager, display_destroy); wl_signal_emit_mutable(&manager->events.destroy, manager); wl_list_remove(&manager->display_destroy.link); wl_global_destroy(manager->global); free(manager); } struct wlr_xdg_decoration_manager_v1 * wlr_xdg_decoration_manager_v1_create(struct wl_display *display) { struct wlr_xdg_decoration_manager_v1 *manager = calloc(1, sizeof(*manager)); if (manager == NULL) { return NULL; } manager->global = wl_global_create(display, &zxdg_decoration_manager_v1_interface, DECORATION_MANAGER_VERSION, manager, decoration_manager_bind); if (manager->global == NULL) { free(manager); return NULL; } wl_list_init(&manager->decorations); wl_signal_init(&manager->events.new_toplevel_decoration); wl_signal_init(&manager->events.destroy); manager->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &manager->display_destroy); return manager; } wlroots-0.17.1/types/wlr_xdg_foreign_registry.c000066400000000000000000000040661454110342200217320ustar00rootroot00000000000000#include #include "util/token.h" #include #include #include bool wlr_xdg_foreign_exported_init( struct wlr_xdg_foreign_exported *exported, struct wlr_xdg_foreign_registry *registry) { do { if (!generate_token(exported->handle)) { return false; } } while (wlr_xdg_foreign_registry_find_by_handle(registry, exported->handle) != NULL); exported->registry = registry; wl_list_insert(®istry->exported_surfaces, &exported->link); wl_signal_init(&exported->events.destroy); return true; } struct wlr_xdg_foreign_exported *wlr_xdg_foreign_registry_find_by_handle( struct wlr_xdg_foreign_registry *registry, const char *handle) { if (handle == NULL || strlen(handle) >= WLR_XDG_FOREIGN_HANDLE_SIZE) { return NULL; } struct wlr_xdg_foreign_exported *exported; wl_list_for_each(exported, ®istry->exported_surfaces, link) { if (strcmp(handle, exported->handle) == 0) { return exported; } } return NULL; } void wlr_xdg_foreign_exported_finish(struct wlr_xdg_foreign_exported *surface) { wl_signal_emit_mutable(&surface->events.destroy, NULL); surface->registry = NULL; wl_list_remove(&surface->link); wl_list_init(&surface->link); } static void foreign_registry_handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_xdg_foreign_registry *registry = wl_container_of(listener, registry, display_destroy); wl_signal_emit_mutable(®istry->events.destroy, NULL); // Implementations are supposed to remove all surfaces assert(wl_list_empty(®istry->exported_surfaces)); free(registry); } struct wlr_xdg_foreign_registry *wlr_xdg_foreign_registry_create( struct wl_display *display) { struct wlr_xdg_foreign_registry *registry = calloc(1, sizeof(*registry)); if (!registry) { return NULL; } registry->display_destroy.notify = foreign_registry_handle_display_destroy; wl_display_add_destroy_listener(display, ®istry->display_destroy); wl_list_init(®istry->exported_surfaces); wl_signal_init(®istry->events.destroy); return registry; } wlroots-0.17.1/types/wlr_xdg_foreign_v1.c000066400000000000000000000313161454110342200204060ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include "xdg-foreign-unstable-v1-protocol.h" #define FOREIGN_V1_VERSION 1 static const struct zxdg_exported_v1_interface xdg_exported_impl; static const struct zxdg_imported_v1_interface xdg_imported_impl; static const struct zxdg_exporter_v1_interface xdg_exporter_impl; static const struct zxdg_importer_v1_interface xdg_importer_impl; static struct wlr_xdg_imported_v1 *xdg_imported_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &zxdg_imported_v1_interface, &xdg_imported_impl)); return wl_resource_get_user_data(resource); } static void xdg_imported_handle_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static struct wlr_xdg_toplevel *verify_is_toplevel(struct wl_resource *resource, struct wlr_surface *surface) { struct wlr_xdg_surface *xdg_surface = wlr_xdg_surface_try_from_wlr_surface(surface); if (xdg_surface == NULL || xdg_surface->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL) { wl_resource_post_error(resource, -1, "surface must be an xdg_toplevel"); return NULL; } return xdg_surface->toplevel; } static void destroy_imported_child(struct wlr_xdg_imported_child_v1 *child) { wl_list_remove(&child->xdg_toplevel_set_parent.link); wl_list_remove(&child->xdg_surface_destroy.link); wl_list_remove(&child->link); free(child); } static void handle_child_xdg_surface_destroy( struct wl_listener *listener, void *data) { struct wlr_xdg_imported_child_v1 *child = wl_container_of(listener, child, xdg_surface_destroy); destroy_imported_child(child); } static void handle_xdg_toplevel_set_parent( struct wl_listener *listener, void *data) { struct wlr_xdg_imported_child_v1 *child = wl_container_of(listener, child, xdg_toplevel_set_parent); destroy_imported_child(child); } static void xdg_imported_handle_set_parent_of(struct wl_client *client, struct wl_resource *resource, struct wl_resource *child_resource) { struct wlr_xdg_imported_v1 *imported = xdg_imported_from_resource(resource); if (imported == NULL) { return; } struct wlr_surface *wlr_surface = imported->exported->surface; struct wlr_surface *wlr_surface_child = wlr_surface_from_resource(child_resource); struct wlr_xdg_toplevel *child_toplevel = verify_is_toplevel(resource, wlr_surface_child); if (!child_toplevel) { return; } struct wlr_xdg_surface *surface = wlr_xdg_surface_try_from_wlr_surface(wlr_surface); if (!surface->surface->mapped) { wlr_xdg_toplevel_set_parent(child_toplevel, NULL); return; } struct wlr_xdg_imported_child_v1 *child; wl_list_for_each(child, &imported->children, link) { if (child->surface == wlr_surface_child) { return; } } child = calloc(1, sizeof(*child)); if (child == NULL) { wl_client_post_no_memory(client); return; } child->surface = wlr_surface_child; child->xdg_surface_destroy.notify = handle_child_xdg_surface_destroy; child->xdg_toplevel_set_parent.notify = handle_xdg_toplevel_set_parent; if (!wlr_xdg_toplevel_set_parent(child_toplevel, surface->toplevel)) { wl_resource_post_error(surface->toplevel->resource, XDG_TOPLEVEL_ERROR_INVALID_PARENT, "a toplevel cannot be a parent of itself or its ancestor"); free(child); return; } wlr_xdg_toplevel_set_parent(child_toplevel, surface->toplevel); wl_signal_add(&child_toplevel->base->events.destroy, &child->xdg_surface_destroy); wl_signal_add(&child_toplevel->events.set_parent, &child->xdg_toplevel_set_parent); wl_list_insert(&imported->children, &child->link); } static const struct zxdg_imported_v1_interface xdg_imported_impl = { .destroy = xdg_imported_handle_destroy, .set_parent_of = xdg_imported_handle_set_parent_of }; static struct wlr_xdg_exported_v1 *xdg_exported_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &zxdg_exported_v1_interface, &xdg_exported_impl)); return wl_resource_get_user_data(resource); } static void xdg_exported_handle_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static const struct zxdg_exported_v1_interface xdg_exported_impl = { .destroy = xdg_exported_handle_destroy }; static void xdg_exporter_handle_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static struct wlr_xdg_foreign_v1 *xdg_foreign_from_exporter_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &zxdg_exporter_v1_interface, &xdg_exporter_impl)); return wl_resource_get_user_data(resource); } static void destroy_imported(struct wlr_xdg_imported_v1 *imported) { imported->exported = NULL; struct wlr_xdg_imported_child_v1 *child, *child_tmp; wl_list_for_each_safe(child, child_tmp, &imported->children, link) { struct wlr_xdg_surface *xdg_child = wlr_xdg_surface_try_from_wlr_surface(child->surface); assert(xdg_child != NULL); wlr_xdg_toplevel_set_parent(xdg_child->toplevel, NULL); } wl_list_remove(&imported->exported_destroyed.link); wl_list_init(&imported->exported_destroyed.link); wl_list_remove(&imported->link); wl_list_init(&imported->link); wl_resource_set_user_data(imported->resource, NULL); free(imported); } static void destroy_exported(struct wlr_xdg_exported_v1 *exported) { wlr_xdg_foreign_exported_finish(&exported->base); wl_list_remove(&exported->xdg_surface_destroy.link); wl_list_remove(&exported->link); wl_resource_set_user_data(exported->resource, NULL); free(exported); } static void xdg_exported_handle_resource_destroy( struct wl_resource *resource) { struct wlr_xdg_exported_v1 *exported = xdg_exported_from_resource(resource); if (exported) { destroy_exported(exported); } } static void handle_xdg_surface_destroy( struct wl_listener *listener, void *data) { struct wlr_xdg_exported_v1 *exported = wl_container_of(listener, exported, xdg_surface_destroy); destroy_exported(exported); } static void xdg_exporter_handle_export(struct wl_client *wl_client, struct wl_resource *client_resource, uint32_t id, struct wl_resource *surface_resource) { struct wlr_xdg_foreign_v1 *foreign = xdg_foreign_from_exporter_resource(client_resource); struct wlr_surface *surface = wlr_surface_from_resource(surface_resource); struct wlr_xdg_toplevel *xdg_toplevel = verify_is_toplevel(client_resource, surface); if (!xdg_toplevel) { return; } struct wlr_xdg_exported_v1 *exported = calloc(1, sizeof(*exported)); if (exported == NULL) { wl_client_post_no_memory(wl_client); return; } if (!wlr_xdg_foreign_exported_init(&exported->base, foreign->registry)) { wl_client_post_no_memory(wl_client); free(exported); return; } exported->base.surface = surface; exported->resource = wl_resource_create(wl_client, &zxdg_exported_v1_interface, wl_resource_get_version(client_resource), id); if (exported->resource == NULL) { wlr_xdg_foreign_exported_finish(&exported->base); wl_client_post_no_memory(wl_client); free(exported); return; } wl_resource_set_implementation(exported->resource, &xdg_exported_impl, exported, xdg_exported_handle_resource_destroy); wl_list_insert(&foreign->exporter.objects, &exported->link); zxdg_exported_v1_send_handle(exported->resource, exported->base.handle); exported->xdg_surface_destroy.notify = handle_xdg_surface_destroy; wl_signal_add(&xdg_toplevel->base->events.destroy, &exported->xdg_surface_destroy); } static const struct zxdg_exporter_v1_interface xdg_exporter_impl = { .destroy = xdg_exporter_handle_destroy, .export = xdg_exporter_handle_export }; static void xdg_exporter_bind(struct wl_client *wl_client, void *data, uint32_t version, uint32_t id) { struct wlr_xdg_foreign_v1 *foreign = data; struct wl_resource *exporter_resource = wl_resource_create(wl_client, &zxdg_exporter_v1_interface, version, id); if (exporter_resource == NULL) { wl_client_post_no_memory(wl_client); return; } wl_resource_set_implementation(exporter_resource, &xdg_exporter_impl, foreign, NULL); } static struct wlr_xdg_foreign_v1 *xdg_foreign_from_importer_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &zxdg_importer_v1_interface, &xdg_importer_impl)); return wl_resource_get_user_data(resource); } static void xdg_importer_handle_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static void xdg_imported_handle_resource_destroy( struct wl_resource *resource) { struct wlr_xdg_imported_v1 *imported = xdg_imported_from_resource(resource); if (!imported) { return; } destroy_imported(imported); } static void xdg_imported_handle_exported_destroy(struct wl_listener *listener, void *data) { struct wlr_xdg_imported_v1 *imported = wl_container_of(listener, imported, exported_destroyed); zxdg_imported_v1_send_destroyed(imported->resource); destroy_imported(imported); } static void xdg_importer_handle_import(struct wl_client *wl_client, struct wl_resource *client_resource, uint32_t id, const char *handle) { struct wlr_xdg_foreign_v1 *foreign = xdg_foreign_from_importer_resource(client_resource); struct wlr_xdg_imported_v1 *imported = calloc(1, sizeof(*imported)); if (imported == NULL) { wl_client_post_no_memory(wl_client); return; } imported->exported = wlr_xdg_foreign_registry_find_by_handle( foreign->registry, handle); imported->resource = wl_resource_create(wl_client, &zxdg_imported_v1_interface, wl_resource_get_version(client_resource), id); if (imported->resource == NULL) { wl_client_post_no_memory(wl_client); free(imported); return; } wl_resource_set_implementation(imported->resource, &xdg_imported_impl, imported, xdg_imported_handle_resource_destroy); if (imported->exported == NULL) { wl_resource_set_user_data(imported->resource, NULL); zxdg_imported_v1_send_destroyed(imported->resource); free(imported); return; } wl_list_init(&imported->children); wl_list_insert(&foreign->importer.objects, &imported->link); imported->exported_destroyed.notify = xdg_imported_handle_exported_destroy; wl_signal_add(&imported->exported->events.destroy, &imported->exported_destroyed); } static const struct zxdg_importer_v1_interface xdg_importer_impl = { .destroy = xdg_importer_handle_destroy, .import = xdg_importer_handle_import }; static void xdg_importer_bind(struct wl_client *wl_client, void *data, uint32_t version, uint32_t id) { struct wlr_xdg_foreign_v1 *foreign = data; struct wl_resource *importer_resource = wl_resource_create(wl_client, &zxdg_importer_v1_interface, version, id); if (importer_resource == NULL) { wl_client_post_no_memory(wl_client); return; } wl_resource_set_implementation(importer_resource, &xdg_importer_impl, foreign, NULL); } static void xdg_foreign_destroy(struct wlr_xdg_foreign_v1 *foreign) { if (!foreign) { return; } wl_signal_emit_mutable(&foreign->events.destroy, NULL); wl_list_remove(&foreign->foreign_registry_destroy.link); wl_list_remove(&foreign->display_destroy.link); wl_global_destroy(foreign->exporter.global); wl_global_destroy(foreign->importer.global); free(foreign); } static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_xdg_foreign_v1 *foreign = wl_container_of(listener, foreign, display_destroy); xdg_foreign_destroy(foreign); } static void handle_foreign_registry_destroy(struct wl_listener *listener, void *data) { struct wlr_xdg_foreign_v1 *foreign = wl_container_of(listener, foreign, foreign_registry_destroy); xdg_foreign_destroy(foreign); } struct wlr_xdg_foreign_v1 *wlr_xdg_foreign_v1_create( struct wl_display *display, struct wlr_xdg_foreign_registry *registry) { struct wlr_xdg_foreign_v1 *foreign = calloc(1, sizeof(*foreign)); if (!foreign) { return NULL; } foreign->exporter.global = wl_global_create(display, &zxdg_exporter_v1_interface, FOREIGN_V1_VERSION, foreign, xdg_exporter_bind); if (!foreign->exporter.global) { free(foreign); return NULL; } foreign->importer.global = wl_global_create(display, &zxdg_importer_v1_interface, FOREIGN_V1_VERSION, foreign, xdg_importer_bind); if (!foreign->importer.global) { wl_global_destroy(foreign->exporter.global); free(foreign); return NULL; } foreign->registry = registry; wl_signal_init(&foreign->events.destroy); wl_list_init(&foreign->exporter.objects); wl_list_init(&foreign->importer.objects); foreign->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &foreign->display_destroy); foreign->foreign_registry_destroy.notify = handle_foreign_registry_destroy; wl_signal_add(®istry->events.destroy, &foreign->foreign_registry_destroy); return foreign; } wlroots-0.17.1/types/wlr_xdg_foreign_v2.c000066400000000000000000000315351454110342200204120ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include "xdg-foreign-unstable-v2-protocol.h" #define FOREIGN_V2_VERSION 1 static const struct zxdg_exported_v2_interface xdg_exported_impl; static const struct zxdg_imported_v2_interface xdg_imported_impl; static const struct zxdg_exporter_v2_interface xdg_exporter_impl; static const struct zxdg_importer_v2_interface xdg_importer_impl; static struct wlr_xdg_imported_v2 *xdg_imported_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &zxdg_imported_v2_interface, &xdg_imported_impl)); return wl_resource_get_user_data(resource); } static void xdg_imported_handle_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static struct wlr_xdg_toplevel *verify_is_toplevel(struct wl_resource *resource, struct wlr_surface *surface) { // Note: the error codes are the same for zxdg_exporter_v2 and // zxdg_importer_v2 struct wlr_xdg_surface *xdg_surface = wlr_xdg_surface_try_from_wlr_surface(surface); if (xdg_surface == NULL || xdg_surface->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL) { wl_resource_post_error(resource, ZXDG_EXPORTER_V2_ERROR_INVALID_SURFACE, "surface must be an xdg_toplevel"); return NULL; } return xdg_surface->toplevel; } static void destroy_imported_child(struct wlr_xdg_imported_child_v2 *child) { wl_list_remove(&child->xdg_toplevel_set_parent.link); wl_list_remove(&child->xdg_surface_destroy.link); wl_list_remove(&child->link); free(child); } static void handle_child_xdg_surface_destroy( struct wl_listener *listener, void *data) { struct wlr_xdg_imported_child_v2 *child = wl_container_of(listener, child, xdg_surface_destroy); destroy_imported_child(child); } static void handle_xdg_toplevel_set_parent( struct wl_listener *listener, void *data) { struct wlr_xdg_imported_child_v2 *child = wl_container_of(listener, child, xdg_toplevel_set_parent); destroy_imported_child(child); } static void xdg_imported_handle_set_parent_of(struct wl_client *client, struct wl_resource *resource, struct wl_resource *child_resource) { struct wlr_xdg_imported_v2 *imported = xdg_imported_from_resource(resource); if (imported == NULL) { return; } struct wlr_surface *wlr_surface = imported->exported->surface; struct wlr_surface *wlr_surface_child = wlr_surface_from_resource(child_resource); struct wlr_xdg_surface *surface = wlr_xdg_surface_try_from_wlr_surface(wlr_surface); struct wlr_xdg_toplevel *child_toplevel = verify_is_toplevel(resource, wlr_surface_child); if (!child_toplevel) { return; } if (!surface->surface->mapped) { wlr_xdg_toplevel_set_parent(child_toplevel, NULL); return; } struct wlr_xdg_imported_child_v2 *child; wl_list_for_each(child, &imported->children, link) { if (child->surface == wlr_surface_child) { return; } } child = calloc(1, sizeof(*child)); if (child == NULL) { wl_client_post_no_memory(client); return; } child->surface = wlr_surface_child; child->xdg_surface_destroy.notify = handle_child_xdg_surface_destroy; child->xdg_toplevel_set_parent.notify = handle_xdg_toplevel_set_parent; if (!wlr_xdg_toplevel_set_parent(child_toplevel, surface->toplevel)) { wl_resource_post_error(surface->toplevel->resource, XDG_TOPLEVEL_ERROR_INVALID_PARENT, "a toplevel cannot be a parent of itself or its ancestor"); free(child); return; } wlr_xdg_toplevel_set_parent(child_toplevel, surface->toplevel); wl_signal_add(&child_toplevel->base->events.destroy, &child->xdg_surface_destroy); wl_signal_add(&child_toplevel->events.set_parent, &child->xdg_toplevel_set_parent); wl_list_insert(&imported->children, &child->link); } static const struct zxdg_imported_v2_interface xdg_imported_impl = { .destroy = xdg_imported_handle_destroy, .set_parent_of = xdg_imported_handle_set_parent_of }; static struct wlr_xdg_exported_v2 *xdg_exported_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &zxdg_exported_v2_interface, &xdg_exported_impl)); return wl_resource_get_user_data(resource); } static void xdg_exported_handle_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static const struct zxdg_exported_v2_interface xdg_exported_impl = { .destroy = xdg_exported_handle_destroy }; static void xdg_exporter_handle_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static struct wlr_xdg_foreign_v2 *xdg_foreign_from_exporter_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &zxdg_exporter_v2_interface, &xdg_exporter_impl)); return wl_resource_get_user_data(resource); } static void destroy_imported(struct wlr_xdg_imported_v2 *imported) { imported->exported = NULL; struct wlr_xdg_imported_child_v2 *child, *child_tmp; wl_list_for_each_safe(child, child_tmp, &imported->children, link) { struct wlr_xdg_surface *xdg_child = wlr_xdg_surface_try_from_wlr_surface(child->surface); assert(xdg_child != NULL); wlr_xdg_toplevel_set_parent(xdg_child->toplevel, NULL); } wl_list_remove(&imported->exported_destroyed.link); wl_list_init(&imported->exported_destroyed.link); wl_list_remove(&imported->link); wl_list_init(&imported->link); wl_resource_set_user_data(imported->resource, NULL); free(imported); } static void destroy_exported(struct wlr_xdg_exported_v2 *exported) { wlr_xdg_foreign_exported_finish(&exported->base); wl_list_remove(&exported->xdg_surface_destroy.link); wl_list_remove(&exported->link); wl_resource_set_user_data(exported->resource, NULL); free(exported); } static void xdg_exported_handle_resource_destroy( struct wl_resource *resource) { struct wlr_xdg_exported_v2 *exported = xdg_exported_from_resource(resource); if (exported) { destroy_exported(exported); } } static void handle_xdg_surface_destroy( struct wl_listener *listener, void *data) { struct wlr_xdg_exported_v2 *exported = wl_container_of(listener, exported, xdg_surface_destroy); destroy_exported(exported); } static void xdg_exporter_handle_export(struct wl_client *wl_client, struct wl_resource *client_resource, uint32_t id, struct wl_resource *surface_resource) { struct wlr_xdg_foreign_v2 *foreign = xdg_foreign_from_exporter_resource(client_resource); struct wlr_surface *surface = wlr_surface_from_resource(surface_resource); struct wlr_xdg_toplevel *xdg_toplevel = verify_is_toplevel(client_resource, surface); if (!xdg_toplevel) { return; } struct wlr_xdg_exported_v2 *exported = calloc(1, sizeof(*exported)); if (exported == NULL) { wl_client_post_no_memory(wl_client); return; } if (!wlr_xdg_foreign_exported_init(&exported->base, foreign->registry)) { wl_client_post_no_memory(wl_client); free(exported); return; } exported->base.surface = surface; exported->resource = wl_resource_create(wl_client, &zxdg_exported_v2_interface, wl_resource_get_version(client_resource), id); if (exported->resource == NULL) { wlr_xdg_foreign_exported_finish(&exported->base); wl_client_post_no_memory(wl_client); free(exported); return; } wl_resource_set_implementation(exported->resource, &xdg_exported_impl, exported, xdg_exported_handle_resource_destroy); wl_list_insert(&foreign->exporter.objects, &exported->link); zxdg_exported_v2_send_handle(exported->resource, exported->base.handle); exported->xdg_surface_destroy.notify = handle_xdg_surface_destroy; wl_signal_add(&xdg_toplevel->base->events.destroy, &exported->xdg_surface_destroy); } static const struct zxdg_exporter_v2_interface xdg_exporter_impl = { .destroy = xdg_exporter_handle_destroy, .export_toplevel = xdg_exporter_handle_export }; static void xdg_exporter_bind(struct wl_client *wl_client, void *data, uint32_t version, uint32_t id) { struct wlr_xdg_foreign_v2 *foreign = data; struct wl_resource *exporter_resource = wl_resource_create(wl_client, &zxdg_exporter_v2_interface, version, id); if (exporter_resource == NULL) { wl_client_post_no_memory(wl_client); return; } wl_resource_set_implementation(exporter_resource, &xdg_exporter_impl, foreign, NULL); } static struct wlr_xdg_foreign_v2 *xdg_foreign_from_importer_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &zxdg_importer_v2_interface, &xdg_importer_impl)); return wl_resource_get_user_data(resource); } static void xdg_importer_handle_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static void xdg_imported_handle_resource_destroy( struct wl_resource *resource) { struct wlr_xdg_imported_v2 *imported = xdg_imported_from_resource(resource); if (!imported) { return; } destroy_imported(imported); } static void xdg_imported_handle_exported_destroy(struct wl_listener *listener, void *data) { struct wlr_xdg_imported_v2 *imported = wl_container_of(listener, imported, exported_destroyed); zxdg_imported_v2_send_destroyed(imported->resource); destroy_imported(imported); } static void xdg_importer_handle_import(struct wl_client *wl_client, struct wl_resource *client_resource, uint32_t id, const char *handle) { struct wlr_xdg_foreign_v2 *foreign = xdg_foreign_from_importer_resource(client_resource); struct wlr_xdg_imported_v2 *imported = calloc(1, sizeof(*imported)); if (imported == NULL) { wl_client_post_no_memory(wl_client); return; } imported->exported = wlr_xdg_foreign_registry_find_by_handle( foreign->registry, handle); imported->resource = wl_resource_create(wl_client, &zxdg_imported_v2_interface, wl_resource_get_version(client_resource), id); if (imported->resource == NULL) { wl_client_post_no_memory(wl_client); free(imported); return; } wl_resource_set_implementation(imported->resource, &xdg_imported_impl, imported, xdg_imported_handle_resource_destroy); if (imported->exported == NULL) { wl_resource_set_user_data(imported->resource, NULL); zxdg_imported_v2_send_destroyed(imported->resource); free(imported); return; } wl_list_init(&imported->children); wl_list_insert(&foreign->importer.objects, &imported->link); imported->exported_destroyed.notify = xdg_imported_handle_exported_destroy; wl_signal_add(&imported->exported->events.destroy, &imported->exported_destroyed); } static const struct zxdg_importer_v2_interface xdg_importer_impl = { .destroy = xdg_importer_handle_destroy, .import_toplevel = xdg_importer_handle_import }; static void xdg_importer_bind(struct wl_client *wl_client, void *data, uint32_t version, uint32_t id) { struct wlr_xdg_foreign_v2 *foreign = data; struct wl_resource *importer_resource = wl_resource_create(wl_client, &zxdg_importer_v2_interface, version, id); if (importer_resource == NULL) { wl_client_post_no_memory(wl_client); return; } wl_resource_set_implementation(importer_resource, &xdg_importer_impl, foreign, NULL); } static void xdg_foreign_destroy(struct wlr_xdg_foreign_v2 *foreign) { if (!foreign) { return; } wl_signal_emit_mutable(&foreign->events.destroy, NULL); wl_list_remove(&foreign->foreign_registry_destroy.link); wl_list_remove(&foreign->display_destroy.link); wl_global_destroy(foreign->exporter.global); wl_global_destroy(foreign->importer.global); free(foreign); } static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_xdg_foreign_v2 *foreign = wl_container_of(listener, foreign, display_destroy); xdg_foreign_destroy(foreign); } static void handle_foreign_registry_destroy(struct wl_listener *listener, void *data) { struct wlr_xdg_foreign_v2 *foreign = wl_container_of(listener, foreign, foreign_registry_destroy); xdg_foreign_destroy(foreign); } struct wlr_xdg_foreign_v2 *wlr_xdg_foreign_v2_create( struct wl_display *display, struct wlr_xdg_foreign_registry *registry) { struct wlr_xdg_foreign_v2 *foreign = calloc(1, sizeof(*foreign)); if (!foreign) { return NULL; } foreign->exporter.global = wl_global_create(display, &zxdg_exporter_v2_interface, FOREIGN_V2_VERSION, foreign, xdg_exporter_bind); if (!foreign->exporter.global) { free(foreign); return NULL; } foreign->importer.global = wl_global_create(display, &zxdg_importer_v2_interface, FOREIGN_V2_VERSION, foreign, xdg_importer_bind); if (!foreign->importer.global) { wl_global_destroy(foreign->exporter.global); free(foreign); return NULL; } foreign->registry = registry; wl_signal_init(&foreign->events.destroy); wl_list_init(&foreign->exporter.objects); wl_list_init(&foreign->importer.objects); foreign->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &foreign->display_destroy); foreign->foreign_registry_destroy.notify = handle_foreign_registry_destroy; wl_signal_add(®istry->events.destroy, &foreign->foreign_registry_destroy); return foreign; } wlroots-0.17.1/types/wlr_xdg_output_v1.c000066400000000000000000000223661454110342200203220ustar00rootroot00000000000000#include #include #include #include #include #include #include #include "xdg-output-unstable-v1-protocol.h" #define OUTPUT_MANAGER_VERSION 3 #define OUTPUT_DONE_DEPRECATED_SINCE_VERSION 3 #define OUTPUT_DESCRIPTION_MUTABLE_SINCE_VERSION 3 static void output_handle_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static const struct zxdg_output_v1_interface output_implementation = { .destroy = output_handle_destroy, }; static void output_handle_resource_destroy(struct wl_resource *resource) { wl_list_remove(wl_resource_get_link(resource)); } static void output_send_details(struct wlr_xdg_output_v1 *xdg_output, struct wl_resource *resource) { zxdg_output_v1_send_logical_position(resource, xdg_output->x, xdg_output->y); zxdg_output_v1_send_logical_size(resource, xdg_output->width, xdg_output->height); if (wl_resource_get_version(resource) < OUTPUT_DONE_DEPRECATED_SINCE_VERSION) { zxdg_output_v1_send_done(resource); } } static void output_update(struct wlr_xdg_output_v1 *xdg_output) { struct wlr_output_layout_output *layout_output = xdg_output->layout_output; bool updated = false; if (layout_output->x != xdg_output->x || layout_output->y != xdg_output->y) { xdg_output->x = layout_output->x; xdg_output->y = layout_output->y; updated = true; } int width, height; wlr_output_effective_resolution(layout_output->output, &width, &height); if (xdg_output->width != width || xdg_output->height != height) { xdg_output->width = width; xdg_output->height = height; updated = true; } if (updated) { struct wl_resource *resource; wl_resource_for_each(resource, &xdg_output->resources) { output_send_details(xdg_output, resource); } wlr_output_schedule_done(xdg_output->layout_output->output); } } static void output_destroy(struct wlr_xdg_output_v1 *output) { struct wl_resource *resource, *tmp; wl_resource_for_each_safe(resource, tmp, &output->resources) { wl_list_remove(wl_resource_get_link(resource)); wl_list_init(wl_resource_get_link(resource)); } wl_list_remove(&output->destroy.link); wl_list_remove(&output->description.link); wl_list_remove(&output->link); free(output); } static void output_manager_handle_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static const struct zxdg_output_manager_v1_interface output_manager_implementation; static void output_manager_handle_get_xdg_output(struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *output_resource) { assert(wl_resource_instance_of(resource, &zxdg_output_manager_v1_interface, &output_manager_implementation)); struct wlr_xdg_output_manager_v1 *manager = wl_resource_get_user_data(resource); struct wlr_output_layout *layout = manager->layout; struct wlr_output *output = wlr_output_from_resource(output_resource); struct wl_resource *xdg_output_resource = wl_resource_create(client, &zxdg_output_v1_interface, wl_resource_get_version(resource), id); if (!xdg_output_resource) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(xdg_output_resource, &output_implementation, NULL, output_handle_resource_destroy); if (output == NULL) { wl_list_init(wl_resource_get_link(xdg_output_resource)); return; } struct wlr_output_layout_output *layout_output = wlr_output_layout_get(layout, output); assert(layout_output); struct wlr_xdg_output_v1 *_xdg_output, *xdg_output = NULL; wl_list_for_each(_xdg_output, &manager->outputs, link) { if (_xdg_output->layout_output == layout_output) { xdg_output = _xdg_output; break; } } assert(xdg_output); wl_list_insert(&xdg_output->resources, wl_resource_get_link(xdg_output_resource)); // Name and description should only be sent once per output uint32_t xdg_version = wl_resource_get_version(xdg_output_resource); if (xdg_version >= ZXDG_OUTPUT_V1_NAME_SINCE_VERSION) { zxdg_output_v1_send_name(xdg_output_resource, output->name); } if (xdg_version >= ZXDG_OUTPUT_V1_DESCRIPTION_SINCE_VERSION && output->description != NULL) { zxdg_output_v1_send_description(xdg_output_resource, output->description); } output_send_details(xdg_output, xdg_output_resource); uint32_t wl_version = wl_resource_get_version(output_resource); if (wl_version >= WL_OUTPUT_DONE_SINCE_VERSION && xdg_version >= OUTPUT_DONE_DEPRECATED_SINCE_VERSION) { wl_output_send_done(output_resource); } } static const struct zxdg_output_manager_v1_interface output_manager_implementation = { .destroy = output_manager_handle_destroy, .get_xdg_output = output_manager_handle_get_xdg_output, }; static void output_manager_bind(struct wl_client *wl_client, void *data, uint32_t version, uint32_t id) { struct wlr_xdg_output_manager_v1 *manager = data; struct wl_resource *resource = wl_resource_create(wl_client, &zxdg_output_manager_v1_interface, version, id); if (resource == NULL) { wl_client_post_no_memory(wl_client); return; } wl_resource_set_implementation(resource, &output_manager_implementation, manager, NULL); } static void handle_output_destroy(struct wl_listener *listener, void *data) { struct wlr_xdg_output_v1 *output = wl_container_of(listener, output, destroy); output_destroy(output); } static void handle_output_description(struct wl_listener *listener, void *data) { struct wlr_xdg_output_v1 *xdg_output = wl_container_of(listener, xdg_output, description); struct wlr_output *output = xdg_output->layout_output->output; if (output->description == NULL) { return; } struct wl_resource *resource; wl_resource_for_each(resource, &xdg_output->resources) { if (wl_resource_get_version(resource) >= OUTPUT_DESCRIPTION_MUTABLE_SINCE_VERSION) { zxdg_output_v1_send_description(resource, output->description); } } } static void add_output(struct wlr_xdg_output_manager_v1 *manager, struct wlr_output_layout_output *layout_output) { struct wlr_xdg_output_v1 *output = calloc(1, sizeof(*output)); if (output == NULL) { return; } wl_list_init(&output->resources); output->manager = manager; output->layout_output = layout_output; output->destroy.notify = handle_output_destroy; wl_signal_add(&layout_output->events.destroy, &output->destroy); output->description.notify = handle_output_description; wl_signal_add(&layout_output->output->events.description, &output->description); wl_list_insert(&manager->outputs, &output->link); output_update(output); } static void output_manager_send_details( struct wlr_xdg_output_manager_v1 *manager) { struct wlr_xdg_output_v1 *output; wl_list_for_each(output, &manager->outputs, link) { output_update(output); } } static void handle_layout_add(struct wl_listener *listener, void *data) { struct wlr_xdg_output_manager_v1 *manager = wl_container_of(listener, manager, layout_add); struct wlr_output_layout_output *layout_output = data; add_output(manager, layout_output); } static void handle_layout_change(struct wl_listener *listener, void *data) { struct wlr_xdg_output_manager_v1 *manager = wl_container_of(listener, manager, layout_change); output_manager_send_details(manager); } static void manager_destroy(struct wlr_xdg_output_manager_v1 *manager) { struct wlr_xdg_output_v1 *output, *tmp; wl_list_for_each_safe(output, tmp, &manager->outputs, link) { output_destroy(output); } wl_signal_emit_mutable(&manager->events.destroy, manager); wl_list_remove(&manager->display_destroy.link); wl_list_remove(&manager->layout_add.link); wl_list_remove(&manager->layout_change.link); wl_list_remove(&manager->layout_destroy.link); free(manager); } static void handle_layout_destroy(struct wl_listener *listener, void *data) { struct wlr_xdg_output_manager_v1 *manager = wl_container_of(listener, manager, layout_destroy); manager_destroy(manager); } static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_xdg_output_manager_v1 *manager = wl_container_of(listener, manager, display_destroy); manager_destroy(manager); } struct wlr_xdg_output_manager_v1 *wlr_xdg_output_manager_v1_create( struct wl_display *display, struct wlr_output_layout *layout) { struct wlr_xdg_output_manager_v1 *manager = calloc(1, sizeof(*manager)); if (manager == NULL) { return NULL; } manager->layout = layout; manager->global = wl_global_create(display, &zxdg_output_manager_v1_interface, OUTPUT_MANAGER_VERSION, manager, output_manager_bind); if (!manager->global) { free(manager); return NULL; } wl_list_init(&manager->outputs); struct wlr_output_layout_output *layout_output; wl_list_for_each(layout_output, &layout->outputs, link) { add_output(manager, layout_output); } wl_signal_init(&manager->events.destroy); manager->layout_add.notify = handle_layout_add; wl_signal_add(&layout->events.add, &manager->layout_add); manager->layout_change.notify = handle_layout_change; wl_signal_add(&layout->events.change, &manager->layout_change); manager->layout_destroy.notify = handle_layout_destroy; wl_signal_add(&layout->events.destroy, &manager->layout_destroy); manager->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &manager->display_destroy); return manager; } wlroots-0.17.1/types/xdg_shell/000077500000000000000000000000001454110342200164225ustar00rootroot00000000000000wlroots-0.17.1/types/xdg_shell/wlr_xdg_popup.c000066400000000000000000000367661454110342200215010ustar00rootroot00000000000000#include #include #include #include "types/wlr_xdg_shell.h" void handle_xdg_popup_ack_configure( struct wlr_xdg_popup *popup, struct wlr_xdg_popup_configure *configure) { popup->pending.geometry = configure->geometry; popup->pending.reactive = configure->rules.reactive; } struct wlr_xdg_popup_configure *send_xdg_popup_configure( struct wlr_xdg_popup *popup) { struct wlr_xdg_popup_configure *configure = calloc(1, sizeof(*configure)); if (configure == NULL) { wl_resource_post_no_memory(popup->resource); return NULL; } *configure = popup->scheduled; uint32_t version = wl_resource_get_version(popup->resource); if ((configure->fields & WLR_XDG_POPUP_CONFIGURE_REPOSITION_TOKEN) && version >= XDG_POPUP_REPOSITIONED_SINCE_VERSION) { xdg_popup_send_repositioned(popup->resource, configure->reposition_token); } struct wlr_box *geometry = &configure->geometry; xdg_popup_send_configure(popup->resource, geometry->x, geometry->y, geometry->width, geometry->height); popup->scheduled.fields = 0; return configure; } static void xdg_popup_grab_end(struct wlr_xdg_popup_grab *popup_grab) { struct wlr_xdg_popup *popup, *tmp; wl_list_for_each_safe(popup, tmp, &popup_grab->popups, grab_link) { xdg_popup_send_popup_done(popup->resource); } wlr_seat_pointer_end_grab(popup_grab->seat); wlr_seat_keyboard_end_grab(popup_grab->seat); wlr_seat_touch_end_grab(popup_grab->seat); } static void xdg_pointer_grab_enter(struct wlr_seat_pointer_grab *grab, struct wlr_surface *surface, double sx, double sy) { struct wlr_xdg_popup_grab *popup_grab = grab->data; if (wl_resource_get_client(surface->resource) == popup_grab->client) { wlr_seat_pointer_enter(grab->seat, surface, sx, sy); } else { wlr_seat_pointer_clear_focus(grab->seat); } } static void xdg_pointer_grab_clear_focus(struct wlr_seat_pointer_grab *grab) { wlr_seat_pointer_clear_focus(grab->seat); } static void xdg_pointer_grab_motion(struct wlr_seat_pointer_grab *grab, uint32_t time, double sx, double sy) { wlr_seat_pointer_send_motion(grab->seat, time, sx, sy); } static uint32_t xdg_pointer_grab_button(struct wlr_seat_pointer_grab *grab, uint32_t time, uint32_t button, uint32_t state) { uint32_t serial = wlr_seat_pointer_send_button(grab->seat, time, button, state); if (serial) { return serial; } else { xdg_popup_grab_end(grab->data); return 0; } } static void xdg_pointer_grab_axis(struct wlr_seat_pointer_grab *grab, uint32_t time, enum wlr_axis_orientation orientation, double value, int32_t value_discrete, enum wlr_axis_source source) { wlr_seat_pointer_send_axis(grab->seat, time, orientation, value, value_discrete, source); } static void xdg_pointer_grab_frame(struct wlr_seat_pointer_grab *grab) { wlr_seat_pointer_send_frame(grab->seat); } static void xdg_pointer_grab_cancel(struct wlr_seat_pointer_grab *grab) { xdg_popup_grab_end(grab->data); } static const struct wlr_pointer_grab_interface xdg_pointer_grab_impl = { .enter = xdg_pointer_grab_enter, .clear_focus = xdg_pointer_grab_clear_focus, .motion = xdg_pointer_grab_motion, .button = xdg_pointer_grab_button, .cancel = xdg_pointer_grab_cancel, .axis = xdg_pointer_grab_axis, .frame = xdg_pointer_grab_frame, }; static void xdg_keyboard_grab_enter(struct wlr_seat_keyboard_grab *grab, struct wlr_surface *surface, const uint32_t keycodes[], size_t num_keycodes, const struct wlr_keyboard_modifiers *modifiers) { // keyboard focus should remain on the popup } static void xdg_keyboard_grab_clear_focus(struct wlr_seat_keyboard_grab *grab) { // keyboard focus should remain on the popup } static void xdg_keyboard_grab_key(struct wlr_seat_keyboard_grab *grab, uint32_t time, uint32_t key, uint32_t state) { wlr_seat_keyboard_send_key(grab->seat, time, key, state); } static void xdg_keyboard_grab_modifiers(struct wlr_seat_keyboard_grab *grab, const struct wlr_keyboard_modifiers *modifiers) { wlr_seat_keyboard_send_modifiers(grab->seat, modifiers); } static void xdg_keyboard_grab_cancel(struct wlr_seat_keyboard_grab *grab) { wlr_seat_pointer_end_grab(grab->seat); } static const struct wlr_keyboard_grab_interface xdg_keyboard_grab_impl = { .enter = xdg_keyboard_grab_enter, .clear_focus = xdg_keyboard_grab_clear_focus, .key = xdg_keyboard_grab_key, .modifiers = xdg_keyboard_grab_modifiers, .cancel = xdg_keyboard_grab_cancel, }; static uint32_t xdg_touch_grab_down(struct wlr_seat_touch_grab *grab, uint32_t time, struct wlr_touch_point *point) { struct wlr_xdg_popup_grab *popup_grab = grab->data; if (wl_resource_get_client(point->surface->resource) != popup_grab->client) { xdg_popup_grab_end(grab->data); return 0; } return wlr_seat_touch_send_down(grab->seat, point->surface, time, point->touch_id, point->sx, point->sy); } static void xdg_touch_grab_up(struct wlr_seat_touch_grab *grab, uint32_t time, struct wlr_touch_point *point) { wlr_seat_touch_send_up(grab->seat, time, point->touch_id); } static void xdg_touch_grab_motion(struct wlr_seat_touch_grab *grab, uint32_t time, struct wlr_touch_point *point) { wlr_seat_touch_send_motion(grab->seat, time, point->touch_id, point->sx, point->sy); } static void xdg_touch_grab_enter(struct wlr_seat_touch_grab *grab, uint32_t time, struct wlr_touch_point *point) { } static void xdg_touch_grab_frame(struct wlr_seat_touch_grab *grab) { wlr_seat_touch_send_frame(grab->seat); } static void xdg_touch_grab_cancel(struct wlr_seat_touch_grab *grab) { wlr_seat_touch_end_grab(grab->seat); } static const struct wlr_touch_grab_interface xdg_touch_grab_impl = { .down = xdg_touch_grab_down, .up = xdg_touch_grab_up, .motion = xdg_touch_grab_motion, .enter = xdg_touch_grab_enter, .frame = xdg_touch_grab_frame, .cancel = xdg_touch_grab_cancel }; static void destroy_xdg_popup_grab(struct wlr_xdg_popup_grab *xdg_grab) { if (xdg_grab == NULL) { return; } wl_list_remove(&xdg_grab->seat_destroy.link); struct wlr_xdg_popup *popup, *tmp; wl_list_for_each_safe(popup, tmp, &xdg_grab->popups, grab_link) { wlr_xdg_popup_destroy(popup); } wl_list_remove(&xdg_grab->link); free(xdg_grab); } static void xdg_popup_grab_handle_seat_destroy( struct wl_listener *listener, void *data) { struct wlr_xdg_popup_grab *xdg_grab = wl_container_of(listener, xdg_grab, seat_destroy); destroy_xdg_popup_grab(xdg_grab); } static struct wlr_xdg_popup_grab *get_xdg_shell_popup_grab_from_seat( struct wlr_xdg_shell *shell, struct wlr_seat *seat) { struct wlr_xdg_popup_grab *xdg_grab; wl_list_for_each(xdg_grab, &shell->popup_grabs, link) { if (xdg_grab->seat == seat) { return xdg_grab; } } xdg_grab = calloc(1, sizeof(*xdg_grab)); if (!xdg_grab) { return NULL; } xdg_grab->pointer_grab.data = xdg_grab; xdg_grab->pointer_grab.interface = &xdg_pointer_grab_impl; xdg_grab->keyboard_grab.data = xdg_grab; xdg_grab->keyboard_grab.interface = &xdg_keyboard_grab_impl; xdg_grab->touch_grab.data = xdg_grab; xdg_grab->touch_grab.interface = &xdg_touch_grab_impl; wl_list_init(&xdg_grab->popups); wl_list_insert(&shell->popup_grabs, &xdg_grab->link); xdg_grab->seat = seat; xdg_grab->seat_destroy.notify = xdg_popup_grab_handle_seat_destroy; wl_signal_add(&seat->events.destroy, &xdg_grab->seat_destroy); return xdg_grab; } void handle_xdg_popup_committed(struct wlr_xdg_popup *popup) { if (!popup->parent) { wl_resource_post_error(popup->base->resource, XDG_SURFACE_ERROR_NOT_CONSTRUCTED, "xdg_popup has no parent"); return; } popup->current = popup->pending; if (popup->base->initial_commit && !popup->sent_initial_configure) { wlr_xdg_surface_schedule_configure(popup->base); popup->sent_initial_configure = true; } } static const struct xdg_popup_interface xdg_popup_implementation; struct wlr_xdg_popup *wlr_xdg_popup_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &xdg_popup_interface, &xdg_popup_implementation)); return wl_resource_get_user_data(resource); } struct wlr_xdg_popup *wlr_xdg_popup_try_from_wlr_surface(struct wlr_surface *surface) { struct wlr_xdg_surface *xdg_surface = wlr_xdg_surface_try_from_wlr_surface(surface); if (xdg_surface == NULL || xdg_surface->role != WLR_XDG_SURFACE_ROLE_POPUP) { return NULL; } return xdg_surface->popup; } static void xdg_popup_handle_grab(struct wl_client *client, struct wl_resource *resource, struct wl_resource *seat_resource, uint32_t serial) { struct wlr_xdg_popup *popup = wlr_xdg_popup_from_resource(resource); if (!popup) { return; } struct wlr_seat_client *seat_client = wlr_seat_client_from_resource(seat_resource); if (seat_client == NULL) { wlr_xdg_popup_destroy(popup); return; } if (popup->sent_initial_configure) { wl_resource_post_error(popup->resource, XDG_POPUP_ERROR_INVALID_GRAB, "xdg_popup is already mapped"); return; } struct wlr_xdg_popup_grab *popup_grab = get_xdg_shell_popup_grab_from_seat( popup->base->client->shell, seat_client->seat); if (!wl_list_empty(&popup->base->popups)) { wl_resource_post_error(popup->base->client->resource, XDG_WM_BASE_ERROR_NOT_THE_TOPMOST_POPUP, "xdg_popup was not created on the topmost popup"); return; } popup_grab->client = popup->base->client->client; popup->seat = seat_client->seat; wl_list_insert(&popup_grab->popups, &popup->grab_link); wlr_seat_pointer_start_grab(seat_client->seat, &popup_grab->pointer_grab); wlr_seat_keyboard_start_grab(seat_client->seat, &popup_grab->keyboard_grab); wlr_seat_touch_start_grab(seat_client->seat, &popup_grab->touch_grab); } static void xdg_popup_handle_reposition( struct wl_client *client, struct wl_resource *resource, struct wl_resource *positioner_resource, uint32_t token) { struct wlr_xdg_popup *popup = wlr_xdg_popup_from_resource(resource); if (!popup) { return; } struct wlr_xdg_positioner *positioner = wlr_xdg_positioner_from_resource(positioner_resource); wlr_xdg_positioner_rules_get_geometry( &positioner->rules, &popup->scheduled.geometry); popup->scheduled.rules = positioner->rules; popup->scheduled.fields |= WLR_XDG_POPUP_CONFIGURE_REPOSITION_TOKEN; popup->scheduled.reposition_token = token; wlr_xdg_surface_schedule_configure(popup->base); wl_signal_emit_mutable(&popup->events.reposition, NULL); } static void xdg_popup_handle_destroy(struct wl_client *client, struct wl_resource *resource) { struct wlr_xdg_popup *popup = wlr_xdg_popup_from_resource(resource); if (popup && !wl_list_empty(&popup->base->popups)) { wl_resource_post_error(popup->base->client->resource, XDG_WM_BASE_ERROR_NOT_THE_TOPMOST_POPUP, "xdg_popup was destroyed while it was not the topmost popup"); return; } wl_resource_destroy(resource); } static const struct xdg_popup_interface xdg_popup_implementation = { .destroy = xdg_popup_handle_destroy, .grab = xdg_popup_handle_grab, .reposition = xdg_popup_handle_reposition, }; static void xdg_popup_handle_resource_destroy(struct wl_resource *resource) { struct wlr_xdg_popup *popup = wlr_xdg_popup_from_resource(resource); if (popup == NULL) { return; } wlr_xdg_popup_destroy(popup); } void create_xdg_popup(struct wlr_xdg_surface *surface, struct wlr_xdg_surface *parent, struct wlr_xdg_positioner *positioner, uint32_t id) { if (positioner->rules.size.width == 0 || positioner->rules.anchor_rect.width == 0) { wl_resource_post_error(surface->client->resource, XDG_WM_BASE_ERROR_INVALID_POSITIONER, "positioner object is not complete"); return; } if (!set_xdg_surface_role(surface, WLR_XDG_SURFACE_ROLE_POPUP)) { return; } if (parent != NULL && parent->role == WLR_XDG_SURFACE_ROLE_NONE) { wl_resource_post_error(surface->client->resource, XDG_WM_BASE_ERROR_INVALID_POPUP_PARENT, "a popup parent must have a role"); return; } assert(surface->popup == NULL); surface->popup = calloc(1, sizeof(*surface->popup)); if (!surface->popup) { wl_resource_post_no_memory(surface->resource); return; } surface->popup->base = surface; surface->popup->resource = wl_resource_create( surface->client->client, &xdg_popup_interface, wl_resource_get_version(surface->resource), id); if (surface->popup->resource == NULL) { free(surface->popup); surface->popup = NULL; wl_resource_post_no_memory(surface->resource); return; } wl_resource_set_implementation(surface->popup->resource, &xdg_popup_implementation, surface->popup, xdg_popup_handle_resource_destroy); surface->role = WLR_XDG_SURFACE_ROLE_POPUP; wlr_xdg_positioner_rules_get_geometry( &positioner->rules, &surface->popup->scheduled.geometry); surface->popup->scheduled.rules = positioner->rules; wl_signal_init(&surface->popup->events.reposition); if (parent) { surface->popup->parent = parent->surface; wl_list_insert(&parent->popups, &surface->popup->link); wl_signal_emit_mutable(&parent->events.new_popup, surface->popup); } else { wl_list_init(&surface->popup->link); } set_xdg_surface_role_object(surface, surface->popup->resource); } void reset_xdg_popup(struct wlr_xdg_popup *popup) { if (popup->seat != NULL) { struct wlr_xdg_popup_grab *grab = get_xdg_shell_popup_grab_from_seat( popup->base->client->shell, popup->seat); wl_list_remove(&popup->grab_link); if (wl_list_empty(&grab->popups)) { if (grab->seat->pointer_state.grab == &grab->pointer_grab) { wlr_seat_pointer_end_grab(grab->seat); } if (grab->seat->keyboard_state.grab == &grab->keyboard_grab) { wlr_seat_keyboard_end_grab(grab->seat); } if (grab->seat->touch_state.grab == &grab->touch_grab) { wlr_seat_touch_end_grab(grab->seat); } destroy_xdg_popup_grab(grab); } popup->seat = NULL; } popup->sent_initial_configure = false; } void destroy_xdg_popup(struct wlr_xdg_popup *popup) { wlr_surface_unmap(popup->base->surface); reset_xdg_popup(popup); // TODO: improve events if (popup->base->added) { wl_signal_emit_mutable(&popup->base->events.destroy, NULL); popup->base->added = false; } popup->base->popup = NULL; wl_list_remove(&popup->link); wl_resource_set_user_data(popup->resource, NULL); free(popup); } void wlr_xdg_popup_destroy(struct wlr_xdg_popup *popup) { if (popup == NULL) { return; } struct wlr_xdg_popup *child, *child_tmp; wl_list_for_each_safe(child, child_tmp, &popup->base->popups, link) { wlr_xdg_popup_destroy(child); } xdg_popup_send_popup_done(popup->resource); destroy_xdg_popup(popup); } void wlr_xdg_popup_get_toplevel_coords(struct wlr_xdg_popup *popup, int popup_sx, int popup_sy, int *toplevel_sx, int *toplevel_sy) { struct wlr_surface *parent = popup->parent; struct wlr_xdg_surface *xdg_surface; while ((xdg_surface = wlr_xdg_surface_try_from_wlr_surface(parent))) { if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP && xdg_surface->popup != NULL) { popup_sx += xdg_surface->popup->current.geometry.x; popup_sy += xdg_surface->popup->current.geometry.y; parent = xdg_surface->popup->parent; } else { popup_sx += xdg_surface->current.geometry.x; popup_sy += xdg_surface->current.geometry.y; break; } } assert(parent); *toplevel_sx = popup_sx; *toplevel_sy = popup_sy; } void wlr_xdg_popup_unconstrain_from_box(struct wlr_xdg_popup *popup, const struct wlr_box *toplevel_space_box) { int toplevel_sx, toplevel_sy; wlr_xdg_popup_get_toplevel_coords(popup, 0, 0, &toplevel_sx, &toplevel_sy); struct wlr_box popup_constraint = { .x = toplevel_space_box->x - toplevel_sx, .y = toplevel_space_box->y - toplevel_sy, .width = toplevel_space_box->width, .height = toplevel_space_box->height, }; wlr_xdg_positioner_rules_unconstrain_box(&popup->scheduled.rules, &popup_constraint, &popup->scheduled.geometry); wlr_xdg_surface_schedule_configure(popup->base); } wlroots-0.17.1/types/xdg_shell/wlr_xdg_positioner.c000066400000000000000000000375311454110342200225200ustar00rootroot00000000000000#include #include #include #include "types/wlr_xdg_shell.h" static const struct xdg_positioner_interface xdg_positioner_implementation; struct wlr_xdg_positioner *wlr_xdg_positioner_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &xdg_positioner_interface, &xdg_positioner_implementation)); return wl_resource_get_user_data(resource); } static void xdg_positioner_handle_set_size(struct wl_client *client, struct wl_resource *resource, int32_t width, int32_t height) { struct wlr_xdg_positioner *positioner = wlr_xdg_positioner_from_resource(resource); if (width < 1 || height < 1) { wl_resource_post_error(resource, XDG_POSITIONER_ERROR_INVALID_INPUT, "width and height must be positive and non-zero"); return; } positioner->rules.size.width = width; positioner->rules.size.height = height; } static void xdg_positioner_handle_set_anchor_rect(struct wl_client *client, struct wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) { struct wlr_xdg_positioner *positioner = wlr_xdg_positioner_from_resource(resource); if (width < 0 || height < 0) { wl_resource_post_error(resource, XDG_POSITIONER_ERROR_INVALID_INPUT, "width and height must be positive"); return; } positioner->rules.anchor_rect.x = x; positioner->rules.anchor_rect.y = y; positioner->rules.anchor_rect.width = width; positioner->rules.anchor_rect.height = height; } static void xdg_positioner_handle_set_anchor(struct wl_client *client, struct wl_resource *resource, uint32_t anchor) { struct wlr_xdg_positioner *positioner = wlr_xdg_positioner_from_resource(resource); if (anchor > XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT) { wl_resource_post_error(resource, XDG_POSITIONER_ERROR_INVALID_INPUT, "invalid anchor value"); return; } positioner->rules.anchor = anchor; } static void xdg_positioner_handle_set_gravity(struct wl_client *client, struct wl_resource *resource, uint32_t gravity) { struct wlr_xdg_positioner *positioner = wlr_xdg_positioner_from_resource(resource); if (gravity > XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT) { wl_resource_post_error(resource, XDG_POSITIONER_ERROR_INVALID_INPUT, "invalid gravity value"); return; } positioner->rules.gravity = gravity; } static void xdg_positioner_handle_set_constraint_adjustment( struct wl_client *client, struct wl_resource *resource, uint32_t constraint_adjustment) { struct wlr_xdg_positioner *positioner = wlr_xdg_positioner_from_resource(resource); positioner->rules.constraint_adjustment = constraint_adjustment; } static void xdg_positioner_handle_set_offset(struct wl_client *client, struct wl_resource *resource, int32_t x, int32_t y) { struct wlr_xdg_positioner *positioner = wlr_xdg_positioner_from_resource(resource); positioner->rules.offset.x = x; positioner->rules.offset.y = y; } static void xdg_positioner_handle_set_reactive( struct wl_client *client, struct wl_resource *resource) { struct wlr_xdg_positioner *positioner = wlr_xdg_positioner_from_resource(resource); positioner->rules.reactive = true; } static void xdg_positioner_handle_set_parent_configure( struct wl_client *client, struct wl_resource *resource, uint32_t serial) { struct wlr_xdg_positioner *positioner = wlr_xdg_positioner_from_resource(resource); positioner->rules.has_parent_configure_serial = true; positioner->rules.parent_configure_serial = serial; } static void xdg_positioner_handle_set_parent_size(struct wl_client *client, struct wl_resource *resource, int32_t parent_width, int32_t parent_height) { struct wlr_xdg_positioner *positioner = wlr_xdg_positioner_from_resource(resource); positioner->rules.parent_size.width = parent_width; positioner->rules.parent_size.height = parent_height; } static void xdg_positioner_handle_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static const struct xdg_positioner_interface xdg_positioner_implementation = { .destroy = xdg_positioner_handle_destroy, .set_size = xdg_positioner_handle_set_size, .set_anchor_rect = xdg_positioner_handle_set_anchor_rect, .set_anchor = xdg_positioner_handle_set_anchor, .set_gravity = xdg_positioner_handle_set_gravity, .set_constraint_adjustment = xdg_positioner_handle_set_constraint_adjustment, .set_offset = xdg_positioner_handle_set_offset, .set_reactive = xdg_positioner_handle_set_reactive, .set_parent_size = xdg_positioner_handle_set_parent_size, .set_parent_configure = xdg_positioner_handle_set_parent_configure, }; static void xdg_positioner_handle_resource_destroy( struct wl_resource *resource) { struct wlr_xdg_positioner *positioner = wlr_xdg_positioner_from_resource(resource); free(positioner); } void create_xdg_positioner(struct wlr_xdg_client *client, uint32_t id) { struct wlr_xdg_positioner *positioner = calloc(1, sizeof(*positioner)); if (positioner == NULL) { wl_client_post_no_memory(client->client); return; } positioner->resource = wl_resource_create(client->client, &xdg_positioner_interface, wl_resource_get_version(client->resource), id); if (positioner->resource == NULL) { free(positioner); wl_client_post_no_memory(client->client); return; } wl_resource_set_implementation(positioner->resource, &xdg_positioner_implementation, positioner, xdg_positioner_handle_resource_destroy); } static uint32_t xdg_positioner_anchor_to_wlr_edges( enum xdg_positioner_anchor anchor) { switch (anchor) { case XDG_POSITIONER_ANCHOR_NONE: return WLR_EDGE_NONE; case XDG_POSITIONER_ANCHOR_TOP: return WLR_EDGE_TOP; case XDG_POSITIONER_ANCHOR_TOP_LEFT: return WLR_EDGE_TOP | WLR_EDGE_LEFT; case XDG_POSITIONER_ANCHOR_TOP_RIGHT: return WLR_EDGE_TOP | WLR_EDGE_RIGHT; case XDG_POSITIONER_ANCHOR_BOTTOM: return WLR_EDGE_BOTTOM; case XDG_POSITIONER_ANCHOR_BOTTOM_LEFT: return WLR_EDGE_BOTTOM | WLR_EDGE_LEFT; case XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT: return WLR_EDGE_BOTTOM | WLR_EDGE_RIGHT; case XDG_POSITIONER_ANCHOR_LEFT: return WLR_EDGE_LEFT; case XDG_POSITIONER_ANCHOR_RIGHT: return WLR_EDGE_RIGHT; } abort(); // Unreachable } static uint32_t xdg_positioner_gravity_to_wlr_edges( enum xdg_positioner_gravity gravity) { // Gravity and edge enums are the same return xdg_positioner_anchor_to_wlr_edges((enum xdg_positioner_anchor)gravity); } void wlr_xdg_positioner_rules_get_geometry( const struct wlr_xdg_positioner_rules *rules, struct wlr_box *box) { box->x = rules->offset.x; box->y = rules->offset.y; box->width = rules->size.width; box->height = rules->size.height; uint32_t edges = xdg_positioner_anchor_to_wlr_edges(rules->anchor); if (edges & WLR_EDGE_TOP) { box->y += rules->anchor_rect.y; } else if (edges & WLR_EDGE_BOTTOM) { box->y += rules->anchor_rect.y + rules->anchor_rect.height; } else { box->y += rules->anchor_rect.y + rules->anchor_rect.height / 2; } if (edges & WLR_EDGE_LEFT) { box->x += rules->anchor_rect.x; } else if (edges & WLR_EDGE_RIGHT) { box->x += rules->anchor_rect.x + rules->anchor_rect.width; } else { box->x += rules->anchor_rect.x + rules->anchor_rect.width / 2; } edges = xdg_positioner_gravity_to_wlr_edges(rules->gravity); if (edges & WLR_EDGE_TOP) { box->y -= box->height; } else if (~edges & WLR_EDGE_BOTTOM) { box->y -= box->height / 2; } if (edges & WLR_EDGE_LEFT) { box->x -= box->width; } else if (~edges & WLR_EDGE_RIGHT) { box->x -= box->width / 2; } } static enum xdg_positioner_anchor xdg_positioner_anchor_invert_x( enum xdg_positioner_anchor anchor) { switch (anchor) { case XDG_POSITIONER_ANCHOR_LEFT: return XDG_POSITIONER_ANCHOR_RIGHT; case XDG_POSITIONER_ANCHOR_RIGHT: return XDG_POSITIONER_ANCHOR_LEFT; case XDG_POSITIONER_ANCHOR_TOP_LEFT: return XDG_POSITIONER_ANCHOR_TOP_RIGHT; case XDG_POSITIONER_ANCHOR_TOP_RIGHT: return XDG_POSITIONER_ANCHOR_TOP_LEFT; case XDG_POSITIONER_ANCHOR_BOTTOM_LEFT: return XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT; case XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT: return XDG_POSITIONER_ANCHOR_BOTTOM_LEFT; default: return anchor; } } static enum xdg_positioner_gravity xdg_positioner_gravity_invert_x( enum xdg_positioner_gravity gravity) { // gravity and edge enums are the same return (enum xdg_positioner_gravity)xdg_positioner_anchor_invert_x( (enum xdg_positioner_anchor)gravity); } static enum xdg_positioner_anchor xdg_positioner_anchor_invert_y( enum xdg_positioner_anchor anchor) { switch (anchor) { case XDG_POSITIONER_ANCHOR_TOP: return XDG_POSITIONER_ANCHOR_BOTTOM; case XDG_POSITIONER_ANCHOR_BOTTOM: return XDG_POSITIONER_ANCHOR_TOP; case XDG_POSITIONER_ANCHOR_TOP_LEFT: return XDG_POSITIONER_ANCHOR_BOTTOM_LEFT; case XDG_POSITIONER_ANCHOR_BOTTOM_LEFT: return XDG_POSITIONER_ANCHOR_TOP_LEFT; case XDG_POSITIONER_ANCHOR_TOP_RIGHT: return XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT; case XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT: return XDG_POSITIONER_ANCHOR_TOP_RIGHT; default: return anchor; } } static enum xdg_positioner_gravity xdg_positioner_gravity_invert_y( enum xdg_positioner_gravity gravity) { // gravity and edge enums are the same return (enum xdg_positioner_gravity)xdg_positioner_anchor_invert_y( (enum xdg_positioner_anchor)gravity); } /** * Distances from each edge of the box to the corresponding edge of * the anchor rect. Each distance is positive if the edge is outside * the anchor rect, and negative if the edge is inside it. */ struct constraint_offsets { int top; int bottom; int left; int right; }; static bool is_unconstrained(const struct constraint_offsets *offsets) { return offsets->top <= 0 && offsets->bottom <= 0 && offsets->left <= 0 && offsets->right <= 0; } static void get_constrained_box_offsets(const struct wlr_box *constraint, const struct wlr_box *box, struct constraint_offsets *offsets) { offsets->left = constraint->x - box->x; offsets->right = box->x + box->width - constraint->x - constraint->width; offsets->top = constraint->y - box->y; offsets->bottom = box->y + box->height - constraint->y - constraint->height; } static bool xdg_positioner_rules_unconstrain_by_flip( const struct wlr_xdg_positioner_rules *rules, const struct wlr_box *constraint, struct wlr_box *box, struct constraint_offsets *offsets) { // If none of the edges are constrained, no need to flip. // If both edges are constrained, the box is bigger than // the anchor rect and flipping won't help anyway. bool flip_x = ((offsets->left > 0) ^ (offsets->right > 0)) && (rules->constraint_adjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_X); bool flip_y = ((offsets->top > 0) ^ (offsets->bottom > 0)) && (rules->constraint_adjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y); if (!flip_x && !flip_y) { return false; } struct wlr_xdg_positioner_rules flipped = *rules; if (flip_x) { flipped.anchor = xdg_positioner_anchor_invert_x(flipped.anchor); flipped.gravity = xdg_positioner_gravity_invert_x(flipped.gravity); } if (flip_y) { flipped.anchor = xdg_positioner_anchor_invert_y(flipped.anchor); flipped.gravity = xdg_positioner_gravity_invert_y(flipped.gravity); } struct wlr_box flipped_box; wlr_xdg_positioner_rules_get_geometry(&flipped, &flipped_box); struct constraint_offsets flipped_offsets; get_constrained_box_offsets(constraint, &flipped_box, &flipped_offsets); // Only apply flipping if it helps if (flipped_offsets.left <= 0 && flipped_offsets.right <= 0) { box->x = flipped_box.x; offsets->left = flipped_offsets.left; offsets->right = flipped_offsets.right; } if (flipped_offsets.top <= 0 && flipped_offsets.bottom <= 0) { box->y = flipped_box.y; offsets->top = flipped_offsets.top; offsets->bottom = flipped_offsets.bottom; } return is_unconstrained(offsets); } static bool xdg_positioner_rules_unconstrain_by_slide( const struct wlr_xdg_positioner_rules *rules, const struct wlr_box *constraint, struct wlr_box *box, struct constraint_offsets *offsets) { uint32_t gravity = xdg_positioner_gravity_to_wlr_edges(rules->gravity); bool slide_x = (offsets->left > 0 || offsets->right > 0) && (rules->constraint_adjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X); bool slide_y = (offsets->top > 0 || offsets->bottom > 0) && (rules->constraint_adjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y); if (!slide_x && !slide_y) { return false; } if (slide_x) { if (offsets->left > 0 && offsets->right > 0) { // The protocol states: "First try to slide towards the direction of // the gravity [...] Then try to slide towards the opposite direction // of the gravity". The only situation where this order matters is when // the box is bigger than the anchor rect and completely includes it. // In this case, the second slide will fail immediately, so simply // slide towards the direction of the gravity. // Note that the protocol doesn't specify the behavior when there is no // gravity on the axis (which is what e.g. GTK tooltips use). In this // case, fall back to sliding the box to the right/bottom, which is what // GTK X11 popup adjustment code does. if (gravity & WLR_EDGE_LEFT) { box->x -= offsets->right; } else { box->x += offsets->left; } } else { // If at least one edge is already unconstrained, the order of slide // attempts doesn't matter. Slide for the minimal distance needed to // satisfy the requirement of constraining one edge or unconstraining // another. int abs_left = offsets->left > 0 ? offsets->left : -offsets->left; int abs_right = offsets->right > 0 ? offsets->right : -offsets->right; if (abs_left < abs_right) { box->x += offsets->left; } else { box->x -= offsets->right; } } } if (slide_y) { if (offsets->top > 0 && offsets->bottom > 0) { if (gravity & WLR_EDGE_TOP) { box->y -= offsets->bottom; } else { box->y += offsets->top; } } else { int abs_top = offsets->top > 0 ? offsets->top : -offsets->top; int abs_bottom = offsets->bottom > 0 ? offsets->bottom : -offsets->bottom; if (abs_top < abs_bottom) { box->y += offsets->top; } else { box->y -= offsets->bottom; } } } get_constrained_box_offsets(constraint, box, offsets); return is_unconstrained(offsets); } static bool xdg_positioner_rules_unconstrain_by_resize( const struct wlr_xdg_positioner_rules *rules, const struct wlr_box *constraint, struct wlr_box *box, struct constraint_offsets *offsets) { bool resize_x = (offsets->left > 0 || offsets->right > 0) && (rules->constraint_adjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_X); bool resize_y = (offsets->top > 0 || offsets->bottom > 0) && (rules->constraint_adjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_Y); if (!resize_x && !resize_y) { return false; } if (offsets->left < 0) { offsets->left = 0; } if (offsets->right < 0) { offsets->right = 0; } if (offsets->top < 0) { offsets->top = 0; } if (offsets->bottom < 0) { offsets->bottom = 0; } // Try to satisfy the constraints by clipping the box. struct wlr_box resized_box = *box; if (resize_x) { resized_box.x += offsets->left; resized_box.width -= offsets->left + offsets->right; } if (resize_y) { resized_box.y += offsets->top; resized_box.height -= offsets->top + offsets->bottom; } if (wlr_box_empty(&resized_box)) { return false; } *box = resized_box; get_constrained_box_offsets(constraint, box, offsets); return is_unconstrained(offsets); } void wlr_xdg_positioner_rules_unconstrain_box( const struct wlr_xdg_positioner_rules *rules, const struct wlr_box *constraint, struct wlr_box *box) { struct constraint_offsets offsets; get_constrained_box_offsets(constraint, box, &offsets); if (is_unconstrained(&offsets)) { // Already unconstrained return; } if (xdg_positioner_rules_unconstrain_by_flip(rules, constraint, box, &offsets)) { return; } if (xdg_positioner_rules_unconstrain_by_slide(rules, constraint, box, &offsets)) { return; } if (xdg_positioner_rules_unconstrain_by_resize(rules, constraint, box, &offsets)) { return; } } wlroots-0.17.1/types/xdg_shell/wlr_xdg_shell.c000066400000000000000000000114241454110342200214250ustar00rootroot00000000000000#include #include #include "types/wlr_xdg_shell.h" #define WM_BASE_VERSION 6 static const struct xdg_wm_base_interface xdg_shell_impl; static struct wlr_xdg_client *xdg_client_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &xdg_wm_base_interface, &xdg_shell_impl)); return wl_resource_get_user_data(resource); } static void xdg_shell_handle_create_positioner(struct wl_client *wl_client, struct wl_resource *resource, uint32_t id) { struct wlr_xdg_client *client = xdg_client_from_resource(resource); create_xdg_positioner(client, id); } static void xdg_shell_handle_get_xdg_surface(struct wl_client *wl_client, struct wl_resource *client_resource, uint32_t id, struct wl_resource *surface_resource) { struct wlr_xdg_client *client = xdg_client_from_resource(client_resource); struct wlr_surface *surface = wlr_surface_from_resource(surface_resource); create_xdg_surface(client, surface, id); } static void xdg_shell_handle_pong(struct wl_client *wl_client, struct wl_resource *resource, uint32_t serial) { struct wlr_xdg_client *client = xdg_client_from_resource(resource); if (client->ping_serial != serial) { return; } wl_event_source_timer_update(client->ping_timer, 0); client->ping_serial = 0; } static void xdg_shell_handle_destroy(struct wl_client *wl_client, struct wl_resource *resource) { struct wlr_xdg_client *client = xdg_client_from_resource(resource); if (!wl_list_empty(&client->surfaces)) { wl_resource_post_error(client->resource, XDG_WM_BASE_ERROR_DEFUNCT_SURFACES, "xdg_wm_base was destroyed before children"); return; } wl_resource_destroy(resource); } static const struct xdg_wm_base_interface xdg_shell_impl = { .destroy = xdg_shell_handle_destroy, .create_positioner = xdg_shell_handle_create_positioner, .get_xdg_surface = xdg_shell_handle_get_xdg_surface, .pong = xdg_shell_handle_pong, }; static void xdg_client_handle_resource_destroy(struct wl_resource *resource) { struct wlr_xdg_client *client = xdg_client_from_resource(resource); struct wlr_xdg_surface *surface, *tmp = NULL; wl_list_for_each_safe(surface, tmp, &client->surfaces, link) { destroy_xdg_surface(surface); } if (client->ping_timer != NULL) { wl_event_source_remove(client->ping_timer); } wl_list_remove(&client->link); free(client); } static int xdg_client_ping_timeout(void *user_data) { struct wlr_xdg_client *client = user_data; struct wlr_xdg_surface *surface; wl_list_for_each(surface, &client->surfaces, link) { wl_signal_emit_mutable(&surface->events.ping_timeout, NULL); } client->ping_serial = 0; return 1; } static void xdg_shell_bind(struct wl_client *wl_client, void *data, uint32_t version, uint32_t id) { struct wlr_xdg_shell *xdg_shell = data; struct wlr_xdg_client *client = calloc(1, sizeof(*client)); if (client == NULL) { wl_client_post_no_memory(wl_client); return; } wl_list_init(&client->surfaces); client->resource = wl_resource_create(wl_client, &xdg_wm_base_interface, version, id); if (client->resource == NULL) { free(client); wl_client_post_no_memory(wl_client); return; } client->client = wl_client; client->shell = xdg_shell; wl_resource_set_implementation(client->resource, &xdg_shell_impl, client, xdg_client_handle_resource_destroy); wl_list_insert(&xdg_shell->clients, &client->link); struct wl_display *display = wl_client_get_display(client->client); struct wl_event_loop *loop = wl_display_get_event_loop(display); client->ping_timer = wl_event_loop_add_timer(loop, xdg_client_ping_timeout, client); if (client->ping_timer == NULL) { wl_client_post_no_memory(client->client); } } static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_xdg_shell *xdg_shell = wl_container_of(listener, xdg_shell, display_destroy); wl_signal_emit_mutable(&xdg_shell->events.destroy, xdg_shell); wl_list_remove(&xdg_shell->display_destroy.link); wl_global_destroy(xdg_shell->global); free(xdg_shell); } struct wlr_xdg_shell *wlr_xdg_shell_create(struct wl_display *display, uint32_t version) { assert(version <= WM_BASE_VERSION); struct wlr_xdg_shell *xdg_shell = calloc(1, sizeof(*xdg_shell)); if (!xdg_shell) { return NULL; } xdg_shell->version = version; xdg_shell->ping_timeout = 10000; wl_list_init(&xdg_shell->clients); wl_list_init(&xdg_shell->popup_grabs); struct wl_global *global = wl_global_create(display, &xdg_wm_base_interface, version, xdg_shell, xdg_shell_bind); if (!global) { free(xdg_shell); return NULL; } xdg_shell->global = global; wl_signal_init(&xdg_shell->events.new_surface); wl_signal_init(&xdg_shell->events.destroy); xdg_shell->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &xdg_shell->display_destroy); return xdg_shell; } wlroots-0.17.1/types/xdg_shell/wlr_xdg_surface.c000066400000000000000000000426141454110342200217530ustar00rootroot00000000000000#include #include #include #include #include #include "types/wlr_xdg_shell.h" static void xdg_surface_configure_destroy( struct wlr_xdg_surface_configure *configure) { if (configure == NULL) { return; } wl_list_remove(&configure->link); free(configure->toplevel_configure); free(configure); } // An xdg_surface implementation is reset, when: // 1) a surface is unmapped due to a commit with NULL buffer, or // 2) the xdg_surface role object is destroyed, or // 3) wlr_xdg_surface is destroyed // An xdg_surface role object implementation is reset, when: // 1) a surface is unmapped due to a commit with NULL buffer, or // 2) the xdg_surface role object implementation is destroyed static void reset_xdg_surface(struct wlr_xdg_surface *surface) { surface->configured = false; surface->initialized = false; struct wlr_xdg_popup *popup, *popup_tmp; wl_list_for_each_safe(popup, popup_tmp, &surface->popups, link) { wlr_xdg_popup_destroy(popup); } struct wlr_xdg_surface_configure *configure, *tmp; wl_list_for_each_safe(configure, tmp, &surface->configure_list, link) { xdg_surface_configure_destroy(configure); } if (surface->configure_idle) { wl_event_source_remove(surface->configure_idle); surface->configure_idle = NULL; } } static void reset_xdg_surface_role_object(struct wlr_xdg_surface *surface) { switch (surface->role) { case WLR_XDG_SURFACE_ROLE_TOPLEVEL: if (surface->toplevel != NULL) { reset_xdg_toplevel(surface->toplevel); } break; case WLR_XDG_SURFACE_ROLE_POPUP: if (surface->popup != NULL) { reset_xdg_popup(surface->popup); } break; case WLR_XDG_SURFACE_ROLE_NONE: break; } } static void xdg_surface_handle_ack_configure(struct wl_client *client, struct wl_resource *resource, uint32_t serial) { struct wlr_xdg_surface *surface = wlr_xdg_surface_from_resource(resource); if (surface == NULL) { return; } if (surface->role == WLR_XDG_SURFACE_ROLE_NONE) { wl_resource_post_error(surface->resource, XDG_SURFACE_ERROR_NOT_CONSTRUCTED, "xdg_surface must have a role"); return; } // First find the ack'ed configure bool found = false; struct wlr_xdg_surface_configure *configure, *tmp; wl_list_for_each(configure, &surface->configure_list, link) { if (configure->serial == serial) { found = true; break; } } if (!found) { wl_resource_post_error(surface->client->resource, XDG_WM_BASE_ERROR_INVALID_SURFACE_STATE, "wrong configure serial: %u", serial); return; } // Then remove old configures from the list wl_list_for_each_safe(configure, tmp, &surface->configure_list, link) { if (configure->serial == serial) { break; } wl_signal_emit_mutable(&surface->events.ack_configure, configure); xdg_surface_configure_destroy(configure); } switch (surface->role) { case WLR_XDG_SURFACE_ROLE_NONE: assert(0 && "not reached"); break; case WLR_XDG_SURFACE_ROLE_TOPLEVEL: if (surface->toplevel != NULL) { handle_xdg_toplevel_ack_configure(surface->toplevel, configure->toplevel_configure); } break; case WLR_XDG_SURFACE_ROLE_POPUP: if (surface->popup != NULL) { handle_xdg_popup_ack_configure(surface->popup, configure->popup_configure); } break; } surface->configured = true; surface->pending.configure_serial = serial; wl_signal_emit_mutable(&surface->events.ack_configure, configure); xdg_surface_configure_destroy(configure); } static void surface_send_configure(void *user_data) { struct wlr_xdg_surface *surface = user_data; surface->configure_idle = NULL; struct wlr_xdg_surface_configure *configure = calloc(1, sizeof(*configure)); if (configure == NULL) { wl_client_post_no_memory(surface->client->client); return; } wl_list_insert(surface->configure_list.prev, &configure->link); configure->serial = surface->scheduled_serial; configure->surface = surface; switch (surface->role) { case WLR_XDG_SURFACE_ROLE_NONE: assert(0 && "not reached"); break; case WLR_XDG_SURFACE_ROLE_TOPLEVEL: if (surface->toplevel != NULL) { configure->toplevel_configure = send_xdg_toplevel_configure(surface->toplevel); } break; case WLR_XDG_SURFACE_ROLE_POPUP: if (surface->popup != NULL) { configure->popup_configure = send_xdg_popup_configure(surface->popup); } break; } wl_signal_emit_mutable(&surface->events.configure, configure); xdg_surface_send_configure(surface->resource, configure->serial); } uint32_t wlr_xdg_surface_schedule_configure(struct wlr_xdg_surface *surface) { struct wl_display *display = wl_client_get_display(surface->client->client); struct wl_event_loop *loop = wl_display_get_event_loop(display); if (!surface->initialized) { wlr_log(WLR_ERROR, "A configure is scheduled for an uninitialized xdg_surface %p", surface); } if (surface->configure_idle == NULL) { surface->scheduled_serial = wl_display_next_serial(display); surface->configure_idle = wl_event_loop_add_idle(loop, surface_send_configure, surface); if (surface->configure_idle == NULL) { wl_client_post_no_memory(surface->client->client); } } return surface->scheduled_serial; } static void xdg_surface_handle_get_popup(struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *parent_resource, struct wl_resource *positioner_resource) { struct wlr_xdg_surface *xdg_surface = wlr_xdg_surface_from_resource(resource); assert(xdg_surface != NULL); struct wlr_xdg_surface *parent = NULL; if (parent_resource != NULL) { parent = wlr_xdg_surface_from_resource(parent_resource); } struct wlr_xdg_positioner *positioner = wlr_xdg_positioner_from_resource(positioner_resource); create_xdg_popup(xdg_surface, parent, positioner, id); } static void xdg_surface_handle_get_toplevel(struct wl_client *client, struct wl_resource *resource, uint32_t id) { struct wlr_xdg_surface *xdg_surface = wlr_xdg_surface_from_resource(resource); assert(xdg_surface != NULL); create_xdg_toplevel(xdg_surface, id); } static void xdg_surface_handle_set_window_geometry(struct wl_client *client, struct wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) { struct wlr_xdg_surface *surface = wlr_xdg_surface_from_resource(resource); assert(surface != NULL); if (surface->role == WLR_XDG_SURFACE_ROLE_NONE) { wl_resource_post_error(surface->resource, XDG_SURFACE_ERROR_NOT_CONSTRUCTED, "xdg_surface must have a role"); return; } if (width <= 0 || height <= 0) { wl_resource_post_error(resource, XDG_SURFACE_ERROR_INVALID_SIZE, "Tried to set invalid xdg-surface geometry"); return; } surface->pending.geometry.x = x; surface->pending.geometry.y = y; surface->pending.geometry.width = width; surface->pending.geometry.height = height; } static void xdg_surface_handle_destroy(struct wl_client *client, struct wl_resource *resource) { struct wlr_xdg_surface *surface = wlr_xdg_surface_from_resource(resource); if (surface == NULL) { return; } if (surface->role_resource != NULL) { wl_resource_post_error(resource, XDG_SURFACE_ERROR_DEFUNCT_ROLE_OBJECT, "surface was destroyed before its role object"); return; } wl_resource_destroy(resource); } static const struct xdg_surface_interface xdg_surface_implementation = { .destroy = xdg_surface_handle_destroy, .get_toplevel = xdg_surface_handle_get_toplevel, .get_popup = xdg_surface_handle_get_popup, .ack_configure = xdg_surface_handle_ack_configure, .set_window_geometry = xdg_surface_handle_set_window_geometry, }; static void xdg_surface_role_commit(struct wlr_surface *wlr_surface) { struct wlr_xdg_surface *surface = wlr_xdg_surface_try_from_wlr_surface(wlr_surface); assert(surface != NULL); if (wlr_surface_has_buffer(wlr_surface) && !surface->configured) { wl_resource_post_error(surface->resource, XDG_SURFACE_ERROR_UNCONFIGURED_BUFFER, "xdg_surface has never been configured"); return; } if (surface->role_resource == NULL) { wl_resource_post_error(surface->resource, XDG_SURFACE_ERROR_NOT_CONSTRUCTED, "xdg_surface must have a role object"); return; } if (surface->surface->unmap_commit) { reset_xdg_surface_role_object(surface); reset_xdg_surface(surface); assert(!surface->initial_commit); surface->initial_commit = false; } else { surface->initial_commit = !surface->initialized; surface->initialized = true; } surface->current = surface->pending; switch (surface->role) { case WLR_XDG_SURFACE_ROLE_NONE: assert(0 && "not reached"); return; case WLR_XDG_SURFACE_ROLE_TOPLEVEL: if (surface->toplevel != NULL) { handle_xdg_toplevel_committed(surface->toplevel); } else { return; } break; case WLR_XDG_SURFACE_ROLE_POPUP: if (surface->popup != NULL) { handle_xdg_popup_committed(surface->popup); } else { return; } break; } if (!surface->added) { surface->added = true; wl_signal_emit_mutable(&surface->client->shell->events.new_surface, surface); } if (wlr_surface_has_buffer(wlr_surface)) { wlr_surface_map(wlr_surface); } } static void xdg_surface_role_destroy(struct wlr_surface *wlr_surface) { struct wlr_xdg_surface *surface = wlr_xdg_surface_try_from_wlr_surface(wlr_surface); if (surface == NULL) { // This is the only time xdg_surface can be inert return; } destroy_xdg_surface(surface); } static struct wlr_surface_role xdg_surface_role = { .name = "xdg_surface", .commit = xdg_surface_role_commit, .destroy = xdg_surface_role_destroy, }; struct wlr_xdg_surface *wlr_xdg_surface_try_from_wlr_surface( struct wlr_surface *surface) { if (surface->role != &xdg_surface_role || surface->role_resource == NULL) { return NULL; } return wlr_xdg_surface_from_resource(surface->role_resource); } void create_xdg_surface(struct wlr_xdg_client *client, struct wlr_surface *wlr_surface, uint32_t id) { if (!wlr_surface_set_role(wlr_surface, &xdg_surface_role, client->resource, XDG_WM_BASE_ERROR_ROLE)) { return; } struct wlr_xdg_surface *surface = calloc(1, sizeof(*surface)); if (surface == NULL) { wl_client_post_no_memory(client->client); return; } surface->client = client; surface->role = WLR_XDG_SURFACE_ROLE_NONE; surface->surface = wlr_surface; surface->resource = wl_resource_create(client->client, &xdg_surface_interface, wl_resource_get_version(client->resource), id); if (surface->resource == NULL) { free(surface); wl_client_post_no_memory(client->client); return; } if (wlr_surface_has_buffer(surface->surface)) { wl_resource_destroy(surface->resource); free(surface); wl_resource_post_error(client->resource, XDG_SURFACE_ERROR_UNCONFIGURED_BUFFER, "xdg_surface must not have a buffer at creation"); return; } wl_list_init(&surface->configure_list); wl_list_init(&surface->popups); wl_signal_init(&surface->events.destroy); wl_signal_init(&surface->events.ping_timeout); wl_signal_init(&surface->events.new_popup); wl_signal_init(&surface->events.configure); wl_signal_init(&surface->events.ack_configure); wlr_log(WLR_DEBUG, "new xdg_surface %p (res %p)", surface, surface->resource); wl_resource_set_implementation(surface->resource, &xdg_surface_implementation, surface, NULL); wl_list_insert(&client->surfaces, &surface->link); wlr_surface_set_role_object(wlr_surface, surface->resource); } bool set_xdg_surface_role(struct wlr_xdg_surface *surface, enum wlr_xdg_surface_role role) { assert(role != WLR_XDG_SURFACE_ROLE_NONE); static const char *role_names[] = { [WLR_XDG_SURFACE_ROLE_TOPLEVEL] = "xdg_toplevel", [WLR_XDG_SURFACE_ROLE_POPUP] = "xdg_popup", }; if (surface->role != WLR_XDG_SURFACE_ROLE_NONE && surface->role != role) { wl_resource_post_error(surface->client->resource, XDG_WM_BASE_ERROR_ROLE, "Cannot assign role %s to xdg_surface@%" PRIu32 ", already has role %s", role_names[role], wl_resource_get_id(surface->resource), role_names[surface->role]); return false; } if (surface->role_resource != NULL) { wl_resource_post_error(surface->client->resource, XDG_WM_BASE_ERROR_ROLE, "Cannot reassign role %s to xdg_surface@%" PRIu32 ", role object still exists", role_names[role], wl_resource_get_id(surface->resource)); return false; } surface->role = role; return true; } static void destroy_xdg_surface_role_object(struct wlr_xdg_surface *surface) { if (surface->role_resource == NULL) { return; } switch (surface->role) { case WLR_XDG_SURFACE_ROLE_NONE: assert(0 && "not reached"); break; case WLR_XDG_SURFACE_ROLE_TOPLEVEL: if (surface->toplevel != NULL) { destroy_xdg_toplevel(surface->toplevel); } break; case WLR_XDG_SURFACE_ROLE_POPUP: if (surface->popup != NULL) { destroy_xdg_popup(surface->popup); } break; } surface->role_resource = NULL; wl_list_remove(&surface->role_resource_destroy.link); wl_list_init(&surface->role_resource_destroy.link); } static void xdg_surface_handle_role_resource_destroy(struct wl_listener *listener, void *data) { struct wlr_xdg_surface *surface = wl_container_of(listener, surface, role_resource_destroy); destroy_xdg_surface_role_object(surface); reset_xdg_surface(surface); } void set_xdg_surface_role_object(struct wlr_xdg_surface *surface, struct wl_resource *role_resource) { assert(surface->role != WLR_XDG_SURFACE_ROLE_NONE); assert(surface->role_resource == NULL); assert(role_resource != NULL); surface->role_resource = role_resource; surface->role_resource_destroy.notify = xdg_surface_handle_role_resource_destroy; wl_resource_add_destroy_listener(role_resource, &surface->role_resource_destroy); } void destroy_xdg_surface(struct wlr_xdg_surface *surface) { destroy_xdg_surface_role_object(surface); reset_xdg_surface(surface); wl_list_remove(&surface->link); wl_resource_set_user_data(surface->resource, NULL); free(surface); } struct wlr_xdg_surface *wlr_xdg_surface_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &xdg_surface_interface, &xdg_surface_implementation)); return wl_resource_get_user_data(resource); } void wlr_xdg_surface_ping(struct wlr_xdg_surface *surface) { if (surface->client->ping_serial != 0) { // already pinged return; } surface->client->ping_serial = wl_display_next_serial(wl_client_get_display(surface->client->client)); wl_event_source_timer_update(surface->client->ping_timer, surface->client->shell->ping_timeout); xdg_wm_base_send_ping(surface->client->resource, surface->client->ping_serial); } void wlr_xdg_popup_get_position(struct wlr_xdg_popup *popup, double *popup_sx, double *popup_sy) { struct wlr_xdg_surface *parent = wlr_xdg_surface_try_from_wlr_surface(popup->parent); assert(parent != NULL); struct wlr_box parent_geo; wlr_xdg_surface_get_geometry(parent, &parent_geo); *popup_sx = parent_geo.x + popup->current.geometry.x - popup->base->current.geometry.x; *popup_sy = parent_geo.y + popup->current.geometry.y - popup->base->current.geometry.y; } struct wlr_surface *wlr_xdg_surface_surface_at( struct wlr_xdg_surface *surface, double sx, double sy, double *sub_x, double *sub_y) { struct wlr_surface *sub = wlr_xdg_surface_popup_surface_at(surface, sx, sy, sub_x, sub_y); if (sub != NULL) { return sub; } return wlr_surface_surface_at(surface->surface, sx, sy, sub_x, sub_y); } struct wlr_surface *wlr_xdg_surface_popup_surface_at( struct wlr_xdg_surface *surface, double sx, double sy, double *sub_x, double *sub_y) { struct wlr_xdg_popup *popup; wl_list_for_each(popup, &surface->popups, link) { if (!popup->base->surface->mapped) { continue; } double popup_sx, popup_sy; wlr_xdg_popup_get_position(popup, &popup_sx, &popup_sy); struct wlr_surface *sub = wlr_xdg_surface_surface_at( popup->base, sx - popup_sx, sy - popup_sy, sub_x, sub_y); if (sub != NULL) { return sub; } } return NULL; } struct xdg_surface_iterator_data { wlr_surface_iterator_func_t user_iterator; void *user_data; int x, y; }; static void xdg_surface_iterator(struct wlr_surface *surface, int sx, int sy, void *data) { struct xdg_surface_iterator_data *iter_data = data; iter_data->user_iterator(surface, iter_data->x + sx, iter_data->y + sy, iter_data->user_data); } static void xdg_surface_for_each_popup_surface(struct wlr_xdg_surface *surface, int x, int y, wlr_surface_iterator_func_t iterator, void *user_data) { struct wlr_xdg_popup *popup; wl_list_for_each(popup, &surface->popups, link) { if (!popup->base->surface->mapped) { continue; } double popup_sx, popup_sy; wlr_xdg_popup_get_position(popup, &popup_sx, &popup_sy); struct xdg_surface_iterator_data data = { .user_iterator = iterator, .user_data = user_data, .x = x + popup_sx, .y = y + popup_sy, }; wlr_surface_for_each_surface(popup->base->surface, xdg_surface_iterator, &data); xdg_surface_for_each_popup_surface(popup->base, x + popup_sx, y + popup_sy, iterator, user_data); } } void wlr_xdg_surface_for_each_surface(struct wlr_xdg_surface *surface, wlr_surface_iterator_func_t iterator, void *user_data) { wlr_surface_for_each_surface(surface->surface, iterator, user_data); xdg_surface_for_each_popup_surface(surface, 0, 0, iterator, user_data); } void wlr_xdg_surface_for_each_popup_surface(struct wlr_xdg_surface *surface, wlr_surface_iterator_func_t iterator, void *user_data) { xdg_surface_for_each_popup_surface(surface, 0, 0, iterator, user_data); } void wlr_xdg_surface_get_geometry(struct wlr_xdg_surface *surface, struct wlr_box *box) { wlr_surface_get_extends(surface->surface, box); /* The client never set the geometry */ if (wlr_box_empty(&surface->current.geometry)) { return; } wlr_box_intersection(box, &surface->current.geometry, box); } wlroots-0.17.1/types/xdg_shell/wlr_xdg_toplevel.c000066400000000000000000000465271454110342200221640ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include "types/wlr_xdg_shell.h" void handle_xdg_toplevel_ack_configure( struct wlr_xdg_toplevel *toplevel, struct wlr_xdg_toplevel_configure *configure) { toplevel->pending.maximized = configure->maximized; toplevel->pending.fullscreen = configure->fullscreen; toplevel->pending.resizing = configure->resizing; toplevel->pending.activated = configure->activated; toplevel->pending.tiled = configure->tiled; toplevel->pending.suspended = configure->suspended; toplevel->pending.width = configure->width; toplevel->pending.height = configure->height; } struct wlr_xdg_toplevel_configure *send_xdg_toplevel_configure( struct wlr_xdg_toplevel *toplevel) { struct wlr_xdg_toplevel_configure *configure = calloc(1, sizeof(*configure)); if (configure == NULL) { wlr_log(WLR_ERROR, "Allocation failed"); wl_resource_post_no_memory(toplevel->resource); return NULL; } *configure = toplevel->scheduled; uint32_t version = wl_resource_get_version(toplevel->resource); if ((configure->fields & WLR_XDG_TOPLEVEL_CONFIGURE_BOUNDS) && version >= XDG_TOPLEVEL_CONFIGURE_BOUNDS_SINCE_VERSION) { xdg_toplevel_send_configure_bounds(toplevel->resource, configure->bounds.width, configure->bounds.height); } if ((configure->fields & WLR_XDG_TOPLEVEL_CONFIGURE_WM_CAPABILITIES) && version >= XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION) { size_t caps_len = 0; uint32_t caps[32]; if (configure->wm_capabilities & WLR_XDG_TOPLEVEL_WM_CAPABILITIES_WINDOW_MENU) { caps[caps_len++] = XDG_TOPLEVEL_WM_CAPABILITIES_WINDOW_MENU; } if (configure->wm_capabilities & WLR_XDG_TOPLEVEL_WM_CAPABILITIES_MAXIMIZE) { caps[caps_len++] = XDG_TOPLEVEL_WM_CAPABILITIES_MAXIMIZE; } if (configure->wm_capabilities & WLR_XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN) { caps[caps_len++] = XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN; } if (configure->wm_capabilities & WLR_XDG_TOPLEVEL_WM_CAPABILITIES_MINIMIZE) { caps[caps_len++] = XDG_TOPLEVEL_WM_CAPABILITIES_MINIMIZE; } assert(caps_len <= sizeof(caps) / sizeof(caps[0])); struct wl_array caps_array = { .size = caps_len * sizeof(caps[0]), .data = caps, }; xdg_toplevel_send_wm_capabilities(toplevel->resource, &caps_array); } size_t nstates = 0; uint32_t states[32]; if (configure->maximized) { states[nstates++] = XDG_TOPLEVEL_STATE_MAXIMIZED; } if (configure->fullscreen) { states[nstates++] = XDG_TOPLEVEL_STATE_FULLSCREEN; } if (configure->resizing) { states[nstates++] = XDG_TOPLEVEL_STATE_RESIZING; } if (configure->activated) { states[nstates++] = XDG_TOPLEVEL_STATE_ACTIVATED; } if (configure->tiled && version >= XDG_TOPLEVEL_STATE_TILED_LEFT_SINCE_VERSION) { const struct { enum wlr_edges edge; enum xdg_toplevel_state state; } tiled[] = { { WLR_EDGE_LEFT, XDG_TOPLEVEL_STATE_TILED_LEFT }, { WLR_EDGE_RIGHT, XDG_TOPLEVEL_STATE_TILED_RIGHT }, { WLR_EDGE_TOP, XDG_TOPLEVEL_STATE_TILED_TOP }, { WLR_EDGE_BOTTOM, XDG_TOPLEVEL_STATE_TILED_BOTTOM }, }; for (size_t i = 0; i < sizeof(tiled)/sizeof(tiled[0]); ++i) { if ((configure->tiled & tiled[i].edge) == 0) { continue; } states[nstates++] = tiled[i].state; } } if (configure->suspended && version >= XDG_TOPLEVEL_STATE_SUSPENDED_SINCE_VERSION) { states[nstates++] = XDG_TOPLEVEL_STATE_SUSPENDED; } assert(nstates <= sizeof(states) / sizeof(states[0])); int32_t width = configure->width; int32_t height = configure->height; struct wl_array wl_states = { .size = nstates * sizeof(states[0]), .data = states, }; xdg_toplevel_send_configure(toplevel->resource, width, height, &wl_states); toplevel->scheduled.fields = 0; return configure; } void handle_xdg_toplevel_committed(struct wlr_xdg_toplevel *toplevel) { struct wlr_xdg_toplevel_state *pending = &toplevel->pending; // 1) Negative values are prohibited // 2) If both min and max are set (aren't 0), min ≤ max if (pending->min_width < 0 || pending->min_height < 0 || pending->max_width < 0 || pending->max_height < 0 || (pending->max_width != 0 && pending->max_width < pending->min_width) || (pending->max_height != 0 && pending->max_height < pending->min_height)) { wl_resource_post_error(toplevel->resource, XDG_TOPLEVEL_ERROR_INVALID_SIZE, "client provided an invalid min or max size"); return; } toplevel->current = toplevel->pending; if (toplevel->base->initial_commit) { // On the initial commit, send a configure request to tell the client it // is added wlr_xdg_surface_schedule_configure(toplevel->base); if (toplevel->base->client->shell->version >= XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION) { // The first configure event must carry WM capabilities wlr_xdg_toplevel_set_wm_capabilities(toplevel, WLR_XDG_TOPLEVEL_WM_CAPABILITIES_WINDOW_MENU | WLR_XDG_TOPLEVEL_WM_CAPABILITIES_MAXIMIZE | WLR_XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN | WLR_XDG_TOPLEVEL_WM_CAPABILITIES_MINIMIZE); } } } static const struct xdg_toplevel_interface xdg_toplevel_implementation; struct wlr_xdg_toplevel *wlr_xdg_toplevel_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &xdg_toplevel_interface, &xdg_toplevel_implementation)); return wl_resource_get_user_data(resource); } struct wlr_xdg_toplevel *wlr_xdg_toplevel_try_from_wlr_surface(struct wlr_surface *surface) { struct wlr_xdg_surface *xdg_surface = wlr_xdg_surface_try_from_wlr_surface(surface); if (xdg_surface == NULL || xdg_surface->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL) { return NULL; } return xdg_surface->toplevel; } static void handle_parent_unmap(struct wl_listener *listener, void *data) { struct wlr_xdg_toplevel *toplevel = wl_container_of(listener, toplevel, parent_unmap); if (!wlr_xdg_toplevel_set_parent(toplevel, toplevel->parent->parent)) { assert(0 && "Unreachable"); } } bool wlr_xdg_toplevel_set_parent(struct wlr_xdg_toplevel *toplevel, struct wlr_xdg_toplevel *parent) { // Check for a loop struct wlr_xdg_toplevel *iter = parent; while (iter != NULL) { if (iter == toplevel) { return false; } iter = iter->parent; } if (toplevel->parent != NULL) { wl_list_remove(&toplevel->parent_unmap.link); } if (parent != NULL && parent->base->surface->mapped) { toplevel->parent = parent; toplevel->parent_unmap.notify = handle_parent_unmap; wl_signal_add(&toplevel->parent->base->surface->events.unmap, &toplevel->parent_unmap); } else { toplevel->parent = NULL; } wl_signal_emit_mutable(&toplevel->events.set_parent, NULL); return true; } static void xdg_toplevel_handle_set_parent(struct wl_client *client, struct wl_resource *resource, struct wl_resource *parent_resource) { struct wlr_xdg_toplevel *toplevel = wlr_xdg_toplevel_from_resource(resource); struct wlr_xdg_toplevel *parent = NULL; if (parent_resource != NULL) { parent = wlr_xdg_toplevel_from_resource(parent_resource); } if (!wlr_xdg_toplevel_set_parent(toplevel, parent)) { wl_resource_post_error(resource, XDG_TOPLEVEL_ERROR_INVALID_PARENT, "a toplevel cannot be a parent of itself or its ancestor"); } } static void xdg_toplevel_handle_set_title(struct wl_client *client, struct wl_resource *resource, const char *title) { struct wlr_xdg_toplevel *toplevel = wlr_xdg_toplevel_from_resource(resource); char *tmp; tmp = strdup(title); if (tmp == NULL) { wl_resource_post_no_memory(resource); return; } free(toplevel->title); toplevel->title = tmp; wl_signal_emit_mutable(&toplevel->events.set_title, NULL); } static void xdg_toplevel_handle_set_app_id(struct wl_client *client, struct wl_resource *resource, const char *app_id) { struct wlr_xdg_toplevel *toplevel = wlr_xdg_toplevel_from_resource(resource); char *tmp; tmp = strdup(app_id); if (tmp == NULL) { wl_resource_post_no_memory(resource); return; } free(toplevel->app_id); toplevel->app_id = tmp; wl_signal_emit_mutable(&toplevel->events.set_app_id, NULL); } static void xdg_toplevel_handle_show_window_menu(struct wl_client *client, struct wl_resource *resource, struct wl_resource *seat_resource, uint32_t serial, int32_t x, int32_t y) { struct wlr_xdg_toplevel *toplevel = wlr_xdg_toplevel_from_resource(resource); struct wlr_seat_client *seat = wlr_seat_client_from_resource(seat_resource); if (!toplevel->base->configured) { wl_resource_post_error(toplevel->base->resource, XDG_SURFACE_ERROR_NOT_CONSTRUCTED, "surface has not been configured yet"); return; } struct wlr_xdg_toplevel_show_window_menu_event event = { .toplevel = toplevel, .seat = seat, .serial = serial, .x = x, .y = y, }; wl_signal_emit_mutable(&toplevel->events.request_show_window_menu, &event); } static void xdg_toplevel_handle_move(struct wl_client *client, struct wl_resource *resource, struct wl_resource *seat_resource, uint32_t serial) { struct wlr_xdg_toplevel *toplevel = wlr_xdg_toplevel_from_resource(resource); struct wlr_seat_client *seat = wlr_seat_client_from_resource(seat_resource); if (!toplevel->base->configured) { wl_resource_post_error(toplevel->base->resource, XDG_SURFACE_ERROR_NOT_CONSTRUCTED, "surface has not been configured yet"); return; } struct wlr_xdg_toplevel_move_event event = { .toplevel = toplevel, .seat = seat, .serial = serial, }; wl_signal_emit_mutable(&toplevel->events.request_move, &event); } static void xdg_toplevel_handle_resize(struct wl_client *client, struct wl_resource *resource, struct wl_resource *seat_resource, uint32_t serial, uint32_t edges) { struct wlr_xdg_toplevel *toplevel = wlr_xdg_toplevel_from_resource(resource); struct wlr_seat_client *seat = wlr_seat_client_from_resource(seat_resource); switch (edges) { case XDG_TOPLEVEL_RESIZE_EDGE_TOP: case XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM: case XDG_TOPLEVEL_RESIZE_EDGE_LEFT: case XDG_TOPLEVEL_RESIZE_EDGE_RIGHT: case XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT: case XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT: case XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT: case XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT: break; default: wl_resource_post_error(toplevel->base->resource, XDG_TOPLEVEL_ERROR_INVALID_RESIZE_EDGE, "provided value is not a valid variant of the resize_edge enum"); return; } if (!toplevel->base->configured) { wl_resource_post_error(toplevel->base->resource, XDG_SURFACE_ERROR_NOT_CONSTRUCTED, "surface has not been configured yet"); return; } struct wlr_xdg_toplevel_resize_event event = { .toplevel = toplevel, .seat = seat, .serial = serial, .edges = edges, }; wl_signal_emit_mutable(&toplevel->events.request_resize, &event); } static void xdg_toplevel_handle_set_max_size(struct wl_client *client, struct wl_resource *resource, int32_t width, int32_t height) { struct wlr_xdg_toplevel *toplevel = wlr_xdg_toplevel_from_resource(resource); toplevel->pending.max_width = width; toplevel->pending.max_height = height; } static void xdg_toplevel_handle_set_min_size(struct wl_client *client, struct wl_resource *resource, int32_t width, int32_t height) { struct wlr_xdg_toplevel *toplevel = wlr_xdg_toplevel_from_resource(resource); toplevel->pending.min_width = width; toplevel->pending.min_height = height; } static void xdg_toplevel_handle_set_maximized(struct wl_client *client, struct wl_resource *resource) { struct wlr_xdg_toplevel *toplevel = wlr_xdg_toplevel_from_resource(resource); toplevel->requested.maximized = true; wl_signal_emit_mutable(&toplevel->events.request_maximize, NULL); } static void xdg_toplevel_handle_unset_maximized(struct wl_client *client, struct wl_resource *resource) { struct wlr_xdg_toplevel *toplevel = wlr_xdg_toplevel_from_resource(resource); toplevel->requested.maximized = false; wl_signal_emit_mutable(&toplevel->events.request_maximize, NULL); } static void handle_fullscreen_output_destroy(struct wl_listener *listener, void *data) { struct wlr_xdg_toplevel_requested *req = wl_container_of(listener, req, fullscreen_output_destroy); req->fullscreen_output = NULL; wl_list_remove(&req->fullscreen_output_destroy.link); } static void store_fullscreen_requested(struct wlr_xdg_toplevel *toplevel, bool fullscreen, struct wlr_output *output) { struct wlr_xdg_toplevel_requested *req = &toplevel->requested; req->fullscreen = fullscreen; if (req->fullscreen_output) { wl_list_remove(&req->fullscreen_output_destroy.link); } req->fullscreen_output = output; if (req->fullscreen_output) { req->fullscreen_output_destroy.notify = handle_fullscreen_output_destroy; wl_signal_add(&req->fullscreen_output->events.destroy, &req->fullscreen_output_destroy); } } static void xdg_toplevel_handle_set_fullscreen(struct wl_client *client, struct wl_resource *resource, struct wl_resource *output_resource) { struct wlr_xdg_toplevel *toplevel = wlr_xdg_toplevel_from_resource(resource); struct wlr_output *output = NULL; if (output_resource != NULL) { output = wlr_output_from_resource(output_resource); } store_fullscreen_requested(toplevel, true, output); wl_signal_emit_mutable(&toplevel->events.request_fullscreen, NULL); } static void xdg_toplevel_handle_unset_fullscreen(struct wl_client *client, struct wl_resource *resource) { struct wlr_xdg_toplevel *toplevel = wlr_xdg_toplevel_from_resource(resource); store_fullscreen_requested(toplevel, false, NULL); wl_signal_emit_mutable(&toplevel->events.request_fullscreen, NULL); } static void xdg_toplevel_handle_set_minimized(struct wl_client *client, struct wl_resource *resource) { struct wlr_xdg_toplevel *toplevel = wlr_xdg_toplevel_from_resource(resource); toplevel->requested.minimized = true; wl_signal_emit_mutable(&toplevel->events.request_minimize, NULL); } static void xdg_toplevel_handle_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static const struct xdg_toplevel_interface xdg_toplevel_implementation = { .destroy = xdg_toplevel_handle_destroy, .set_parent = xdg_toplevel_handle_set_parent, .set_title = xdg_toplevel_handle_set_title, .set_app_id = xdg_toplevel_handle_set_app_id, .show_window_menu = xdg_toplevel_handle_show_window_menu, .move = xdg_toplevel_handle_move, .resize = xdg_toplevel_handle_resize, .set_max_size = xdg_toplevel_handle_set_max_size, .set_min_size = xdg_toplevel_handle_set_min_size, .set_maximized = xdg_toplevel_handle_set_maximized, .unset_maximized = xdg_toplevel_handle_unset_maximized, .set_fullscreen = xdg_toplevel_handle_set_fullscreen, .unset_fullscreen = xdg_toplevel_handle_unset_fullscreen, .set_minimized = xdg_toplevel_handle_set_minimized, }; void create_xdg_toplevel(struct wlr_xdg_surface *surface, uint32_t id) { if (!set_xdg_surface_role(surface, WLR_XDG_SURFACE_ROLE_TOPLEVEL)) { return; } assert(surface->toplevel == NULL); surface->toplevel = calloc(1, sizeof(*surface->toplevel)); if (surface->toplevel == NULL) { wl_resource_post_no_memory(surface->resource); return; } surface->toplevel->base = surface; wl_signal_init(&surface->toplevel->events.request_maximize); wl_signal_init(&surface->toplevel->events.request_fullscreen); wl_signal_init(&surface->toplevel->events.request_minimize); wl_signal_init(&surface->toplevel->events.request_move); wl_signal_init(&surface->toplevel->events.request_resize); wl_signal_init(&surface->toplevel->events.request_show_window_menu); wl_signal_init(&surface->toplevel->events.set_parent); wl_signal_init(&surface->toplevel->events.set_title); wl_signal_init(&surface->toplevel->events.set_app_id); surface->toplevel->resource = wl_resource_create( surface->client->client, &xdg_toplevel_interface, wl_resource_get_version(surface->resource), id); if (surface->toplevel->resource == NULL) { free(surface->toplevel); surface->toplevel = NULL; wl_resource_post_no_memory(surface->resource); return; } wl_resource_set_implementation(surface->toplevel->resource, &xdg_toplevel_implementation, surface->toplevel, NULL); set_xdg_surface_role_object(surface, surface->toplevel->resource); } void reset_xdg_toplevel(struct wlr_xdg_toplevel *toplevel) { if (toplevel->parent) { wl_list_remove(&toplevel->parent_unmap.link); toplevel->parent = NULL; } free(toplevel->title); toplevel->title = NULL; free(toplevel->app_id); toplevel->app_id = NULL; if (toplevel->requested.fullscreen_output) { wl_list_remove(&toplevel->requested.fullscreen_output_destroy.link); toplevel->requested.fullscreen_output = NULL; } toplevel->requested.fullscreen = false; toplevel->requested.maximized = false; toplevel->requested.minimized = false; } void destroy_xdg_toplevel(struct wlr_xdg_toplevel *toplevel) { wlr_surface_unmap(toplevel->base->surface); reset_xdg_toplevel(toplevel); // TODO: improve events if (toplevel->base->added) { wl_signal_emit_mutable(&toplevel->base->events.destroy, NULL); toplevel->base->added = false; } toplevel->base->toplevel = NULL; wl_resource_set_user_data(toplevel->resource, NULL); free(toplevel); } void wlr_xdg_toplevel_send_close(struct wlr_xdg_toplevel *toplevel) { xdg_toplevel_send_close(toplevel->resource); } uint32_t wlr_xdg_toplevel_set_size(struct wlr_xdg_toplevel *toplevel, int32_t width, int32_t height) { toplevel->scheduled.width = width; toplevel->scheduled.height = height; return wlr_xdg_surface_schedule_configure(toplevel->base); } uint32_t wlr_xdg_toplevel_set_activated(struct wlr_xdg_toplevel *toplevel, bool activated) { toplevel->scheduled.activated = activated; return wlr_xdg_surface_schedule_configure(toplevel->base); } uint32_t wlr_xdg_toplevel_set_maximized(struct wlr_xdg_toplevel *toplevel, bool maximized) { toplevel->scheduled.maximized = maximized; return wlr_xdg_surface_schedule_configure(toplevel->base); } uint32_t wlr_xdg_toplevel_set_fullscreen(struct wlr_xdg_toplevel *toplevel, bool fullscreen) { toplevel->scheduled.fullscreen = fullscreen; return wlr_xdg_surface_schedule_configure(toplevel->base); } uint32_t wlr_xdg_toplevel_set_resizing(struct wlr_xdg_toplevel *toplevel, bool resizing) { toplevel->scheduled.resizing = resizing; return wlr_xdg_surface_schedule_configure(toplevel->base); } uint32_t wlr_xdg_toplevel_set_tiled(struct wlr_xdg_toplevel *toplevel, uint32_t tiled) { assert(toplevel->base->client->shell->version >= XDG_TOPLEVEL_STATE_TILED_LEFT_SINCE_VERSION); toplevel->scheduled.tiled = tiled; return wlr_xdg_surface_schedule_configure(toplevel->base); } uint32_t wlr_xdg_toplevel_set_bounds(struct wlr_xdg_toplevel *toplevel, int32_t width, int32_t height) { assert(toplevel->base->client->shell->version >= XDG_TOPLEVEL_CONFIGURE_BOUNDS_SINCE_VERSION); assert(width >= 0 && height >= 0); toplevel->scheduled.fields |= WLR_XDG_TOPLEVEL_CONFIGURE_BOUNDS; toplevel->scheduled.bounds.width = width; toplevel->scheduled.bounds.height = height; return wlr_xdg_surface_schedule_configure(toplevel->base); } uint32_t wlr_xdg_toplevel_set_wm_capabilities(struct wlr_xdg_toplevel *toplevel, uint32_t caps) { assert(toplevel->base->client->shell->version >= XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION); toplevel->scheduled.fields |= WLR_XDG_TOPLEVEL_CONFIGURE_WM_CAPABILITIES; toplevel->scheduled.wm_capabilities = caps; return wlr_xdg_surface_schedule_configure(toplevel->base); } uint32_t wlr_xdg_toplevel_set_suspended(struct wlr_xdg_toplevel *toplevel, bool suspended) { assert(toplevel->base->client->shell->version >= XDG_TOPLEVEL_STATE_SUSPENDED_SINCE_VERSION); toplevel->scheduled.suspended = suspended; return wlr_xdg_surface_schedule_configure(toplevel->base); } wlroots-0.17.1/util/000077500000000000000000000000001454110342200142625ustar00rootroot00000000000000wlroots-0.17.1/util/addon.c000066400000000000000000000027731454110342200155240ustar00rootroot00000000000000#include #include #include #include #include #include void wlr_addon_set_init(struct wlr_addon_set *set) { *set = (struct wlr_addon_set){0}; wl_list_init(&set->addons); } void wlr_addon_set_finish(struct wlr_addon_set *set) { while (!wl_list_empty(&set->addons)) { struct wl_list *link = set->addons.next; struct wlr_addon *addon = wl_container_of(link, addon, link); const struct wlr_addon_interface *impl = addon->impl; addon->impl->destroy(addon); if (set->addons.next == link) { wlr_log(WLR_ERROR, "Dangling addon: %s", impl->name); abort(); } } } void wlr_addon_init(struct wlr_addon *addon, struct wlr_addon_set *set, const void *owner, const struct wlr_addon_interface *impl) { assert(impl); *addon = (struct wlr_addon){ .impl = impl, .owner = owner, }; struct wlr_addon *iter; wl_list_for_each(iter, &set->addons, link) { if (iter->owner == addon->owner && iter->impl == addon->impl) { assert(0 && "Can't have two addons of the same type with the same owner"); } } wl_list_insert(&set->addons, &addon->link); } void wlr_addon_finish(struct wlr_addon *addon) { wl_list_remove(&addon->link); } struct wlr_addon *wlr_addon_find(struct wlr_addon_set *set, const void *owner, const struct wlr_addon_interface *impl) { struct wlr_addon *addon; wl_list_for_each(addon, &set->addons, link) { if (addon->owner == owner && addon->impl == impl) { return addon; } } return NULL; } wlroots-0.17.1/util/array.c000066400000000000000000000016561454110342200155540ustar00rootroot00000000000000#include "util/array.h" #include #include void array_remove_at(struct wl_array *arr, size_t offset, size_t size) { assert(arr->size >= offset + size); char *data = arr->data; memmove(&data[offset], &data[offset + size], arr->size - offset - size); arr->size -= size; } bool array_realloc(struct wl_array *arr, size_t size) { // If the size is less than 1/4th of the allocation size, we shrink it. // 1/4th is picked to provide hysteresis, without which an array with size // arr->alloc would constantly reallocate if an element is added and then // removed continously. size_t alloc; if (arr->alloc > 0 && size > arr->alloc / 4) { alloc = arr->alloc; } else { alloc = 16; } while (alloc < size) { alloc *= 2; } if (alloc == arr->alloc) { return true; } void *data = realloc(arr->data, alloc); if (data == NULL) { return false; } arr->data = data; arr->alloc = alloc; return true; } wlroots-0.17.1/util/box.c000066400000000000000000000113321454110342200152160ustar00rootroot00000000000000#include #include #include #include #include #include void wlr_box_closest_point(const struct wlr_box *box, double x, double y, double *dest_x, double *dest_y) { // if box is empty, then it contains no points, so no closest point either if (wlr_box_empty(box)) { *dest_x = NAN; *dest_y = NAN; return; } // find the closest x point if (x < box->x) { *dest_x = box->x; } else if (x >= box->x + box->width) { *dest_x = box->x + box->width - 1; } else { *dest_x = x; } // find closest y point if (y < box->y) { *dest_y = box->y; } else if (y >= box->y + box->height) { *dest_y = box->y + box->height - 1; } else { *dest_y = y; } } bool wlr_box_empty(const struct wlr_box *box) { return box == NULL || box->width <= 0 || box->height <= 0; } bool wlr_box_intersection(struct wlr_box *dest, const struct wlr_box *box_a, const struct wlr_box *box_b) { bool a_empty = wlr_box_empty(box_a); bool b_empty = wlr_box_empty(box_b); if (a_empty || b_empty) { *dest = (struct wlr_box){0}; return false; } int x1 = fmax(box_a->x, box_b->x); int y1 = fmax(box_a->y, box_b->y); int x2 = fmin(box_a->x + box_a->width, box_b->x + box_b->width); int y2 = fmin(box_a->y + box_a->height, box_b->y + box_b->height); dest->x = x1; dest->y = y1; dest->width = x2 - x1; dest->height = y2 - y1; return !wlr_box_empty(dest); } bool wlr_box_contains_point(const struct wlr_box *box, double x, double y) { if (wlr_box_empty(box)) { return false; } else { return x >= box->x && x < box->x + box->width && y >= box->y && y < box->y + box->height; } } void wlr_box_transform(struct wlr_box *dest, const struct wlr_box *box, enum wl_output_transform transform, int width, int height) { struct wlr_box src = {0}; if (box != NULL) { src = *box; } if (transform % 2 == 0) { dest->width = src.width; dest->height = src.height; } else { dest->width = src.height; dest->height = src.width; } switch (transform) { case WL_OUTPUT_TRANSFORM_NORMAL: dest->x = src.x; dest->y = src.y; break; case WL_OUTPUT_TRANSFORM_90: dest->x = height - src.y - src.height; dest->y = src.x; break; case WL_OUTPUT_TRANSFORM_180: dest->x = width - src.x - src.width; dest->y = height - src.y - src.height; break; case WL_OUTPUT_TRANSFORM_270: dest->x = src.y; dest->y = width - src.x - src.width; break; case WL_OUTPUT_TRANSFORM_FLIPPED: dest->x = width - src.x - src.width; dest->y = src.y; break; case WL_OUTPUT_TRANSFORM_FLIPPED_90: dest->x = src.y; dest->y = src.x; break; case WL_OUTPUT_TRANSFORM_FLIPPED_180: dest->x = src.x; dest->y = height - src.y - src.height; break; case WL_OUTPUT_TRANSFORM_FLIPPED_270: dest->x = height - src.y - src.height; dest->y = width - src.x - src.width; break; } } bool wlr_fbox_empty(const struct wlr_fbox *box) { return box == NULL || box->width <= 0 || box->height <= 0; } void wlr_fbox_transform(struct wlr_fbox *dest, const struct wlr_fbox *box, enum wl_output_transform transform, double width, double height) { struct wlr_fbox src = {0}; if (box != NULL) { src = *box; } if (transform % 2 == 0) { dest->width = src.width; dest->height = src.height; } else { dest->width = src.height; dest->height = src.width; } switch (transform) { case WL_OUTPUT_TRANSFORM_NORMAL: dest->x = src.x; dest->y = src.y; break; case WL_OUTPUT_TRANSFORM_90: dest->x = height - src.y - src.height; dest->y = src.x; break; case WL_OUTPUT_TRANSFORM_180: dest->x = width - src.x - src.width; dest->y = height - src.y - src.height; break; case WL_OUTPUT_TRANSFORM_270: dest->x = src.y; dest->y = width - src.x - src.width; break; case WL_OUTPUT_TRANSFORM_FLIPPED: dest->x = width - src.x - src.width; dest->y = src.y; break; case WL_OUTPUT_TRANSFORM_FLIPPED_90: dest->x = src.y; dest->y = src.x; break; case WL_OUTPUT_TRANSFORM_FLIPPED_180: dest->x = src.x; dest->y = height - src.y - src.height; break; case WL_OUTPUT_TRANSFORM_FLIPPED_270: dest->x = height - src.y - src.height; dest->y = width - src.x - src.width; break; } } #ifdef WLR_USE_UNSTABLE bool wlr_box_equal(const struct wlr_box *a, const struct wlr_box *b) { if (wlr_box_empty(a)) { a = NULL; } if (wlr_box_empty(b)) { b = NULL; } if (a == NULL || b == NULL) { return a == b; } return a->x == b->x && a->y == b->y && a->width == b->width && a->height == b->height; } bool wlr_fbox_equal(const struct wlr_fbox *a, const struct wlr_fbox *b) { if (wlr_fbox_empty(a)) { a = NULL; } if (wlr_fbox_empty(b)) { b = NULL; } if (a == NULL || b == NULL) { return a == b; } return a->x == b->x && a->y == b->y && a->width == b->width && a->height == b->height; } #endif wlroots-0.17.1/util/env.c000066400000000000000000000014421454110342200152170ustar00rootroot00000000000000#include #include #include #include "util/env.h" bool env_parse_bool(const char *option) { const char *env = getenv(option); if (env) { wlr_log(WLR_INFO, "Loading %s option: %s", option, env); } if (!env || strcmp(env, "0") == 0) { return false; } else if (strcmp(env, "1") == 0) { return true; } wlr_log(WLR_ERROR, "Unknown %s option: %s", option, env); return false; } size_t env_parse_switch(const char *option, const char **switches) { const char *env = getenv(option); if (env) { wlr_log(WLR_INFO, "Loading %s option: %s", option, env); } else { return 0; } for (ssize_t i = 0; switches[i]; i++) { if (strcmp(env, switches[i]) == 0) { return i; } } wlr_log(WLR_ERROR, "Unknown %s option: %s", option, env); return 0; } wlroots-0.17.1/util/global.c000066400000000000000000000034141454110342200156700ustar00rootroot00000000000000#include #include "util/global.h" struct destroy_global_data { struct wl_global *global; struct wl_event_source *event_source; struct wl_listener display_destroy; }; static void destroy_global(struct destroy_global_data *data) { wl_list_remove(&data->display_destroy.link); wl_global_destroy(data->global); wl_event_source_remove(data->event_source); free(data); } static int handle_timer_event(void *data) { destroy_global(data); return 0; } static void handle_display_destroy(struct wl_listener *listener, void *_data) { struct destroy_global_data *data = wl_container_of(listener, data, display_destroy); destroy_global(data); } void wlr_global_destroy_safe(struct wl_global *global) { // Don't destroy the global immediately. If the global has been created // recently, clients might try to bind to it after we've destroyed it. // Instead, remove the global so that clients stop seeing it and wait an // arbitrary amount of time before destroying the global as a workaround. // See: https://gitlab.freedesktop.org/wayland/wayland/issues/10 wl_global_remove(global); wl_global_set_user_data(global, NULL); // safety net struct wl_display *display = wl_global_get_display(global); struct wl_event_loop *event_loop = wl_display_get_event_loop(display); struct destroy_global_data *data = calloc(1, sizeof(*data)); if (data == NULL) { wl_global_destroy(global); return; } data->global = global; data->event_source = wl_event_loop_add_timer(event_loop, handle_timer_event, data); if (data->event_source == NULL) { free(data); wl_global_destroy(global); return; } wl_event_source_timer_update(data->event_source, 5000); data->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &data->display_destroy); } wlroots-0.17.1/util/log.c000066400000000000000000000050241454110342200152100ustar00rootroot00000000000000#define _XOPEN_SOURCE 700 // for snprintf #include #include #include #include #include #include #include #include #include #include "util/time.h" static bool colored = true; static enum wlr_log_importance log_importance = WLR_ERROR; static struct timespec start_time = {-1}; static const char *verbosity_colors[] = { [WLR_SILENT] = "", [WLR_ERROR] = "\x1B[1;31m", [WLR_INFO] = "\x1B[1;34m", [WLR_DEBUG] = "\x1B[1;90m", }; static const char *verbosity_headers[] = { [WLR_SILENT] = "", [WLR_ERROR] = "[ERROR]", [WLR_INFO] = "[INFO]", [WLR_DEBUG] = "[DEBUG]", }; static void init_start_time(void) { if (start_time.tv_sec >= 0) { return; } clock_gettime(CLOCK_MONOTONIC, &start_time); } static void log_stderr(enum wlr_log_importance verbosity, const char *fmt, va_list args) { init_start_time(); if (verbosity > log_importance) { return; } struct timespec ts = {0}; clock_gettime(CLOCK_MONOTONIC, &ts); timespec_sub(&ts, &ts, &start_time); fprintf(stderr, "%02d:%02d:%02d.%03ld ", (int)(ts.tv_sec / 60 / 60), (int)(ts.tv_sec / 60 % 60), (int)(ts.tv_sec % 60), ts.tv_nsec / 1000000); unsigned c = (verbosity < WLR_LOG_IMPORTANCE_LAST) ? verbosity : WLR_LOG_IMPORTANCE_LAST - 1; if (colored && isatty(STDERR_FILENO)) { fprintf(stderr, "%s", verbosity_colors[c]); } else { fprintf(stderr, "%s ", verbosity_headers[c]); } vfprintf(stderr, fmt, args); if (colored && isatty(STDERR_FILENO)) { fprintf(stderr, "\x1B[0m"); } fprintf(stderr, "\n"); } static wlr_log_func_t log_callback = log_stderr; static void log_wl(const char *fmt, va_list args) { static char wlr_fmt[1024]; int n = snprintf(wlr_fmt, sizeof(wlr_fmt), "[wayland] %s", fmt); size_t len = strlen(wlr_fmt); if (n > 0 && wlr_fmt[len - 1] == '\n') { wlr_fmt[len - 1] = '\0'; } _wlr_vlog(WLR_INFO, wlr_fmt, args); } void wlr_log_init(enum wlr_log_importance verbosity, wlr_log_func_t callback) { init_start_time(); if (verbosity < WLR_LOG_IMPORTANCE_LAST) { log_importance = verbosity; } if (callback) { log_callback = callback; } wl_log_set_handler_server(log_wl); } void _wlr_vlog(enum wlr_log_importance verbosity, const char *fmt, va_list args) { log_callback(verbosity, fmt, args); } void _wlr_log(enum wlr_log_importance verbosity, const char *fmt, ...) { va_list args; va_start(args, fmt); log_callback(verbosity, fmt, args); va_end(args); } enum wlr_log_importance wlr_log_get_verbosity(void) { return log_importance; } wlroots-0.17.1/util/meson.build000066400000000000000000000002431454110342200164230ustar00rootroot00000000000000wlr_files += files( 'addon.c', 'array.c', 'box.c', 'env.c', 'global.c', 'log.c', 'rect_union.c', 'region.c', 'set.c', 'shm.c', 'time.c', 'token.c', ) wlroots-0.17.1/util/rect_union.c000066400000000000000000000042551454110342200166010ustar00rootroot00000000000000#include #include "util/rect_union.h" static void box_union(pixman_box32_t *dst, pixman_box32_t box) { dst->x1 = dst->x1 < box.x1 ? dst->x1 : box.x1; dst->y1 = dst->y1 < box.y1 ? dst->y1 : box.y1; dst->x2 = dst->x2 > box.x2 ? dst->x2 : box.x2; dst->y2 = dst->y2 > box.y2 ? dst->y2 : box.y2; } static bool box_empty_or_invalid(pixman_box32_t box) { return box.x1 >= box.x2 || box.y1 >= box.y2; } void rect_union_init(struct rect_union *ru) { *ru = (struct rect_union) { .alloc_failure = false, .bounding_box = (pixman_box32_t) { .x1 = INT_MAX, .x2 = INT_MIN, .y1 = INT_MAX, .y2 = INT_MIN, } }; pixman_region32_init(&ru->region); wl_array_init(&ru->unsorted); }; void rect_union_finish(struct rect_union *ru) { pixman_region32_fini(&ru->region); wl_array_release(&ru->unsorted); } static void handle_alloc_failure(struct rect_union *ru) { ru->alloc_failure = true; wl_array_release(&ru->unsorted); wl_array_init(&ru->unsorted); } void rect_union_add(struct rect_union *ru, pixman_box32_t box) { if (box_empty_or_invalid(box)) { return; } box_union(&ru->bounding_box, box); if (!ru->alloc_failure) { pixman_box32_t *entry = wl_array_add(&ru->unsorted, sizeof(*entry)); if (entry) { *entry = box; } else { handle_alloc_failure(ru); } } } const pixman_region32_t *rect_union_evaluate(struct rect_union *ru) { if (ru->alloc_failure) { goto bounding_box; } int nrects = (int)(ru->unsorted.size / sizeof(pixman_box32_t)); pixman_region32_t reg; bool ok = pixman_region32_init_rects(®, ru->unsorted.data, nrects); if (!ok) { handle_alloc_failure(ru); goto bounding_box; } ok = pixman_region32_union(®, ®, &ru->region); if (!ok) { pixman_region32_fini(®); handle_alloc_failure(ru); goto bounding_box; } pixman_region32_fini(&ru->region); // pixman_region32_t is safe to move ru->region = reg; wl_array_release(&ru->unsorted); wl_array_init(&ru->unsorted); return &ru->region; bounding_box: pixman_region32_fini(&ru->region); if (box_empty_or_invalid(ru->bounding_box)) { pixman_region32_init(&ru->region); } else { pixman_region32_init_with_extents(&ru->region, &ru->bounding_box); } return &ru->region; } wlroots-0.17.1/util/region.c000066400000000000000000000172511454110342200157170ustar00rootroot00000000000000#include #include #include #include #include void wlr_region_scale(pixman_region32_t *dst, const pixman_region32_t *src, float scale) { wlr_region_scale_xy(dst, src, scale, scale); } void wlr_region_scale_xy(pixman_region32_t *dst, const pixman_region32_t *src, float scale_x, float scale_y) { if (scale_x == 1.0 && scale_y == 1.0) { pixman_region32_copy(dst, src); return; } int nrects; const pixman_box32_t *src_rects = pixman_region32_rectangles(src, &nrects); pixman_box32_t *dst_rects = malloc(nrects * sizeof(pixman_box32_t)); if (dst_rects == NULL) { return; } for (int i = 0; i < nrects; ++i) { dst_rects[i].x1 = floor(src_rects[i].x1 * scale_x); dst_rects[i].x2 = ceil(src_rects[i].x2 * scale_x); dst_rects[i].y1 = floor(src_rects[i].y1 * scale_y); dst_rects[i].y2 = ceil(src_rects[i].y2 * scale_y); } pixman_region32_fini(dst); pixman_region32_init_rects(dst, dst_rects, nrects); free(dst_rects); } void wlr_region_transform(pixman_region32_t *dst, const pixman_region32_t *src, enum wl_output_transform transform, int width, int height) { if (transform == WL_OUTPUT_TRANSFORM_NORMAL) { pixman_region32_copy(dst, src); return; } int nrects; const pixman_box32_t *src_rects = pixman_region32_rectangles(src, &nrects); pixman_box32_t *dst_rects = malloc(nrects * sizeof(pixman_box32_t)); if (dst_rects == NULL) { return; } for (int i = 0; i < nrects; ++i) { switch (transform) { case WL_OUTPUT_TRANSFORM_NORMAL: dst_rects[i].x1 = src_rects[i].x1; dst_rects[i].y1 = src_rects[i].y1; dst_rects[i].x2 = src_rects[i].x2; dst_rects[i].y2 = src_rects[i].y2; break; case WL_OUTPUT_TRANSFORM_90: dst_rects[i].x1 = height - src_rects[i].y2; dst_rects[i].y1 = src_rects[i].x1; dst_rects[i].x2 = height - src_rects[i].y1; dst_rects[i].y2 = src_rects[i].x2; break; case WL_OUTPUT_TRANSFORM_180: dst_rects[i].x1 = width - src_rects[i].x2; dst_rects[i].y1 = height - src_rects[i].y2; dst_rects[i].x2 = width - src_rects[i].x1; dst_rects[i].y2 = height - src_rects[i].y1; break; case WL_OUTPUT_TRANSFORM_270: dst_rects[i].x1 = src_rects[i].y1; dst_rects[i].y1 = width - src_rects[i].x2; dst_rects[i].x2 = src_rects[i].y2; dst_rects[i].y2 = width - src_rects[i].x1; break; case WL_OUTPUT_TRANSFORM_FLIPPED: dst_rects[i].x1 = width - src_rects[i].x2; dst_rects[i].y1 = src_rects[i].y1; dst_rects[i].x2 = width - src_rects[i].x1; dst_rects[i].y2 = src_rects[i].y2; break; case WL_OUTPUT_TRANSFORM_FLIPPED_90: dst_rects[i].x1 = src_rects[i].y1; dst_rects[i].y1 = src_rects[i].x1; dst_rects[i].x2 = src_rects[i].y2; dst_rects[i].y2 = src_rects[i].x2; break; case WL_OUTPUT_TRANSFORM_FLIPPED_180: dst_rects[i].x1 = src_rects[i].x1; dst_rects[i].y1 = height - src_rects[i].y2; dst_rects[i].x2 = src_rects[i].x2; dst_rects[i].y2 = height - src_rects[i].y1; break; case WL_OUTPUT_TRANSFORM_FLIPPED_270: dst_rects[i].x1 = height - src_rects[i].y2; dst_rects[i].y1 = width - src_rects[i].x2; dst_rects[i].x2 = height - src_rects[i].y1; dst_rects[i].y2 = width - src_rects[i].x1; break; } } pixman_region32_fini(dst); pixman_region32_init_rects(dst, dst_rects, nrects); free(dst_rects); } void wlr_region_expand(pixman_region32_t *dst, const pixman_region32_t *src, int distance) { assert(distance >= 0); if (distance == 0) { pixman_region32_copy(dst, src); return; } int nrects; const pixman_box32_t *src_rects = pixman_region32_rectangles(src, &nrects); pixman_box32_t *dst_rects = malloc(nrects * sizeof(pixman_box32_t)); if (dst_rects == NULL) { return; } for (int i = 0; i < nrects; ++i) { dst_rects[i].x1 = src_rects[i].x1 - distance; dst_rects[i].x2 = src_rects[i].x2 + distance; dst_rects[i].y1 = src_rects[i].y1 - distance; dst_rects[i].y2 = src_rects[i].y2 + distance; } pixman_region32_fini(dst); pixman_region32_init_rects(dst, dst_rects, nrects); free(dst_rects); } void wlr_region_rotated_bounds(pixman_region32_t *dst, const pixman_region32_t *src, float rotation, int ox, int oy) { if (rotation == 0) { pixman_region32_copy(dst, src); return; } int nrects; const pixman_box32_t *src_rects = pixman_region32_rectangles(src, &nrects); pixman_box32_t *dst_rects = malloc(nrects * sizeof(pixman_box32_t)); if (dst_rects == NULL) { return; } for (int i = 0; i < nrects; ++i) { double x1 = src_rects[i].x1 - ox; double y1 = src_rects[i].y1 - oy; double x2 = src_rects[i].x2 - ox; double y2 = src_rects[i].y2 - oy; double rx1 = x1 * cos(rotation) - y1 * sin(rotation); double ry1 = x1 * sin(rotation) + y1 * cos(rotation); double rx2 = x2 * cos(rotation) - y1 * sin(rotation); double ry2 = x2 * sin(rotation) + y1 * cos(rotation); double rx3 = x2 * cos(rotation) - y2 * sin(rotation); double ry3 = x2 * sin(rotation) + y2 * cos(rotation); double rx4 = x1 * cos(rotation) - y2 * sin(rotation); double ry4 = x1 * sin(rotation) + y2 * cos(rotation); x1 = fmin(fmin(rx1, rx2), fmin(rx3, rx4)); y1 = fmin(fmin(ry1, ry2), fmin(ry3, ry4)); x2 = fmax(fmax(rx1, rx2), fmax(rx3, rx4)); y2 = fmax(fmax(ry1, ry2), fmax(ry3, ry4)); dst_rects[i].x1 = floor(ox + x1); dst_rects[i].x2 = ceil(ox + x2); dst_rects[i].y1 = floor(oy + y1); dst_rects[i].y2 = ceil(oy + y2); } pixman_region32_fini(dst); pixman_region32_init_rects(dst, dst_rects, nrects); free(dst_rects); } static void region_confine(const pixman_region32_t *region, double x1, double y1, double x2, double y2, double *x2_out, double *y2_out, pixman_box32_t box) { double x_clamped = fmax(fmin(x2, box.x2 - 1), box.x1); double y_clamped = fmax(fmin(y2, box.y2 - 1), box.y1); // If the target coordinates are above box.{x,y}2 - 1, but less than // box.{x,y}2, then they are still within the box. if (floor(x_clamped) == floor(x2) && floor(y_clamped) == floor(y2)) { *x2_out = x2; *y2_out = y2; return; } double dx = x2 - x1; double dy = y2 - y1; // We use fabs to avoid negative zeroes and thus avoid a bug // with negative infinity. double delta = fmin(fabs(x_clamped - x1) / fabs(dx), fabs(y_clamped - y1) / fabs(dy)); // We clamp it again due to precision errors. double x = fmax(fmin(delta * dx + x1, box.x2 - 1), box.x1); double y = fmax(fmin(delta * dy + y1, box.y2 - 1), box.y1); // Go one unit past the boundary to find an adjacent box. int x_ext = floor(x) + (dx == 0 ? 0 : dx > 0 ? 1 : -1); int y_ext = floor(y) + (dy == 0 ? 0 : dy > 0 ? 1 : -1); if (pixman_region32_contains_point(region, x_ext, y_ext, &box)) { return region_confine(region, x, y, x2, y2, x2_out, y2_out, box); } else if (dx == 0 || dy == 0) { *x2_out = x; *y2_out = y; } else { bool bordering_x = x == box.x1 || x == box.x2 - 1; bool bordering_y = y == box.y1 || y == box.y2 - 1; if (bordering_x == bordering_y) { double x2_potential, y2_potential; double tmp1, tmp2; region_confine(region, x, y, x, y2, &tmp1, &y2_potential, box); region_confine(region, x, y, x2, y, &x2_potential, &tmp2, box); if (fabs(x2_potential - x) > fabs(y2_potential - y)) { *x2_out = x2_potential; *y2_out = y; } else { *x2_out = x; *y2_out = y2_potential; } } else if (bordering_x) { return region_confine(region, x, y, x, y2, x2_out, y2_out, box); } else if (bordering_y) { return region_confine(region, x, y, x2, y, x2_out, y2_out, box); } } } bool wlr_region_confine(const pixman_region32_t *region, double x1, double y1, double x2, double y2, double *x2_out, double *y2_out) { pixman_box32_t box; if (pixman_region32_contains_point(region, floor(x1), floor(y1), &box)) { region_confine(region, x1, y1, x2, y2, x2_out, y2_out, box); return true; } else { return false; } } wlroots-0.17.1/util/set.c000066400000000000000000000007611454110342200152250ustar00rootroot00000000000000#include "util/set.h" ssize_t set_add(uint32_t values[], size_t *len, size_t cap, uint32_t target) { for (uint32_t i = 0; i < *len; ++i) { if (values[i] == target) { return i; } } if (*len == cap) { return -1; } values[*len] = target; return (*len)++; } ssize_t set_remove(uint32_t values[], size_t *len, size_t cap, uint32_t target) { for (uint32_t i = 0; i < *len; ++i) { if (values[i] == target) { --(*len); values[i] = values[*len]; return i; } } return -1; } wlroots-0.17.1/util/shm.c000066400000000000000000000034651454110342200152250ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200112L #include #include #include #include #include #include #include #include #include "util/shm.h" #define RANDNAME_PATTERN "/wlroots-XXXXXX" static void randname(char *buf) { struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts); long r = ts.tv_nsec; for (int i = 0; i < 6; ++i) { buf[i] = 'A'+(r&15)+(r&16)*2; r >>= 5; } } static int excl_shm_open(char *name) { int retries = 100; do { randname(name + strlen(RANDNAME_PATTERN) - 6); --retries; // CLOEXEC is guaranteed to be set by shm_open int fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600); if (fd >= 0) { return fd; } } while (retries > 0 && errno == EEXIST); return -1; } int allocate_shm_file(size_t size) { char name[] = RANDNAME_PATTERN; int fd = excl_shm_open(name); if (fd < 0) { return -1; } shm_unlink(name); int ret; do { ret = ftruncate(fd, size); } while (ret < 0 && errno == EINTR); if (ret < 0) { close(fd); return -1; } return fd; } bool allocate_shm_file_pair(size_t size, int *rw_fd_ptr, int *ro_fd_ptr) { char name[] = RANDNAME_PATTERN; int rw_fd = excl_shm_open(name); if (rw_fd < 0) { return false; } // CLOEXEC is guaranteed to be set by shm_open int ro_fd = shm_open(name, O_RDONLY, 0); if (ro_fd < 0) { shm_unlink(name); close(rw_fd); return false; } shm_unlink(name); // Make sure the file cannot be re-opened in read-write mode (e.g. via // "/proc/self/fd/" on Linux) if (fchmod(rw_fd, 0) != 0) { close(rw_fd); close(ro_fd); return false; } int ret; do { ret = ftruncate(rw_fd, size); } while (ret < 0 && errno == EINTR); if (ret < 0) { close(rw_fd); close(ro_fd); return false; } *rw_fd_ptr = rw_fd; *ro_fd_ptr = ro_fd; return true; } wlroots-0.17.1/util/time.c000066400000000000000000000015531454110342200153700ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200809L #include #include #include "util/time.h" static const long NSEC_PER_SEC = 1000000000; int64_t timespec_to_msec(const struct timespec *a) { return (int64_t)a->tv_sec * 1000 + a->tv_nsec / 1000000; } int64_t timespec_to_nsec(const struct timespec *a) { return (int64_t)a->tv_sec * NSEC_PER_SEC + a->tv_nsec; } void timespec_from_nsec(struct timespec *r, int64_t nsec) { r->tv_sec = nsec / NSEC_PER_SEC; r->tv_nsec = nsec % NSEC_PER_SEC; } int64_t get_current_time_msec(void) { struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); return timespec_to_msec(&now); } void timespec_sub(struct timespec *r, const struct timespec *a, const struct timespec *b) { r->tv_sec = a->tv_sec - b->tv_sec; r->tv_nsec = a->tv_nsec - b->tv_nsec; if (r->tv_nsec < 0) { r->tv_sec--; r->tv_nsec += NSEC_PER_SEC; } } wlroots-0.17.1/util/token.c000066400000000000000000000017211454110342200155470ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200809L #include "util/token.h" #include "wlr/util/log.h" #include #include #include #include #include #include #include #include bool generate_token(char out[static TOKEN_SIZE]) { static FILE *urandom = NULL; uint64_t data[2]; if (!urandom) { int fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC); if (fd < 0) { wlr_log_errno(WLR_ERROR, "Failed to open random device"); return false; } if (!(urandom = fdopen(fd, "r"))) { wlr_log_errno(WLR_ERROR, "fdopen failed"); close(fd); return false; } } if (fread(data, sizeof(data), 1, urandom) != 1) { wlr_log_errno(WLR_ERROR, "Failed to read from random device"); return false; } if (snprintf(out, TOKEN_SIZE, "%016" PRIx64 "%016" PRIx64, data[0], data[1]) != TOKEN_SIZE - 1) { wlr_log_errno(WLR_ERROR, "Failed to format hex string token"); return false; } return true; } wlroots-0.17.1/wlroots.syms000066400000000000000000000000561454110342200157340ustar00rootroot00000000000000{ global: wlr_*; _wlr_*; local: *; }; wlroots-0.17.1/xcursor/000077500000000000000000000000001454110342200150125ustar00rootroot00000000000000wlroots-0.17.1/xcursor/meson.build000066400000000000000000000003461454110342200171570ustar00rootroot00000000000000icondir = get_option('icon_directory') if icondir == '' icondir = get_option('prefix') / get_option('datadir') / 'icons' endif internal_config.set_quoted('ICONDIR', icondir) wlr_files += files( 'wlr_xcursor.c', 'xcursor.c', ) wlroots-0.17.1/xcursor/wlr_xcursor.c000066400000000000000000000221631454110342200175530ustar00rootroot00000000000000/* * Copyright © 2012 Intel Corporation * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice (including the * next paragraph) shall be included in all copies or substantial * portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include "xcursor/xcursor.h" static void xcursor_destroy(struct wlr_xcursor *cursor) { for (size_t i = 0; i < cursor->image_count; i++) { free(cursor->images[i]->buffer); free(cursor->images[i]); } free(cursor->images); free(cursor->name); free(cursor); } #include "xcursor/cursor_data.h" static struct wlr_xcursor *xcursor_create_from_data( const struct cursor_metadata *metadata, struct wlr_xcursor_theme *theme) { struct wlr_xcursor *cursor = calloc(1, sizeof(*cursor)); if (!cursor) { return NULL; } cursor->image_count = 1; cursor->images = calloc(1, sizeof(*cursor->images)); if (!cursor->images) { goto err_free_cursor; } cursor->name = strdup(metadata->name); cursor->total_delay = 0; struct wlr_xcursor_image *image = calloc(1, sizeof(*image)); if (!image) { goto err_free_images; } cursor->images[0] = image; image->buffer = NULL; image->width = metadata->width; image->height = metadata->height; image->hotspot_x = metadata->hotspot_x; image->hotspot_y = metadata->hotspot_y; image->delay = 0; int size = metadata->width * metadata->height * sizeof(uint32_t); image->buffer = malloc(size); if (!image->buffer) { goto err_free_image; } memcpy(image->buffer, cursor_data + metadata->offset, size); return cursor; err_free_image: free(image); err_free_images: free(cursor->name); free(cursor->images); err_free_cursor: free(cursor); return NULL; } static void load_default_theme(struct wlr_xcursor_theme *theme) { free(theme->name); theme->name = strdup("default"); size_t cursor_count = sizeof(cursor_metadata) / sizeof(cursor_metadata[0]); theme->cursor_count = 0; theme->cursors = malloc(cursor_count * sizeof(*theme->cursors)); if (theme->cursors == NULL) { return; } for (uint32_t i = 0; i < cursor_count; ++i) { theme->cursors[i] = xcursor_create_from_data(&cursor_metadata[i], theme); if (theme->cursors[i] == NULL) { break; } ++theme->cursor_count; } } static struct wlr_xcursor *xcursor_create_from_xcursor_images( struct xcursor_images *images, struct wlr_xcursor_theme *theme) { struct wlr_xcursor *cursor = calloc(1, sizeof(*cursor)); if (!cursor) { return NULL; } cursor->images = calloc(images->nimage, sizeof(cursor->images[0])); if (!cursor->images) { free(cursor); return NULL; } cursor->name = strdup(images->name); cursor->total_delay = 0; for (int i = 0; i < images->nimage; i++) { struct wlr_xcursor_image *image = calloc(1, sizeof(*image)); if (image == NULL) { break; } image->buffer = NULL; image->width = images->images[i]->width; image->height = images->images[i]->height; image->hotspot_x = images->images[i]->xhot; image->hotspot_y = images->images[i]->yhot; image->delay = images->images[i]->delay; size_t size = image->width * image->height * 4; image->buffer = malloc(size); if (!image->buffer) { free(image); break; } /* copy pixels to shm pool */ memcpy(image->buffer, images->images[i]->pixels, size); cursor->total_delay += image->delay; cursor->images[i] = image; cursor->image_count++; } if (cursor->image_count == 0) { free(cursor->name); free(cursor->images); free(cursor); return NULL; } return cursor; } static struct wlr_xcursor *xcursor_theme_get_cursor(struct wlr_xcursor_theme *theme, const char *name); static void load_callback(struct xcursor_images *images, void *data) { struct wlr_xcursor_theme *theme = data; if (xcursor_theme_get_cursor(theme, images->name)) { xcursor_images_destroy(images); return; } struct wlr_xcursor *cursor = xcursor_create_from_xcursor_images(images, theme); if (cursor) { theme->cursor_count++; struct wlr_xcursor **cursors = realloc(theme->cursors, theme->cursor_count * sizeof(theme->cursors[0])); if (cursors == NULL) { theme->cursor_count--; free(cursor); } else { theme->cursors = cursors; theme->cursors[theme->cursor_count - 1] = cursor; } } xcursor_images_destroy(images); } struct wlr_xcursor_theme *wlr_xcursor_theme_load(const char *name, int size) { struct wlr_xcursor_theme *theme = calloc(1, sizeof(*theme)); if (!theme) { return NULL; } if (!name) { name = "default"; } theme->name = strdup(name); if (!theme->name) { goto out_error_name; } theme->size = size; theme->cursor_count = 0; theme->cursors = NULL; xcursor_load_theme(name, size, load_callback, theme); if (theme->cursor_count == 0) { load_default_theme(theme); } wlr_log(WLR_DEBUG, "Loaded cursor theme '%s' at size %d (%d available cursors)", theme->name, size, theme->cursor_count); return theme; out_error_name: free(theme); return NULL; } void wlr_xcursor_theme_destroy(struct wlr_xcursor_theme *theme) { for (unsigned int i = 0; i < theme->cursor_count; i++) { xcursor_destroy(theme->cursors[i]); } free(theme->name); free(theme->cursors); free(theme); } static struct wlr_xcursor *xcursor_theme_get_cursor(struct wlr_xcursor_theme *theme, const char *name) { for (unsigned int i = 0; i < theme->cursor_count; i++) { if (strcmp(name, theme->cursors[i]->name) == 0) { return theme->cursors[i]; } } return NULL; } struct wlr_xcursor *wlr_xcursor_theme_get_cursor(struct wlr_xcursor_theme *theme, const char *name) { struct wlr_xcursor *xcursor = xcursor_theme_get_cursor(theme, name); if (xcursor) { return xcursor; } // Try the legacy name as a fallback const char *fallback; if (strcmp(name, "default") == 0) { fallback = "left_ptr"; } else if (strcmp(name, "text") == 0) { fallback = "xterm"; } else if (strcmp(name, "pointer") == 0) { fallback = "hand1"; } else if (strcmp(name, "wait") == 0) { fallback = "watch"; } else if (strcmp(name, "all-scroll") == 0) { fallback = "grabbing"; } else if (strcmp(name, "sw-resize") == 0) { fallback = "bottom_left_corner"; } else if (strcmp(name, "se-resize") == 0) { fallback = "bottom_right_corner"; } else if (strcmp(name, "s-resize") == 0) { fallback = "bottom_side"; } else if (strcmp(name, "w-resize") == 0) { fallback = "left_side"; } else if (strcmp(name, "e-resize") == 0) { fallback = "right_side"; } else if (strcmp(name, "nw-resize") == 0) { fallback = "top_left_corner"; } else if (strcmp(name, "ne-resize") == 0) { fallback = "top_right_corner"; } else if (strcmp(name, "n-resize") == 0) { fallback = "top_side"; } else { return NULL; } return xcursor_theme_get_cursor(theme, fallback); } static int xcursor_frame_and_duration(struct wlr_xcursor *cursor, uint32_t time, uint32_t *duration) { if (cursor->image_count == 1) { if (duration) { *duration = 0; } return 0; } int i = 0; uint32_t t = time % cursor->total_delay; /* If there is a 0 delay in the image set then this * loop breaks on it and we display that cursor until * time % cursor->total_delay wraps again. * Since a 0 delay is silly, and we've never actually * seen one in a cursor file, we haven't bothered to * "fix" this. */ while (t - cursor->images[i]->delay < t) { t -= cursor->images[i++]->delay; } if (!duration) { return i; } /* Make sure we don't accidentally tell the caller this is * a static cursor image. */ if (t >= cursor->images[i]->delay) { *duration = 1; } else { *duration = cursor->images[i]->delay - t; } return i; } int wlr_xcursor_frame(struct wlr_xcursor *_cursor, uint32_t time) { return xcursor_frame_and_duration(_cursor, time, NULL); } const char *wlr_xcursor_get_resize_name(enum wlr_edges edges) { if (edges & WLR_EDGE_TOP) { if (edges & WLR_EDGE_RIGHT) { return "ne-resize"; } else if (edges & WLR_EDGE_LEFT) { return "nw-resize"; } return "n-resize"; } else if (edges & WLR_EDGE_BOTTOM) { if (edges & WLR_EDGE_RIGHT) { return "se-resize"; } else if (edges & WLR_EDGE_LEFT) { return "sw-resize"; } return "s-resize"; } else if (edges & WLR_EDGE_RIGHT) { return "e-resize"; } else if (edges & WLR_EDGE_LEFT) { return "w-resize"; } return "se-resize"; // fallback } wlroots-0.17.1/xcursor/xcursor.c000066400000000000000000000446161454110342200166760ustar00rootroot00000000000000/* * Copyright © 2002 Keith Packard * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice (including the * next paragraph) shall be included in all copies or substantial * portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #define _DEFAULT_SOURCE #include #include #include #include #include #include "config.h" #include "xcursor/xcursor.h" /* * Cursor files start with a header. The header * contains a magic number, a version number and a * table of contents which has type and offset information * for the remaining tables in the file. * * File minor versions increment for compatible changes * File major versions increment for incompatible changes (never, we hope) * * Chunks of the same type are always upward compatible. Incompatible * changes are made with new chunk types; the old data can remain under * the old type. Upward compatible changes can add header data as the * header lengths are specified in the file. * * File: * FileHeader * LISTofChunk * * FileHeader: * CARD32 magic magic number * CARD32 header bytes in file header * CARD32 version file version * CARD32 ntoc number of toc entries * LISTofFileToc toc table of contents * * FileToc: * CARD32 type entry type * CARD32 subtype entry subtype (size for images) * CARD32 position absolute file position */ #define XCURSOR_MAGIC 0x72756358 /* "Xcur" LSBFirst */ /* * This version number is stored in cursor files; changes to the * file format require updating this version number */ #define XCURSOR_FILE_MAJOR 1 #define XCURSOR_FILE_MINOR 0 #define XCURSOR_FILE_VERSION ((XCURSOR_FILE_MAJOR << 16) | (XCURSOR_FILE_MINOR)) #define XCURSOR_FILE_HEADER_LEN (4 * 4) #define XCURSOR_FILE_TOC_LEN (3 * 4) struct xcursor_file_toc { uint32_t type; /* chunk type */ uint32_t subtype; /* subtype (size for images) */ uint32_t position; /* absolute position in file */ }; struct xcursor_file_header { uint32_t magic; /* magic number */ uint32_t header; /* byte length of header */ uint32_t version; /* file version number */ uint32_t ntoc; /* number of toc entries */ struct xcursor_file_toc *tocs; /* table of contents */ }; /* * The rest of the file is a list of chunks, each tagged by type * and version. * * Chunk: * ChunkHeader * * * * ChunkHeader: * CARD32 header bytes in chunk header + type header * CARD32 type chunk type * CARD32 subtype chunk subtype * CARD32 version chunk type version */ #define XCURSOR_CHUNK_HEADER_LEN (4 * 4) struct xcursor_chunk_header { uint32_t header; /* bytes in chunk header */ uint32_t type; /* chunk type */ uint32_t subtype; /* chunk subtype (size for images) */ uint32_t version; /* version of this type */ }; /* * Each cursor image occupies a separate image chunk. * The length of the image header follows the chunk header * so that future versions can extend the header without * breaking older applications * * Image: * ChunkHeader header chunk header * CARD32 width actual width * CARD32 height actual height * CARD32 xhot hot spot x * CARD32 yhot hot spot y * CARD32 delay animation delay * LISTofCARD32 pixels ARGB pixels */ #define XCURSOR_IMAGE_TYPE 0xfffd0002 #define XCURSOR_IMAGE_VERSION 1 #define XCURSOR_IMAGE_HEADER_LEN (XCURSOR_CHUNK_HEADER_LEN + (5*4)) #define XCURSOR_IMAGE_MAX_SIZE 0x7fff /* 32767x32767 max cursor size */ /* * From libXcursor/src/file.c */ static struct xcursor_image * xcursor_image_create(int width, int height) { struct xcursor_image *image; if (width < 0 || height < 0) return NULL; if (width > XCURSOR_IMAGE_MAX_SIZE || height > XCURSOR_IMAGE_MAX_SIZE) return NULL; image = malloc(sizeof(*image) + width * height * sizeof(uint32_t)); if (!image) return NULL; image->version = XCURSOR_IMAGE_VERSION; image->pixels = (uint32_t *) (image + 1); image->size = width > height ? width : height; image->width = width; image->height = height; image->delay = 0; return image; } static void xcursor_image_destroy(struct xcursor_image *image) { free(image); } static struct xcursor_images * xcursor_images_create(int size) { struct xcursor_images *images; images = malloc(sizeof(*images) + size * sizeof(struct xcursor_image *)); if (!images) return NULL; images->nimage = 0; images->images = (struct xcursor_image **) (images + 1); images->name = NULL; return images; } void xcursor_images_destroy(struct xcursor_images *images) { int n; if (!images) return; for (n = 0; n < images->nimage; n++) xcursor_image_destroy(images->images[n]); free(images->name); free(images); } static bool xcursor_read_uint(FILE *file, uint32_t *u) { unsigned char bytes[4]; if (!file || !u) return false; if (fread(bytes, 1, 4, file) != 4) return false; *u = ((uint32_t)(bytes[0]) << 0) | ((uint32_t)(bytes[1]) << 8) | ((uint32_t)(bytes[2]) << 16) | ((uint32_t)(bytes[3]) << 24); return true; } static void xcursor_file_header_destroy(struct xcursor_file_header *file_header) { free(file_header); } static struct xcursor_file_header * xcursor_file_header_create(uint32_t ntoc) { struct xcursor_file_header *file_header; if (ntoc > 0x10000) return NULL; file_header = malloc(sizeof(*file_header) + ntoc * sizeof(struct xcursor_file_toc)); if (!file_header) return NULL; file_header->magic = XCURSOR_MAGIC; file_header->header = XCURSOR_FILE_HEADER_LEN; file_header->version = XCURSOR_FILE_VERSION; file_header->ntoc = ntoc; file_header->tocs = (struct xcursor_file_toc *) (file_header + 1); return file_header; } static struct xcursor_file_header * xcursor_read_file_header(FILE *file) { struct xcursor_file_header head, *file_header; uint32_t skip; unsigned int n; if (!file) return NULL; if (!xcursor_read_uint(file, &head.magic)) return NULL; if (head.magic != XCURSOR_MAGIC) return NULL; if (!xcursor_read_uint(file, &head.header)) return NULL; if (!xcursor_read_uint(file, &head.version)) return NULL; if (!xcursor_read_uint(file, &head.ntoc)) return NULL; skip = head.header - XCURSOR_FILE_HEADER_LEN; if (skip) if (fseek(file, skip, SEEK_CUR) == EOF) return NULL; file_header = xcursor_file_header_create(head.ntoc); if (!file_header) return NULL; file_header->magic = head.magic; file_header->header = head.header; file_header->version = head.version; file_header->ntoc = head.ntoc; for (n = 0; n < file_header->ntoc; n++) { if (!xcursor_read_uint(file, &file_header->tocs[n].type)) break; if (!xcursor_read_uint(file, &file_header->tocs[n].subtype)) break; if (!xcursor_read_uint(file, &file_header->tocs[n].position)) break; } if (n != file_header->ntoc) { xcursor_file_header_destroy(file_header); return NULL; } return file_header; } static bool xcursor_seek_to_toc(FILE *file, struct xcursor_file_header *file_header, int toc) { if (!file || !file_header || fseek(file, file_header->tocs[toc].position, SEEK_SET) == EOF) return false; return true; } static bool xcursor_file_read_chunk_header(FILE *file, struct xcursor_file_header *file_header, int toc, struct xcursor_chunk_header *chunk_header) { if (!file || !file_header || !chunk_header) return false; if (!xcursor_seek_to_toc(file, file_header, toc)) return false; if (!xcursor_read_uint(file, &chunk_header->header)) return false; if (!xcursor_read_uint(file, &chunk_header->type)) return false; if (!xcursor_read_uint(file, &chunk_header->subtype)) return false; if (!xcursor_read_uint(file, &chunk_header->version)) return false; /* sanity check */ if (chunk_header->type != file_header->tocs[toc].type || chunk_header->subtype != file_header->tocs[toc].subtype) return false; return true; } static uint32_t dist(uint32_t a, uint32_t b) { return a > b ? a - b : b - a; } static uint32_t xcursor_file_best_size(struct xcursor_file_header *file_header, uint32_t size, int *nsizesp) { unsigned int n; int nsizes = 0; uint32_t best_size = 0; uint32_t this_size; if (!file_header || !nsizesp) return 0; for (n = 0; n < file_header->ntoc; n++) { if (file_header->tocs[n].type != XCURSOR_IMAGE_TYPE) continue; this_size = file_header->tocs[n].subtype; if (!best_size || dist(this_size, size) < dist(best_size, size)) { best_size = this_size; nsizes = 1; } else if (this_size == best_size) { nsizes++; } } *nsizesp = nsizes; return best_size; } static int xcursor_find_image_toc(struct xcursor_file_header *file_header, uint32_t size, int count) { unsigned int toc; uint32_t this_size; if (!file_header) return 0; for (toc = 0; toc < file_header->ntoc; toc++) { if (file_header->tocs[toc].type != XCURSOR_IMAGE_TYPE) continue; this_size = file_header->tocs[toc].subtype; if (this_size != size) continue; if (!count) break; count--; } if (toc == file_header->ntoc) return -1; return toc; } static struct xcursor_image * xcursor_read_image(FILE *file, struct xcursor_file_header *file_header, int toc) { struct xcursor_chunk_header chunk_header; struct xcursor_image head; struct xcursor_image *image; int n; uint32_t *p; if (!file || !file_header) return NULL; if (!xcursor_file_read_chunk_header(file, file_header, toc, &chunk_header)) return NULL; if (!xcursor_read_uint(file, &head.width)) return NULL; if (!xcursor_read_uint(file, &head.height)) return NULL; if (!xcursor_read_uint(file, &head.xhot)) return NULL; if (!xcursor_read_uint(file, &head.yhot)) return NULL; if (!xcursor_read_uint(file, &head.delay)) return NULL; /* sanity check data */ if (head.width > XCURSOR_IMAGE_MAX_SIZE || head.height > XCURSOR_IMAGE_MAX_SIZE) return NULL; if (head.width == 0 || head.height == 0) return NULL; if (head.xhot > head.width || head.yhot > head.height) return NULL; /* Create the image and initialize it */ image = xcursor_image_create(head.width, head.height); if (image == NULL) return NULL; if (chunk_header.version < image->version) image->version = chunk_header.version; image->size = chunk_header.subtype; image->xhot = head.xhot; image->yhot = head.yhot; image->delay = head.delay; n = image->width * image->height; p = image->pixels; while (n--) { if (!xcursor_read_uint(file, p)) { xcursor_image_destroy(image); return NULL; } p++; } return image; } static struct xcursor_images * xcursor_xc_file_load_images(FILE *file, int size) { struct xcursor_file_header *file_header; uint32_t best_size; int nsize; struct xcursor_images *images; int n; int toc; if (!file || size < 0) return NULL; file_header = xcursor_read_file_header(file); if (!file_header) return NULL; best_size = xcursor_file_best_size(file_header, (uint32_t) size, &nsize); if (!best_size) { xcursor_file_header_destroy(file_header); return NULL; } images = xcursor_images_create(nsize); if (!images) { xcursor_file_header_destroy(file_header); return NULL; } for (n = 0; n < nsize; n++) { toc = xcursor_find_image_toc(file_header, best_size, n); if (toc < 0) break; images->images[images->nimage] = xcursor_read_image(file, file_header, toc); if (!images->images[images->nimage]) break; images->nimage++; } xcursor_file_header_destroy(file_header); if (images->nimage != nsize) { xcursor_images_destroy(images); images = NULL; } return images; } /* * From libXcursor/src/library.c */ #ifndef ICONDIR #define ICONDIR "/usr/X11R6/lib/X11/icons" #endif #ifndef XCURSORPATH #define XCURSORPATH "~/.icons:/usr/share/icons:/usr/share/pixmaps:~/.cursors:/usr/share/cursors/xorg-x11:"ICONDIR #endif #define XDG_DATA_HOME_FALLBACK "~/.local/share" #define CURSORDIR "/icons" /** Get search path for cursor themes * * This function builds the list of directories to look for cursor * themes in. The format is PATH-like: directories are separated by * colons. * * The memory block returned by this function is allocated on the heap * and must be freed by the caller. */ static char * xcursor_library_path(void) { const char *env_var, *suffix; char *path; size_t path_size; env_var = getenv("XCURSOR_PATH"); if (env_var) return strdup(env_var); env_var = getenv("XDG_DATA_HOME"); if (!env_var || env_var[0] != '/') env_var = XDG_DATA_HOME_FALLBACK; suffix = CURSORDIR ":" XCURSORPATH; path_size = strlen(env_var) + strlen(suffix) + 1; path = malloc(path_size); if (!path) return NULL; snprintf(path, path_size, "%s%s", env_var, suffix); return path; } static char * xcursor_build_theme_dir(const char *dir, const char *theme) { const char *colon; const char *tcolon; char *full; const char *home, *homesep; size_t dirlen; size_t homelen; size_t themelen; size_t full_size; if (!dir || !theme) return NULL; colon = strchr(dir, ':'); if (!colon) colon = dir + strlen(dir); dirlen = colon - dir; tcolon = strchr(theme, ':'); if (!tcolon) tcolon = theme + strlen(theme); themelen = tcolon - theme; home = ""; homelen = 0; homesep = ""; if (*dir == '~') { home = getenv("HOME"); if (!home) return NULL; homelen = strlen(home); homesep = "/"; dir++; dirlen--; } /* * add space for any needed directory separators, one per component, * and one for the trailing null */ full_size = 1 + homelen + 1 + dirlen + 1 + themelen + 1; full = malloc(full_size); if (!full) return NULL; snprintf(full, full_size, "%s%s%.*s/%.*s", home, homesep, (int)dirlen, dir, (int)themelen, theme); return full; } static char * xcursor_build_fullname(const char *dir, const char *subdir, const char *file) { char *full; size_t full_size; if (!dir || !subdir || !file) return NULL; full_size = strlen(dir) + 1 + strlen(subdir) + 1 + strlen(file) + 1; full = malloc(full_size); if (!full) return NULL; snprintf(full, full_size, "%s/%s/%s", dir, subdir, file); return full; } static const char * xcursor_next_path(const char *path) { char *colon = strchr(path, ':'); if (!colon) return NULL; return colon + 1; } static bool xcursor_white(char c) { return c == ' ' || c == '\t' || c == '\n'; } static bool xcursor_sep(char c) { return c == ';' || c == ','; } static char * xcursor_theme_inherits(const char *full) { char *line = NULL; size_t line_size = 0; char *result = NULL; FILE *f; if (!full) return NULL; f = fopen(full, "r"); if (!f) return NULL; while (getline(&line, &line_size, f) >= 0) { const char *l; char *r; if (strncmp(line, "Inherits", 8)) continue; l = line + 8; while (*l == ' ') l++; if (*l != '=') continue; l++; while (*l == ' ') l++; result = malloc(strlen(l) + 1); if (!result) break; r = result; while (*l) { while (xcursor_sep(*l) || xcursor_white(*l)) l++; if (!*l) break; if (r != result) *r++ = ':'; while (*l && !xcursor_white(*l) && !xcursor_sep(*l)) *r++ = *l++; } *r++ = '\0'; break; } fclose(f); free(line); return result; } static void load_all_cursors_from_dir(const char *path, int size, void (*load_callback)(struct xcursor_images *, void *), void *user_data) { FILE *f; DIR *dir = opendir(path); struct dirent *ent; char *full; struct xcursor_images *images; if (!dir) return; for (ent = readdir(dir); ent; ent = readdir(dir)) { #ifdef _DIRENT_HAVE_D_TYPE if (ent->d_type != DT_UNKNOWN && ent->d_type != DT_REG && ent->d_type != DT_LNK) continue; #endif full = xcursor_build_fullname(path, "", ent->d_name); if (!full) continue; f = fopen(full, "r"); if (!f) { free(full); continue; } images = xcursor_xc_file_load_images(f, size); if (images) { images->name = strdup(ent->d_name); load_callback(images, user_data); } fclose(f); free(full); } closedir(dir); } /** Load all the cursor of a theme * * This function loads all the cursor images of a given theme and its * inherited themes. Each cursor is loaded into an struct xcursor_images object * which is passed to the caller's load callback. If a cursor appears * more than once across all the inherited themes, the load callback * will be called multiple times, with possibly different struct xcursor_images * object which have the same name. The user is expected to destroy the * struct xcursor_images objects passed to the callback with * xcursor_images_destroy(). * * \param theme The name of theme that should be loaded * \param size The desired size of the cursor images * \param load_callback A callback function that will be called * for each cursor loaded. The first parameter is the struct xcursor_images * object representing the loaded cursor and the second is a pointer * to data provided by the user. * \param user_data The data that should be passed to the load callback */ void xcursor_load_theme(const char *theme, int size, void (*load_callback)(struct xcursor_images *, void *), void *user_data) { char *full, *dir; char *inherits = NULL; const char *path, *i; char *xcursor_path; if (!theme) theme = "default"; xcursor_path = xcursor_library_path(); for (path = xcursor_path; path; path = xcursor_next_path(path)) { dir = xcursor_build_theme_dir(path, theme); if (!dir) continue; full = xcursor_build_fullname(dir, "cursors", ""); if (full) { load_all_cursors_from_dir(full, size, load_callback, user_data); free(full); } if (!inherits) { full = xcursor_build_fullname(dir, "", "index.theme"); inherits = xcursor_theme_inherits(full); free(full); } free(dir); } for (i = inherits; i; i = xcursor_next_path(i)) xcursor_load_theme(i, size, load_callback, user_data); free(inherits); free(xcursor_path); } wlroots-0.17.1/xwayland/000077500000000000000000000000001454110342200151345ustar00rootroot00000000000000wlroots-0.17.1/xwayland/meson.build000066400000000000000000000040371454110342200173020ustar00rootroot00000000000000xwayland_libs = [] xwayland_required = [ 'xcb', 'xcb-composite', 'xcb-ewmh', 'xcb-icccm', 'xcb-render', 'xcb-res', 'xcb-xfixes', ] xwayland_optional = { 'xcb-errors': 'Required for printing X11 errors.', } msg = [] if get_option('xwayland').enabled() msg += 'Install "@0@" or pass "-Dxwayland=disabled".' endif if not get_option('xwayland').disabled() msg += 'Required for Xwayland support.' endif xwayland = dependency( 'xwayland', required: get_option('xwayland'), fallback: 'xserver', default_options: [ 'werror=false', 'xorg=false', 'xephyr=false', 'xwayland=true', 'xnest=false', 'xvfb=false', ], ) if not xwayland.found() subdir_done() endif foreach lib : xwayland_required dep = dependency(lib, required: get_option('xwayland'), not_found_message: '\n'.join(msg).format(lib), ) if not dep.found() subdir_done() endif xwayland_libs += dep endforeach foreach lib, desc : xwayland_optional msg = [] if get_option(lib).enabled() msg += 'Install "@0@" or pass "-D@0@=disabled".' endif if not get_option(lib).disabled() msg += desc endif dep = dependency(lib, required: get_option(lib), not_found_message: '\n'.join(msg).format(lib), ) internal_features += { lib: dep.found() } xwayland_libs += dep endforeach xwayland_feature_names = [ 'listenfd', 'no_touch_pointer_emulation', 'force_xrandr_emulation', 'terminate_delay', ] internal_config.set_quoted('XWAYLAND_PATH', xwayland.get_variable('xwayland')) foreach name : xwayland_feature_names have = xwayland.get_variable('have_' + name, default_value: 'false') == 'true' internal_config.set10('HAVE_XWAYLAND_' + name.to_upper(), have) endforeach wlr_files += files( 'selection/dnd.c', 'selection/incoming.c', 'selection/outgoing.c', 'selection/selection.c', 'server.c', 'shell.c', 'sockets.c', 'xwayland.c', 'xwm.c', ) wlr_deps += xwayland_libs features += { 'xwayland': true } have = cc.has_function('xcb_xfixes_set_client_disconnect_mode', dependencies: xwayland_libs) internal_config.set10('HAVE_XCB_XFIXES_SET_CLIENT_DISCONNECT_MODE', have) wlroots-0.17.1/xwayland/selection/000077500000000000000000000000001454110342200171215ustar00rootroot00000000000000wlroots-0.17.1/xwayland/selection/dnd.c000066400000000000000000000236671454110342200200500ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include "xwayland/xwm.h" #include "xwayland/selection.h" static xcb_atom_t data_device_manager_dnd_action_to_atom( struct wlr_xwm *xwm, enum wl_data_device_manager_dnd_action action) { if (action & WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY) { return xwm->atoms[DND_ACTION_COPY]; } else if (action & WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE) { return xwm->atoms[DND_ACTION_MOVE]; } else if (action & WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK) { return xwm->atoms[DND_ACTION_ASK]; } return XCB_ATOM_NONE; } static enum wl_data_device_manager_dnd_action data_device_manager_dnd_action_from_atom(struct wlr_xwm *xwm, enum atom_name atom) { if (atom == xwm->atoms[DND_ACTION_COPY] || atom == xwm->atoms[DND_ACTION_PRIVATE]) { return WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY; } else if (atom == xwm->atoms[DND_ACTION_MOVE]) { return WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE; } else if (atom == xwm->atoms[DND_ACTION_ASK]) { return WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK; } return WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE; } static void xwm_dnd_send_event(struct wlr_xwm *xwm, xcb_atom_t type, xcb_client_message_data_t *data) { struct wlr_xwayland_surface *dest = xwm->drag_focus; assert(dest != NULL); xcb_client_message_event_t event = { .response_type = XCB_CLIENT_MESSAGE, .format = 32, .sequence = 0, .window = dest->window_id, .type = type, .data = *data, }; xcb_send_event(xwm->xcb_conn, 0, // propagate dest->window_id, XCB_EVENT_MASK_NO_EVENT, (const char *)&event); xcb_flush(xwm->xcb_conn); } static void xwm_dnd_send_enter(struct wlr_xwm *xwm) { struct wlr_drag *drag = xwm->drag; assert(drag != NULL); struct wl_array *mime_types = &drag->source->mime_types; xcb_client_message_data_t data = { 0 }; data.data32[0] = xwm->dnd_selection.window; data.data32[1] = XDND_VERSION << 24; // If we have 3 MIME types or less, we can send them directly in the // DND_ENTER message size_t n = mime_types->size / sizeof(char *); if (n <= 3) { size_t i = 0; char **mime_type_ptr; wl_array_for_each(mime_type_ptr, mime_types) { char *mime_type = *mime_type_ptr; data.data32[2+i] = xwm_mime_type_to_atom(xwm, mime_type); ++i; } } else { // Let the client know that targets are not contained in the message // data and must be retrieved with the DND_TYPE_LIST property data.data32[1] |= 1; xcb_atom_t targets[n]; size_t i = 0; char **mime_type_ptr; wl_array_for_each(mime_type_ptr, mime_types) { char *mime_type = *mime_type_ptr; targets[i] = xwm_mime_type_to_atom(xwm, mime_type); ++i; } xcb_change_property(xwm->xcb_conn, XCB_PROP_MODE_REPLACE, xwm->dnd_selection.window, xwm->atoms[DND_TYPE_LIST], XCB_ATOM_ATOM, 32, // format n, targets); } xwm_dnd_send_event(xwm, xwm->atoms[DND_ENTER], &data); } static void xwm_dnd_send_position(struct wlr_xwm *xwm, uint32_t time, int16_t x, int16_t y) { struct wlr_drag *drag = xwm->drag; assert(drag != NULL); xcb_client_message_data_t data = { 0 }; data.data32[0] = xwm->dnd_selection.window; data.data32[2] = (x << 16) | y; data.data32[3] = time; data.data32[4] = data_device_manager_dnd_action_to_atom(xwm, drag->source->actions); xwm_dnd_send_event(xwm, xwm->atoms[DND_POSITION], &data); } static void xwm_dnd_send_drop(struct wlr_xwm *xwm, uint32_t time) { struct wlr_drag *drag = xwm->drag; assert(drag != NULL); struct wlr_xwayland_surface *dest = xwm->drag_focus; assert(dest != NULL); xcb_client_message_data_t data = { 0 }; data.data32[0] = xwm->dnd_selection.window; data.data32[2] = time; xwm_dnd_send_event(xwm, xwm->atoms[DND_DROP], &data); } static void xwm_dnd_send_leave(struct wlr_xwm *xwm) { struct wlr_drag *drag = xwm->drag; assert(drag != NULL); struct wlr_xwayland_surface *dest = xwm->drag_focus; assert(dest != NULL); xcb_client_message_data_t data = { 0 }; data.data32[0] = xwm->dnd_selection.window; xwm_dnd_send_event(xwm, xwm->atoms[DND_LEAVE], &data); } /*static void xwm_dnd_send_finished(struct wlr_xwm *xwm) { struct wlr_drag *drag = xwm->drag; assert(drag != NULL); struct wlr_xwayland_surface *dest = xwm->drag_focus; assert(dest != NULL); xcb_client_message_data_t data = { 0 }; data.data32[0] = xwm->dnd_selection.window; data.data32[1] = drag->source->accepted; if (drag->source->accepted) { data.data32[2] = data_device_manager_dnd_action_to_atom(xwm, drag->source->current_dnd_action); } xwm_dnd_send_event(xwm, xwm->atoms[DND_FINISHED], &data); }*/ int xwm_handle_selection_client_message(struct wlr_xwm *xwm, xcb_client_message_event_t *ev) { if (ev->type == xwm->atoms[DND_STATUS]) { if (xwm->drag == NULL) { wlr_log(WLR_DEBUG, "ignoring XdndStatus client message because " "there's no drag"); return 1; } xcb_client_message_data_t *data = &ev->data; xcb_window_t target_window = data->data32[0]; bool accepted = data->data32[1] & 1; xcb_atom_t action_atom = data->data32[4]; if (xwm->drag_focus == NULL || target_window != xwm->drag_focus->window_id) { wlr_log(WLR_DEBUG, "ignoring XdndStatus client message because " "it doesn't match the current drag focus window ID"); return 1; } enum wl_data_device_manager_dnd_action action = data_device_manager_dnd_action_from_atom(xwm, action_atom); struct wlr_drag *drag = xwm->drag; assert(drag != NULL); drag->source->accepted = accepted; wlr_data_source_dnd_action(drag->source, action); wlr_log(WLR_DEBUG, "DND_STATUS window=%" PRIu32 " accepted=%d action=%d", target_window, accepted, action); return 1; } else if (ev->type == xwm->atoms[DND_FINISHED]) { // This should only happen after the drag has ended, but before the drag // source is destroyed if (xwm->seat == NULL || xwm->seat->drag_source == NULL || xwm->drag != NULL) { wlr_log(WLR_DEBUG, "ignoring XdndFinished client message because " "there's no finished drag"); return 1; } struct wlr_data_source *source = xwm->seat->drag_source; xcb_client_message_data_t *data = &ev->data; xcb_window_t target_window = data->data32[0]; bool performed = data->data32[1] & 1; xcb_atom_t action_atom = data->data32[2]; if (xwm->drag_focus == NULL || target_window != xwm->drag_focus->window_id) { wlr_log(WLR_DEBUG, "ignoring XdndFinished client message because " "it doesn't match the finished drag focus window ID"); return 1; } enum wl_data_device_manager_dnd_action action = data_device_manager_dnd_action_from_atom(xwm, action_atom); if (performed) { wlr_data_source_dnd_finish(source); } wlr_log(WLR_DEBUG, "DND_FINISH window=%" PRIu32 " performed=%d action=%d", target_window, performed, action); return 1; } else { return 0; } } static void seat_handle_drag_focus(struct wl_listener *listener, void *data) { struct wlr_drag *drag = data; struct wlr_xwm *xwm = wl_container_of(listener, xwm, seat_drag_focus); struct wlr_xwayland_surface *focus = NULL; if (drag->focus != NULL) { // TODO: check for subsurfaces? struct wlr_xwayland_surface *surface; wl_list_for_each(surface, &xwm->surfaces, link) { if (surface->surface == drag->focus) { focus = surface; break; } } } if (focus == xwm->drag_focus) { return; } if (xwm->drag_focus != NULL) { wlr_data_source_dnd_action(drag->source, WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE); xwm_dnd_send_leave(xwm); } xwm->drag_focus = focus; if (xwm->drag_focus != NULL) { xwm_dnd_send_enter(xwm); } } static void seat_handle_drag_motion(struct wl_listener *listener, void *data) { struct wlr_xwm *xwm = wl_container_of(listener, xwm, seat_drag_motion); struct wlr_drag_motion_event *event = data; struct wlr_xwayland_surface *surface = xwm->drag_focus; if (surface == NULL) { return; // No xwayland surface focused } xwm_dnd_send_position(xwm, event->time, surface->x + (int16_t)event->sx, surface->y + (int16_t)event->sy); } static void seat_handle_drag_drop(struct wl_listener *listener, void *data) { struct wlr_xwm *xwm = wl_container_of(listener, xwm, seat_drag_drop); struct wlr_drag_drop_event *event = data; if (xwm->drag_focus == NULL) { return; // No xwayland surface focused } wlr_log(WLR_DEBUG, "Wayland drag dropped over an Xwayland window"); xwm_dnd_send_drop(xwm, event->time); } static void seat_handle_drag_destroy(struct wl_listener *listener, void *data) { struct wlr_xwm *xwm = wl_container_of(listener, xwm, seat_drag_destroy); // Don't reset drag focus yet because the target will read the drag source // right after if (xwm->drag_focus != NULL && !xwm->drag->source->accepted) { wlr_log(WLR_DEBUG, "Wayland drag cancelled over an Xwayland window"); xwm_dnd_send_leave(xwm); } wl_list_remove(&xwm->seat_drag_focus.link); wl_list_remove(&xwm->seat_drag_motion.link); wl_list_remove(&xwm->seat_drag_drop.link); wl_list_remove(&xwm->seat_drag_destroy.link); xwm->drag = NULL; } static void seat_handle_drag_source_destroy(struct wl_listener *listener, void *data) { struct wlr_xwm *xwm = wl_container_of(listener, xwm, seat_drag_source_destroy); wl_list_remove(&xwm->seat_drag_source_destroy.link); xwm->drag_focus = NULL; } void xwm_seat_handle_start_drag(struct wlr_xwm *xwm, struct wlr_drag *drag) { xwm->drag = drag; xwm->drag_focus = NULL; if (drag != NULL) { wl_signal_add(&drag->events.focus, &xwm->seat_drag_focus); xwm->seat_drag_focus.notify = seat_handle_drag_focus; wl_signal_add(&drag->events.motion, &xwm->seat_drag_motion); xwm->seat_drag_motion.notify = seat_handle_drag_motion; wl_signal_add(&drag->events.drop, &xwm->seat_drag_drop); xwm->seat_drag_drop.notify = seat_handle_drag_drop; wl_signal_add(&drag->events.destroy, &xwm->seat_drag_destroy); xwm->seat_drag_destroy.notify = seat_handle_drag_destroy; wl_signal_add(&drag->source->events.destroy, &xwm->seat_drag_source_destroy); xwm->seat_drag_source_destroy.notify = seat_handle_drag_source_destroy; } } wlroots-0.17.1/xwayland/selection/incoming.c000066400000000000000000000365661454110342200211100ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include #include #include "xwayland/selection.h" #include "xwayland/xwm.h" static struct wlr_xwm_selection_transfer * xwm_selection_transfer_create_incoming(struct wlr_xwm_selection *selection) { struct wlr_xwm_selection_transfer *transfer = calloc(1, sizeof(*transfer)); if (!transfer) { return NULL; } xwm_selection_transfer_init(transfer, selection); wl_list_insert(&selection->incoming, &transfer->link); struct wlr_xwm *xwm = selection->xwm; transfer->incoming_window = xcb_generate_id(xwm->xcb_conn); xcb_create_window( xwm->xcb_conn, XCB_COPY_FROM_PARENT, transfer->incoming_window, xwm->screen->root, 0, 0, 10, 10, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, xwm->screen->root_visual, XCB_CW_EVENT_MASK, (uint32_t[]){ XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_PROPERTY_CHANGE } ); xcb_flush(xwm->xcb_conn); return transfer; } struct wlr_xwm_selection_transfer * xwm_selection_find_incoming_transfer_by_window( struct wlr_xwm_selection *selection, xcb_window_t window) { struct wlr_xwm_selection_transfer *transfer; wl_list_for_each(transfer, &selection->incoming, link) { if (transfer->incoming_window == window) { return transfer; } } return NULL; } static bool xwm_selection_transfer_get_incoming_selection_property( struct wlr_xwm_selection_transfer *transfer, bool delete) { struct wlr_xwm *xwm = transfer->selection->xwm; xcb_get_property_cookie_t cookie = xcb_get_property( xwm->xcb_conn, delete, transfer->incoming_window, xwm->atoms[WL_SELECTION], XCB_GET_PROPERTY_TYPE_ANY, 0, // offset 0x1fffffff // length ); transfer->property_start = 0; transfer->property_reply = xcb_get_property_reply(xwm->xcb_conn, cookie, NULL); if (!transfer->property_reply) { wlr_log(WLR_ERROR, "cannot get selection property"); return false; } return true; } static void xwm_notify_ready_for_next_incr_chunk( struct wlr_xwm_selection_transfer *transfer) { struct wlr_xwm *xwm = transfer->selection->xwm; assert(transfer->incr); wlr_log(WLR_DEBUG, "deleting property"); xcb_delete_property(xwm->xcb_conn, transfer->incoming_window, xwm->atoms[WL_SELECTION]); xcb_flush(xwm->xcb_conn); xwm_selection_transfer_remove_event_source(transfer); xwm_selection_transfer_destroy_property_reply(transfer); } /** * Write the X11 selection to a Wayland client. Returns a nonzero value if the * Wayland client might become writeable again in the future. */ static int write_selection_property_to_wl_client(int fd, uint32_t mask, void *data) { struct wlr_xwm_selection_transfer *transfer = data; char *property = xcb_get_property_value(transfer->property_reply); int remainder = xcb_get_property_value_length(transfer->property_reply) - transfer->property_start; ssize_t len = write(fd, property + transfer->property_start, remainder); if (len == -1) { wlr_log_errno(WLR_ERROR, "write error to target fd %d", fd); xwm_selection_transfer_destroy(transfer); return 0; } wlr_log(WLR_DEBUG, "wrote %zd (total %zd, remaining %d) of %d bytes to fd %d", len, transfer->property_start + len, remainder, xcb_get_property_value_length(transfer->property_reply), fd); if (len < remainder) { transfer->property_start += len; return 1; } else if (transfer->incr) { xwm_notify_ready_for_next_incr_chunk(transfer); } else { wlr_log(WLR_DEBUG, "transfer complete"); xwm_selection_transfer_destroy(transfer); } return 0; } static void xwm_write_selection_property_to_wl_client( struct wlr_xwm_selection_transfer *transfer) { if (transfer->incr && transfer->wl_client_fd < 0) { // Wayland client closed its pipe prematurely before the X11 client finished // its incremental transfer. Continue draining the X11 client. xwm_notify_ready_for_next_incr_chunk(transfer); return; } bool wl_client_finished_consuming = !write_selection_property_to_wl_client( transfer->wl_client_fd, WL_EVENT_WRITABLE, transfer); if (!wl_client_finished_consuming) { // Wrote out part of the property to the Wayland client, but the client was // unable to accept all of it. Schedule an event to asynchronously complete // the transfer. struct wl_event_loop *loop = wl_display_get_event_loop(transfer->selection->xwm->xwayland->wl_display); transfer->event_source = wl_event_loop_add_fd(loop, transfer->wl_client_fd, WL_EVENT_WRITABLE, write_selection_property_to_wl_client, transfer); } } void xwm_get_incr_chunk(struct wlr_xwm_selection_transfer *transfer) { wlr_log(WLR_DEBUG, "xwm_get_incr_chunk"); if (transfer->property_reply) { wlr_log(WLR_ERROR, "X11 client offered a new property before we deleted"); return; } if (!xwm_selection_transfer_get_incoming_selection_property(transfer, false)) { return; } if (xcb_get_property_value_length(transfer->property_reply) > 0) { xwm_write_selection_property_to_wl_client(transfer); } else { wlr_log(WLR_DEBUG, "incremental transfer complete"); xwm_selection_transfer_destroy(transfer); } } static void xwm_selection_transfer_get_data( struct wlr_xwm_selection_transfer *transfer) { struct wlr_xwm *xwm = transfer->selection->xwm; if (!xwm_selection_transfer_get_incoming_selection_property(transfer, true)) { return; } if (transfer->property_reply->type == xwm->atoms[INCR]) { transfer->incr = true; xwm_selection_transfer_destroy_property_reply(transfer); } else { // Reply's ownership is transferred to wm, which is responsible for freeing // it. xwm_write_selection_property_to_wl_client(transfer); } } static void source_send(struct wlr_xwm_selection *selection, struct wl_array *mime_types, struct wl_array *mime_types_atoms, const char *requested_mime_type, int fd) { struct wlr_xwm *xwm = selection->xwm; xcb_atom_t *atoms = mime_types_atoms->data; bool found = false; xcb_atom_t mime_type_atom; char **mime_type_ptr; size_t i = 0; wl_array_for_each(mime_type_ptr, mime_types) { char *mime_type = *mime_type_ptr; if (strcmp(mime_type, requested_mime_type) == 0) { found = true; mime_type_atom = atoms[i]; break; } ++i; } if (!found) { wlr_log(WLR_DEBUG, "Cannot send X11 selection to Wayland: " "unsupported MIME type"); close(fd); return; } struct wlr_xwm_selection_transfer *transfer = xwm_selection_transfer_create_incoming(selection); if (!transfer) { wlr_log(WLR_ERROR, "Cannot create transfer"); close(fd); return; } xcb_convert_selection(xwm->xcb_conn, transfer->incoming_window, selection->atom, mime_type_atom, xwm->atoms[WL_SELECTION], XCB_TIME_CURRENT_TIME); xcb_flush(xwm->xcb_conn); fcntl(fd, F_SETFL, O_WRONLY | O_NONBLOCK); transfer->wl_client_fd = fd; } struct x11_data_source { struct wlr_data_source base; struct wlr_xwm_selection *selection; struct wl_array mime_types_atoms; }; static const struct wlr_data_source_impl data_source_impl; bool data_source_is_xwayland( struct wlr_data_source *wlr_source) { return wlr_source->impl == &data_source_impl; } static struct x11_data_source *data_source_from_wlr_data_source( struct wlr_data_source *wlr_source) { assert(data_source_is_xwayland(wlr_source)); struct x11_data_source *source = wl_container_of(wlr_source, source, base); return source; } static void data_source_send(struct wlr_data_source *wlr_source, const char *mime_type, int32_t fd) { struct x11_data_source *source = data_source_from_wlr_data_source(wlr_source); struct wlr_xwm_selection *selection = source->selection; source_send(selection, &wlr_source->mime_types, &source->mime_types_atoms, mime_type, fd); } static void data_source_destroy(struct wlr_data_source *wlr_source) { struct x11_data_source *source = data_source_from_wlr_data_source(wlr_source); wl_array_release(&source->mime_types_atoms); free(source); } static const struct wlr_data_source_impl data_source_impl = { .send = data_source_send, .destroy = data_source_destroy, }; struct x11_primary_selection_source { struct wlr_primary_selection_source base; struct wlr_xwm_selection *selection; struct wl_array mime_types_atoms; }; static const struct wlr_primary_selection_source_impl primary_selection_source_impl; bool primary_selection_source_is_xwayland( struct wlr_primary_selection_source *wlr_source) { return wlr_source->impl == &primary_selection_source_impl; } static void primary_selection_source_send( struct wlr_primary_selection_source *wlr_source, const char *mime_type, int fd) { struct x11_primary_selection_source *source = wl_container_of(wlr_source, source, base); struct wlr_xwm_selection *selection = source->selection; source_send(selection, &wlr_source->mime_types, &source->mime_types_atoms, mime_type, fd); } static void primary_selection_source_destroy( struct wlr_primary_selection_source *wlr_source) { struct x11_primary_selection_source *source = wl_container_of(wlr_source, source, base); wl_array_release(&source->mime_types_atoms); free(source); } static const struct wlr_primary_selection_source_impl primary_selection_source_impl = { .send = primary_selection_source_send, .destroy = primary_selection_source_destroy, }; static bool source_get_targets(struct wlr_xwm_selection *selection, struct wl_array *mime_types, struct wl_array *mime_types_atoms) { struct wlr_xwm *xwm = selection->xwm; xcb_get_property_cookie_t cookie = xcb_get_property(xwm->xcb_conn, 1, // delete selection->window, xwm->atoms[WL_SELECTION], XCB_GET_PROPERTY_TYPE_ANY, 0, // offset 4096 // length ); xcb_get_property_reply_t *reply = xcb_get_property_reply(xwm->xcb_conn, cookie, NULL); if (reply == NULL) { return false; } if (reply->type != XCB_ATOM_ATOM) { free(reply); return false; } xcb_atom_t *value = xcb_get_property_value(reply); for (uint32_t i = 0; i < reply->value_len; i++) { char *mime_type = NULL; if (value[i] == xwm->atoms[UTF8_STRING]) { mime_type = strdup("text/plain;charset=utf-8"); } else if (value[i] == xwm->atoms[TEXT]) { mime_type = strdup("text/plain"); } else if (value[i] != xwm->atoms[TARGETS] && value[i] != xwm->atoms[TIMESTAMP]) { xcb_get_atom_name_cookie_t name_cookie = xcb_get_atom_name(xwm->xcb_conn, value[i]); xcb_get_atom_name_reply_t *name_reply = xcb_get_atom_name_reply(xwm->xcb_conn, name_cookie, NULL); if (name_reply == NULL) { continue; } size_t len = xcb_get_atom_name_name_length(name_reply); char *name = xcb_get_atom_name_name(name_reply); // not a C string if (memchr(name, '/', len) != NULL) { mime_type = malloc((len + 1) * sizeof(char)); if (mime_type == NULL) { free(name_reply); continue; } memcpy(mime_type, name, len); mime_type[len] = '\0'; } free(name_reply); } if (mime_type != NULL) { char **mime_type_ptr = wl_array_add(mime_types, sizeof(*mime_type_ptr)); if (mime_type_ptr == NULL) { free(mime_type); break; } *mime_type_ptr = mime_type; xcb_atom_t *atom_ptr = wl_array_add(mime_types_atoms, sizeof(*atom_ptr)); if (atom_ptr == NULL) { break; } *atom_ptr = value[i]; } } free(reply); return true; } static void xwm_selection_get_targets(struct wlr_xwm_selection *selection) { // set the wayland selection to the X11 selection struct wlr_xwm *xwm = selection->xwm; if (selection == &xwm->clipboard_selection) { struct x11_data_source *source = calloc(1, sizeof(*source)); if (source == NULL) { return; } wlr_data_source_init(&source->base, &data_source_impl); source->selection = selection; wl_array_init(&source->mime_types_atoms); bool ok = source_get_targets(selection, &source->base.mime_types, &source->mime_types_atoms); if (ok) { wlr_seat_request_set_selection(xwm->seat, NULL, &source->base, wl_display_next_serial(xwm->xwayland->wl_display)); } else { wlr_data_source_destroy(&source->base); } } else if (selection == &xwm->primary_selection) { struct x11_primary_selection_source *source = calloc(1, sizeof(*source)); if (source == NULL) { return; } wlr_primary_selection_source_init(&source->base, &primary_selection_source_impl); source->selection = selection; wl_array_init(&source->mime_types_atoms); bool ok = source_get_targets(selection, &source->base.mime_types, &source->mime_types_atoms); if (ok) { wlr_seat_set_primary_selection(xwm->seat, &source->base, wl_display_next_serial(xwm->xwayland->wl_display)); } else { wlr_primary_selection_source_destroy(&source->base); } } else if (selection == &xwm->dnd_selection) { // TODO } } void xwm_handle_selection_notify(struct wlr_xwm *xwm, xcb_selection_notify_event_t *event) { wlr_log(WLR_DEBUG, "XCB_SELECTION_NOTIFY (selection=%u, property=%u, target=%u)", event->selection, event->property, event->target); struct wlr_xwm_selection *selection = xwm_get_selection(xwm, event->selection); if (selection == NULL) { return; } struct wlr_xwm_selection_transfer *transfer = xwm_selection_find_incoming_transfer_by_window(selection, event->requestor); if (event->property == XCB_ATOM_NONE) { if (transfer) { wlr_log(WLR_ERROR, "convert selection failed"); xwm_selection_transfer_destroy(transfer); } } else if (event->target == xwm->atoms[TARGETS]) { // No xwayland surface focused, deny access to clipboard if (xwm->focus_surface == NULL) { wlr_log(WLR_DEBUG, "denying write access to clipboard: " "no xwayland surface focused"); return; } // This sets the Wayland clipboard (by calling wlr_seat_set_selection) xwm_selection_get_targets(selection); } else if (transfer) { xwm_selection_transfer_get_data(transfer); } } int xwm_handle_xfixes_selection_notify(struct wlr_xwm *xwm, xcb_xfixes_selection_notify_event_t *event) { wlr_log(WLR_DEBUG, "XCB_XFIXES_SELECTION_NOTIFY (selection=%u, owner=%u)", event->selection, event->owner); struct wlr_xwm_selection *selection = xwm_get_selection(xwm, event->selection); if (selection == NULL) { return 0; } if (event->owner == XCB_WINDOW_NONE) { if (selection->owner != selection->window) { // A real X client selection went away, not our proxy selection if (selection == &xwm->clipboard_selection) { wlr_seat_request_set_selection(xwm->seat, NULL, NULL, wl_display_next_serial(xwm->xwayland->wl_display)); } else if (selection == &xwm->primary_selection) { wlr_seat_request_set_primary_selection(xwm->seat, NULL, NULL, wl_display_next_serial(xwm->xwayland->wl_display)); } else if (selection == &xwm->dnd_selection) { // TODO: DND } else { wlr_log(WLR_DEBUG, "X11 selection has been cleared, but cannot " "clear Wayland selection"); } } selection->owner = XCB_WINDOW_NONE; return 1; } if (selection->owner == selection->window && event->owner != selection->owner) { wlr_log(WLR_DEBUG, "proxy window lost selection ownership"); } selection->owner = event->owner; if (selection->owner == selection->window) { // We have to use XCB_TIME_CURRENT_TIME when we claim the selection, so // grab the actual timestamp here so we can answer TIMESTAMP conversion // requests correctly. selection->timestamp = event->timestamp; return 1; } // doing this will give a selection notify where we actually handle the sync xcb_convert_selection( xwm->xcb_conn, selection->window, selection->atom, xwm->atoms[TARGETS], xwm->atoms[WL_SELECTION], event->timestamp ); xcb_flush(xwm->xcb_conn); return 1; } wlroots-0.17.1/xwayland/selection/outgoing.c000066400000000000000000000353671454110342200211360ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include "xwayland/selection.h" #include "xwayland/xwm.h" static void xwm_selection_send_notify(struct wlr_xwm *xwm, xcb_selection_request_event_t *req, bool success) { xcb_selection_notify_event_t selection_notify = { .response_type = XCB_SELECTION_NOTIFY, .sequence = 0, .time = req->time, .requestor = req->requestor, .selection = req->selection, .target = req->target, .property = success ? req->property : XCB_ATOM_NONE, }; wlr_log(WLR_DEBUG, "SendEvent destination=%" PRIu32 " SelectionNotify(31) time=%" PRIu32 " requestor=%" PRIu32 " selection=%" PRIu32 " target=%" PRIu32 " property=%" PRIu32, req->requestor, req->time, req->requestor, req->selection, req->target, selection_notify.property); xcb_send_event(xwm->xcb_conn, 0, // propagate req->requestor, XCB_EVENT_MASK_NO_EVENT, (const char *)&selection_notify); xcb_flush(xwm->xcb_conn); } static int xwm_selection_flush_source_data( struct wlr_xwm_selection_transfer *transfer) { xcb_change_property(transfer->selection->xwm->xcb_conn, XCB_PROP_MODE_REPLACE, transfer->request.requestor, transfer->request.property, transfer->request.target, 8, // format transfer->source_data.size, transfer->source_data.data); xcb_flush(transfer->selection->xwm->xcb_conn); transfer->property_set = true; size_t length = transfer->source_data.size; transfer->source_data.size = 0; return length; } static void xwm_selection_transfer_start_outgoing( struct wlr_xwm_selection_transfer *transfer); void xwm_selection_transfer_destroy_outgoing( struct wlr_xwm_selection_transfer *transfer) { wl_list_remove(&transfer->link); wlr_log(WLR_DEBUG, "Destroying transfer %p", transfer); xwm_selection_transfer_remove_event_source(transfer); xwm_selection_transfer_close_wl_client_fd(transfer); wl_array_release(&transfer->source_data); free(transfer); } static int xwm_data_source_read(int fd, uint32_t mask, void *data) { struct wlr_xwm_selection_transfer *transfer = data; struct wlr_xwm *xwm = transfer->selection->xwm; void *p; size_t current = transfer->source_data.size; if (transfer->source_data.size < INCR_CHUNK_SIZE) { p = wl_array_add(&transfer->source_data, INCR_CHUNK_SIZE); if (p == NULL) { wlr_log(WLR_ERROR, "Could not allocate selection source_data"); goto error_out; } } else { p = (char *)transfer->source_data.data + transfer->source_data.size; } size_t available = transfer->source_data.alloc - current; ssize_t len = read(fd, p, available); if (len == -1) { wlr_log_errno(WLR_ERROR, "read error from data source"); goto error_out; } wlr_log(WLR_DEBUG, "read %zd bytes (available %zu, mask 0x%x)", len, available, mask); transfer->source_data.size = current + len; if (transfer->source_data.size >= INCR_CHUNK_SIZE) { if (!transfer->incr) { wlr_log(WLR_DEBUG, "got %zu bytes, starting incr", transfer->source_data.size); size_t incr_chunk_size = INCR_CHUNK_SIZE; xcb_change_property(xwm->xcb_conn, XCB_PROP_MODE_REPLACE, transfer->request.requestor, transfer->request.property, xwm->atoms[INCR], 32, /* format */ 1, &incr_chunk_size); transfer->incr = true; transfer->property_set = true; transfer->flush_property_on_delete = true; xwm_selection_transfer_remove_event_source(transfer); xwm_selection_send_notify(xwm, &transfer->request, true); } else if (transfer->property_set) { wlr_log(WLR_DEBUG, "got %zu bytes, waiting for property delete", transfer->source_data.size); transfer->flush_property_on_delete = true; xwm_selection_transfer_remove_event_source(transfer); } else { wlr_log(WLR_DEBUG, "got %zu bytes, property deleted, setting new " "property", transfer->source_data.size); xwm_selection_flush_source_data(transfer); } } else if (len == 0 && !transfer->incr) { wlr_log(WLR_DEBUG, "non-incr transfer complete"); xwm_selection_flush_source_data(transfer); xwm_selection_send_notify(xwm, &transfer->request, true); xwm_selection_transfer_destroy_outgoing(transfer); } else if (len == 0 && transfer->incr) { wlr_log(WLR_DEBUG, "incr transfer complete"); transfer->flush_property_on_delete = true; if (transfer->property_set) { wlr_log(WLR_DEBUG, "got %zu bytes, waiting for property delete", transfer->source_data.size); } else { wlr_log(WLR_DEBUG, "got %zu bytes, property deleted, setting new " "property", transfer->source_data.size); xwm_selection_flush_source_data(transfer); } xwm_selection_transfer_remove_event_source(transfer); xwm_selection_transfer_close_wl_client_fd(transfer); } else { wlr_log(WLR_DEBUG, "nothing happened, buffered the bytes"); } return 1; error_out: xwm_selection_send_notify(xwm, &transfer->request, false); xwm_selection_transfer_destroy_outgoing(transfer); return 0; } void xwm_send_incr_chunk(struct wlr_xwm_selection_transfer *transfer) { wlr_log(WLR_DEBUG, "property deleted"); transfer->property_set = false; if (transfer->flush_property_on_delete) { wlr_log(WLR_DEBUG, "setting new property, %zu bytes", transfer->source_data.size); transfer->flush_property_on_delete = false; int length = xwm_selection_flush_source_data(transfer); if (transfer->wl_client_fd >= 0) { xwm_selection_transfer_start_outgoing(transfer); } else if (length > 0) { /* Transfer is all done, but queue a flush for * the delete of the last chunk so we can set * the 0 sized property to signal the end of * the transfer. */ transfer->flush_property_on_delete = true; wl_array_release(&transfer->source_data); wl_array_init(&transfer->source_data); } else { xwm_selection_transfer_destroy_outgoing(transfer); } } } static void xwm_selection_source_send(struct wlr_xwm_selection *selection, const char *mime_type, int32_t fd) { if (selection == &selection->xwm->clipboard_selection) { struct wlr_data_source *source = selection->xwm->seat->selection_source; if (source != NULL) { wlr_data_source_send(source, mime_type, fd); return; } } else if (selection == &selection->xwm->primary_selection) { struct wlr_primary_selection_source *source = selection->xwm->seat->primary_selection_source; if (source != NULL) { wlr_primary_selection_source_send(source, mime_type, fd); return; } } else if (selection == &selection->xwm->dnd_selection) { struct wlr_data_source *source = selection->xwm->seat->drag_source; if (source != NULL) { wlr_data_source_send(source, mime_type, fd); return; } } wlr_log(WLR_DEBUG, "not sending selection: no selection source available"); } static void xwm_selection_transfer_start_outgoing( struct wlr_xwm_selection_transfer *transfer) { struct wlr_xwm *xwm = transfer->selection->xwm; struct wl_event_loop *loop = wl_display_get_event_loop(xwm->xwayland->wl_display); wlr_log(WLR_DEBUG, "Starting transfer %p", transfer); transfer->event_source = wl_event_loop_add_fd(loop, transfer->wl_client_fd, WL_EVENT_READABLE, xwm_data_source_read, transfer); } static struct wl_array *xwm_selection_source_get_mime_types( struct wlr_xwm_selection *selection) { if (selection == &selection->xwm->clipboard_selection) { struct wlr_data_source *source = selection->xwm->seat->selection_source; if (source != NULL) { return &source->mime_types; } } else if (selection == &selection->xwm->primary_selection) { struct wlr_primary_selection_source *source = selection->xwm->seat->primary_selection_source; if (source != NULL) { return &source->mime_types; } } else if (selection == &selection->xwm->dnd_selection) { struct wlr_data_source *source = selection->xwm->seat->drag_source; if (source != NULL) { return &source->mime_types; } } return NULL; } /** * Read the Wayland selection and send it to an Xwayland client. */ static bool xwm_selection_send_data(struct wlr_xwm_selection *selection, xcb_selection_request_event_t *req, const char *mime_type) { // Check MIME type struct wl_array *mime_types = xwm_selection_source_get_mime_types(selection); if (mime_types == NULL) { wlr_log(WLR_ERROR, "not sending selection: no MIME type list available"); return false; } bool found = false; char **mime_type_ptr; wl_array_for_each(mime_type_ptr, mime_types) { char *t = *mime_type_ptr; if (strcmp(t, mime_type) == 0) { found = true; break; } } if (!found) { wlr_log(WLR_ERROR, "not sending selection: " "requested an unsupported MIME type %s", mime_type); return false; } struct wlr_xwm_selection_transfer *transfer = calloc(1, sizeof(*transfer)); if (transfer == NULL) { wlr_log(WLR_ERROR, "Allocation failed"); return false; } xwm_selection_transfer_init(transfer, selection); transfer->request = *req; wl_array_init(&transfer->source_data); int p[2]; if (pipe(p) == -1) { wlr_log_errno(WLR_ERROR, "pipe() failed"); return false; } fcntl(p[0], F_SETFD, FD_CLOEXEC); fcntl(p[0], F_SETFL, O_NONBLOCK); fcntl(p[1], F_SETFD, FD_CLOEXEC); fcntl(p[1], F_SETFL, O_NONBLOCK); transfer->wl_client_fd = p[0]; wlr_log(WLR_DEBUG, "Sending Wayland selection %u to Xwayland window with " "MIME type %s, target %u, transfer %p", req->target, mime_type, req->target, transfer); xwm_selection_source_send(selection, mime_type, p[1]); // It seems that if we ever try to reply to a selection request after // another has been sent by the same requestor, the requestor never reads // from it. It appears to only ever read from the latest, so purge stale // transfers to prevent clipboard hangs. struct wlr_xwm_selection_transfer *outgoing, *tmp; wl_list_for_each_safe(outgoing, tmp, &selection->outgoing, link) { if (transfer->request.requestor == outgoing->request.requestor) { wlr_log(WLR_DEBUG, "Destroying stale transfer %p", outgoing); xwm_selection_send_notify(selection->xwm, &outgoing->request, false); xwm_selection_transfer_destroy_outgoing(outgoing); } else { wlr_log(WLR_DEBUG, "Transfer %p still running", outgoing); } } wl_list_insert(&selection->outgoing, &transfer->link); xwm_selection_transfer_start_outgoing(transfer); return true; } static void xwm_selection_send_targets(struct wlr_xwm_selection *selection, xcb_selection_request_event_t *req) { struct wlr_xwm *xwm = selection->xwm; struct wl_array *mime_types = xwm_selection_source_get_mime_types(selection); if (mime_types == NULL) { wlr_log(WLR_ERROR, "not sending selection targets: " "no selection source available"); xwm_selection_send_notify(selection->xwm, req, false); return; } size_t n = 2 + mime_types->size / sizeof(char *); xcb_atom_t targets[n]; targets[0] = xwm->atoms[TIMESTAMP]; targets[1] = xwm->atoms[TARGETS]; size_t i = 0; char **mime_type_ptr; wl_array_for_each(mime_type_ptr, mime_types) { char *mime_type = *mime_type_ptr; targets[2+i] = xwm_mime_type_to_atom(xwm, mime_type); ++i; } xcb_change_property(xwm->xcb_conn, XCB_PROP_MODE_REPLACE, req->requestor, req->property, XCB_ATOM_ATOM, 32, // format n, targets); xwm_selection_send_notify(selection->xwm, req, true); } static void xwm_selection_send_timestamp(struct wlr_xwm_selection *selection, xcb_selection_request_event_t *req) { xcb_change_property(selection->xwm->xcb_conn, XCB_PROP_MODE_REPLACE, req->requestor, req->property, XCB_ATOM_INTEGER, 32, // format 1, &selection->timestamp); xwm_selection_send_notify(selection->xwm, req, true); } void xwm_handle_selection_request(struct wlr_xwm *xwm, xcb_selection_request_event_t *req) { wlr_log(WLR_DEBUG, "XCB_SELECTION_REQUEST (time=%u owner=%u, requestor=%u " "selection=%u, target=%u, property=%u)", req->time, req->owner, req->requestor, req->selection, req->target, req->property); if (req->selection == xwm->atoms[CLIPBOARD_MANAGER]) { // The wlroots clipboard should already have grabbed the first target, // so just send selection notify now. This isn't synchronized with the // clipboard finishing getting the data, so there's a race here. xwm_selection_send_notify(xwm, req, true); return; } struct wlr_xwm_selection *selection = xwm_get_selection(xwm, req->selection); if (selection == NULL) { wlr_log(WLR_DEBUG, "received selection request for unknown selection"); goto fail_notify_requestor; } if (req->requestor == selection->window) { wlr_log(WLR_ERROR, "selection request should have been caught before"); goto fail_notify_requestor; } if (selection->window != req->owner) { if (req->time != XCB_CURRENT_TIME && req->time < selection->timestamp) { wlr_log(WLR_DEBUG, "ignored old request from timestamp %d; expected > %d", req->time, selection->timestamp); goto fail_notify_requestor; } wlr_log(WLR_DEBUG, "received selection request with invalid owner"); // Don't fail (`goto fail_notify_requestor`) the selection request if we're // no longer the selection owner. return; } // No xwayland surface focused, deny access to clipboard if (xwm->focus_surface == NULL && xwm->drag_focus == NULL) { if (wlr_log_get_verbosity() >= WLR_DEBUG) { char *selection_name = xwm_get_atom_name(xwm, selection->atom); wlr_log(WLR_DEBUG, "denying read access to selection %u (%s): " "no xwayland surface focused", selection->atom, selection_name); free(selection_name); } goto fail_notify_requestor; } if (req->target == xwm->atoms[TARGETS]) { xwm_selection_send_targets(selection, req); } else if (req->target == xwm->atoms[TIMESTAMP]) { xwm_selection_send_timestamp(selection, req); } else if (req->target == xwm->atoms[DELETE]) { xwm_selection_send_notify(selection->xwm, req, true); } else { // Send data char *mime_type = xwm_mime_type_from_atom(xwm, req->target); if (mime_type == NULL) { wlr_log(WLR_ERROR, "ignoring selection request: unknown atom %u", req->target); goto fail_notify_requestor; } bool send_success = xwm_selection_send_data(selection, req, mime_type); free(mime_type); if (!send_success) { goto fail_notify_requestor; } } return; fail_notify_requestor: // Something went wrong, and there won't be any data being sent to the // requestor, so let them know. xwm_selection_send_notify(xwm, req, false); } void xwm_handle_selection_destroy_notify(struct wlr_xwm *xwm, xcb_destroy_notify_event_t *event) { struct wlr_xwm_selection *selections[] = { &xwm->clipboard_selection, &xwm->primary_selection, &xwm->dnd_selection, }; for (size_t i = 0; i < sizeof(selections)/sizeof(selections[0]); ++i) { struct wlr_xwm_selection *selection = selections[i]; struct wlr_xwm_selection_transfer *outgoing, *tmp; wl_list_for_each_safe(outgoing, tmp, &selection->outgoing, link) { if (event->window == outgoing->request.requestor) { xwm_selection_transfer_destroy_outgoing(outgoing); } } } } wlroots-0.17.1/xwayland/selection/selection.c000066400000000000000000000225461454110342200212630ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include #include "xwayland/selection.h" #include "xwayland/xwm.h" void xwm_selection_transfer_remove_event_source( struct wlr_xwm_selection_transfer *transfer) { if (transfer->event_source != NULL) { wl_event_source_remove(transfer->event_source); transfer->event_source = NULL; } } void xwm_selection_transfer_close_wl_client_fd( struct wlr_xwm_selection_transfer *transfer) { if (transfer->wl_client_fd >= 0) { close(transfer->wl_client_fd); transfer->wl_client_fd = -1; } } void xwm_selection_transfer_destroy_property_reply( struct wlr_xwm_selection_transfer *transfer) { free(transfer->property_reply); transfer->property_reply = NULL; } void xwm_selection_transfer_init(struct wlr_xwm_selection_transfer *transfer, struct wlr_xwm_selection *selection) { *transfer = (struct wlr_xwm_selection_transfer){ .selection = selection, .wl_client_fd = -1, }; } void xwm_selection_transfer_destroy( struct wlr_xwm_selection_transfer *transfer) { if (!transfer) { return; } xwm_selection_transfer_destroy_property_reply(transfer); xwm_selection_transfer_remove_event_source(transfer); xwm_selection_transfer_close_wl_client_fd(transfer); if (transfer->incoming_window) { struct wlr_xwm *xwm = transfer->selection->xwm; xcb_destroy_window(xwm->xcb_conn, transfer->incoming_window); xcb_flush(xwm->xcb_conn); } wl_list_remove(&transfer->link); free(transfer); } xcb_atom_t xwm_mime_type_to_atom(struct wlr_xwm *xwm, char *mime_type) { if (strcmp(mime_type, "text/plain;charset=utf-8") == 0) { return xwm->atoms[UTF8_STRING]; } else if (strcmp(mime_type, "text/plain") == 0) { return xwm->atoms[TEXT]; } xcb_intern_atom_cookie_t cookie = xcb_intern_atom(xwm->xcb_conn, 0, strlen(mime_type), mime_type); xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(xwm->xcb_conn, cookie, NULL); if (reply == NULL) { return XCB_ATOM_NONE; } xcb_atom_t atom = reply->atom; free(reply); return atom; } char *xwm_mime_type_from_atom(struct wlr_xwm *xwm, xcb_atom_t atom) { if (atom == xwm->atoms[UTF8_STRING]) { return strdup("text/plain;charset=utf-8"); } else if (atom == xwm->atoms[TEXT]) { return strdup("text/plain"); } else { return xwm_get_atom_name(xwm, atom); } } struct wlr_xwm_selection *xwm_get_selection(struct wlr_xwm *xwm, xcb_atom_t selection_atom) { if (selection_atom == xwm->atoms[CLIPBOARD]) { return &xwm->clipboard_selection; } else if (selection_atom == xwm->atoms[PRIMARY]) { return &xwm->primary_selection; } else if (selection_atom == xwm->atoms[DND_SELECTION]) { return &xwm->dnd_selection; } else { return NULL; } } static int xwm_handle_selection_property_notify(struct wlr_xwm *xwm, xcb_property_notify_event_t *event) { struct wlr_xwm_selection *selections[] = { &xwm->clipboard_selection, &xwm->primary_selection, &xwm->dnd_selection, }; for (size_t i = 0; i < sizeof(selections)/sizeof(selections[0]); ++i) { struct wlr_xwm_selection *selection = selections[i]; if (event->state == XCB_PROPERTY_NEW_VALUE && event->atom == xwm->atoms[WL_SELECTION]) { struct wlr_xwm_selection_transfer *transfer = xwm_selection_find_incoming_transfer_by_window(selection, event->window); if (transfer) { if (transfer->incr) { xwm_get_incr_chunk(transfer); } return 1; } } struct wlr_xwm_selection_transfer *outgoing; wl_list_for_each(outgoing, &selection->outgoing, link) { if (event->window == outgoing->request.requestor) { if (event->state == XCB_PROPERTY_DELETE && event->atom == outgoing->request.property && outgoing->incr) { xwm_send_incr_chunk(outgoing); } return 1; } } } return 0; } int xwm_handle_selection_event(struct wlr_xwm *xwm, xcb_generic_event_t *event) { if (xwm->seat == NULL) { wlr_log(WLR_DEBUG, "not handling selection events: " "no seat assigned to xwayland"); return 0; } switch (event->response_type & XCB_EVENT_RESPONSE_TYPE_MASK) { case XCB_SELECTION_NOTIFY: xwm_handle_selection_notify(xwm, (xcb_selection_notify_event_t *)event); return 1; case XCB_PROPERTY_NOTIFY: return xwm_handle_selection_property_notify(xwm, (xcb_property_notify_event_t *)event); case XCB_SELECTION_REQUEST: xwm_handle_selection_request(xwm, (xcb_selection_request_event_t *)event); return 1; } switch (event->response_type - xwm->xfixes->first_event) { case XCB_XFIXES_SELECTION_NOTIFY: // an X11 window has copied something to the clipboard return xwm_handle_xfixes_selection_notify(xwm, (xcb_xfixes_selection_notify_event_t *)event); } return 0; } void xwm_selection_init(struct wlr_xwm_selection *selection, struct wlr_xwm *xwm, xcb_atom_t atom) { *selection = (struct wlr_xwm_selection){ .xwm = xwm, .atom = atom, .window = xcb_generate_id(xwm->xcb_conn), }; wl_list_init(&selection->incoming); wl_list_init(&selection->outgoing); if (atom == xwm->atoms[DND_SELECTION]) { xcb_create_window( xwm->xcb_conn, XCB_COPY_FROM_PARENT, selection->window, xwm->screen->root, 0, 0, 8192, 8192, 0, XCB_WINDOW_CLASS_INPUT_ONLY, xwm->screen->root_visual, XCB_CW_EVENT_MASK, (uint32_t[]){ XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_PROPERTY_CHANGE } ); xcb_change_property( xwm->xcb_conn, XCB_PROP_MODE_REPLACE, selection->window, xwm->atoms[DND_AWARE], XCB_ATOM_ATOM, 32, // format 1, &(uint32_t){XDND_VERSION} ); } else { xcb_create_window( xwm->xcb_conn, XCB_COPY_FROM_PARENT, selection->window, xwm->screen->root, 0, 0, 10, 10, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, xwm->screen->root_visual, XCB_CW_EVENT_MASK, (uint32_t[]){ XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_PROPERTY_CHANGE } ); if (atom == xwm->atoms[CLIPBOARD]) { xcb_set_selection_owner(xwm->xcb_conn, selection->window, xwm->atoms[CLIPBOARD_MANAGER], XCB_TIME_CURRENT_TIME); } else { assert(atom == xwm->atoms[PRIMARY]); } } uint32_t mask = XCB_XFIXES_SELECTION_EVENT_MASK_SET_SELECTION_OWNER | XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_WINDOW_DESTROY | XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_CLIENT_CLOSE; xcb_xfixes_select_selection_input(xwm->xcb_conn, selection->window, selection->atom, mask); } void xwm_selection_finish(struct wlr_xwm_selection *selection) { if (!selection) { return; } struct wlr_xwm_selection_transfer *outgoing, *tmp; wl_list_for_each_safe(outgoing, tmp, &selection->outgoing, link) { wlr_log(WLR_INFO, "destroyed pending transfer %p", outgoing); xwm_selection_transfer_destroy_outgoing(outgoing); } struct wlr_xwm_selection_transfer *incoming; wl_list_for_each_safe(incoming, tmp, &selection->incoming, link) { xwm_selection_transfer_destroy(incoming); } xcb_destroy_window(selection->xwm->xcb_conn, selection->window); } static void xwm_selection_set_owner(struct wlr_xwm_selection *selection, bool set) { if (set) { xcb_set_selection_owner(selection->xwm->xcb_conn, selection->window, selection->atom, XCB_TIME_CURRENT_TIME); xcb_flush(selection->xwm->xcb_conn); } else { if (selection->owner == selection->window) { xcb_set_selection_owner(selection->xwm->xcb_conn, XCB_WINDOW_NONE, selection->atom, selection->timestamp); xcb_flush(selection->xwm->xcb_conn); } } } static void handle_seat_set_selection(struct wl_listener *listener, void *data) { struct wlr_seat *seat = data; struct wlr_xwm *xwm = wl_container_of(listener, xwm, seat_set_selection); struct wlr_data_source *source = seat->selection_source; if (source != NULL && data_source_is_xwayland(source)) { return; } xwm_selection_set_owner(&xwm->clipboard_selection, source != NULL); } static void handle_seat_set_primary_selection(struct wl_listener *listener, void *data) { struct wlr_seat *seat = data; struct wlr_xwm *xwm = wl_container_of(listener, xwm, seat_set_primary_selection); struct wlr_primary_selection_source *source = seat->primary_selection_source; if (source != NULL && primary_selection_source_is_xwayland(source)) { return; } xwm_selection_set_owner(&xwm->primary_selection, source != NULL); } static void seat_handle_start_drag(struct wl_listener *listener, void *data) { struct wlr_xwm *xwm = wl_container_of(listener, xwm, seat_start_drag); struct wlr_drag *drag = data; xwm_selection_set_owner(&xwm->dnd_selection, drag != NULL); xwm_seat_handle_start_drag(xwm, drag); } void xwm_set_seat(struct wlr_xwm *xwm, struct wlr_seat *seat) { if (xwm->seat != NULL) { wl_list_remove(&xwm->seat_set_selection.link); wl_list_remove(&xwm->seat_set_primary_selection.link); wl_list_remove(&xwm->seat_start_drag.link); xwm->seat = NULL; } if (seat == NULL) { return; } xwm->seat = seat; wl_signal_add(&seat->events.set_selection, &xwm->seat_set_selection); xwm->seat_set_selection.notify = handle_seat_set_selection; wl_signal_add(&seat->events.set_primary_selection, &xwm->seat_set_primary_selection); xwm->seat_set_primary_selection.notify = handle_seat_set_primary_selection; wl_signal_add(&seat->events.start_drag, &xwm->seat_start_drag); xwm->seat_start_drag.notify = seat_handle_start_drag; handle_seat_set_selection(&xwm->seat_set_selection, seat); handle_seat_set_primary_selection(&xwm->seat_set_primary_selection, seat); } wlroots-0.17.1/xwayland/server.c000066400000000000000000000323361454110342200166150ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "config.h" #include "sockets.h" static void safe_close(int fd) { if (fd >= 0) { close(fd); } } noreturn static void exec_xwayland(struct wlr_xwayland_server *server, int notify_fd) { if (!set_cloexec(server->x_fd[0], false) || !set_cloexec(server->x_fd[1], false) || !set_cloexec(server->wl_fd[1], false)) { wlr_log(WLR_ERROR, "Failed to unset CLOEXEC on FD"); _exit(EXIT_FAILURE); } if (server->options.enable_wm && !set_cloexec(server->wm_fd[1], false)) { wlr_log(WLR_ERROR, "Failed to unset CLOEXEC on FD"); _exit(EXIT_FAILURE); } char *argv[64] = {0}; size_t i = 0; char listenfd0[16], listenfd1[16], displayfd[16]; snprintf(listenfd0, sizeof(listenfd0), "%d", server->x_fd[0]); snprintf(listenfd1, sizeof(listenfd1), "%d", server->x_fd[1]); snprintf(displayfd, sizeof(displayfd), "%d", notify_fd); argv[i++] = "Xwayland"; argv[i++] = server->display_name; argv[i++] = "-rootless"; argv[i++] = "-core"; argv[i++] = "-terminate"; #if HAVE_XWAYLAND_TERMINATE_DELAY char terminate_delay[16]; if (server->options.terminate_delay > 0) { snprintf(terminate_delay, sizeof(terminate_delay), "%d", server->options.terminate_delay); argv[i++] = terminate_delay; } #endif #if HAVE_XWAYLAND_LISTENFD argv[i++] = "-listenfd"; argv[i++] = listenfd0; argv[i++] = "-listenfd"; argv[i++] = listenfd1; #else argv[i++] = "-listen"; argv[i++] = listenfd0; argv[i++] = "-listen"; argv[i++] = listenfd1; #endif argv[i++] = "-displayfd"; argv[i++] = displayfd; char wmfd[16]; if (server->options.enable_wm) { snprintf(wmfd, sizeof(wmfd), "%d", server->wm_fd[1]); argv[i++] = "-wm"; argv[i++] = wmfd; } #if HAVE_XWAYLAND_NO_TOUCH_POINTER_EMULATION if (server->options.no_touch_pointer_emulation) { argv[i++] = "-noTouchPointerEmulation"; } #else server->options.no_touch_pointer_emulation = false; #endif #if HAVE_XWAYLAND_FORCE_XRANDR_EMULATION if (server->options.force_xrandr_emulation) { argv[i++] = "-force-xrandr-emulation"; } #else server->options.force_xrandr_emulation = false; #endif argv[i++] = NULL; assert(i < sizeof(argv) / sizeof(argv[0])); char wayland_socket_str[16]; snprintf(wayland_socket_str, sizeof(wayland_socket_str), "%d", server->wl_fd[1]); setenv("WAYLAND_SOCKET", wayland_socket_str, true); wlr_log(WLR_INFO, "Starting Xwayland on :%d", server->display); // Closes stdout/stderr depending on log verbosity enum wlr_log_importance verbosity = wlr_log_get_verbosity(); int devnull = open("/dev/null", O_WRONLY | O_CREAT | O_CLOEXEC, 0666); if (devnull < 0) { wlr_log_errno(WLR_ERROR, "XWayland: failed to open /dev/null"); _exit(EXIT_FAILURE); } if (verbosity < WLR_INFO) { dup2(devnull, STDOUT_FILENO); } if (verbosity < WLR_ERROR) { dup2(devnull, STDERR_FILENO); } const char *xwayland_path = getenv("WLR_XWAYLAND"); if (xwayland_path) { wlr_log(WLR_INFO, "Using Xwayland binary '%s' due to WLR_XWAYLAND", xwayland_path); } else { xwayland_path = XWAYLAND_PATH; } // This returns if and only if the call fails execvp(xwayland_path, argv); wlr_log_errno(WLR_ERROR, "failed to exec %s", xwayland_path); close(devnull); _exit(EXIT_FAILURE); } static void server_finish_process(struct wlr_xwayland_server *server) { if (!server || server->display == -1) { return; } if (server->x_fd_read_event[0]) { wl_event_source_remove(server->x_fd_read_event[0]); wl_event_source_remove(server->x_fd_read_event[1]); server->x_fd_read_event[0] = server->x_fd_read_event[1] = NULL; } if (server->client) { wl_list_remove(&server->client_destroy.link); wl_client_destroy(server->client); } if (server->pipe_source) { wl_event_source_remove(server->pipe_source); } safe_close(server->wl_fd[0]); safe_close(server->wl_fd[1]); safe_close(server->wm_fd[0]); safe_close(server->wm_fd[1]); memset(server, 0, offsetof(struct wlr_xwayland_server, display)); server->wl_fd[0] = server->wl_fd[1] = -1; server->wm_fd[0] = server->wm_fd[1] = -1; /* We do not kill the Xwayland process, it dies to broken pipe * after we close our side of the wm/wl fds. This is more reliable * than trying to kill something that might no longer be Xwayland. */ } static void server_finish_display(struct wlr_xwayland_server *server) { if (!server) { return; } wl_list_remove(&server->display_destroy.link); wl_list_init(&server->display_destroy.link); if (server->display == -1) { return; } safe_close(server->x_fd[0]); safe_close(server->x_fd[1]); server->x_fd[0] = server->x_fd[1] = -1; unlink_display_sockets(server->display); server->display = -1; server->display_name[0] = '\0'; } static bool server_start(struct wlr_xwayland_server *server); static bool server_start_lazy(struct wlr_xwayland_server *server); static void handle_client_destroy(struct wl_listener *listener, void *data) { struct wlr_xwayland_server *server = wl_container_of(listener, server, client_destroy); if (server->pipe_source) { // Xwayland failed to start, let the readiness handler deal with it return; } // Don't call client destroy: it's being destroyed already server->client = NULL; wl_list_remove(&server->client_destroy.link); server_finish_process(server); if (time(NULL) - server->server_start > 5) { if (server->options.lazy) { wlr_log(WLR_INFO, "Restarting Xwayland (lazy)"); server_start_lazy(server); } else { wlr_log(WLR_INFO, "Restarting Xwayland"); server_start(server); } } } static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_xwayland_server *server = wl_container_of(listener, server, display_destroy); // Don't call client destroy: the display is being destroyed, it's too late if (server->client) { server->client = NULL; wl_list_remove(&server->client_destroy.link); } wlr_xwayland_server_destroy(server); } static int xserver_handle_ready(int fd, uint32_t mask, void *data) { struct wlr_xwayland_server *server = data; if (mask & WL_EVENT_READABLE) { /* Xwayland writes to the pipe twice, so if we close it too early * it's possible the second write will fail and Xwayland shuts down. * Make sure we read until end of line marker to avoid this. */ char buf[64]; ssize_t n = read(fd, buf, sizeof(buf)); if (n < 0 && errno != EINTR) { /* Clear mask to signal start failure after reaping child */ wlr_log_errno(WLR_ERROR, "read from Xwayland display_fd failed"); mask = 0; } else if (n <= 0 || buf[n-1] != '\n') { /* Returning 1 here means recheck and call us again if required. */ return 1; } } while (waitpid(server->pid, NULL, 0) < 0) { if (errno == EINTR) { continue; } wlr_log_errno(WLR_ERROR, "waitpid for Xwayland fork failed"); goto error; } /* Xwayland will only write on the fd once it has finished its * initial setup. Getting an event here without READABLE means * the server end failed. */ if (!(mask & WL_EVENT_READABLE)) { assert(mask & WL_EVENT_HANGUP); wlr_log(WLR_ERROR, "Xwayland startup failed, not setting up xwm"); goto error; } wlr_log(WLR_DEBUG, "Xserver is ready"); close(fd); wl_event_source_remove(server->pipe_source); server->pipe_source = NULL; server->ready = true; struct wlr_xwayland_server_ready_event event = { .server = server, .wm_fd = server->wm_fd[0], }; wl_signal_emit_mutable(&server->events.ready, &event); /* We removed the source, so don't need recheck */ return 0; error: /* clean up */ close(fd); server_finish_process(server); server_finish_display(server); return 0; } static bool server_start_display(struct wlr_xwayland_server *server, struct wl_display *wl_display) { server->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(wl_display, &server->display_destroy); server->display = open_display_sockets(server->x_fd); if (server->display < 0) { server_finish_display(server); return false; } snprintf(server->display_name, sizeof(server->display_name), ":%d", server->display); return true; } static bool server_start(struct wlr_xwayland_server *server) { if (socketpair(AF_UNIX, SOCK_STREAM, 0, server->wl_fd) != 0) { wlr_log_errno(WLR_ERROR, "socketpair failed"); server_finish_process(server); return false; } if (!set_cloexec(server->wl_fd[0], true) || !set_cloexec(server->wl_fd[1], true)) { wlr_log(WLR_ERROR, "Failed to set O_CLOEXEC on socket"); server_finish_process(server); return false; } if (server->options.enable_wm) { if (socketpair(AF_UNIX, SOCK_STREAM, 0, server->wm_fd) != 0) { wlr_log_errno(WLR_ERROR, "socketpair failed"); server_finish_process(server); return false; } if (!set_cloexec(server->wm_fd[0], true) || !set_cloexec(server->wm_fd[1], true)) { wlr_log(WLR_ERROR, "Failed to set O_CLOEXEC on socket"); server_finish_process(server); return false; } } server->server_start = time(NULL); server->client = wl_client_create(server->wl_display, server->wl_fd[0]); if (!server->client) { wlr_log_errno(WLR_ERROR, "wl_client_create failed"); server_finish_process(server); return false; } server->wl_fd[0] = -1; /* not ours anymore */ server->client_destroy.notify = handle_client_destroy; wl_client_add_destroy_listener(server->client, &server->client_destroy); int notify_fd[2]; if (pipe(notify_fd) == -1) { wlr_log_errno(WLR_ERROR, "pipe failed"); server_finish_process(server); return false; } if (!set_cloexec(notify_fd[0], true)) { wlr_log(WLR_ERROR, "Failed to set CLOEXEC on FD"); server_finish_process(server); return false; } struct wl_event_loop *loop = wl_display_get_event_loop(server->wl_display); server->pipe_source = wl_event_loop_add_fd(loop, notify_fd[0], WL_EVENT_READABLE, xserver_handle_ready, server); wl_signal_emit_mutable(&server->events.start, NULL); server->pid = fork(); if (server->pid < 0) { wlr_log_errno(WLR_ERROR, "fork failed"); close(notify_fd[0]); close(notify_fd[1]); server_finish_process(server); return false; } else if (server->pid == 0) { pid_t pid = fork(); if (pid < 0) { wlr_log_errno(WLR_ERROR, "second fork failed"); _exit(EXIT_FAILURE); } else if (pid == 0) { exec_xwayland(server, notify_fd[1]); } _exit(EXIT_SUCCESS); } /* close child fds */ /* remain managing x sockets for lazy start */ close(notify_fd[1]); close(server->wl_fd[1]); safe_close(server->wm_fd[1]); server->wl_fd[1] = server->wm_fd[1] = -1; return true; } static int xwayland_socket_connected(int fd, uint32_t mask, void *data) { struct wlr_xwayland_server *server = data; wl_event_source_remove(server->x_fd_read_event[0]); wl_event_source_remove(server->x_fd_read_event[1]); server->x_fd_read_event[0] = server->x_fd_read_event[1] = NULL; server_start(server); return 0; } static bool server_start_lazy(struct wlr_xwayland_server *server) { struct wl_event_loop *loop = wl_display_get_event_loop(server->wl_display); if (!(server->x_fd_read_event[0] = wl_event_loop_add_fd(loop, server->x_fd[0], WL_EVENT_READABLE, xwayland_socket_connected, server))) { return false; } if (!(server->x_fd_read_event[1] = wl_event_loop_add_fd(loop, server->x_fd[1], WL_EVENT_READABLE, xwayland_socket_connected, server))) { wl_event_source_remove(server->x_fd_read_event[0]); server->x_fd_read_event[0] = NULL; return false; } return true; } static void handle_idle(void *data) { struct wlr_xwayland_server *server = data; server->idle_source = NULL; server_start(server); } void wlr_xwayland_server_destroy(struct wlr_xwayland_server *server) { if (!server) { return; } if (server->idle_source != NULL) { wl_event_source_remove(server->idle_source); } server_finish_process(server); server_finish_display(server); wl_signal_emit_mutable(&server->events.destroy, NULL); free(server); } struct wlr_xwayland_server *wlr_xwayland_server_create( struct wl_display *wl_display, struct wlr_xwayland_server_options *options) { if (!getenv("WLR_XWAYLAND") && access(XWAYLAND_PATH, X_OK) != 0) { wlr_log(WLR_ERROR, "Cannot find Xwayland binary \"%s\"", XWAYLAND_PATH); return NULL; } struct wlr_xwayland_server *server = calloc(1, sizeof(*server)); if (!server) { return NULL; } server->wl_display = wl_display; server->options = *options; #if !HAVE_XWAYLAND_TERMINATE_DELAY server->options.terminate_delay = 0; #endif server->x_fd[0] = server->x_fd[1] = -1; server->wl_fd[0] = server->wl_fd[1] = -1; server->wm_fd[0] = server->wm_fd[1] = -1; wl_signal_init(&server->events.start); wl_signal_init(&server->events.ready); wl_signal_init(&server->events.destroy); if (!server_start_display(server, wl_display)) { goto error_alloc; } if (server->options.lazy) { if (!server_start_lazy(server)) { goto error_display; } } else { struct wl_event_loop *loop = wl_display_get_event_loop(wl_display); server->idle_source = wl_event_loop_add_idle(loop, handle_idle, server); if (server->idle_source == NULL) { goto error_display; } } return server; error_display: server_finish_display(server); error_alloc: free(server); return NULL; } wlroots-0.17.1/xwayland/shell.c000066400000000000000000000155421454110342200164160ustar00rootroot00000000000000#include #include #include #include #include "xwayland-shell-v1-protocol.h" #define SHELL_VERSION 1 static void destroy_resource(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } static const struct xwayland_shell_v1_interface shell_impl; static const struct xwayland_surface_v1_interface xwl_surface_impl; static void xwl_surface_destroy(struct wlr_xwayland_surface_v1 *xwl_surface) { wl_list_remove(&xwl_surface->link); wl_resource_set_user_data(xwl_surface->resource, NULL); // make inert free(xwl_surface); } /** * Get a struct wlr_xwayland_shell_v1 from a resource. */ static struct wlr_xwayland_shell_v1 *shell_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &xwayland_shell_v1_interface, &shell_impl)); return wl_resource_get_user_data(resource); } /** * Get a struct wlr_xwayland_surface_v1 from a resource. * * Returns NULL if the Xwayland surface is inert. */ static struct wlr_xwayland_surface_v1 *xwl_surface_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &xwayland_surface_v1_interface, &xwl_surface_impl)); return wl_resource_get_user_data(resource); } static void xwl_surface_role_commit(struct wlr_surface *surface) { struct wlr_xwayland_surface_v1 *xwl_surface = xwl_surface_from_resource(surface->role_resource); if (xwl_surface == NULL) { return; } if (xwl_surface->serial != 0 && !xwl_surface->added) { xwl_surface->added = true; wl_signal_emit_mutable(&xwl_surface->shell->events.new_surface, xwl_surface); } } static void xwl_surface_role_destroy(struct wlr_surface *surface) { struct wlr_xwayland_surface_v1 *xwl_surface = xwl_surface_from_resource(surface->role_resource); if (xwl_surface == NULL) { return; } xwl_surface_destroy(xwl_surface); } static const struct wlr_surface_role xwl_surface_role = { .name = "xwayland_surface_v1", .commit = xwl_surface_role_commit, .destroy = xwl_surface_role_destroy, }; static void xwl_surface_handle_set_serial(struct wl_client *client, struct wl_resource *resource, uint32_t serial_lo, uint32_t serial_hi) { struct wlr_xwayland_surface_v1 *xwl_surface = xwl_surface_from_resource(resource); if (xwl_surface == NULL) { return; } if (xwl_surface->serial != 0) { wl_resource_post_error(resource, XWAYLAND_SURFACE_V1_ERROR_ALREADY_ASSOCIATED, "xwayland_surface_v1 is already associated with another X11 serial"); return; } xwl_surface->serial = ((uint64_t)serial_hi << 32) | serial_lo; } static const struct xwayland_surface_v1_interface xwl_surface_impl = { .destroy = destroy_resource, .set_serial = xwl_surface_handle_set_serial, }; static void shell_handle_get_xwayland_surface(struct wl_client *client, struct wl_resource *shell_resource, uint32_t id, struct wl_resource *surface_resource) { struct wlr_xwayland_shell_v1 *shell = shell_from_resource(shell_resource); struct wlr_surface *surface = wlr_surface_from_resource(surface_resource); struct wlr_xwayland_surface_v1 *xwl_surface = calloc(1, sizeof(*xwl_surface)); if (xwl_surface == NULL) { wl_client_post_no_memory(client); return; } if (!wlr_surface_set_role(surface, &xwl_surface_role, shell_resource, XWAYLAND_SHELL_V1_ERROR_ROLE)) { free(xwl_surface); return; } xwl_surface->surface = surface; xwl_surface->shell = shell; uint32_t version = wl_resource_get_version(shell_resource); xwl_surface->resource = wl_resource_create(client, &xwayland_surface_v1_interface, version, id); if (xwl_surface->resource == NULL) { free(xwl_surface); wl_client_post_no_memory(client); return; } wl_resource_set_implementation(xwl_surface->resource, &xwl_surface_impl, xwl_surface, NULL); wl_list_insert(&shell->surfaces, &xwl_surface->link); wlr_surface_set_role_object(surface, xwl_surface->resource); } static const struct xwayland_shell_v1_interface shell_impl = { .destroy = destroy_resource, .get_xwayland_surface = shell_handle_get_xwayland_surface, }; static void shell_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id) { struct wlr_xwayland_shell_v1 *shell = data; if (client != shell->client) { wl_client_post_implementation_error(client, "Permission denied to bind to %s", xwayland_shell_v1_interface.name); return; } struct wl_resource *resource = wl_resource_create(client, &xwayland_shell_v1_interface, version, id); if (resource == NULL) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(resource, &shell_impl, shell, NULL); } static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_xwayland_shell_v1 *shell = wl_container_of(listener, shell, display_destroy); wlr_xwayland_shell_v1_destroy(shell); } struct wlr_xwayland_shell_v1 *wlr_xwayland_shell_v1_create( struct wl_display *display, uint32_t version) { assert(version <= SHELL_VERSION); struct wlr_xwayland_shell_v1 *shell = calloc(1, sizeof(*shell)); if (shell == NULL) { return NULL; } shell->global = wl_global_create(display, &xwayland_shell_v1_interface, version, shell, shell_bind); if (shell->global == NULL) { free(shell); return NULL; } wl_list_init(&shell->surfaces); wl_signal_init(&shell->events.new_surface); wl_signal_init(&shell->events.destroy); shell->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &shell->display_destroy); wl_list_init(&shell->client_destroy.link); return shell; } void wlr_xwayland_shell_v1_destroy(struct wlr_xwayland_shell_v1 *shell) { if (shell == NULL) { return; } wl_signal_emit_mutable(&shell->events.destroy, NULL); struct wlr_xwayland_surface_v1 *xwl_surface, *tmp; wl_list_for_each_safe(xwl_surface, tmp, &shell->surfaces, link) { xwl_surface_destroy(xwl_surface); } wl_list_remove(&shell->display_destroy.link); wl_list_remove(&shell->client_destroy.link); wl_global_destroy(shell->global); free(shell); } static void shell_handle_client_destroy(struct wl_listener *listener, void *data) { struct wlr_xwayland_shell_v1 *shell = wl_container_of(listener, shell, client_destroy); wlr_xwayland_shell_v1_set_client(shell, NULL); } void wlr_xwayland_shell_v1_set_client(struct wlr_xwayland_shell_v1 *shell, struct wl_client *client) { wl_list_remove(&shell->client_destroy.link); shell->client = client; if (client != NULL) { shell->client_destroy.notify = shell_handle_client_destroy; wl_client_add_destroy_listener(client, &shell->client_destroy); } else { wl_list_init(&shell->client_destroy.link); } } struct wlr_surface *wlr_xwayland_shell_v1_surface_from_serial( struct wlr_xwayland_shell_v1 *shell, uint64_t serial) { struct wlr_xwayland_surface_v1 *xwl_surface; wl_list_for_each(xwl_surface, &shell->surfaces, link) { if (xwl_surface->serial == serial) { return xwl_surface->surface; } } return NULL; } wlroots-0.17.1/xwayland/sockets.c000066400000000000000000000122011454110342200167470ustar00rootroot00000000000000#define _XOPEN_SOURCE 700 #include #include #include #include #include #include #include #include #include #include #include #include #include #include "sockets.h" static const char lock_fmt[] = "/tmp/.X%d-lock"; static const char socket_dir[] = "/tmp/.X11-unix"; static const char socket_fmt[] = "/tmp/.X11-unix/X%d"; #ifndef __linux__ static const char socket_fmt2[] = "/tmp/.X11-unix/X%d_"; #endif bool set_cloexec(int fd, bool cloexec) { int flags = fcntl(fd, F_GETFD); if (flags == -1) { wlr_log_errno(WLR_ERROR, "fcntl failed"); return false; } if (cloexec) { flags = flags | FD_CLOEXEC; } else { flags = flags & ~FD_CLOEXEC; } if (fcntl(fd, F_SETFD, flags) == -1) { wlr_log_errno(WLR_ERROR, "fcntl failed"); return false; } return true; } static int open_socket(struct sockaddr_un *addr, size_t path_size) { int fd, rc; socklen_t size = offsetof(struct sockaddr_un, sun_path) + path_size + 1; fd = socket(AF_UNIX, SOCK_STREAM, 0); if (fd < 0) { wlr_log_errno(WLR_ERROR, "Failed to create socket %c%s", addr->sun_path[0] ? addr->sun_path[0] : '@', addr->sun_path + 1); return -1; } if (!set_cloexec(fd, true)) { close(fd); return -1; } if (addr->sun_path[0]) { unlink(addr->sun_path); } if (bind(fd, (struct sockaddr*)addr, size) < 0) { rc = errno; wlr_log_errno(WLR_ERROR, "Failed to bind socket %c%s", addr->sun_path[0] ? addr->sun_path[0] : '@', addr->sun_path + 1); goto cleanup; } if (listen(fd, 1) < 0) { rc = errno; wlr_log_errno(WLR_ERROR, "Failed to listen to socket %c%s", addr->sun_path[0] ? addr->sun_path[0] : '@', addr->sun_path + 1); goto cleanup; } return fd; cleanup: close(fd); if (addr->sun_path[0]) { unlink(addr->sun_path); } errno = rc; return -1; } static bool check_socket_dir(void) { struct stat buf; if (lstat(socket_dir, &buf)) { wlr_log_errno(WLR_ERROR, "Failed to stat %s", socket_dir); return false; } if (!(buf.st_mode & S_IFDIR)) { wlr_log(WLR_ERROR, "%s is not a directory", socket_dir); return false; } if (!((buf.st_uid == 0) || (buf.st_uid == getuid()))) { wlr_log(WLR_ERROR, "%s not owned by root or us", socket_dir); return false; } if (!(buf.st_mode & S_ISVTX)) { /* we can deal with no sticky bit... */ if ((buf.st_mode & (S_IWGRP | S_IWOTH))) { /* but not if other users can mess with our sockets */ wlr_log(WLR_ERROR, "sticky bit not set on %s", socket_dir); return false; } } return true; } static bool open_sockets(int socks[2], int display) { struct sockaddr_un addr = { .sun_family = AF_UNIX }; size_t path_size; if (mkdir(socket_dir, 0755) == 0) { wlr_log(WLR_INFO, "Created %s ourselves -- other users will " "be unable to create X11 UNIX sockets of their own", socket_dir); } else if (errno != EEXIST) { wlr_log_errno(WLR_ERROR, "Unable to mkdir %s", socket_dir); return false; } else if (!check_socket_dir()) { return false; } #ifdef __linux__ addr.sun_path[0] = 0; path_size = snprintf(addr.sun_path + 1, sizeof(addr.sun_path) - 1, socket_fmt, display); #else path_size = snprintf(addr.sun_path, sizeof(addr.sun_path), socket_fmt2, display); #endif socks[0] = open_socket(&addr, path_size); if (socks[0] < 0) { return false; } path_size = snprintf(addr.sun_path, sizeof(addr.sun_path), socket_fmt, display); socks[1] = open_socket(&addr, path_size); if (socks[1] < 0) { close(socks[0]); socks[0] = -1; return false; } return true; } void unlink_display_sockets(int display) { char sun_path[64]; snprintf(sun_path, sizeof(sun_path), socket_fmt, display); unlink(sun_path); #ifndef __linux__ snprintf(sun_path, sizeof(sun_path), socket_fmt2, display); unlink(sun_path); #endif snprintf(sun_path, sizeof(sun_path), lock_fmt, display); unlink(sun_path); } int open_display_sockets(int socks[2]) { int lock_fd, display; char lock_name[64]; for (display = 0; display <= 32; display++) { snprintf(lock_name, sizeof(lock_name), lock_fmt, display); if ((lock_fd = open(lock_name, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, 0444)) >= 0) { if (!open_sockets(socks, display)) { unlink(lock_name); close(lock_fd); continue; } char pid[12]; snprintf(pid, sizeof(pid), "%10d", getpid()); if (write(lock_fd, pid, sizeof(pid) - 1) != sizeof(pid) - 1) { unlink(lock_name); close(lock_fd); continue; } close(lock_fd); break; } if ((lock_fd = open(lock_name, O_RDONLY | O_CLOEXEC)) < 0) { continue; } char pid[12] = { 0 }, *end_pid; ssize_t bytes = read(lock_fd, pid, sizeof(pid) - 1); close(lock_fd); if (bytes != sizeof(pid) - 1) { continue; } long int read_pid; read_pid = strtol(pid, &end_pid, 10); if (read_pid < 0 || read_pid > INT32_MAX || end_pid != pid + sizeof(pid) - 2) { continue; } errno = 0; if (kill((pid_t)read_pid, 0) != 0 && errno == ESRCH) { if (unlink(lock_name) != 0) { continue; } // retry display--; continue; } } if (display > 32) { wlr_log(WLR_ERROR, "No display available in the first 33"); return -1; } return display; } wlroots-0.17.1/xwayland/sockets.h000066400000000000000000000003171454110342200167610ustar00rootroot00000000000000#ifndef XWAYLAND_SOCKETS_H #define XWAYLAND_SOCKETS_H #include bool set_cloexec(int fd, bool cloexec); void unlink_display_sockets(int display); int open_display_sockets(int socks[2]); #endif wlroots-0.17.1/xwayland/xwayland.c000066400000000000000000000137071454110342200171370ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "sockets.h" #include "xwayland/xwm.h" struct wlr_xwayland_cursor { uint8_t *pixels; uint32_t stride; uint32_t width; uint32_t height; int32_t hotspot_x; int32_t hotspot_y; }; static void handle_server_destroy(struct wl_listener *listener, void *data) { struct wlr_xwayland *xwayland = wl_container_of(listener, xwayland, server_destroy); // Server is being destroyed so avoid destroying it once again. xwayland->server = NULL; wlr_xwayland_destroy(xwayland); } static void handle_server_start(struct wl_listener *listener, void *data) { struct wlr_xwayland *xwayland = wl_container_of(listener, xwayland, server_start); if (xwayland->shell_v1 != NULL) { wlr_xwayland_shell_v1_set_client(xwayland->shell_v1, xwayland->server->client); } } static void xwayland_mark_ready(struct wlr_xwayland *xwayland) { assert(xwayland->server->wm_fd[0] >= 0); xwayland->xwm = xwm_create(xwayland, xwayland->server->wm_fd[0]); if (!xwayland->xwm) { return; } if (xwayland->seat) { xwm_set_seat(xwayland->xwm, xwayland->seat); } if (xwayland->cursor != NULL) { struct wlr_xwayland_cursor *cur = xwayland->cursor; xwm_set_cursor(xwayland->xwm, cur->pixels, cur->stride, cur->width, cur->height, cur->hotspot_x, cur->hotspot_y); } wl_signal_emit_mutable(&xwayland->events.ready, NULL); } static void handle_server_ready(struct wl_listener *listener, void *data) { struct wlr_xwayland *xwayland = wl_container_of(listener, xwayland, server_ready); xwayland_mark_ready(xwayland); } static void handle_shell_destroy(struct wl_listener *listener, void *data) { struct wlr_xwayland *xwayland = wl_container_of(listener, xwayland, shell_destroy); xwayland->shell_v1 = NULL; } void wlr_xwayland_destroy(struct wlr_xwayland *xwayland) { if (!xwayland) { return; } wl_list_remove(&xwayland->server_destroy.link); wl_list_remove(&xwayland->server_start.link); wl_list_remove(&xwayland->server_ready.link); wl_list_remove(&xwayland->shell_destroy.link); free(xwayland->cursor); wlr_xwayland_set_seat(xwayland, NULL); if (xwayland->own_server) { wlr_xwayland_server_destroy(xwayland->server); } xwayland->server = NULL; wlr_xwayland_shell_v1_destroy(xwayland->shell_v1); free(xwayland); } struct wlr_xwayland *wlr_xwayland_create_with_server(struct wl_display *wl_display, struct wlr_compositor *compositor, struct wlr_xwayland_server *server) { struct wlr_xwayland *xwayland = calloc(1, sizeof(*xwayland)); if (!xwayland) { return NULL; } xwayland->wl_display = wl_display; xwayland->compositor = compositor; wl_signal_init(&xwayland->events.new_surface); wl_signal_init(&xwayland->events.ready); wl_signal_init(&xwayland->events.remove_startup_info); xwayland->server = server; xwayland->display_name = xwayland->server->display_name; xwayland->server_destroy.notify = handle_server_destroy; wl_signal_add(&xwayland->server->events.destroy, &xwayland->server_destroy); xwayland->server_start.notify = handle_server_start; wl_signal_add(&xwayland->server->events.start, &xwayland->server_start); xwayland->server_ready.notify = handle_server_ready; wl_signal_add(&xwayland->server->events.ready, &xwayland->server_ready); wl_list_init(&xwayland->shell_destroy.link); if (server->ready) { xwayland_mark_ready(xwayland); } return xwayland; } struct wlr_xwayland *wlr_xwayland_create(struct wl_display *wl_display, struct wlr_compositor *compositor, bool lazy) { struct wlr_xwayland_shell_v1 *shell_v1 = wlr_xwayland_shell_v1_create(wl_display, 1); if (shell_v1 == NULL) { return NULL; } struct wlr_xwayland_server_options options = { .lazy = lazy, .enable_wm = true, #if HAVE_XCB_XFIXES_SET_CLIENT_DISCONNECT_MODE .terminate_delay = lazy ? 10 : 0, #endif }; struct wlr_xwayland_server *server = wlr_xwayland_server_create(wl_display, &options); if (server == NULL) { goto error_shell_v1; } struct wlr_xwayland *xwayland = wlr_xwayland_create_with_server(wl_display, compositor, server); if (xwayland == NULL) { goto error_server; } xwayland->shell_v1 = shell_v1; xwayland->own_server = true; xwayland->shell_destroy.notify = handle_shell_destroy; wl_signal_add(&xwayland->shell_v1->events.destroy, &xwayland->shell_destroy); return xwayland; error_server: wlr_xwayland_server_destroy(server); error_shell_v1: wlr_xwayland_shell_v1_destroy(shell_v1); return NULL; } void wlr_xwayland_set_cursor(struct wlr_xwayland *xwayland, uint8_t *pixels, uint32_t stride, uint32_t width, uint32_t height, int32_t hotspot_x, int32_t hotspot_y) { if (xwayland->xwm != NULL) { xwm_set_cursor(xwayland->xwm, pixels, stride, width, height, hotspot_x, hotspot_y); return; } free(xwayland->cursor); xwayland->cursor = calloc(1, sizeof(*xwayland->cursor)); if (xwayland->cursor == NULL) { return; } xwayland->cursor->pixels = pixels; xwayland->cursor->stride = stride; xwayland->cursor->width = width; xwayland->cursor->height = height; xwayland->cursor->hotspot_x = hotspot_x; xwayland->cursor->hotspot_y = hotspot_y; } static void xwayland_handle_seat_destroy(struct wl_listener *listener, void *data) { struct wlr_xwayland *xwayland = wl_container_of(listener, xwayland, seat_destroy); wlr_xwayland_set_seat(xwayland, NULL); } void wlr_xwayland_set_seat(struct wlr_xwayland *xwayland, struct wlr_seat *seat) { if (xwayland->seat) { wl_list_remove(&xwayland->seat_destroy.link); } xwayland->seat = seat; if (xwayland->xwm) { xwm_set_seat(xwayland->xwm, seat); } if (seat == NULL) { return; } xwayland->seat_destroy.notify = xwayland_handle_seat_destroy; wl_signal_add(&seat->events.destroy, &xwayland->seat_destroy); } wlroots-0.17.1/xwayland/xwm.c000066400000000000000000002122341454110342200161170ustar00rootroot00000000000000#ifndef _POSIX_C_SOURCE #define _POSIX_C_SOURCE 200809L #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "xwayland/xwm.h" static const char *const atom_map[ATOM_LAST] = { [WL_SURFACE_ID] = "WL_SURFACE_ID", [WL_SURFACE_SERIAL] = "WL_SURFACE_SERIAL", [WM_DELETE_WINDOW] = "WM_DELETE_WINDOW", [WM_PROTOCOLS] = "WM_PROTOCOLS", [WM_HINTS] = "WM_HINTS", [WM_NORMAL_HINTS] = "WM_NORMAL_HINTS", [WM_SIZE_HINTS] = "WM_SIZE_HINTS", [WM_WINDOW_ROLE] = "WM_WINDOW_ROLE", [MOTIF_WM_HINTS] = "_MOTIF_WM_HINTS", [UTF8_STRING] = "UTF8_STRING", [WM_S0] = "WM_S0", [NET_SUPPORTED] = "_NET_SUPPORTED", [NET_WM_CM_S0] = "_NET_WM_CM_S0", [NET_WM_PID] = "_NET_WM_PID", [NET_WM_NAME] = "_NET_WM_NAME", [NET_WM_STATE] = "_NET_WM_STATE", [NET_WM_STRUT_PARTIAL] = "_NET_WM_STRUT_PARTIAL", [NET_WM_WINDOW_TYPE] = "_NET_WM_WINDOW_TYPE", [WM_TAKE_FOCUS] = "WM_TAKE_FOCUS", [WINDOW] = "WINDOW", [NET_ACTIVE_WINDOW] = "_NET_ACTIVE_WINDOW", [NET_WM_MOVERESIZE] = "_NET_WM_MOVERESIZE", [NET_SUPPORTING_WM_CHECK] = "_NET_SUPPORTING_WM_CHECK", [NET_WM_STATE_FOCUSED] = "_NET_WM_STATE_FOCUSED", [NET_WM_STATE_MODAL] = "_NET_WM_STATE_MODAL", [NET_WM_STATE_FULLSCREEN] = "_NET_WM_STATE_FULLSCREEN", [NET_WM_STATE_MAXIMIZED_VERT] = "_NET_WM_STATE_MAXIMIZED_VERT", [NET_WM_STATE_MAXIMIZED_HORZ] = "_NET_WM_STATE_MAXIMIZED_HORZ", [NET_WM_STATE_HIDDEN] = "_NET_WM_STATE_HIDDEN", [NET_WM_PING] = "_NET_WM_PING", [WM_CHANGE_STATE] = "WM_CHANGE_STATE", [WM_STATE] = "WM_STATE", [CLIPBOARD] = "CLIPBOARD", [PRIMARY] = "PRIMARY", [WL_SELECTION] = "_WL_SELECTION", [TARGETS] = "TARGETS", [CLIPBOARD_MANAGER] = "CLIPBOARD_MANAGER", [INCR] = "INCR", [TEXT] = "TEXT", [TIMESTAMP] = "TIMESTAMP", [DELETE] = "DELETE", [NET_STARTUP_ID] = "_NET_STARTUP_ID", [NET_STARTUP_INFO] = "_NET_STARTUP_INFO", [NET_STARTUP_INFO_BEGIN] = "_NET_STARTUP_INFO_BEGIN", [NET_WM_WINDOW_TYPE_NORMAL] = "_NET_WM_WINDOW_TYPE_NORMAL", [NET_WM_WINDOW_TYPE_UTILITY] = "_NET_WM_WINDOW_TYPE_UTILITY", [NET_WM_WINDOW_TYPE_TOOLTIP] = "_NET_WM_WINDOW_TYPE_TOOLTIP", [NET_WM_WINDOW_TYPE_DND] = "_NET_WM_WINDOW_TYPE_DND", [NET_WM_WINDOW_TYPE_DROPDOWN_MENU] = "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU", [NET_WM_WINDOW_TYPE_POPUP_MENU] = "_NET_WM_WINDOW_TYPE_POPUP_MENU", [NET_WM_WINDOW_TYPE_COMBO] = "_NET_WM_WINDOW_TYPE_COMBO", [NET_WM_WINDOW_TYPE_MENU] = "_NET_WM_WINDOW_TYPE_MENU", [NET_WM_WINDOW_TYPE_NOTIFICATION] = "_NET_WM_WINDOW_TYPE_NOTIFICATION", [NET_WM_WINDOW_TYPE_SPLASH] = "_NET_WM_WINDOW_TYPE_SPLASH", [DND_SELECTION] = "XdndSelection", [DND_AWARE] = "XdndAware", [DND_STATUS] = "XdndStatus", [DND_POSITION] = "XdndPosition", [DND_ENTER] = "XdndEnter", [DND_LEAVE] = "XdndLeave", [DND_DROP] = "XdndDrop", [DND_FINISHED] = "XdndFinished", [DND_PROXY] = "XdndProxy", [DND_TYPE_LIST] = "XdndTypeList", [DND_ACTION_MOVE] = "XdndActionMove", [DND_ACTION_COPY] = "XdndActionCopy", [DND_ACTION_ASK] = "XdndActionAsk", [DND_ACTION_PRIVATE] = "XdndActionPrivate", [NET_CLIENT_LIST] = "_NET_CLIENT_LIST", [NET_CLIENT_LIST_STACKING] = "_NET_CLIENT_LIST_STACKING", [NET_WORKAREA] = "_NET_WORKAREA", }; #define STARTUP_INFO_REMOVE_PREFIX "remove: ID=" struct pending_startup_id { char *msg; size_t len; xcb_window_t window; struct wl_list link; }; static const struct wlr_addon_interface surface_addon_impl; struct wlr_xwayland_surface *wlr_xwayland_surface_try_from_wlr_surface( struct wlr_surface *surface) { struct wlr_addon *addon = wlr_addon_find(&surface->addons, NULL, &surface_addon_impl); if (addon == NULL) { return NULL; } struct wlr_xwayland_surface *xsurface = wl_container_of(addon, xsurface, surface_addon); return xsurface; } // TODO: replace this with hash table? static struct wlr_xwayland_surface *lookup_surface(struct wlr_xwm *xwm, xcb_window_t window_id) { struct wlr_xwayland_surface *surface; wl_list_for_each(surface, &xwm->surfaces, link) { if (surface->window_id == window_id) { return surface; } } return NULL; } static int xwayland_surface_handle_ping_timeout(void *data) { struct wlr_xwayland_surface *surface = data; wl_signal_emit_mutable(&surface->events.ping_timeout, NULL); surface->pinging = false; return 1; } static struct wlr_xwayland_surface *xwayland_surface_create( struct wlr_xwm *xwm, xcb_window_t window_id, int16_t x, int16_t y, uint16_t width, uint16_t height, bool override_redirect) { struct wlr_xwayland_surface *surface = calloc(1, sizeof(*surface)); if (!surface) { wlr_log(WLR_ERROR, "Could not allocate wlr xwayland surface"); return NULL; } xcb_get_geometry_cookie_t geometry_cookie = xcb_get_geometry(xwm->xcb_conn, window_id); uint32_t values[1]; values[0] = XCB_EVENT_MASK_FOCUS_CHANGE | XCB_EVENT_MASK_PROPERTY_CHANGE; xcb_change_window_attributes(xwm->xcb_conn, window_id, XCB_CW_EVENT_MASK, values); surface->xwm = xwm; surface->window_id = window_id; surface->x = x; surface->y = y; surface->width = width; surface->height = height; surface->override_redirect = override_redirect; wl_list_init(&surface->children); wl_list_init(&surface->stack_link); wl_list_init(&surface->parent_link); wl_list_init(&surface->unpaired_link); wl_signal_init(&surface->events.destroy); wl_signal_init(&surface->events.request_configure); wl_signal_init(&surface->events.request_move); wl_signal_init(&surface->events.request_resize); wl_signal_init(&surface->events.request_minimize); wl_signal_init(&surface->events.request_maximize); wl_signal_init(&surface->events.request_fullscreen); wl_signal_init(&surface->events.request_activate); wl_signal_init(&surface->events.associate); wl_signal_init(&surface->events.dissociate); wl_signal_init(&surface->events.set_class); wl_signal_init(&surface->events.set_role); wl_signal_init(&surface->events.set_title); wl_signal_init(&surface->events.set_parent); wl_signal_init(&surface->events.set_startup_id); wl_signal_init(&surface->events.set_window_type); wl_signal_init(&surface->events.set_hints); wl_signal_init(&surface->events.set_decorations); wl_signal_init(&surface->events.set_strut_partial); wl_signal_init(&surface->events.set_override_redirect); wl_signal_init(&surface->events.ping_timeout); wl_signal_init(&surface->events.set_geometry); xcb_get_geometry_reply_t *geometry_reply = xcb_get_geometry_reply(xwm->xcb_conn, geometry_cookie, NULL); if (geometry_reply != NULL) { surface->has_alpha = geometry_reply->depth == 32; } free(geometry_reply); struct wl_display *display = xwm->xwayland->wl_display; struct wl_event_loop *loop = wl_display_get_event_loop(display); surface->ping_timer = wl_event_loop_add_timer(loop, xwayland_surface_handle_ping_timeout, surface); if (surface->ping_timer == NULL) { free(surface); wlr_log(WLR_ERROR, "Could not add timer to event loop"); return NULL; } wl_list_insert(&xwm->surfaces, &surface->link); wl_signal_emit_mutable(&xwm->xwayland->events.new_surface, surface); return surface; } static void xwm_set_net_active_window(struct wlr_xwm *xwm, xcb_window_t window) { xcb_change_property(xwm->xcb_conn, XCB_PROP_MODE_REPLACE, xwm->screen->root, xwm->atoms[NET_ACTIVE_WINDOW], xwm->atoms[WINDOW], 32, 1, &window); } static void xwm_send_wm_message(struct wlr_xwayland_surface *surface, xcb_client_message_data_t *data, uint32_t event_mask) { struct wlr_xwm *xwm = surface->xwm; xcb_client_message_event_t event = { .response_type = XCB_CLIENT_MESSAGE, .format = 32, .sequence = 0, .window = surface->window_id, .type = xwm->atoms[WM_PROTOCOLS], .data = *data, }; xcb_send_event(xwm->xcb_conn, 0, // propagate surface->window_id, event_mask, (const char *)&event); xcb_flush(xwm->xcb_conn); } static void xwm_set_net_client_list(struct wlr_xwm *xwm) { // FIXME: _NET_CLIENT_LIST is expected to be ordered by map time, but the // order of surfaces in `xwm->surfaces` is by creation time. The order of // windows _NET_CLIENT_LIST exposed by wlroots is wrong. size_t mapped_surfaces = 0; struct wlr_xwayland_surface *surface; wl_list_for_each(surface, &xwm->surfaces, link) { if (surface->surface != NULL && surface->surface->mapped) { mapped_surfaces++; } } xcb_window_t *windows = malloc(sizeof(xcb_window_t) * mapped_surfaces); if (!windows) { return; } size_t index = 0; wl_list_for_each(surface, &xwm->surfaces, link) { if (surface->surface != NULL && surface->surface->mapped) { windows[index++] = surface->window_id; } } xcb_change_property(xwm->xcb_conn, XCB_PROP_MODE_REPLACE, xwm->screen->root, xwm->atoms[NET_CLIENT_LIST], XCB_ATOM_WINDOW, 32, mapped_surfaces, windows); free(windows); } static void xwm_set_net_client_list_stacking(struct wlr_xwm *xwm) { size_t num_surfaces = wl_list_length(&xwm->surfaces_in_stack_order); xcb_window_t *windows = malloc(sizeof(xcb_window_t) * num_surfaces); if (!windows) { return; } size_t i = 0; struct wlr_xwayland_surface *xsurface; wl_list_for_each(xsurface, &xwm->surfaces_in_stack_order, stack_link) { windows[i++] = xsurface->window_id; } xcb_change_property(xwm->xcb_conn, XCB_PROP_MODE_REPLACE, xwm->screen->root, xwm->atoms[NET_CLIENT_LIST_STACKING], XCB_ATOM_WINDOW, 32, num_surfaces, windows); free(windows); } static void xsurface_set_net_wm_state(struct wlr_xwayland_surface *xsurface); static void xwm_set_focus_window(struct wlr_xwm *xwm, struct wlr_xwayland_surface *xsurface) { struct wlr_xwayland_surface *unfocus_surface = xwm->focus_surface; // We handle cases where focus_surface == xsurface because we // want to be able to deny FocusIn events. xwm->focus_surface = xsurface; if (unfocus_surface) { xsurface_set_net_wm_state(unfocus_surface); } if (!xsurface) { xcb_set_input_focus_checked(xwm->xcb_conn, XCB_INPUT_FOCUS_POINTER_ROOT, XCB_NONE, XCB_CURRENT_TIME); return; } if (xsurface->override_redirect) { return; } xcb_client_message_data_t message_data = { 0 }; message_data.data32[0] = xwm->atoms[WM_TAKE_FOCUS]; message_data.data32[1] = XCB_TIME_CURRENT_TIME; if (xsurface->hints && !xsurface->hints->input) { // if the surface doesn't allow the focus request, we will send him // only the take focus event. It will get the focus by itself. xwm_send_wm_message(xsurface, &message_data, XCB_EVENT_MASK_NO_EVENT); } else { xwm_send_wm_message(xsurface, &message_data, XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT); xcb_void_cookie_t cookie = xcb_set_input_focus(xwm->xcb_conn, XCB_INPUT_FOCUS_POINTER_ROOT, xsurface->window_id, XCB_CURRENT_TIME); xwm->last_focus_seq = cookie.sequence; } xsurface_set_net_wm_state(xsurface); } static void xwm_surface_activate(struct wlr_xwm *xwm, struct wlr_xwayland_surface *xsurface) { if (xwm->focus_surface == xsurface || (xsurface && xsurface->override_redirect)) { return; } if (xsurface) { xwm_set_net_active_window(xwm, xsurface->window_id); } else { xwm_set_net_active_window(xwm, XCB_WINDOW_NONE); } xwm_set_focus_window(xwm, xsurface); xcb_flush(xwm->xcb_conn); } static void xsurface_set_net_wm_state(struct wlr_xwayland_surface *xsurface) { struct wlr_xwm *xwm = xsurface->xwm; // EWMH says _NET_WM_STATE should be unset if the window is withdrawn if (xsurface->withdrawn) { xcb_delete_property(xwm->xcb_conn, xsurface->window_id, xwm->atoms[NET_WM_STATE]); return; } uint32_t property[6]; size_t i = 0; if (xsurface->modal) { property[i++] = xwm->atoms[NET_WM_STATE_MODAL]; } if (xsurface->fullscreen) { property[i++] = xwm->atoms[NET_WM_STATE_FULLSCREEN]; } if (xsurface->maximized_vert) { property[i++] = xwm->atoms[NET_WM_STATE_MAXIMIZED_VERT]; } if (xsurface->maximized_horz) { property[i++] = xwm->atoms[NET_WM_STATE_MAXIMIZED_HORZ]; } if (xsurface->minimized) { property[i++] = xwm->atoms[NET_WM_STATE_HIDDEN]; } if (xsurface == xwm->focus_surface) { property[i++] = xwm->atoms[NET_WM_STATE_FOCUSED]; } assert(i <= sizeof(property) / sizeof(property[0])); xcb_change_property(xwm->xcb_conn, XCB_PROP_MODE_REPLACE, xsurface->window_id, xwm->atoms[NET_WM_STATE], XCB_ATOM_ATOM, 32, // format i, property); } static void xwayland_surface_dissociate(struct wlr_xwayland_surface *xsurface) { if (xsurface->surface != NULL) { wlr_surface_unmap(xsurface->surface); wl_signal_emit_mutable(&xsurface->events.dissociate, NULL); wl_list_remove(&xsurface->surface_commit.link); wl_list_remove(&xsurface->surface_map.link); wl_list_remove(&xsurface->surface_unmap.link); wlr_addon_finish(&xsurface->surface_addon); xsurface->surface = NULL; } // Make sure we're not on the unpaired surface list or we // could be assigned a surface during surface creation that // was mapped before this unmap request. wl_list_remove(&xsurface->unpaired_link); wl_list_init(&xsurface->unpaired_link); xsurface->surface_id = 0; xsurface->serial = 0; } static void xwayland_surface_destroy(struct wlr_xwayland_surface *xsurface) { xwayland_surface_dissociate(xsurface); wl_signal_emit_mutable(&xsurface->events.destroy, NULL); if (xsurface == xsurface->xwm->focus_surface) { xwm_surface_activate(xsurface->xwm, NULL); } wl_list_remove(&xsurface->link); wl_list_remove(&xsurface->stack_link); wl_list_remove(&xsurface->parent_link); struct wlr_xwayland_surface *child, *next; wl_list_for_each_safe(child, next, &xsurface->children, parent_link) { wl_list_remove(&child->parent_link); wl_list_init(&child->parent_link); child->parent = NULL; } wl_list_remove(&xsurface->unpaired_link); wl_event_source_remove(xsurface->ping_timer); free(xsurface->title); free(xsurface->class); free(xsurface->instance); free(xsurface->role); free(xsurface->window_type); free(xsurface->protocols); free(xsurface->startup_id); free(xsurface->hints); free(xsurface->size_hints); free(xsurface->strut_partial); free(xsurface); } static void read_surface_class(struct wlr_xwm *xwm, struct wlr_xwayland_surface *surface, xcb_get_property_reply_t *reply) { if (reply->type != XCB_ATOM_STRING && reply->type != xwm->atoms[UTF8_STRING]) { return; } size_t len = xcb_get_property_value_length(reply); char *class = xcb_get_property_value(reply); // Unpack two sequentially stored strings: instance, class size_t instance_len = strnlen(class, len); free(surface->instance); if (len > 0 && instance_len < len) { surface->instance = strndup(class, instance_len); class += instance_len + 1; } else { surface->instance = NULL; } free(surface->class); if (len > 0) { surface->class = strndup(class, len); } else { surface->class = NULL; } wl_signal_emit_mutable(&surface->events.set_class, NULL); } static void read_surface_startup_id(struct wlr_xwm *xwm, struct wlr_xwayland_surface *xsurface, xcb_get_property_reply_t *reply) { if (reply->type != XCB_ATOM_STRING && reply->type != xwm->atoms[UTF8_STRING]) { return; } size_t len = xcb_get_property_value_length(reply); char *startup_id = xcb_get_property_value(reply); free(xsurface->startup_id); if (len > 0) { xsurface->startup_id = strndup(startup_id, len); } else { xsurface->startup_id = NULL; } wlr_log(WLR_DEBUG, "XCB_ATOM_NET_STARTUP_ID: %s", xsurface->startup_id ? xsurface->startup_id: "(null)"); wl_signal_emit_mutable(&xsurface->events.set_startup_id, NULL); } static void read_surface_role(struct wlr_xwm *xwm, struct wlr_xwayland_surface *xsurface, xcb_get_property_reply_t *reply) { if (reply->type != XCB_ATOM_STRING && reply->type != xwm->atoms[UTF8_STRING]) { return; } size_t len = xcb_get_property_value_length(reply); char *role = xcb_get_property_value(reply); free(xsurface->role); if (len > 0) { xsurface->role = strndup(role, len); } else { xsurface->role = NULL; } wl_signal_emit_mutable(&xsurface->events.set_role, NULL); } static void read_surface_title(struct wlr_xwm *xwm, struct wlr_xwayland_surface *xsurface, xcb_get_property_reply_t *reply) { if (reply->type != XCB_ATOM_STRING && reply->type != xwm->atoms[UTF8_STRING]) { return; } bool is_utf8 = reply->type == xwm->atoms[UTF8_STRING]; if (!is_utf8 && xsurface->has_utf8_title) { return; } size_t len = xcb_get_property_value_length(reply); char *title = xcb_get_property_value(reply); free(xsurface->title); if (len > 0) { xsurface->title = strndup(title, len); } else { xsurface->title = NULL; } xsurface->has_utf8_title = is_utf8; wl_signal_emit_mutable(&xsurface->events.set_title, NULL); } static bool has_parent(struct wlr_xwayland_surface *parent, struct wlr_xwayland_surface *child) { while (parent) { if (child == parent) { return true; } parent = parent->parent; } return false; } static void read_surface_parent(struct wlr_xwm *xwm, struct wlr_xwayland_surface *xsurface, xcb_get_property_reply_t *reply) { struct wlr_xwayland_surface *found_parent = NULL; if (reply->type != XCB_ATOM_WINDOW) { return; } xcb_window_t *xid = xcb_get_property_value(reply); if (xid != NULL) { found_parent = lookup_surface(xwm, *xid); if (!has_parent(found_parent, xsurface)) { xsurface->parent = found_parent; } else { wlr_log(WLR_INFO, "%p with %p would create a loop", xsurface, found_parent); } } else { xsurface->parent = NULL; } wl_list_remove(&xsurface->parent_link); if (xsurface->parent != NULL) { wl_list_insert(&xsurface->parent->children, &xsurface->parent_link); } else { wl_list_init(&xsurface->parent_link); } wl_signal_emit_mutable(&xsurface->events.set_parent, NULL); } static void read_surface_client_id(struct wlr_xwm *xwm, struct wlr_xwayland_surface *xsurface, xcb_res_query_client_ids_cookie_t cookie) { xcb_res_query_client_ids_reply_t *reply = xcb_res_query_client_ids_reply( xwm->xcb_conn, cookie, NULL); if (reply == NULL) { return; } uint32_t *pid = NULL; xcb_res_client_id_value_iterator_t iter = xcb_res_query_client_ids_ids_iterator(reply); while (iter.rem > 0) { if (iter.data->spec.mask & XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID && xcb_res_client_id_value_value_length(iter.data) > 0) { pid = xcb_res_client_id_value_value(iter.data); break; } xcb_res_client_id_value_next(&iter); } if (pid == NULL) { free(reply); return; } xsurface->pid = *pid; free(reply); } static void read_surface_window_type(struct wlr_xwm *xwm, struct wlr_xwayland_surface *xsurface, xcb_get_property_reply_t *reply) { if (reply->type != XCB_ATOM_ATOM) { return; } xcb_atom_t *atoms = xcb_get_property_value(reply); size_t atoms_len = reply->value_len; size_t atoms_size = sizeof(xcb_atom_t) * atoms_len; free(xsurface->window_type); xsurface->window_type = malloc(atoms_size); if (xsurface->window_type == NULL) { return; } memcpy(xsurface->window_type, atoms, atoms_size); xsurface->window_type_len = atoms_len; wl_signal_emit_mutable(&xsurface->events.set_window_type, NULL); } static void read_surface_protocols(struct wlr_xwm *xwm, struct wlr_xwayland_surface *xsurface, xcb_get_property_reply_t *reply) { if (reply->type != XCB_ATOM_ATOM) { return; } xcb_atom_t *atoms = xcb_get_property_value(reply); size_t atoms_len = reply->value_len; size_t atoms_size = sizeof(xcb_atom_t) * atoms_len; free(xsurface->protocols); xsurface->protocols = malloc(atoms_size); if (xsurface->protocols == NULL) { return; } memcpy(xsurface->protocols, atoms, atoms_size); xsurface->protocols_len = atoms_len; } static void read_surface_hints(struct wlr_xwm *xwm, struct wlr_xwayland_surface *xsurface, xcb_get_property_reply_t *reply) { // According to the docs, reply->type == xwm->atoms[WM_HINTS] // In practice, reply->type == XCB_ATOM_ATOM if (reply->value_len == 0) { return; } free(xsurface->hints); xsurface->hints = calloc(1, sizeof(*xsurface->hints)); if (xsurface->hints == NULL) { return; } xcb_icccm_get_wm_hints_from_reply(xsurface->hints, reply); if (!(xsurface->hints->flags & XCB_ICCCM_WM_HINT_INPUT)) { // The client didn't specify whether it wants input. // Assume it does. xsurface->hints->input = true; } wl_signal_emit_mutable(&xsurface->events.set_hints, NULL); } static void read_surface_normal_hints(struct wlr_xwm *xwm, struct wlr_xwayland_surface *xsurface, xcb_get_property_reply_t *reply) { if (reply->type != xwm->atoms[WM_SIZE_HINTS] || reply->value_len == 0) { return; } free(xsurface->size_hints); xsurface->size_hints = calloc(1, sizeof(*xsurface->size_hints)); if (xsurface->size_hints == NULL) { return; } xcb_icccm_get_wm_size_hints_from_reply(xsurface->size_hints, reply); int32_t flags = xsurface->size_hints->flags; bool has_min_size_hints = (flags & XCB_ICCCM_SIZE_HINT_P_MIN_SIZE) != 0; bool has_base_size_hints = (flags & XCB_ICCCM_SIZE_HINT_BASE_SIZE) != 0; /* ICCCM says that if absent, min size is equal to base size and vice versa */ if (!has_min_size_hints && !has_base_size_hints) { xsurface->size_hints->min_width = -1; xsurface->size_hints->min_height = -1; xsurface->size_hints->base_width = -1; xsurface->size_hints->base_height = -1; } else if (!has_base_size_hints) { xsurface->size_hints->base_width = xsurface->size_hints->min_width; xsurface->size_hints->base_height = xsurface->size_hints->min_height; } else if (!has_min_size_hints) { xsurface->size_hints->min_width = xsurface->size_hints->base_width; xsurface->size_hints->min_height = xsurface->size_hints->base_height; } if ((flags & XCB_ICCCM_SIZE_HINT_P_MAX_SIZE) == 0) { xsurface->size_hints->max_width = -1; xsurface->size_hints->max_height = -1; } } #define MWM_HINTS_FLAGS_FIELD 0 #define MWM_HINTS_DECORATIONS_FIELD 2 #define MWM_HINTS_DECORATIONS (1 << 1) #define MWM_DECOR_ALL (1 << 0) #define MWM_DECOR_BORDER (1 << 1) #define MWM_DECOR_TITLE (1 << 3) static void read_surface_motif_hints(struct wlr_xwm *xwm, struct wlr_xwayland_surface *xsurface, xcb_get_property_reply_t *reply) { if (reply->value_len < 5) { return; } uint32_t *motif_hints = xcb_get_property_value(reply); if (motif_hints[MWM_HINTS_FLAGS_FIELD] & MWM_HINTS_DECORATIONS) { xsurface->decorations = WLR_XWAYLAND_SURFACE_DECORATIONS_ALL; uint32_t decorations = motif_hints[MWM_HINTS_DECORATIONS_FIELD]; if ((decorations & MWM_DECOR_ALL) == 0) { if ((decorations & MWM_DECOR_BORDER) == 0) { xsurface->decorations |= WLR_XWAYLAND_SURFACE_DECORATIONS_NO_BORDER; } if ((decorations & MWM_DECOR_TITLE) == 0) { xsurface->decorations |= WLR_XWAYLAND_SURFACE_DECORATIONS_NO_TITLE; } } wl_signal_emit_mutable(&xsurface->events.set_decorations, NULL); } } static void read_surface_strut_partial(struct wlr_xwm *xwm, struct wlr_xwayland_surface *xsurface, xcb_get_property_reply_t *reply) { if (reply->type != XCB_ATOM_CARDINAL || reply->format != 32 || xcb_get_property_value_length(reply) != sizeof(xcb_ewmh_wm_strut_partial_t)) { return; } free(xsurface->strut_partial); xsurface->strut_partial = calloc(1, sizeof(*xsurface->strut_partial)); if (xsurface->strut_partial == NULL) { return; } xcb_ewmh_get_wm_strut_partial_from_reply(xsurface->strut_partial, reply); wl_signal_emit_mutable(&xsurface->events.set_strut_partial, NULL); } static void read_surface_net_wm_state(struct wlr_xwm *xwm, struct wlr_xwayland_surface *xsurface, xcb_get_property_reply_t *reply) { xsurface->fullscreen = 0; xcb_atom_t *atom = xcb_get_property_value(reply); for (uint32_t i = 0; i < reply->value_len; i++) { if (atom[i] == xwm->atoms[NET_WM_STATE_MODAL]) { xsurface->modal = true; } else if (atom[i] == xwm->atoms[NET_WM_STATE_FULLSCREEN]) { xsurface->fullscreen = true; } else if (atom[i] == xwm->atoms[NET_WM_STATE_MAXIMIZED_VERT]) { xsurface->maximized_vert = true; } else if (atom[i] == xwm->atoms[NET_WM_STATE_MAXIMIZED_HORZ]) { xsurface->maximized_horz = true; } else if (atom[i] == xwm->atoms[NET_WM_STATE_HIDDEN]) { xsurface->minimized = true; } } } char *xwm_get_atom_name(struct wlr_xwm *xwm, xcb_atom_t atom) { xcb_get_atom_name_cookie_t name_cookie = xcb_get_atom_name(xwm->xcb_conn, atom); xcb_get_atom_name_reply_t *name_reply = xcb_get_atom_name_reply(xwm->xcb_conn, name_cookie, NULL); if (name_reply == NULL) { return NULL; } size_t len = xcb_get_atom_name_name_length(name_reply); char *buf = xcb_get_atom_name_name(name_reply); // not a C string char *name = strndup(buf, len); free(name_reply); return name; } static void read_surface_property(struct wlr_xwm *xwm, struct wlr_xwayland_surface *xsurface, xcb_atom_t property, xcb_get_property_reply_t *reply) { if (property == XCB_ATOM_WM_CLASS) { read_surface_class(xwm, xsurface, reply); } else if (property == XCB_ATOM_WM_NAME || property == xwm->atoms[NET_WM_NAME]) { read_surface_title(xwm, xsurface, reply); } else if (property == XCB_ATOM_WM_TRANSIENT_FOR) { read_surface_parent(xwm, xsurface, reply); } else if (property == xwm->atoms[NET_WM_PID]) { // intentionally ignored } else if (property == xwm->atoms[NET_WM_WINDOW_TYPE]) { read_surface_window_type(xwm, xsurface, reply); } else if (property == xwm->atoms[WM_PROTOCOLS]) { read_surface_protocols(xwm, xsurface, reply); } else if (property == xwm->atoms[NET_WM_STATE]) { read_surface_net_wm_state(xwm, xsurface, reply); } else if (property == xwm->atoms[WM_HINTS]) { read_surface_hints(xwm, xsurface, reply); } else if (property == xwm->atoms[WM_NORMAL_HINTS]) { read_surface_normal_hints(xwm, xsurface, reply); } else if (property == xwm->atoms[MOTIF_WM_HINTS]) { read_surface_motif_hints(xwm, xsurface, reply); } else if (property == xwm->atoms[NET_WM_STRUT_PARTIAL]) { read_surface_strut_partial(xwm, xsurface, reply); } else if (property == xwm->atoms[WM_WINDOW_ROLE]) { read_surface_role(xwm, xsurface, reply); } else if (property == xwm->atoms[NET_STARTUP_ID]) { read_surface_startup_id(xwm, xsurface, reply); } else if (wlr_log_get_verbosity() >= WLR_DEBUG) { char *prop_name = xwm_get_atom_name(xwm, property); wlr_log(WLR_DEBUG, "unhandled X11 property %" PRIu32 " (%s) for window %" PRIu32, property, prop_name ? prop_name : "(null)", xsurface->window_id); free(prop_name); } } static void xwayland_surface_handle_commit(struct wl_listener *listener, void *data) { struct wlr_xwayland_surface *xsurface = wl_container_of(listener, xsurface, surface_commit); if (wlr_surface_has_buffer(xsurface->surface)) { wlr_surface_map(xsurface->surface); } } static void xwayland_surface_handle_map(struct wl_listener *listener, void *data) { struct wlr_xwayland_surface *xsurface = wl_container_of(listener, xsurface, surface_map); xwm_set_net_client_list(xsurface->xwm); } static void xwayland_surface_handle_unmap(struct wl_listener *listener, void *data) { struct wlr_xwayland_surface *xsurface = wl_container_of(listener, xsurface, surface_unmap); xwm_set_net_client_list(xsurface->xwm); } static void xwayland_surface_handle_addon_destroy(struct wlr_addon *addon) { struct wlr_xwayland_surface *xsurface = wl_container_of(addon, xsurface, surface_addon); xwayland_surface_dissociate(xsurface); } static const struct wlr_addon_interface surface_addon_impl = { .name = "wlr_xwayland_surface", .destroy = xwayland_surface_handle_addon_destroy, }; static void xwayland_surface_associate(struct wlr_xwm *xwm, struct wlr_xwayland_surface *xsurface, struct wlr_surface *surface) { assert(xsurface->surface == NULL); wl_list_remove(&xsurface->unpaired_link); wl_list_init(&xsurface->unpaired_link); xsurface->surface_id = 0; xsurface->surface = surface; wlr_addon_init(&xsurface->surface_addon, &surface->addons, NULL, &surface_addon_impl); xsurface->surface_commit.notify = xwayland_surface_handle_commit; wl_signal_add(&surface->events.commit, &xsurface->surface_commit); xsurface->surface_map.notify = xwayland_surface_handle_map; wl_signal_add(&surface->events.map, &xsurface->surface_map); xsurface->surface_unmap.notify = xwayland_surface_handle_unmap; wl_signal_add(&surface->events.unmap, &xsurface->surface_unmap); // read all surface properties const xcb_atom_t props[] = { XCB_ATOM_WM_CLASS, XCB_ATOM_WM_NAME, XCB_ATOM_WM_TRANSIENT_FOR, xwm->atoms[WM_PROTOCOLS], xwm->atoms[WM_HINTS], xwm->atoms[WM_NORMAL_HINTS], xwm->atoms[MOTIF_WM_HINTS], xwm->atoms[NET_STARTUP_ID], xwm->atoms[NET_WM_STATE], xwm->atoms[NET_WM_STRUT_PARTIAL], xwm->atoms[NET_WM_WINDOW_TYPE], xwm->atoms[NET_WM_NAME], }; xcb_get_property_cookie_t cookies[sizeof(props) / sizeof(props[0])] = {0}; for (size_t i = 0; i < sizeof(props) / sizeof(props[0]); i++) { cookies[i] = xcb_get_property(xwm->xcb_conn, 0, xsurface->window_id, props[i], XCB_ATOM_ANY, 0, 2048); } xcb_res_query_client_ids_cookie_t client_id_cookie; if (xwm->xres) { xcb_res_client_id_spec_t spec = { .client = xsurface->window_id, .mask = XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID }; client_id_cookie = xcb_res_query_client_ids(xwm->xcb_conn, 1, &spec); } for (size_t i = 0; i < sizeof(props) / sizeof(props[0]); i++) { xcb_get_property_reply_t *reply = xcb_get_property_reply(xwm->xcb_conn, cookies[i], NULL); if (reply == NULL) { wlr_log(WLR_ERROR, "Failed to get window property"); return; } read_surface_property(xwm, xsurface, props[i], reply); free(reply); } if (xwm->xres) { read_surface_client_id(xwm, xsurface, client_id_cookie); } wl_signal_emit_mutable(&xsurface->events.associate, NULL); } static void xwm_handle_create_notify(struct wlr_xwm *xwm, xcb_create_notify_event_t *ev) { if (ev->window == xwm->window || ev->window == xwm->primary_selection.window || ev->window == xwm->clipboard_selection.window || ev->window == xwm->dnd_selection.window) { return; } xwayland_surface_create(xwm, ev->window, ev->x, ev->y, ev->width, ev->height, ev->override_redirect); } static void xwm_handle_destroy_notify(struct wlr_xwm *xwm, xcb_destroy_notify_event_t *ev) { struct wlr_xwayland_surface *xsurface = lookup_surface(xwm, ev->window); if (xsurface == NULL) { return; } xwayland_surface_destroy(xsurface); xwm_handle_selection_destroy_notify(xwm, ev); } static void xwm_handle_configure_request(struct wlr_xwm *xwm, xcb_configure_request_event_t *ev) { struct wlr_xwayland_surface *surface = lookup_surface(xwm, ev->window); if (surface == NULL) { return; } // TODO: handle ev->{parent,sibling}? uint16_t mask = ev->value_mask; uint16_t geo_mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT; if ((mask & geo_mask) == 0) { return; } struct wlr_xwayland_surface_configure_event wlr_event = { .surface = surface, .x = mask & XCB_CONFIG_WINDOW_X ? ev->x : surface->x, .y = mask & XCB_CONFIG_WINDOW_Y ? ev->y : surface->y, .width = mask & XCB_CONFIG_WINDOW_WIDTH ? ev->width : surface->width, .height = mask & XCB_CONFIG_WINDOW_HEIGHT ? ev->height : surface->height, .mask = mask, }; wl_signal_emit_mutable(&surface->events.request_configure, &wlr_event); } static void xwm_handle_configure_notify(struct wlr_xwm *xwm, xcb_configure_notify_event_t *ev) { struct wlr_xwayland_surface *xsurface = lookup_surface(xwm, ev->window); if (!xsurface) { return; } bool geometry_changed = (xsurface->x != ev->x || xsurface->y != ev->y || xsurface->width != ev->width || xsurface->height != ev->height); if (geometry_changed) { xsurface->x = ev->x; xsurface->y = ev->y; xsurface->width = ev->width; xsurface->height = ev->height; } if (xsurface->override_redirect != ev->override_redirect) { xsurface->override_redirect = ev->override_redirect; wl_signal_emit_mutable(&xsurface->events.set_override_redirect, NULL); } if (geometry_changed) { wl_signal_emit_mutable(&xsurface->events.set_geometry, NULL); } } static void xsurface_set_wm_state(struct wlr_xwayland_surface *xsurface) { struct wlr_xwm *xwm = xsurface->xwm; uint32_t property[] = { XCB_ICCCM_WM_STATE_NORMAL, XCB_WINDOW_NONE }; if (xsurface->withdrawn) { property[0] = XCB_ICCCM_WM_STATE_WITHDRAWN; } else if (xsurface->minimized) { property[0] = XCB_ICCCM_WM_STATE_ICONIC; } else { property[0] = XCB_ICCCM_WM_STATE_NORMAL; } xcb_change_property(xwm->xcb_conn, XCB_PROP_MODE_REPLACE, xsurface->window_id, xwm->atoms[WM_STATE], xwm->atoms[WM_STATE], 32, // format sizeof(property) / sizeof(property[0]), property); } void wlr_xwayland_surface_restack(struct wlr_xwayland_surface *xsurface, struct wlr_xwayland_surface *sibling, enum xcb_stack_mode_t mode) { struct wlr_xwm *xwm = xsurface->xwm; uint32_t values[2]; size_t idx = 0; uint32_t flags = XCB_CONFIG_WINDOW_STACK_MODE; if (sibling != NULL) { values[idx++] = sibling->window_id; flags |= XCB_CONFIG_WINDOW_SIBLING; } values[idx++] = mode; xcb_configure_window(xwm->xcb_conn, xsurface->window_id, flags, values); wl_list_remove(&xsurface->stack_link); struct wl_list *node; if (mode == XCB_STACK_MODE_ABOVE) { if (sibling) { node = &sibling->stack_link; } else { node = xwm->surfaces_in_stack_order.prev; } } else if (mode == XCB_STACK_MODE_BELOW) { if (sibling) { node = sibling->stack_link.prev; } else { node = &xwm->surfaces_in_stack_order; } } else { // Not implementing XCB_STACK_MODE_TOP_IF | XCB_STACK_MODE_BOTTOM_IF | // XCB_STACK_MODE_OPPOSITE. abort(); } wl_list_insert(node, &xsurface->stack_link); xwm_set_net_client_list_stacking(xwm); xcb_flush(xwm->xcb_conn); } static void xwm_handle_map_request(struct wlr_xwm *xwm, xcb_map_request_event_t *ev) { struct wlr_xwayland_surface *xsurface = lookup_surface(xwm, ev->window); if (!xsurface) { return; } wlr_xwayland_surface_set_withdrawn(xsurface, false); wlr_xwayland_surface_restack(xsurface, NULL, XCB_STACK_MODE_BELOW); xcb_map_window(xwm->xcb_conn, ev->window); } static void xwm_handle_map_notify(struct wlr_xwm *xwm, xcb_map_notify_event_t *ev) { struct wlr_xwayland_surface *xsurface = lookup_surface(xwm, ev->window); if (!xsurface) { return; } if (xsurface->override_redirect != ev->override_redirect) { xsurface->override_redirect = ev->override_redirect; wl_signal_emit_mutable(&xsurface->events.set_override_redirect, NULL); } } static void xwm_handle_unmap_notify(struct wlr_xwm *xwm, xcb_unmap_notify_event_t *ev) { struct wlr_xwayland_surface *xsurface = lookup_surface(xwm, ev->window); if (xsurface == NULL) { return; } xwayland_surface_dissociate(xsurface); if (!xsurface->override_redirect) { wlr_xwayland_surface_set_withdrawn(xsurface, true); } } static void xwm_handle_property_notify(struct wlr_xwm *xwm, xcb_property_notify_event_t *ev) { struct wlr_xwayland_surface *xsurface = lookup_surface(xwm, ev->window); if (xsurface == NULL) { return; } xcb_get_property_cookie_t cookie = xcb_get_property(xwm->xcb_conn, 0, xsurface->window_id, ev->atom, XCB_ATOM_ANY, 0, 2048); xcb_get_property_reply_t *reply = xcb_get_property_reply(xwm->xcb_conn, cookie, NULL); if (reply == NULL) { wlr_log(WLR_ERROR, "Failed to get window property"); return; } read_surface_property(xwm, xsurface, ev->atom, reply); free(reply); } static void xwm_handle_surface_id_message(struct wlr_xwm *xwm, xcb_client_message_event_t *ev) { struct wlr_xwayland_surface *xsurface = lookup_surface(xwm, ev->window); if (xsurface == NULL) { wlr_log(WLR_DEBUG, "client message WL_SURFACE_ID but no new window %u ?", ev->window); return; } /* Check if we got notified after wayland surface create event */ uint32_t id = ev->data.data32[0]; struct wl_resource *resource = wl_client_get_object(xwm->xwayland->server->client, id); if (resource) { struct wlr_surface *surface = wlr_surface_from_resource(resource); xwayland_surface_associate(xwm, xsurface, surface); } else { xsurface->surface_id = id; wl_list_remove(&xsurface->unpaired_link); wl_list_insert(&xwm->unpaired_surfaces, &xsurface->unpaired_link); } } static void xwm_handle_surface_serial_message(struct wlr_xwm *xwm, xcb_client_message_event_t *ev) { struct wlr_xwayland_surface *xsurface = lookup_surface(xwm, ev->window); if (xsurface == NULL) { wlr_log(WLR_DEBUG, "Received client message WL_SURFACE_SERIAL but no X11 window %u", ev->window); return; } if (xsurface->serial != 0) { wlr_log(WLR_DEBUG, "Received multiple client messages WL_SURFACE_SERIAL " "for the same X11 window %u", ev->window); return; } uint32_t serial_lo = ev->data.data32[0]; uint32_t serial_hi = ev->data.data32[1]; xsurface->serial = ((uint64_t)serial_hi << 32) | serial_lo; struct wlr_surface *surface = wlr_xwayland_shell_v1_surface_from_serial( xwm->xwayland->shell_v1, xsurface->serial); if (surface != NULL) { xwayland_surface_associate(xwm, xsurface, surface); } else { wl_list_remove(&xsurface->unpaired_link); wl_list_insert(&xwm->unpaired_surfaces, &xsurface->unpaired_link); } } #define _NET_WM_MOVERESIZE_SIZE_TOPLEFT 0 #define _NET_WM_MOVERESIZE_SIZE_TOP 1 #define _NET_WM_MOVERESIZE_SIZE_TOPRIGHT 2 #define _NET_WM_MOVERESIZE_SIZE_RIGHT 3 #define _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT 4 #define _NET_WM_MOVERESIZE_SIZE_BOTTOM 5 #define _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT 6 #define _NET_WM_MOVERESIZE_SIZE_LEFT 7 #define _NET_WM_MOVERESIZE_MOVE 8 // movement only #define _NET_WM_MOVERESIZE_SIZE_KEYBOARD 9 // size via keyboard #define _NET_WM_MOVERESIZE_MOVE_KEYBOARD 10 // move via keyboard #define _NET_WM_MOVERESIZE_CANCEL 11 // cancel operation static enum wlr_edges net_wm_edges_to_wlr(uint32_t net_wm_edges) { switch(net_wm_edges) { case _NET_WM_MOVERESIZE_SIZE_TOPLEFT: return WLR_EDGE_TOP | WLR_EDGE_LEFT; case _NET_WM_MOVERESIZE_SIZE_TOP: return WLR_EDGE_TOP; case _NET_WM_MOVERESIZE_SIZE_TOPRIGHT: return WLR_EDGE_TOP | WLR_EDGE_RIGHT; case _NET_WM_MOVERESIZE_SIZE_RIGHT: return WLR_EDGE_RIGHT; case _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT: return WLR_EDGE_BOTTOM | WLR_EDGE_RIGHT; case _NET_WM_MOVERESIZE_SIZE_BOTTOM: return WLR_EDGE_BOTTOM; case _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT: return WLR_EDGE_BOTTOM | WLR_EDGE_LEFT; case _NET_WM_MOVERESIZE_SIZE_LEFT: return WLR_EDGE_LEFT; default: return WLR_EDGE_NONE; } } static void xwm_handle_net_wm_moveresize_message(struct wlr_xwm *xwm, xcb_client_message_event_t *ev) { struct wlr_xwayland_surface *xsurface = lookup_surface(xwm, ev->window); if (!xsurface) { return; } int detail = ev->data.data32[2]; switch (detail) { case _NET_WM_MOVERESIZE_MOVE: wl_signal_emit_mutable(&xsurface->events.request_move, NULL); break; case _NET_WM_MOVERESIZE_SIZE_TOPLEFT: case _NET_WM_MOVERESIZE_SIZE_TOP: case _NET_WM_MOVERESIZE_SIZE_TOPRIGHT: case _NET_WM_MOVERESIZE_SIZE_RIGHT: case _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT: case _NET_WM_MOVERESIZE_SIZE_BOTTOM: case _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT: case _NET_WM_MOVERESIZE_SIZE_LEFT:; struct wlr_xwayland_resize_event resize_event = { .surface = xsurface, .edges = net_wm_edges_to_wlr(detail), }; wl_signal_emit_mutable(&xsurface->events.request_resize, &resize_event); break; case _NET_WM_MOVERESIZE_CANCEL: // handled by the compositor break; } } #define _NET_WM_STATE_REMOVE 0 #define _NET_WM_STATE_ADD 1 #define _NET_WM_STATE_TOGGLE 2 static bool update_state(int action, bool *state) { int new_state, changed; switch (action) { case _NET_WM_STATE_REMOVE: new_state = false; break; case _NET_WM_STATE_ADD: new_state = true; break; case _NET_WM_STATE_TOGGLE: new_state = !*state; break; default: return false; } changed = (*state != new_state); *state = new_state; return changed; } static bool xsurface_is_maximized( struct wlr_xwayland_surface *xsurface) { return xsurface->maximized_horz && xsurface->maximized_vert; } static void xwm_handle_net_wm_state_message(struct wlr_xwm *xwm, xcb_client_message_event_t *client_message) { struct wlr_xwayland_surface *xsurface = lookup_surface(xwm, client_message->window); if (!xsurface) { return; } if (client_message->format != 32) { return; } bool fullscreen = xsurface->fullscreen; bool maximized = xsurface_is_maximized(xsurface); bool minimized = xsurface->minimized; uint32_t action = client_message->data.data32[0]; for (size_t i = 0; i < 2; ++i) { xcb_atom_t property = client_message->data.data32[1 + i]; bool changed = false; if (property == xwm->atoms[NET_WM_STATE_MODAL]) { changed = update_state(action, &xsurface->modal); } else if (property == xwm->atoms[NET_WM_STATE_FULLSCREEN]) { changed = update_state(action, &xsurface->fullscreen); } else if (property == xwm->atoms[NET_WM_STATE_MAXIMIZED_VERT]) { changed = update_state(action, &xsurface->maximized_vert); } else if (property == xwm->atoms[NET_WM_STATE_MAXIMIZED_HORZ]) { changed = update_state(action, &xsurface->maximized_horz); } else if (property == xwm->atoms[NET_WM_STATE_HIDDEN]) { changed = update_state(action, &xsurface->minimized); } else if (property != XCB_ATOM_NONE && wlr_log_get_verbosity() >= WLR_DEBUG) { char *prop_name = xwm_get_atom_name(xwm, property); wlr_log(WLR_DEBUG, "Unhandled NET_WM_STATE property change " "%"PRIu32" (%s)", property, prop_name ? prop_name : "(null)"); free(prop_name); } if (changed) { xsurface_set_net_wm_state(xsurface); } } // client_message->data.data32[3] is the source indication // all other values are set to 0 if (fullscreen != xsurface->fullscreen) { if (xsurface->fullscreen) { xsurface->saved_width = xsurface->width; xsurface->saved_height = xsurface->height; } wl_signal_emit_mutable(&xsurface->events.request_fullscreen, NULL); } if (maximized != xsurface_is_maximized(xsurface)) { if (xsurface_is_maximized(xsurface)) { xsurface->saved_width = xsurface->width; xsurface->saved_height = xsurface->height; } wl_signal_emit_mutable(&xsurface->events.request_maximize, NULL); } if (minimized != xsurface->minimized) { if (xsurface->minimized) { xsurface->saved_width = xsurface->width; xsurface->saved_height = xsurface->height; } struct wlr_xwayland_minimize_event minimize_event = { .surface = xsurface, .minimize = xsurface->minimized, }; wl_signal_emit_mutable(&xsurface->events.request_minimize, &minimize_event); } } static void xwm_handle_wm_protocols_message(struct wlr_xwm *xwm, xcb_client_message_event_t *ev) { xcb_atom_t type = ev->data.data32[0]; if (type == xwm->atoms[NET_WM_PING]) { xcb_window_t window_id = ev->data.data32[2]; struct wlr_xwayland_surface *surface = lookup_surface(xwm, window_id); if (surface == NULL) { return; } if (!surface->pinging) { return; } wl_event_source_timer_update(surface->ping_timer, 0); surface->pinging = false; } else if (wlr_log_get_verbosity() >= WLR_DEBUG) { char *type_name = xwm_get_atom_name(xwm, type); wlr_log(WLR_DEBUG, "unhandled WM_PROTOCOLS client message %" PRIu32 " (%s)", type, type_name ? type_name : "(null)"); free(type_name); } } static void xwm_handle_net_active_window_message(struct wlr_xwm *xwm, xcb_client_message_event_t *ev) { struct wlr_xwayland_surface *surface = lookup_surface(xwm, ev->window); if (surface == NULL) { return; } wl_signal_emit_mutable(&surface->events.request_activate, NULL); } static void pending_startup_id_destroy(struct pending_startup_id *pending) { wl_list_remove(&pending->link); free(pending->msg); free(pending); } static void xwm_handle_net_startup_info_message(struct wlr_xwm *xwm, xcb_client_message_event_t *ev) { struct pending_startup_id *pending, *curr = NULL; wl_list_for_each(pending, &xwm->pending_startup_ids, link) { if (pending->window == ev->window) { curr = pending; break; } } char *start; size_t buf_len = sizeof(ev->data); if (curr) { curr->msg = realloc(curr->msg, curr->len + buf_len); if (!curr->msg) { pending_startup_id_destroy(curr); return; } start = curr->msg + curr->len; curr->len += buf_len; } else { curr = calloc(1, sizeof(*curr)); if (!curr) return; curr->window = ev->window; curr->msg = malloc(buf_len); if (!curr->msg) { free(curr); return; } start = curr->msg; curr->len = buf_len; wl_list_insert(&xwm->pending_startup_ids, &curr->link); } char *id = NULL; const char *data = (const char *)ev->data.data8; for (size_t i = 0; i < buf_len; i++) { start[i] = data[i]; if (start[i] == '\0') { if (strncmp(curr->msg, STARTUP_INFO_REMOVE_PREFIX, strlen(STARTUP_INFO_REMOVE_PREFIX)) == 0 && strlen(curr->msg) > strlen(STARTUP_INFO_REMOVE_PREFIX)) { id = curr->msg + strlen(STARTUP_INFO_REMOVE_PREFIX); break; } else { wlr_log(WLR_ERROR, "Unhandled message '%s'\n", curr->msg); pending_startup_id_destroy(curr); return; } } } if (id) { struct wlr_xwayland_remove_startup_info_event data = { id, ev->window }; wlr_log(WLR_DEBUG, "Got startup id: %s", id); wl_signal_emit_mutable(&xwm->xwayland->events.remove_startup_info, &data); pending_startup_id_destroy(curr); } } static void xwm_handle_wm_change_state_message(struct wlr_xwm *xwm, xcb_client_message_event_t *ev) { struct wlr_xwayland_surface *xsurface = lookup_surface(xwm, ev->window); uint32_t detail = ev->data.data32[0]; if (xsurface == NULL) { return; } bool minimize; if (detail == XCB_ICCCM_WM_STATE_ICONIC) { minimize = true; } else if (detail == XCB_ICCCM_WM_STATE_NORMAL) { minimize = false; } else { wlr_log(WLR_DEBUG, "unhandled wm_change_state event %u", detail); return; } struct wlr_xwayland_minimize_event minimize_event = { .surface = xsurface, .minimize = minimize, }; wl_signal_emit_mutable(&xsurface->events.request_minimize, &minimize_event); } static void xwm_handle_client_message(struct wlr_xwm *xwm, xcb_client_message_event_t *ev) { if (ev->type == xwm->atoms[WL_SURFACE_ID]) { xwm_handle_surface_id_message(xwm, ev); } else if (ev->type == xwm->atoms[WL_SURFACE_SERIAL]) { xwm_handle_surface_serial_message(xwm, ev); } else if (ev->type == xwm->atoms[NET_WM_STATE]) { xwm_handle_net_wm_state_message(xwm, ev); } else if (ev->type == xwm->atoms[NET_WM_MOVERESIZE]) { xwm_handle_net_wm_moveresize_message(xwm, ev); } else if (ev->type == xwm->atoms[WM_PROTOCOLS]) { xwm_handle_wm_protocols_message(xwm, ev); } else if (ev->type == xwm->atoms[NET_ACTIVE_WINDOW]) { xwm_handle_net_active_window_message(xwm, ev); } else if (ev->type == xwm->atoms[NET_STARTUP_INFO] || ev->type == xwm->atoms[NET_STARTUP_INFO_BEGIN]) { xwm_handle_net_startup_info_message(xwm, ev); } else if (ev->type == xwm->atoms[WM_CHANGE_STATE]) { xwm_handle_wm_change_state_message(xwm, ev); } else if (!xwm_handle_selection_client_message(xwm, ev) && wlr_log_get_verbosity() >= WLR_DEBUG) { char *type_name = xwm_get_atom_name(xwm, ev->type); wlr_log(WLR_DEBUG, "unhandled x11 client message %" PRIu32 " (%s)", ev->type, type_name ? type_name : "(null)"); free(type_name); } } static bool validate_focus_serial(uint16_t last_focus_seq, uint16_t event_seq) { uint16_t rev_dist = event_seq - last_focus_seq; if (rev_dist >= UINT16_MAX / 2) { // Probably overflow or too old return false; } return true; } static void xwm_handle_focus_in(struct wlr_xwm *xwm, xcb_focus_in_event_t *ev) { // Do not interfere with grabs if (ev->mode == XCB_NOTIFY_MODE_GRAB || ev->mode == XCB_NOTIFY_MODE_UNGRAB) { return; } // Ignore pointer focus change events if (ev->detail == XCB_NOTIFY_DETAIL_POINTER) { return; } // Do not let X clients change the focus behind the compositor's // back. Reset the focus to the old one if it changed. // // Note: Some applications rely on being able to change focus, for ex. Steam: // https://github.com/swaywm/sway/issues/1865 // Because of that, we allow changing focus between surfaces belonging to the // same application. We must be careful to ignore requests that are too old // though, because otherwise it may lead to race conditions: // https://github.com/swaywm/wlroots/issues/2324 struct wlr_xwayland_surface *requested_focus = lookup_surface(xwm, ev->event); if (xwm->focus_surface && requested_focus && requested_focus->pid == xwm->focus_surface->pid && validate_focus_serial(xwm->last_focus_seq, ev->sequence)) { xwm_set_focus_window(xwm, requested_focus); } else { xwm_set_focus_window(xwm, xwm->focus_surface); } } static void xwm_handle_xcb_error(struct wlr_xwm *xwm, xcb_value_error_t *ev) { #if HAVE_XCB_ERRORS const char *major_name = xcb_errors_get_name_for_major_code(xwm->errors_context, ev->major_opcode); if (!major_name) { wlr_log(WLR_DEBUG, "xcb error happened, but could not get major name"); goto log_raw; } const char *minor_name = xcb_errors_get_name_for_minor_code(xwm->errors_context, ev->major_opcode, ev->minor_opcode); const char *extension; const char *error_name = xcb_errors_get_name_for_error(xwm->errors_context, ev->error_code, &extension); if (!error_name) { wlr_log(WLR_DEBUG, "xcb error happened, but could not get error name"); goto log_raw; } wlr_log(WLR_ERROR, "xcb error: op %s (%s), code %s (%s), sequence %"PRIu16", value %"PRIu32, major_name, minor_name ? minor_name : "no minor", error_name, extension ? extension : "no extension", ev->sequence, ev->bad_value); return; log_raw: #endif wlr_log(WLR_ERROR, "xcb error: op %"PRIu8":%"PRIu16", code %"PRIu8", sequence %"PRIu16", value %"PRIu32, ev->major_opcode, ev->minor_opcode, ev->error_code, ev->sequence, ev->bad_value); } static void xwm_handle_unhandled_event(struct wlr_xwm *xwm, xcb_generic_event_t *ev) { #if HAVE_XCB_ERRORS const char *extension; const char *event_name = xcb_errors_get_name_for_xcb_event(xwm->errors_context, ev, &extension); if (!event_name) { wlr_log(WLR_DEBUG, "no name for unhandled event: %u", ev->response_type); return; } wlr_log(WLR_DEBUG, "unhandled X11 event: %s (%u)", event_name, ev->response_type); #else wlr_log(WLR_DEBUG, "unhandled X11 event: %u", ev->response_type); #endif } static int x11_event_handler(int fd, uint32_t mask, void *data) { int count = 0; xcb_generic_event_t *event; struct wlr_xwm *xwm = data; if ((mask & WL_EVENT_HANGUP) || (mask & WL_EVENT_ERROR)) { xwm_destroy(xwm); return 0; } while ((event = xcb_poll_for_event(xwm->xcb_conn))) { count++; if (xwm->xwayland->user_event_handler && xwm->xwayland->user_event_handler(xwm, event)) { free(event); continue; } if (xwm_handle_selection_event(xwm, event)) { free(event); continue; } switch (event->response_type & XCB_EVENT_RESPONSE_TYPE_MASK) { case XCB_CREATE_NOTIFY: xwm_handle_create_notify(xwm, (xcb_create_notify_event_t *)event); break; case XCB_DESTROY_NOTIFY: xwm_handle_destroy_notify(xwm, (xcb_destroy_notify_event_t *)event); break; case XCB_CONFIGURE_REQUEST: xwm_handle_configure_request(xwm, (xcb_configure_request_event_t *)event); break; case XCB_CONFIGURE_NOTIFY: xwm_handle_configure_notify(xwm, (xcb_configure_notify_event_t *)event); break; case XCB_MAP_REQUEST: xwm_handle_map_request(xwm, (xcb_map_request_event_t *)event); break; case XCB_MAP_NOTIFY: xwm_handle_map_notify(xwm, (xcb_map_notify_event_t *)event); break; case XCB_UNMAP_NOTIFY: xwm_handle_unmap_notify(xwm, (xcb_unmap_notify_event_t *)event); break; case XCB_PROPERTY_NOTIFY: xwm_handle_property_notify(xwm, (xcb_property_notify_event_t *)event); break; case XCB_CLIENT_MESSAGE: xwm_handle_client_message(xwm, (xcb_client_message_event_t *)event); break; case XCB_FOCUS_IN: xwm_handle_focus_in(xwm, (xcb_focus_in_event_t *)event); break; case 0: xwm_handle_xcb_error(xwm, (xcb_value_error_t *)event); break; default: xwm_handle_unhandled_event(xwm, event); break; } free(event); } if (count) { xcb_flush(xwm->xcb_conn); } return count; } static void handle_compositor_new_surface(struct wl_listener *listener, void *data) { struct wlr_xwm *xwm = wl_container_of(listener, xwm, compositor_new_surface); struct wlr_surface *surface = data; struct wl_client *client = wl_resource_get_client(surface->resource); if (client != xwm->xwayland->server->client) { return; } wlr_log(WLR_DEBUG, "New xwayland surface: %p", surface); uint32_t surface_id = wl_resource_get_id(surface->resource); struct wlr_xwayland_surface *xsurface; wl_list_for_each(xsurface, &xwm->unpaired_surfaces, unpaired_link) { if (xsurface->surface_id == surface_id) { xwayland_surface_associate(xwm, xsurface, surface); xcb_flush(xwm->xcb_conn); return; } } } static void handle_compositor_destroy(struct wl_listener *listener, void *data) { struct wlr_xwm *xwm = wl_container_of(listener, xwm, compositor_destroy); wl_list_remove(&xwm->compositor_new_surface.link); wl_list_remove(&xwm->compositor_destroy.link); wl_list_init(&xwm->compositor_new_surface.link); wl_list_init(&xwm->compositor_destroy.link); } static void handle_shell_v1_new_surface(struct wl_listener *listener, void *data) { struct wlr_xwm *xwm = wl_container_of(listener, xwm, shell_v1_new_surface); struct wlr_xwayland_surface_v1 *shell_surface = data; struct wlr_xwayland_surface *xsurface; wl_list_for_each(xsurface, &xwm->unpaired_surfaces, unpaired_link) { if (xsurface->serial == shell_surface->serial) { xwayland_surface_associate(xwm, xsurface, shell_surface->surface); return; } } } void wlr_xwayland_surface_activate(struct wlr_xwayland_surface *xsurface, bool activated) { struct wlr_xwayland_surface *focused = xsurface->xwm->focus_surface; if (activated) { xwm_surface_activate(xsurface->xwm, xsurface); } else if (focused == xsurface) { xwm_surface_activate(xsurface->xwm, NULL); } } void wlr_xwayland_surface_configure(struct wlr_xwayland_surface *xsurface, int16_t x, int16_t y, uint16_t width, uint16_t height) { int old_w = xsurface->width; int old_h = xsurface->height; xsurface->x = x; xsurface->y = y; xsurface->width = width; xsurface->height = height; struct wlr_xwm *xwm = xsurface->xwm; uint32_t mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT | XCB_CONFIG_WINDOW_BORDER_WIDTH; uint32_t values[] = {x, y, width, height, 0}; xcb_configure_window(xwm->xcb_conn, xsurface->window_id, mask, values); // If the window size did not change, then we cannot rely on // the X server to generate a ConfigureNotify event. Instead, // we are supposed to send a synthetic event. See ICCCM part // 4.1.5. But we ignore override-redirect windows as ICCCM does // not apply to them. if (width == old_w && height == old_h && !xsurface->override_redirect) { xcb_configure_notify_event_t configure_notify = { .response_type = XCB_CONFIGURE_NOTIFY, .event = xsurface->window_id, .window = xsurface->window_id, .x = x, .y = y, .width = width, .height = height, }; xcb_send_event(xwm->xcb_conn, 0, xsurface->window_id, XCB_EVENT_MASK_STRUCTURE_NOTIFY, (const char *)&configure_notify); } xcb_flush(xwm->xcb_conn); } void wlr_xwayland_surface_close(struct wlr_xwayland_surface *xsurface) { struct wlr_xwm *xwm = xsurface->xwm; bool supports_delete = false; for (size_t i = 0; i < xsurface->protocols_len; i++) { if (xsurface->protocols[i] == xwm->atoms[WM_DELETE_WINDOW]) { supports_delete = true; break; } } if (supports_delete) { xcb_client_message_data_t message_data = {0}; message_data.data32[0] = xwm->atoms[WM_DELETE_WINDOW]; message_data.data32[1] = XCB_CURRENT_TIME; xwm_send_wm_message(xsurface, &message_data, XCB_EVENT_MASK_NO_EVENT); } else { xcb_kill_client(xwm->xcb_conn, xsurface->window_id); xcb_flush(xwm->xcb_conn); } } void xwm_destroy(struct wlr_xwm *xwm) { if (!xwm) { return; } xwm_selection_finish(&xwm->clipboard_selection); xwm_selection_finish(&xwm->primary_selection); xwm_selection_finish(&xwm->dnd_selection); if (xwm->seat) { if (xwm->seat->selection_source && data_source_is_xwayland(xwm->seat->selection_source)) { wlr_seat_set_selection(xwm->seat, NULL, wl_display_next_serial(xwm->xwayland->wl_display)); } if (xwm->seat->primary_selection_source && primary_selection_source_is_xwayland( xwm->seat->primary_selection_source)) { wlr_seat_set_primary_selection(xwm->seat, NULL, wl_display_next_serial(xwm->xwayland->wl_display)); } wlr_xwayland_set_seat(xwm->xwayland, NULL); } if (xwm->cursor) { xcb_free_cursor(xwm->xcb_conn, xwm->cursor); } if (xwm->colormap) { xcb_free_colormap(xwm->xcb_conn, xwm->colormap); } if (xwm->window) { xcb_destroy_window(xwm->xcb_conn, xwm->window); } if (xwm->event_source) { wl_event_source_remove(xwm->event_source); } #if HAVE_XCB_ERRORS if (xwm->errors_context) { xcb_errors_context_free(xwm->errors_context); } #endif struct wlr_xwayland_surface *xsurface, *tmp; wl_list_for_each_safe(xsurface, tmp, &xwm->surfaces, link) { xwayland_surface_destroy(xsurface); } wl_list_for_each_safe(xsurface, tmp, &xwm->unpaired_surfaces, unpaired_link) { xwayland_surface_destroy(xsurface); } wl_list_remove(&xwm->compositor_new_surface.link); wl_list_remove(&xwm->compositor_destroy.link); wl_list_remove(&xwm->shell_v1_new_surface.link); xcb_disconnect(xwm->xcb_conn); struct pending_startup_id *pending, *next; wl_list_for_each_safe(pending, next, &xwm->pending_startup_ids, link) { pending_startup_id_destroy(pending); } xwm->xwayland->xwm = NULL; free(xwm); } static void xwm_get_resources(struct wlr_xwm *xwm) { xcb_prefetch_extension_data(xwm->xcb_conn, &xcb_xfixes_id); xcb_prefetch_extension_data(xwm->xcb_conn, &xcb_composite_id); xcb_prefetch_extension_data(xwm->xcb_conn, &xcb_res_id); size_t i; xcb_intern_atom_cookie_t cookies[ATOM_LAST]; for (i = 0; i < ATOM_LAST; i++) { cookies[i] = xcb_intern_atom(xwm->xcb_conn, 0, strlen(atom_map[i]), atom_map[i]); } for (i = 0; i < ATOM_LAST; i++) { xcb_generic_error_t *error; xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(xwm->xcb_conn, cookies[i], &error); if (reply && !error) { xwm->atoms[i] = reply->atom; } free(reply); if (error) { wlr_log(WLR_ERROR, "could not resolve atom %s, x11 error code %d", atom_map[i], error->error_code); free(error); return; } } xwm->xfixes = xcb_get_extension_data(xwm->xcb_conn, &xcb_xfixes_id); if (!xwm->xfixes || !xwm->xfixes->present) { wlr_log(WLR_DEBUG, "xfixes not available"); } xcb_xfixes_query_version_cookie_t xfixes_cookie; xcb_xfixes_query_version_reply_t *xfixes_reply; xfixes_cookie = xcb_xfixes_query_version(xwm->xcb_conn, XCB_XFIXES_MAJOR_VERSION, XCB_XFIXES_MINOR_VERSION); xfixes_reply = xcb_xfixes_query_version_reply(xwm->xcb_conn, xfixes_cookie, NULL); wlr_log(WLR_DEBUG, "xfixes version: %" PRIu32 ".%" PRIu32, xfixes_reply->major_version, xfixes_reply->minor_version); xwm->xfixes_major_version = xfixes_reply->major_version; free(xfixes_reply); const xcb_query_extension_reply_t *xres = xcb_get_extension_data(xwm->xcb_conn, &xcb_res_id); if (!xres || !xres->present) { return; } xcb_res_query_version_cookie_t xres_cookie = xcb_res_query_version(xwm->xcb_conn, XCB_RES_MAJOR_VERSION, XCB_RES_MINOR_VERSION); xcb_res_query_version_reply_t *xres_reply = xcb_res_query_version_reply(xwm->xcb_conn, xres_cookie, NULL); if (xres_reply == NULL) { return; } wlr_log(WLR_DEBUG, "xres version: %" PRIu32 ".%" PRIu32, xres_reply->server_major, xres_reply->server_minor); if (xres_reply->server_major > 1 || (xres_reply->server_major == 1 && xres_reply->server_minor >= 2)) { xwm->xres = xres; } free(xres_reply); } static void xwm_create_wm_window(struct wlr_xwm *xwm) { static const char name[] = "wlroots wm"; xwm->window = xcb_generate_id(xwm->xcb_conn); xcb_create_window(xwm->xcb_conn, XCB_COPY_FROM_PARENT, xwm->window, xwm->screen->root, 0, 0, 10, 10, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, xwm->screen->root_visual, 0, NULL); xcb_change_property(xwm->xcb_conn, XCB_PROP_MODE_REPLACE, xwm->window, xwm->atoms[NET_WM_NAME], xwm->atoms[UTF8_STRING], 8, // format strlen(name), name); xcb_change_property(xwm->xcb_conn, XCB_PROP_MODE_REPLACE, xwm->screen->root, xwm->atoms[NET_SUPPORTING_WM_CHECK], XCB_ATOM_WINDOW, 32, // format 1, &xwm->window); xcb_change_property(xwm->xcb_conn, XCB_PROP_MODE_REPLACE, xwm->window, xwm->atoms[NET_SUPPORTING_WM_CHECK], XCB_ATOM_WINDOW, 32, // format 1, &xwm->window); xcb_set_selection_owner(xwm->xcb_conn, xwm->window, xwm->atoms[WM_S0], XCB_CURRENT_TIME); xcb_set_selection_owner(xwm->xcb_conn, xwm->window, xwm->atoms[NET_WM_CM_S0], XCB_CURRENT_TIME); } // TODO use me to support 32 bit color somehow static void xwm_get_visual_and_colormap(struct wlr_xwm *xwm) { xcb_depth_iterator_t d_iter; xcb_visualtype_iterator_t vt_iter; xcb_visualtype_t *visualtype; d_iter = xcb_screen_allowed_depths_iterator(xwm->screen); visualtype = NULL; while (d_iter.rem > 0) { if (d_iter.data->depth == 32) { vt_iter = xcb_depth_visuals_iterator(d_iter.data); visualtype = vt_iter.data; break; } xcb_depth_next(&d_iter); } if (visualtype == NULL) { wlr_log(WLR_DEBUG, "No 32 bit visualtype\n"); return; } xwm->visual_id = visualtype->visual_id; xwm->colormap = xcb_generate_id(xwm->xcb_conn); xcb_create_colormap(xwm->xcb_conn, XCB_COLORMAP_ALLOC_NONE, xwm->colormap, xwm->screen->root, xwm->visual_id); } static void xwm_get_render_format(struct wlr_xwm *xwm) { xcb_render_query_pict_formats_cookie_t cookie = xcb_render_query_pict_formats(xwm->xcb_conn); xcb_render_query_pict_formats_reply_t *reply = xcb_render_query_pict_formats_reply(xwm->xcb_conn, cookie, NULL); if (!reply) { wlr_log(WLR_ERROR, "Did not get any reply from xcb_render_query_pict_formats"); return; } xcb_render_pictforminfo_iterator_t iter = xcb_render_query_pict_formats_formats_iterator(reply); xcb_render_pictforminfo_t *format = NULL; while (iter.rem > 0) { if (iter.data->depth == 32) { format = iter.data; break; } xcb_render_pictforminfo_next(&iter); } if (format == NULL) { wlr_log(WLR_DEBUG, "No 32 bit render format"); free(reply); return; } xwm->render_format_id = format->id; free(reply); } void xwm_set_cursor(struct wlr_xwm *xwm, const uint8_t *pixels, uint32_t stride, uint32_t width, uint32_t height, int32_t hotspot_x, int32_t hotspot_y) { if (!xwm->render_format_id) { wlr_log(WLR_ERROR, "Cannot set xwm cursor: no render format available"); return; } if (xwm->cursor) { xcb_free_cursor(xwm->xcb_conn, xwm->cursor); } int depth = 32; xcb_pixmap_t pix = xcb_generate_id(xwm->xcb_conn); xcb_create_pixmap(xwm->xcb_conn, depth, pix, xwm->screen->root, width, height); xcb_render_picture_t pic = xcb_generate_id(xwm->xcb_conn); xcb_render_create_picture(xwm->xcb_conn, pic, pix, xwm->render_format_id, 0, 0); xcb_gcontext_t gc = xcb_generate_id(xwm->xcb_conn); xcb_create_gc(xwm->xcb_conn, gc, pix, 0, NULL); xcb_put_image(xwm->xcb_conn, XCB_IMAGE_FORMAT_Z_PIXMAP, pix, gc, width, height, 0, 0, 0, depth, stride * height * sizeof(uint8_t), pixels); xcb_free_gc(xwm->xcb_conn, gc); xwm->cursor = xcb_generate_id(xwm->xcb_conn); xcb_render_create_cursor(xwm->xcb_conn, xwm->cursor, pic, hotspot_x, hotspot_y); xcb_free_pixmap(xwm->xcb_conn, pix); xcb_render_free_picture(xwm->xcb_conn, pic); uint32_t values[] = {xwm->cursor}; xcb_change_window_attributes(xwm->xcb_conn, xwm->screen->root, XCB_CW_CURSOR, values); xcb_flush(xwm->xcb_conn); } struct wlr_xwm *xwm_create(struct wlr_xwayland *xwayland, int wm_fd) { struct wlr_xwm *xwm = calloc(1, sizeof(*xwm)); if (xwm == NULL) { return NULL; } xwm->xwayland = xwayland; wl_list_init(&xwm->surfaces); wl_list_init(&xwm->surfaces_in_stack_order); wl_list_init(&xwm->unpaired_surfaces); wl_list_init(&xwm->pending_startup_ids); xwm->ping_timeout = 10000; xwm->xcb_conn = xcb_connect_to_fd(wm_fd, NULL); int rc = xcb_connection_has_error(xwm->xcb_conn); if (rc) { wlr_log(WLR_ERROR, "xcb connect failed: %d", rc); free(xwm); return NULL; } #if HAVE_XCB_ERRORS if (xcb_errors_context_new(xwm->xcb_conn, &xwm->errors_context)) { wlr_log(WLR_ERROR, "Could not allocate error context"); xwm_destroy(xwm); return NULL; } #endif xcb_screen_iterator_t screen_iterator = xcb_setup_roots_iterator(xcb_get_setup(xwm->xcb_conn)); xwm->screen = screen_iterator.data; struct wl_event_loop *event_loop = wl_display_get_event_loop(xwayland->wl_display); xwm->event_source = wl_event_loop_add_fd(event_loop, wm_fd, WL_EVENT_READABLE, x11_event_handler, xwm); wl_event_source_check(xwm->event_source); xwm_get_resources(xwm); xwm_get_visual_and_colormap(xwm); xwm_get_render_format(xwm); uint32_t values[] = { XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_PROPERTY_CHANGE, }; xcb_change_window_attributes(xwm->xcb_conn, xwm->screen->root, XCB_CW_EVENT_MASK, values); xcb_composite_redirect_subwindows(xwm->xcb_conn, xwm->screen->root, XCB_COMPOSITE_REDIRECT_MANUAL); xcb_atom_t supported[] = { xwm->atoms[NET_WM_STATE], xwm->atoms[NET_ACTIVE_WINDOW], xwm->atoms[NET_WM_MOVERESIZE], xwm->atoms[NET_WM_STATE_FOCUSED], xwm->atoms[NET_WM_STATE_MODAL], xwm->atoms[NET_WM_STATE_FULLSCREEN], xwm->atoms[NET_WM_STATE_MAXIMIZED_VERT], xwm->atoms[NET_WM_STATE_MAXIMIZED_HORZ], xwm->atoms[NET_WM_STATE_HIDDEN], xwm->atoms[NET_CLIENT_LIST], xwm->atoms[NET_CLIENT_LIST_STACKING], }; xcb_change_property(xwm->xcb_conn, XCB_PROP_MODE_REPLACE, xwm->screen->root, xwm->atoms[NET_SUPPORTED], XCB_ATOM_ATOM, 32, sizeof(supported)/sizeof(*supported), supported); #if HAVE_XCB_XFIXES_SET_CLIENT_DISCONNECT_MODE if (xwm->xwayland->server->options.terminate_delay > 0 && xwm->xfixes_major_version >= 6) { xcb_xfixes_set_client_disconnect_mode(xwm->xcb_conn, XCB_XFIXES_CLIENT_DISCONNECT_FLAGS_TERMINATE); } #endif xcb_flush(xwm->xcb_conn); xwm_set_net_active_window(xwm, XCB_WINDOW_NONE); xwm_selection_init(&xwm->clipboard_selection, xwm, xwm->atoms[CLIPBOARD]); xwm_selection_init(&xwm->primary_selection, xwm, xwm->atoms[PRIMARY]); xwm_selection_init(&xwm->dnd_selection, xwm, xwm->atoms[DND_SELECTION]); xwm->compositor_new_surface.notify = handle_compositor_new_surface; wl_signal_add(&xwayland->compositor->events.new_surface, &xwm->compositor_new_surface); xwm->compositor_destroy.notify = handle_compositor_destroy; wl_signal_add(&xwayland->compositor->events.destroy, &xwm->compositor_destroy); xwm->shell_v1_new_surface.notify = handle_shell_v1_new_surface; wl_signal_add(&xwayland->shell_v1->events.new_surface, &xwm->shell_v1_new_surface); xwm_create_wm_window(xwm); xcb_flush(xwm->xcb_conn); return xwm; } void wlr_xwayland_surface_set_withdrawn(struct wlr_xwayland_surface *surface, bool withdrawn) { surface->withdrawn = withdrawn; xsurface_set_wm_state(surface); xsurface_set_net_wm_state(surface); xcb_flush(surface->xwm->xcb_conn); } void wlr_xwayland_surface_set_minimized(struct wlr_xwayland_surface *surface, bool minimized) { surface->minimized = minimized; xsurface_set_wm_state(surface); xsurface_set_net_wm_state(surface); xcb_flush(surface->xwm->xcb_conn); } void wlr_xwayland_surface_set_maximized(struct wlr_xwayland_surface *surface, bool maximized) { surface->maximized_horz = maximized; surface->maximized_vert = maximized; xsurface_set_net_wm_state(surface); xcb_flush(surface->xwm->xcb_conn); } void wlr_xwayland_surface_set_fullscreen(struct wlr_xwayland_surface *surface, bool fullscreen) { surface->fullscreen = fullscreen; xsurface_set_net_wm_state(surface); xcb_flush(surface->xwm->xcb_conn); } bool xwm_atoms_contains(struct wlr_xwm *xwm, xcb_atom_t *atoms, size_t num_atoms, enum atom_name needle) { xcb_atom_t atom = xwm->atoms[needle]; for (size_t i = 0; i < num_atoms; ++i) { if (atom == atoms[i]) { return true; } } return false; } void wlr_xwayland_surface_ping(struct wlr_xwayland_surface *surface) { xcb_client_message_data_t data = { 0 }; data.data32[0] = surface->xwm->atoms[NET_WM_PING]; data.data32[1] = XCB_CURRENT_TIME; data.data32[2] = surface->window_id; xwm_send_wm_message(surface, &data, XCB_EVENT_MASK_NO_EVENT); wl_event_source_timer_update(surface->ping_timer, surface->xwm->ping_timeout); surface->pinging = true; } bool wlr_xwayland_or_surface_wants_focus( const struct wlr_xwayland_surface *xsurface) { static const enum atom_name needles[] = { NET_WM_WINDOW_TYPE_COMBO, NET_WM_WINDOW_TYPE_DND, NET_WM_WINDOW_TYPE_DROPDOWN_MENU, NET_WM_WINDOW_TYPE_MENU, NET_WM_WINDOW_TYPE_NOTIFICATION, NET_WM_WINDOW_TYPE_POPUP_MENU, NET_WM_WINDOW_TYPE_SPLASH, NET_WM_WINDOW_TYPE_TOOLTIP, NET_WM_WINDOW_TYPE_UTILITY, }; for (size_t i = 0; i < sizeof(needles) / sizeof(needles[0]); ++i) { if (xwm_atoms_contains(xsurface->xwm, xsurface->window_type, xsurface->window_type_len, needles[i])) { return false; } } return true; } enum wlr_xwayland_icccm_input_model wlr_xwayland_icccm_input_model( const struct wlr_xwayland_surface *xsurface) { bool take_focus = xwm_atoms_contains(xsurface->xwm, xsurface->protocols, xsurface->protocols_len, WM_TAKE_FOCUS); if (!xsurface->hints || xsurface->hints->input) { if (take_focus) { return WLR_ICCCM_INPUT_MODEL_LOCAL; } return WLR_ICCCM_INPUT_MODEL_PASSIVE; } else { if (take_focus) { return WLR_ICCCM_INPUT_MODEL_GLOBAL; } } return WLR_ICCCM_INPUT_MODEL_NONE; } void wlr_xwayland_set_workareas(struct wlr_xwayland *wlr_xwayland, const struct wlr_box *workareas, size_t num_workareas) { uint32_t *data = malloc(4 * sizeof(uint32_t) * num_workareas); if (!data) { return; } for (size_t i = 0; i < num_workareas; i++) { data[4 * i] = workareas[i].x; data[4 * i + 1] = workareas[i].y; data[4 * i + 2] = workareas[i].width; data[4 * i + 3] = workareas[i].height; } struct wlr_xwm *xwm = wlr_xwayland->xwm; xcb_change_property(xwm->xcb_conn, XCB_PROP_MODE_REPLACE, xwm->screen->root, xwm->atoms[NET_WORKAREA], XCB_ATOM_CARDINAL, 32, 4 * num_workareas, data); free(data); }