pax_global_header00006660000000000000000000000064136121113140014503gustar00rootroot0000000000000052 comment=57ffb35de009611d8edecb297bc2fd10a9a86702 wlroots-0.10.0/000077500000000000000000000000001361211131400132725ustar00rootroot00000000000000wlroots-0.10.0/.builds/000077500000000000000000000000001361211131400146325ustar00rootroot00000000000000wlroots-0.10.0/.builds/alpine.yml000066400000000000000000000010751361211131400166300ustar00rootroot00000000000000image: alpine/edge packages: - eudev-dev - ffmpeg-dev - libcap-dev - libinput-dev - libxkbcommon-dev - mesa-dev - meson - pixman-dev - wayland-dev - wayland-protocols - xcb-util-image-dev - xcb-util-wm-dev sources: - https://github.com/swaywm/wlroots tasks: - setup: | cd wlroots meson build -Dauto_features=enabled -Dlogind=disabled -Dxcb-errors=disabled - build: | cd wlroots ninja -C build - build-features-disabled: | cd wlroots meson build --reconfigure -Dauto_features=disabled ninja -C build wlroots-0.10.0/.builds/archlinux.yml000066400000000000000000000007721361211131400173600ustar00rootroot00000000000000image: archlinux packages: - clang - ffmpeg - libcap - libinput - libxkbcommon - mesa - meson - pixman - wayland - wayland-protocols - xcb-util-errors - xcb-util-image - xcb-util-wm sources: - https://github.com/swaywm/wlroots tasks: - setup: | cd wlroots CC=gcc meson build-gcc -Dauto_features=enabled CC=clang meson build-clang -Dauto_features=enabled - gcc: | cd wlroots/build-gcc ninja - clang: | cd wlroots/build-clang ninja wlroots-0.10.0/.builds/freebsd.yml000066400000000000000000000010471361211131400167710ustar00rootroot00000000000000image: freebsd/latest packages: - devel/evdev-proto - devel/libepoll-shim - devel/libudev-devd - devel/meson # implies ninja - devel/pkgconf - graphics/libdrm - graphics/mesa-libs - graphics/png - graphics/wayland - graphics/wayland-protocols - multimedia/ffmpeg - x11/libX11 - x11/libinput - x11/libxcb - x11/libxkbcommon - x11/pixman - x11/xcb-util-errors - x11/xcb-util-wm sources: - https://github.com/swaywm/wlroots tasks: - wlroots: | cd wlroots meson build -Dauto_features=enabled -Dlogind=disabled -Dlibcap=disabled ninja -C build wlroots-0.10.0/.editorconfig000066400000000000000000000002771361211131400157550ustar00rootroot00000000000000root = true [*] end_of_line = lf insert_final_newline = true charset = utf-8 trim_trailing_whitespace = true indent_style = tab indent_size = 4 [*.xml] indent_style = space indent_size = 2 wlroots-0.10.0/.gitignore000066400000000000000000000001301361211131400152540ustar00rootroot00000000000000.clang_complete *.o *.a bin/ test/ build/ build-*/ wayland-*-protocol.* wlr-example.ini wlroots-0.10.0/CONTRIBUTING.md000066400000000000000000000270371361211131400155340ustar00rootroot00000000000000# Contributing to wlroots Contributing just involves sending a pull request. You will probably be more successful with your contribution if you visit [#sway-devel](https://webchat.freenode.net/?channels=sway-devel) on irc.freenode.net 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. ## Pull Requests If you already have your own pull 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 https://github.com/username/wlroots && cd wlroots` 3. `git remote add upstream https://github.com/swaywm/wlroots` 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 pull request from your feature branch When you submit your pull 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 pull 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 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 *"Fix #742"* 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 Github issues](https://help.github.com/articles/closing-issues-via-commit-messages/), 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 pull 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 pull request description on Github is probably fair game for going into the extended commit message as well. See [here](https://chris.beams.io/posts/git-commit/) 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 pull 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 GitHub 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 pull request when all reviewers approve. 5. **File** follow-up tickets if appropriate. ## Style Reference wlroots is written in C with a style similar to the [kernel style](https://www.kernel.org/doc/Documentation/process/coding-style.rst), 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 80 columns, but you can go up to 100 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 For functions that are responsible for constructing and destructing an object, they should be written as a pair of one of two forms: * `init`/`finish`: These initialize/deinitialize a type, but are **NOT** responsible for allocating it. They should accept a pointer to some pre-allocated memory (e.g. a member of a struct). * `create`/`destroy`: These also initialize/deinitialize, but will return a pointer to a `malloc`ed chunk of memory, and will `free` it in `destroy`. A destruction function should always be able to accept a NULL pointer or a zeroed value and exit cleanly; this simplifies error handling a lot. ### Error Codes For functions not returning a value, they should return a (stdbool.h) bool to indicated if 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. ### 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); } ``` ### 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`). Destructor requests should only call `wl_resource_destroy`. The compositor should not destroy resources on its own. ### 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-0.10.0/LICENSE000066400000000000000000000021101361211131400142710ustar00rootroot00000000000000Copyright (c) 2017, 2018 Drew DeVault Copyright (c) 2014 Jari Vetoniemi 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.10.0/README.md000066400000000000000000000054071361211131400145570ustar00rootroot00000000000000# wlroots Pluggable, composable, unopinionated modules for building a [Wayland](http://wayland.freedesktop.org/) compositor; or about 50,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](https://github.com/swaywm/wlroots/wiki/Getting-started) to get started with wlroots. wlroots is developed under the direction of the [sway](https://github.com/swaywm/sway) project. A variety of wrapper libraries [are available](https://github.com/swaywm) for using it with your favorite programming language. ## Building Install dependencies: * meson * wayland * wayland-protocols * EGL * GLESv2 * libdrm * GBM * libinput * xkbcommon * udev * pixman * systemd (optional, for logind support) * elogind (optional, for logind support on systems without systemd) * libcap (optional, for capability support) If you choose to enable X11 support: * xcb * xcb-composite * xcb-xfixes * xcb-xinput * xcb-image * xcb-render * x11-xcb * xcb-errors (optional, for improved error reporting) * x11-icccm (optional, for improved Xwayland introspection) Run these commands: meson build ninja -C build Install like so: sudo ninja -C build install ## Contributing See [CONTRIBUTING.md](https://github.com/swaywm/wlroots/blob/master/CONTRIBUTING.md). wlroots-0.10.0/backend/000077500000000000000000000000001361211131400146615ustar00rootroot00000000000000wlroots-0.10.0/backend/backend.c000066400000000000000000000204111361211131400164120ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "backend/multi.h" #if WLR_HAS_X11_BACKEND #include #endif void wlr_backend_init(struct wlr_backend *backend, const struct wlr_backend_impl *impl) { assert(backend); backend->impl = impl; wl_signal_init(&backend->events.destroy); wl_signal_init(&backend->events.new_input); wl_signal_init(&backend->events.new_output); } 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); } } struct wlr_renderer *wlr_backend_get_renderer(struct wlr_backend *backend) { if (backend->impl->get_renderer) { return backend->impl->get_renderer(backend); } return NULL; } struct wlr_session *wlr_backend_get_session(struct wlr_backend *backend) { if (backend->impl->get_session) { return backend->impl->get_session(backend); } return NULL; } clockid_t wlr_backend_get_presentation_clock(struct wlr_backend *backend) { if (backend->impl->get_presentation_clock) { return backend->impl->get_presentation_clock(backend); } return CLOCK_MONOTONIC; } 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, wlr_renderer_create_func_t create_renderer_func) { struct wlr_backend *backend = wlr_wl_backend_create(display, NULL, create_renderer_func); 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; } #if WLR_HAS_X11_BACKEND static struct wlr_backend *attempt_x11_backend(struct wl_display *display, const char *x11_display, wlr_renderer_create_func_t create_renderer_func) { struct wlr_backend *backend = wlr_x11_backend_create(display, x11_display, create_renderer_func); 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; } #endif static struct wlr_backend *attempt_headless_backend( struct wl_display *display, wlr_renderer_create_func_t create_renderer_func) { struct wlr_backend *backend = wlr_headless_backend_create(display, create_renderer_func); 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 struct wlr_backend *attempt_noop_backend(struct wl_display *display) { struct wlr_backend *backend = wlr_noop_backend_create(display); if (backend == NULL) { return NULL; } size_t outputs = parse_outputs_env("WLR_NOOP_OUTPUTS"); for (size_t i = 0; i < outputs; ++i) { wlr_noop_add_output(backend); } return backend; } static struct wlr_backend *attempt_drm_backend(struct wl_display *display, struct wlr_backend *backend, struct wlr_session *session, wlr_renderer_create_func_t create_renderer_func) { int gpus[8]; size_t num_gpus = wlr_session_find_gpus(session, 8, gpus); struct wlr_backend *primary_drm = NULL; wlr_log(WLR_INFO, "Found %zu GPUs", num_gpus); for (size_t i = 0; i < num_gpus; ++i) { struct wlr_backend *drm = wlr_drm_backend_create(display, session, gpus[i], primary_drm, create_renderer_func); if (!drm) { wlr_log(WLR_ERROR, "Failed to open DRM device %d", gpus[i]); continue; } if (!primary_drm) { primary_drm = drm; } wlr_multi_backend_add(backend, drm); } return primary_drm; } static struct wlr_backend *attempt_backend_by_name(struct wl_display *display, struct wlr_backend *backend, struct wlr_session **session, const char *name, wlr_renderer_create_func_t create_renderer_func) { if (strcmp(name, "wayland") == 0) { return attempt_wl_backend(display, create_renderer_func); #if WLR_HAS_X11_BACKEND } else if (strcmp(name, "x11") == 0) { return attempt_x11_backend(display, NULL, create_renderer_func); #endif } else if (strcmp(name, "headless") == 0) { return attempt_headless_backend(display, create_renderer_func); } else if (strcmp(name, "noop") == 0) { return attempt_noop_backend(display); } else if (strcmp(name, "drm") == 0 || strcmp(name, "libinput") == 0) { // DRM and libinput need a session if (!*session) { *session = wlr_session_create(display); if (!*session) { wlr_log(WLR_ERROR, "failed to start a session"); return NULL; } } if (strcmp(name, "libinput") == 0) { return wlr_libinput_backend_create(display, *session); } else { return attempt_drm_backend(display, backend, *session, create_renderer_func); } } wlr_log(WLR_ERROR, "unrecognized backend '%s'", name); return NULL; } struct wlr_backend *wlr_backend_autocreate(struct wl_display *display, wlr_renderer_create_func_t create_renderer_func) { struct wlr_backend *backend = wlr_multi_backend_create(display); struct wlr_multi_backend *multi = (struct wlr_multi_backend *)backend; if (!backend) { wlr_log(WLR_ERROR, "could not allocate multibackend"); return NULL; } char *names = getenv("WLR_BACKENDS"); if (names) { names = strdup(names); if (names == NULL) { wlr_log(WLR_ERROR, "allocation failed"); wlr_backend_destroy(backend); return NULL; } char *saveptr; char *name = strtok_r(names, ",", &saveptr); while (name != NULL) { struct wlr_backend *subbackend = attempt_backend_by_name(display, backend, &multi->session, name, create_renderer_func); if (subbackend == NULL) { wlr_log(WLR_ERROR, "failed to start backend '%s'", name); wlr_session_destroy(multi->session); wlr_backend_destroy(backend); free(names); return NULL; } if (!wlr_multi_backend_add(backend, subbackend)) { wlr_log(WLR_ERROR, "failed to add backend '%s'", name); wlr_session_destroy(multi->session); wlr_backend_destroy(backend); free(names); return NULL; } name = strtok_r(NULL, ",", &saveptr); } free(names); return backend; } if (getenv("WAYLAND_DISPLAY") || getenv("_WAYLAND_DISPLAY") || getenv("WAYLAND_SOCKET")) { struct wlr_backend *wl_backend = attempt_wl_backend(display, create_renderer_func); if (!wl_backend) { goto error; } wlr_multi_backend_add(backend, wl_backend); return backend; } #if WLR_HAS_X11_BACKEND const char *x11_display = getenv("DISPLAY"); if (x11_display) { struct wlr_backend *x11_backend = attempt_x11_backend(display, x11_display, create_renderer_func); if (!x11_backend) { goto error; } wlr_multi_backend_add(backend, x11_backend); return backend; } #endif // Attempt DRM+libinput multi->session = wlr_session_create(display); if (!multi->session) { wlr_log(WLR_ERROR, "Failed to start a DRM session"); wlr_backend_destroy(backend); return NULL; } struct wlr_backend *libinput = wlr_libinput_backend_create(display, multi->session); if (!libinput) { wlr_log(WLR_ERROR, "Failed to start libinput backend"); wlr_session_destroy(multi->session); wlr_backend_destroy(backend); return NULL; } wlr_multi_backend_add(backend, libinput); struct wlr_backend *primary_drm = attempt_drm_backend(display, backend, multi->session, create_renderer_func); if (!primary_drm) { wlr_log(WLR_ERROR, "Failed to open any DRM device"); wlr_backend_destroy(libinput); wlr_session_destroy(multi->session); wlr_backend_destroy(backend); return NULL; } return backend; error: wlr_backend_destroy(backend); return NULL; } wlroots-0.10.0/backend/drm/000077500000000000000000000000001361211131400154435ustar00rootroot00000000000000wlroots-0.10.0/backend/drm/atomic.c000066400000000000000000000171111361211131400170640ustar00rootroot00000000000000#include #include #include #include #include #include "backend/drm/drm.h" #include "backend/drm/iface.h" #include "backend/drm/util.h" struct atomic { drmModeAtomicReq *req; int cursor; bool failed; }; static void atomic_begin(struct wlr_drm_crtc *crtc, struct atomic *atom) { if (!crtc->atomic) { crtc->atomic = drmModeAtomicAlloc(); if (!crtc->atomic) { wlr_log_errno(WLR_ERROR, "Allocation failed"); atom->failed = true; return; } } atom->req = crtc->atomic; atom->cursor = drmModeAtomicGetCursor(atom->req); atom->failed = false; } static bool atomic_end(int drm_fd, struct atomic *atom) { if (atom->failed) { return false; } uint32_t flags = DRM_MODE_ATOMIC_TEST_ONLY | DRM_MODE_ATOMIC_NONBLOCK; if (drmModeAtomicCommit(drm_fd, atom->req, flags, NULL)) { wlr_log_errno(WLR_ERROR, "Atomic test failed"); drmModeAtomicSetCursor(atom->req, atom->cursor); return false; } return true; } static bool atomic_commit(int drm_fd, struct atomic *atom, struct wlr_drm_connector *conn, uint32_t flags, bool modeset) { struct wlr_drm_backend *drm = get_drm_backend_from_backend(conn->output.backend); if (atom->failed) { return false; } int ret = drmModeAtomicCommit(drm_fd, atom->req, flags, drm); if (ret) { wlr_log_errno(WLR_ERROR, "%s: Atomic commit failed (%s)", conn->output.name, modeset ? "modeset" : "pageflip"); // Try to commit without new changes drmModeAtomicSetCursor(atom->req, atom->cursor); if (drmModeAtomicCommit(drm_fd, atom->req, flags, drm)) { wlr_log_errno(WLR_ERROR, "%s: Atomic commit without new changes failed (%s)", conn->output.name, modeset ? "modeset" : "pageflip"); } } drmModeAtomicSetCursor(atom->req, 0); return !ret; } static inline 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; } } static void set_plane_props(struct atomic *atom, struct wlr_drm_plane *plane, uint32_t crtc_id, uint32_t fb_id, bool set_crtc_xy) { uint32_t id = plane->id; const union wlr_drm_plane_props *props = &plane->props; // 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)plane->surf.width << 16); atomic_add(atom, id, props->src_h, (uint64_t)plane->surf.height << 16); atomic_add(atom, id, props->crtc_w, plane->surf.width); atomic_add(atom, id, props->crtc_h, plane->surf.height); atomic_add(atom, id, props->fb_id, fb_id); atomic_add(atom, id, props->crtc_id, crtc_id); if (set_crtc_xy) { atomic_add(atom, id, props->crtc_x, 0); atomic_add(atom, id, props->crtc_y, 0); } } static bool atomic_crtc_pageflip(struct wlr_drm_backend *drm, struct wlr_drm_connector *conn, struct wlr_drm_crtc *crtc, uint32_t fb_id, drmModeModeInfo *mode) { if (mode != NULL) { if (crtc->mode_id != 0) { drmModeDestroyPropertyBlob(drm->fd, crtc->mode_id); } if (drmModeCreatePropertyBlob(drm->fd, mode, sizeof(*mode), &crtc->mode_id)) { wlr_log_errno(WLR_ERROR, "Unable to create property blob"); return false; } } uint32_t flags = DRM_MODE_PAGE_FLIP_EVENT; if (mode != NULL) { flags |= DRM_MODE_ATOMIC_ALLOW_MODESET; } else { flags |= DRM_MODE_ATOMIC_NONBLOCK; } struct atomic atom; atomic_begin(crtc, &atom); atomic_add(&atom, conn->id, conn->props.crtc_id, crtc->id); if (mode != NULL && conn->props.link_status != 0) { atomic_add(&atom, conn->id, conn->props.link_status, DRM_MODE_LINK_STATUS_GOOD); } atomic_add(&atom, crtc->id, crtc->props.mode_id, crtc->mode_id); atomic_add(&atom, crtc->id, crtc->props.active, 1); set_plane_props(&atom, crtc->primary, crtc->id, fb_id, true); return atomic_commit(drm->fd, &atom, conn, flags, mode); } static bool atomic_conn_enable(struct wlr_drm_backend *drm, struct wlr_drm_connector *conn, bool enable) { struct wlr_drm_crtc *crtc = conn->crtc; if (crtc == NULL) { return !enable; } struct atomic atom; atomic_begin(crtc, &atom); atomic_add(&atom, crtc->id, crtc->props.active, enable); if (enable) { atomic_add(&atom, conn->id, conn->props.crtc_id, crtc->id); atomic_add(&atom, crtc->id, crtc->props.mode_id, crtc->mode_id); } else { atomic_add(&atom, conn->id, conn->props.crtc_id, 0); atomic_add(&atom, crtc->id, crtc->props.mode_id, 0); } return atomic_commit(drm->fd, &atom, conn, DRM_MODE_ATOMIC_ALLOW_MODESET, true); } static bool atomic_crtc_set_cursor(struct wlr_drm_backend *drm, struct wlr_drm_crtc *crtc, struct gbm_bo *bo) { if (!crtc || !crtc->cursor) { return true; } struct wlr_drm_plane *plane = crtc->cursor; // We can't use atomic operations on fake planes if (plane->id == 0) { return legacy_crtc_set_cursor(drm, crtc, bo); } struct atomic atom; atomic_begin(crtc, &atom); if (bo) { uint32_t fb_id = get_fb_for_bo(bo, plane->drm_format, drm->addfb2_modifiers); set_plane_props(&atom, plane, crtc->id, fb_id, false); } else { atomic_add(&atom, plane->id, plane->props.fb_id, 0); atomic_add(&atom, plane->id, plane->props.crtc_id, 0); } return atomic_end(drm->fd, &atom); } static bool atomic_crtc_move_cursor(struct wlr_drm_backend *drm, struct wlr_drm_crtc *crtc, int x, int y) { if (!crtc || !crtc->cursor) { return true; } struct wlr_drm_plane *plane = crtc->cursor; // We can't use atomic operations on fake planes if (plane->id == 0) { return legacy_crtc_move_cursor(drm, crtc, x, y); } struct atomic atom; atomic_begin(crtc, &atom); atomic_add(&atom, plane->id, plane->props.crtc_x, x); atomic_add(&atom, plane->id, plane->props.crtc_y, y); return atomic_end(drm->fd, &atom); } static bool atomic_crtc_set_gamma(struct wlr_drm_backend *drm, struct wlr_drm_crtc *crtc, size_t size, uint16_t *r, uint16_t *g, uint16_t *b) { // 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) { return legacy_iface.crtc_set_gamma(drm, crtc, size, r, g, b); } struct drm_color_lut *gamma = malloc(size * sizeof(struct drm_color_lut)); if (gamma == NULL) { wlr_log(WLR_ERROR, "Failed to allocate gamma table"); return false; } for (size_t i = 0; i < size; i++) { gamma[i].red = r[i]; gamma[i].green = g[i]; gamma[i].blue = b[i]; } if (crtc->gamma_lut != 0) { drmModeDestroyPropertyBlob(drm->fd, crtc->gamma_lut); } if (drmModeCreatePropertyBlob(drm->fd, gamma, size * sizeof(struct drm_color_lut), &crtc->gamma_lut)) { free(gamma); wlr_log_errno(WLR_ERROR, "Unable to create property blob"); return false; } free(gamma); struct atomic atom; atomic_begin(crtc, &atom); atomic_add(&atom, crtc->id, crtc->props.gamma_lut, crtc->gamma_lut); return atomic_end(drm->fd, &atom); } static size_t atomic_crtc_get_gamma_size(struct wlr_drm_backend *drm, struct wlr_drm_crtc *crtc) { if (crtc->props.gamma_lut_size == 0) { return legacy_iface.crtc_get_gamma_size(drm, crtc); } 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 (size_t)gamma_lut_size; } const struct wlr_drm_interface atomic_iface = { .conn_enable = atomic_conn_enable, .crtc_pageflip = atomic_crtc_pageflip, .crtc_set_cursor = atomic_crtc_set_cursor, .crtc_move_cursor = atomic_crtc_move_cursor, .crtc_set_gamma = atomic_crtc_set_gamma, .crtc_get_gamma_size = atomic_crtc_get_gamma_size, }; wlroots-0.10.0/backend/drm/backend.c000066400000000000000000000143051361211131400172010ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "backend/drm/drm.h" #include "util/signal.h" struct wlr_drm_backend *get_drm_backend_from_backend( struct wlr_backend *wlr_backend) { assert(wlr_backend_is_drm(wlr_backend)); return (struct wlr_drm_backend *)wlr_backend; } static bool backend_start(struct wlr_backend *backend) { struct wlr_drm_backend *drm = get_drm_backend_from_backend(backend); scan_drm_connectors(drm); return true; } static void backend_destroy(struct wlr_backend *backend) { if (!backend) { return; } struct wlr_drm_backend *drm = get_drm_backend_from_backend(backend); restore_drm_outputs(drm); struct wlr_drm_connector *conn, *next; wl_list_for_each_safe(conn, next, &drm->outputs, link) { wlr_output_destroy(&conn->output); } wlr_signal_emit_safe(&backend->events.destroy, backend); wl_list_remove(&drm->display_destroy.link); wl_list_remove(&drm->session_destroy.link); wl_list_remove(&drm->session_signal.link); wl_list_remove(&drm->drm_invalidated.link); finish_drm_resources(drm); finish_drm_renderer(&drm->renderer); wlr_session_close_file(drm->session, drm->fd); wl_event_source_remove(drm->drm_event); free(drm); } static struct wlr_renderer *backend_get_renderer( struct wlr_backend *backend) { struct wlr_drm_backend *drm = get_drm_backend_from_backend(backend); if (drm->parent) { return drm->parent->renderer.wlr_rend; } else { return drm->renderer.wlr_rend; } } static clockid_t backend_get_presentation_clock(struct wlr_backend *backend) { struct wlr_drm_backend *drm = get_drm_backend_from_backend(backend); return drm->clock; } static struct wlr_backend_impl backend_impl = { .start = backend_start, .destroy = backend_destroy, .get_renderer = backend_get_renderer, .get_presentation_clock = backend_get_presentation_clock, }; bool wlr_backend_is_drm(struct wlr_backend *b) { return b->impl == &backend_impl; } static void session_signal(struct wl_listener *listener, void *data) { struct wlr_drm_backend *drm = wl_container_of(listener, drm, session_signal); struct wlr_session *session = data; if (session->active) { wlr_log(WLR_INFO, "DRM fd resumed"); scan_drm_connectors(drm); struct wlr_drm_connector *conn; wl_list_for_each(conn, &drm->outputs, link){ if (conn->output.enabled && conn->output.current_mode != NULL) { drm_connector_set_mode(&conn->output, conn->output.current_mode); } else { enable_drm_connector(&conn->output, false); } if (!conn->crtc) { continue; } struct wlr_drm_plane *plane = conn->crtc->cursor; drm->iface->crtc_set_cursor(drm, conn->crtc, (plane && plane->cursor_enabled) ? plane->surf.back : NULL); drm->iface->crtc_move_cursor(drm, conn->crtc, conn->cursor_x, conn->cursor_y); if (conn->crtc->gamma_table != NULL) { size_t size = conn->crtc->gamma_table_size; uint16_t *r = conn->crtc->gamma_table; uint16_t *g = conn->crtc->gamma_table + size; uint16_t *b = conn->crtc->gamma_table + 2 * size; drm->iface->crtc_set_gamma(drm, conn->crtc, size, r, g, b); } else { set_drm_connector_gamma(&conn->output, 0, NULL, NULL, NULL); } } } else { wlr_log(WLR_INFO, "DRM fd paused"); } } static void drm_invalidated(struct wl_listener *listener, void *data) { struct wlr_drm_backend *drm = wl_container_of(listener, drm, drm_invalidated); char *name = drmGetDeviceNameFromFd2(drm->fd); wlr_log(WLR_DEBUG, "%s invalidated", name); free(name); scan_drm_connectors(drm); } 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); } struct wlr_backend *wlr_drm_backend_create(struct wl_display *display, struct wlr_session *session, int gpu_fd, struct wlr_backend *parent, wlr_renderer_create_func_t create_renderer_func) { assert(display && session && gpu_fd >= 0); assert(!parent || wlr_backend_is_drm(parent)); char *name = drmGetDeviceNameFromFd2(gpu_fd); drmVersion *version = drmGetVersion(gpu_fd); wlr_log(WLR_INFO, "Initializing DRM backend for %s (%s)", name, version->name); free(name); drmFreeVersion(version); struct wlr_drm_backend *drm = calloc(1, sizeof(struct wlr_drm_backend)); 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->outputs); drm->fd = gpu_fd; if (parent != NULL) { drm->parent = get_drm_backend_from_backend(parent); } drm->drm_invalidated.notify = drm_invalidated; wlr_session_signal_add(session, gpu_fd, &drm->drm_invalidated); 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, NULL); if (!drm->drm_event) { wlr_log(WLR_ERROR, "Failed to create DRM event source"); goto error_fd; } drm->session_signal.notify = session_signal; wl_signal_add(&session->session_signal, &drm->session_signal); if (!check_drm_features(drm)) { goto error_event; } if (!init_drm_resources(drm)) { goto error_event; } if (!init_drm_renderer(drm, &drm->renderer, create_renderer_func)) { wlr_log(WLR_ERROR, "Failed to initialize renderer"); goto error_event; } 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_event: wl_list_remove(&drm->session_signal.link); wl_event_source_remove(drm->drm_event); error_fd: wlr_session_close_file(drm->session, drm->fd); free(drm); return NULL; } wlroots-0.10.0/backend/drm/cvt.c000066400000000000000000000204061361211131400164050ustar00rootroot00000000000000/* * Copyright 2005-2006 Luc Verhaegen. * * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. */ #include "backend/drm/cvt.h" /* top/bottom margin size (% of height) - default: 1.8 */ #define CVT_MARGIN_PERCENTAGE 1.8 /* character cell horizontal granularity (pixels) - default 8 */ #define CVT_H_GRANULARITY 8 /* Minimum vertical porch (lines) - default 3 */ #define CVT_MIN_V_PORCH 3 /* Minimum number of vertical back porch lines - default 6 */ #define CVT_MIN_V_BPORCH 6 /* Pixel clock step (kHz) */ #define CVT_CLOCK_STEP 250 /* Minimum time of vertical sync + back porch interval (µs) * default 550.0 */ #define CVT_MIN_VSYNC_BP 550.0 /* Nominal hsync width (% of line period) - default 8 */ #define CVT_HSYNC_PERCENTAGE 8 /* Definition of Horizontal blanking time limitation */ /* Gradient (%/kHz) - default 600 */ #define CVT_M_FACTOR 600 /* Offset (%) - default 40 */ #define CVT_C_FACTOR 40 /* Blanking time scaling factor - default 128 */ #define CVT_K_FACTOR 128 /* Scaling factor weighting - default 20 */ #define CVT_J_FACTOR 20 #define CVT_M_PRIME CVT_M_FACTOR * CVT_K_FACTOR / 256 #define CVT_C_PRIME (CVT_C_FACTOR - CVT_J_FACTOR) * CVT_K_FACTOR / 256 + \ CVT_J_FACTOR /* Minimum vertical blanking interval time (µs) - default 460 */ #define CVT_RB_MIN_VBLANK 460.0 /* Fixed number of clocks for horizontal sync */ #define CVT_RB_H_SYNC 32.0 /* Fixed number of clocks for horizontal blanking */ #define CVT_RB_H_BLANK 160.0 /* Fixed number of lines for vertical front porch - default 3 */ #define CVT_RB_VFPORCH 3 /* * Generate a CVT standard mode from hdisplay, vdisplay and vrefresh. * * These calculations are stolen from the CVT calculation spreadsheet written * by Graham Loveridge. He seems to be claiming no copyright and there seems to * be no license attached to this. He apparently just wants to see his name * mentioned. * * This file can be found at http://www.vesa.org/Public/CVT/CVTd6r1.xls * * Comments and structure corresponds to the comments and structure of the xls. * This should ease importing of future changes to the standard (not very * likely though). * * This function is borrowed from xorg-xserver's xf86CVTmode. */ void generate_cvt_mode(drmModeModeInfo *mode, int hdisplay, int vdisplay, float vrefresh, bool reduced, bool interlaced) { bool margins = false; float vfield_rate, hperiod; int hdisplay_rnd, hmargin; int vdisplay_rnd, vmargin, vsync; float interlace; /* Please rename this */ /* CVT default is 60.0Hz */ if (!vrefresh) { vrefresh = 60.0; } /* 1. Required field rate */ if (interlaced) { vfield_rate = vrefresh * 2; } else { vfield_rate = vrefresh; } /* 2. Horizontal pixels */ hdisplay_rnd = hdisplay - (hdisplay % CVT_H_GRANULARITY); /* 3. Determine left and right borders */ if (margins) { /* right margin is actually exactly the same as left */ hmargin = (((float) hdisplay_rnd) * CVT_MARGIN_PERCENTAGE / 100.0); hmargin -= hmargin % CVT_H_GRANULARITY; } else { hmargin = 0; } /* 4. Find total active pixels */ mode->hdisplay = hdisplay_rnd + 2 * hmargin; /* 5. Find number of lines per field */ if (interlaced) { vdisplay_rnd = vdisplay / 2; } else { vdisplay_rnd = vdisplay; } /* 6. Find top and bottom margins */ /* nope. */ if (margins) { /* top and bottom margins are equal again. */ vmargin = (((float) vdisplay_rnd) * CVT_MARGIN_PERCENTAGE / 100.0); } else { vmargin = 0; } mode->vdisplay = vdisplay + 2 * vmargin; /* 7. interlace */ if (interlaced) { interlace = 0.5; } else { interlace = 0.0; } /* Determine vsync Width from aspect ratio */ if (!(vdisplay % 3) && ((vdisplay * 4 / 3) == hdisplay)) { vsync = 4; } else if (!(vdisplay % 9) && ((vdisplay * 16 / 9) == hdisplay)) { vsync = 5; } else if (!(vdisplay % 10) && ((vdisplay * 16 / 10) == hdisplay)) { vsync = 6; } else if (!(vdisplay % 4) && ((vdisplay * 5 / 4) == hdisplay)) { vsync = 7; } else if (!(vdisplay % 9) && ((vdisplay * 15 / 9) == hdisplay)) { vsync = 7; } else { /* Custom */ vsync = 10; } if (!reduced) { /* simplified GTF calculation */ float hblank_percentage; int vsync_and_back_porch, vblank_porch; int hblank; /* 8. Estimated Horizontal period */ hperiod = ((float) (1000000.0 / vfield_rate - CVT_MIN_VSYNC_BP)) / (vdisplay_rnd + 2 * vmargin + CVT_MIN_V_PORCH + interlace); /* 9. Find number of lines in sync + backporch */ if (((int) (CVT_MIN_VSYNC_BP / hperiod) + 1) < (vsync + CVT_MIN_V_PORCH)) { vsync_and_back_porch = vsync + CVT_MIN_V_PORCH; } else { vsync_and_back_porch = (int) (CVT_MIN_VSYNC_BP / hperiod) + 1; } /* 10. Find number of lines in back porch */ vblank_porch = vsync_and_back_porch - vsync; (void) vblank_porch; /* 11. Find total number of lines in vertical field */ mode->vtotal = vdisplay_rnd + 2 * vmargin + vsync_and_back_porch + interlace + CVT_MIN_V_PORCH; /* 12. Find ideal blanking duty cycle from formula */ hblank_percentage = CVT_C_PRIME - CVT_M_PRIME * hperiod / 1000.0; /* 13. Blanking time */ if (hblank_percentage < 20) { hblank_percentage = 20; } hblank = mode->hdisplay * hblank_percentage / (100.0 - hblank_percentage); hblank -= hblank % (2 * CVT_H_GRANULARITY); /* 14. Find total number of pixels in a line. */ mode->htotal = mode->hdisplay + hblank; /* Fill in hsync values */ mode->hsync_end = mode->hdisplay + hblank / 2; mode->hsync_start = mode->hsync_end - (mode->htotal * CVT_HSYNC_PERCENTAGE) / 100; mode->hsync_start += CVT_H_GRANULARITY - mode->hsync_start % CVT_H_GRANULARITY; /* Fill in vsync values */ mode->vsync_start = mode->vdisplay + CVT_MIN_V_PORCH; mode->vsync_end = mode->vsync_start + vsync; } else { /* reduced blanking */ int vbi_lines; /* 8. Estimate Horizontal period. */ hperiod = ((float) (1000000.0 / vfield_rate - CVT_RB_MIN_VBLANK)) / (vdisplay_rnd + 2 * vmargin); /* 9. Find number of lines in vertical blanking */ vbi_lines = ((float) CVT_RB_MIN_VBLANK) / hperiod + 1; /* 10. Check if vertical blanking is sufficient */ if (vbi_lines < (CVT_RB_VFPORCH + vsync + CVT_MIN_V_BPORCH)) { vbi_lines = CVT_RB_VFPORCH + vsync + CVT_MIN_V_BPORCH; } /* 11. Find total number of lines in vertical field */ mode->vtotal = vdisplay_rnd + 2 * vmargin + interlace + vbi_lines; /* 12. Find total number of pixels in a line */ mode->htotal = mode->hdisplay + CVT_RB_H_BLANK; /* Fill in hsync values */ mode->hsync_end = mode->hdisplay + CVT_RB_H_BLANK / 2; mode->hsync_start = mode->hsync_end - CVT_RB_H_SYNC; /* Fill in vsync values */ mode->vsync_start = mode->vdisplay + CVT_RB_VFPORCH; mode->vsync_end = mode->vsync_start + vsync; } /* 15/13. Find pixel clock frequency (kHz for xf86) */ mode->clock = mode->htotal * 1000.0 / hperiod; mode->clock -= mode->clock % CVT_CLOCK_STEP; /* 17/15. Find actual Field rate */ mode->vrefresh = (1000.0 * ((float) mode->clock)) / ((float) (mode->htotal * mode->vtotal)); /* 18/16. Find actual vertical frame frequency */ /* ignore - just set the mode flag for interlaced */ if (interlaced) { mode->vtotal *= 2; mode->flags |= DRM_MODE_FLAG_INTERLACE; } snprintf(mode->name, sizeof(mode->name), "%dx%d", hdisplay, vdisplay); if (reduced) { mode->flags |= DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC; } else { mode->flags |= DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC; } } wlroots-0.10.0/backend/drm/drm.c000066400000000000000000001333431361211131400164000ustar00rootroot00000000000000#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 #include #include "backend/drm/cvt.h" #include "backend/drm/drm.h" #include "backend/drm/iface.h" #include "backend/drm/util.h" #include "util/signal.h" bool check_drm_features(struct wlr_drm_backend *drm) { uint64_t cap; if (drm->parent) { if (drmGetCap(drm->fd, DRM_CAP_PRIME, &cap) || !(cap & DRM_PRIME_CAP_IMPORT)) { wlr_log(WLR_ERROR, "PRIME import not supported on secondary GPU"); return false; } 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; } const char *no_atomic = getenv("WLR_DRM_NO_ATOMIC"); if (no_atomic && strcmp(no_atomic, "1") == 0) { 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; } int ret = drmGetCap(drm->fd, DRM_CAP_TIMESTAMP_MONOTONIC, &cap); drm->clock = (ret == 0 && cap == 1) ? CLOCK_MONOTONIC : CLOCK_REALTIME; ret = drmGetCap(drm->fd, DRM_CAP_ADDFB2_MODIFIERS, &cap); drm->addfb2_modifiers = ret == 0 && cap == 1; return true; } static bool add_plane(struct wlr_drm_backend *drm, struct wlr_drm_crtc *crtc, drmModePlane *drm_plane, uint32_t type, union wlr_drm_plane_props *props) { assert(!(type == DRM_PLANE_TYPE_PRIMARY && crtc->primary)); if (type == DRM_PLANE_TYPE_CURSOR && crtc->cursor) { return true; } struct wlr_drm_plane *p = calloc(1, sizeof(*p)); if (!p) { wlr_log_errno(WLR_ERROR, "Allocation failed"); return false; } p->type = type; p->id = drm_plane->plane_id; p->props = *props; for (size_t j = 0; j < drm_plane->count_formats; ++j) { wlr_drm_format_set_add(&p->formats, drm_plane->formats[j], DRM_FORMAT_MOD_INVALID); } // Choose an RGB format for the plane uint32_t rgb_format = DRM_FORMAT_INVALID; for (size_t j = 0; j < drm_plane->count_formats; ++j) { uint32_t fmt = drm_plane->formats[j]; if (fmt == DRM_FORMAT_ARGB8888) { // Prefer formats with alpha channel rgb_format = fmt; break; } else if (fmt == DRM_FORMAT_XRGB8888) { rgb_format = fmt; } } p->drm_format = rgb_format; if (p->props.in_formats) { 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; } struct drm_format_modifier_blob *data = blob->data; uint32_t *fmts = (uint32_t *)((char *)data + data->formats_offset); struct drm_format_modifier *mods = (struct drm_format_modifier *) ((char *)data + data->modifiers_offset); for (uint32_t i = 0; i < data->count_modifiers; ++i) { for (int j = 0; j < 64; ++j) { if (mods[i].formats & ((uint64_t)1 << j)) { wlr_drm_format_set_add(&p->formats, fmts[j + mods[i].offset], mods[i].modifier); } } } drmModeFreePropertyBlob(blob); } switch (type) { case DRM_PLANE_TYPE_PRIMARY: crtc->primary = p; break; case DRM_PLANE_TYPE_CURSOR: crtc->cursor = p; break; default: abort(); } 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); for (uint32_t i = 0; i < plane_res->count_planes; ++i) { uint32_t id = plane_res->planes[i]; drmModePlane *plane = drmModeGetPlane(drm->fd, id); if (!plane) { wlr_log_errno(WLR_ERROR, "Failed to get DRM plane"); goto error; } union wlr_drm_plane_props props = {0}; if (!get_drm_plane_props(drm->fd, id, &props)) { drmModeFreePlane(plane); goto error; } uint64_t type; if (!get_drm_prop(drm->fd, id, props.type, &type)) { drmModeFreePlane(plane); goto error; } /* * This is a very naive implementation of the plane matching * logic. Primary and cursor planes should only work on a * single CRTC, and this should be perfectly adequate, but * overlay planes can potentially work with multiple CRTCs, * meaning this could return inefficient/skewed results. * * However, we don't really care about overlay planes, as we * don't support them yet. We only bother to keep basic * tracking of them for DRM lease clients. * * possible_crtcs is a bitmask of crtcs, where each bit is an * index into drmModeRes.crtcs. So if bit 0 is set (ffs starts * counting from 1), crtc 0 is possible. */ int crtc_bit = ffs(plane->possible_crtcs) - 1; // This would be a kernel bug assert(crtc_bit >= 0 && (size_t)crtc_bit < drm->num_crtcs); struct wlr_drm_crtc *crtc = &drm->crtcs[crtc_bit]; if (type == DRM_PLANE_TYPE_OVERLAY) { uint32_t *tmp = realloc(crtc->overlays, sizeof(*crtc->overlays) * (crtc->num_overlays + 1)); if (tmp) { crtc->overlays = tmp; crtc->overlays[crtc->num_overlays++] = id; } drmModeFreePlane(plane); continue; } if (!add_plane(drm, crtc, plane, type, &props)) { drmModeFreePlane(plane); goto error; } drmModeFreePlane(plane); } drmModeFreePlaneResources(plane_res); return true; error: 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]; crtc->legacy_crtc = drmModeGetCrtc(drm->fd, crtc->id); get_drm_crtc_props(drm->fd, crtc->id, &crtc->props); } if (!init_planes(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; } for (size_t i = 0; i < drm->num_crtcs; ++i) { struct wlr_drm_crtc *crtc = &drm->crtcs[i]; drmModeAtomicFree(crtc->atomic); drmModeFreeCrtc(crtc->legacy_crtc); if (crtc->mode_id) { drmModeDestroyPropertyBlob(drm->fd, crtc->mode_id); } if (crtc->gamma_lut) { drmModeDestroyPropertyBlob(drm->fd, crtc->gamma_lut); } free(crtc->gamma_table); if (crtc->primary) { wlr_drm_format_set_finish(&crtc->primary->formats); free(crtc->primary); } if (crtc->cursor) { wlr_drm_format_set_finish(&crtc->cursor->formats); free(crtc->cursor); } free(crtc->overlays); } free(drm->crtcs); } static struct wlr_drm_connector *get_drm_connector_from_output( struct wlr_output *wlr_output) { assert(wlr_output_is_drm(wlr_output)); return (struct wlr_drm_connector *)wlr_output; } static bool drm_connector_attach_render(struct wlr_output *output, int *buffer_age) { struct wlr_drm_connector *conn = get_drm_connector_from_output(output); return make_drm_surface_current(&conn->crtc->primary->surf, buffer_age); } static bool drm_connector_commit_buffer(struct wlr_output *output) { struct wlr_drm_connector *conn = get_drm_connector_from_output(output); struct wlr_drm_backend *drm = get_drm_backend_from_backend(output->backend); struct wlr_drm_crtc *crtc = conn->crtc; if (!crtc) { return false; } struct wlr_drm_plane *plane = crtc->primary; pixman_region32_t *damage = NULL; if (output->pending.committed & WLR_OUTPUT_STATE_DAMAGE) { damage = &output->pending.damage; } struct gbm_bo *bo; uint32_t fb_id = 0; assert(output->pending.committed & WLR_OUTPUT_STATE_BUFFER); switch (output->pending.buffer_type) { case WLR_OUTPUT_STATE_BUFFER_RENDER: bo = swap_drm_surface_buffers(&plane->surf, damage); if (bo == NULL) { wlr_log(WLR_ERROR, "swap_drm_surface_buffers failed"); return false; } if (drm->parent) { bo = copy_drm_surface_mgpu(&plane->mgpu_surf, bo); if (bo == NULL) { wlr_log(WLR_ERROR, "copy_drm_surface_mgpu failed"); return false; } } fb_id = get_fb_for_bo(bo, plane->drm_format, drm->addfb2_modifiers); if (fb_id == 0) { wlr_log(WLR_ERROR, "get_fb_for_bo failed"); return false; } break; case WLR_OUTPUT_STATE_BUFFER_SCANOUT: bo = import_gbm_bo(&drm->renderer, &conn->pending_dmabuf); if (bo == NULL) { wlr_log(WLR_ERROR, "import_gbm_bo failed"); return false; } if (conn->pending_bo != NULL) { gbm_bo_destroy(conn->pending_bo); } conn->pending_bo = bo; fb_id = get_fb_for_bo(bo, gbm_bo_get_format(bo), drm->addfb2_modifiers); if (fb_id == 0) { wlr_log(WLR_ERROR, "get_fb_for_bo failed"); return false; } break; } if (conn->pageflip_pending) { wlr_log(WLR_ERROR, "Skipping pageflip on output '%s'", conn->output.name); return false; } if (!drm->iface->crtc_pageflip(drm, conn, crtc, fb_id, NULL)) { return false; } conn->pageflip_pending = true; if (output->pending.buffer_type == WLR_OUTPUT_STATE_BUFFER_SCANOUT) { wlr_buffer_unref(conn->pending_buffer); conn->pending_buffer = wlr_buffer_ref(output->pending.buffer); } wlr_output_update_enabled(output, true); return true; } static bool drm_connector_set_custom_mode(struct wlr_output *output, int32_t width, int32_t height, int32_t refresh); static bool drm_connector_commit(struct wlr_output *output) { struct wlr_drm_backend *drm = get_drm_backend_from_backend(output->backend); if (!drm->session->active) { return false; } if (output->pending.committed & WLR_OUTPUT_STATE_MODE) { switch (output->pending.mode_type) { case WLR_OUTPUT_STATE_MODE_FIXED: if (!drm_connector_set_mode(output, output->pending.mode)) { return false; } break; case WLR_OUTPUT_STATE_MODE_CUSTOM: if (!drm_connector_set_custom_mode(output, output->pending.custom_mode.width, output->pending.custom_mode.height, output->pending.custom_mode.refresh)) { return false; } break; } } if (output->pending.committed & WLR_OUTPUT_STATE_ENABLED) { if (!enable_drm_connector(output, output->pending.enabled)) { return false; } } // TODO: support modesetting with a buffer if (output->pending.committed & WLR_OUTPUT_STATE_BUFFER && !(output->pending.committed & WLR_OUTPUT_STATE_MODE)) { if (!drm_connector_commit_buffer(output)) { return false; } } return true; } static void fill_empty_gamma_table(size_t size, uint16_t *r, uint16_t *g, uint16_t *b) { for (uint32_t i = 0; i < size; ++i) { uint16_t val = (uint32_t)0xffff * i / (size - 1); r[i] = g[i] = b[i] = val; } } 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 = get_drm_backend_from_backend(output->backend); if (conn->crtc) { return drm->iface->crtc_get_gamma_size(drm, conn->crtc); } return 0; } bool set_drm_connector_gamma(struct wlr_output *output, size_t size, const uint16_t *r, const uint16_t *g, const uint16_t *b) { struct wlr_drm_connector *conn = get_drm_connector_from_output(output); struct wlr_drm_backend *drm = get_drm_backend_from_backend(output->backend); if (!conn->crtc) { return false; } bool reset = false; if (size == 0) { reset = true; size = drm_connector_get_gamma_size(output); if (size == 0) { return false; } } uint16_t *gamma_table = malloc(3 * size * sizeof(uint16_t)); if (gamma_table == NULL) { wlr_log(WLR_ERROR, "Failed to allocate gamma table"); return false; } uint16_t *_r = gamma_table; uint16_t *_g = gamma_table + size; uint16_t *_b = gamma_table + 2 * size; if (reset) { fill_empty_gamma_table(size, _r, _g, _b); } else { memcpy(_r, r, size * sizeof(uint16_t)); memcpy(_g, g, size * sizeof(uint16_t)); memcpy(_b, b, size * sizeof(uint16_t)); } bool ok = drm->iface->crtc_set_gamma(drm, conn->crtc, size, _r, _g, _b); if (ok) { wlr_output_update_needs_frame(output); free(conn->crtc->gamma_table); conn->crtc->gamma_table = gamma_table; conn->crtc->gamma_table_size = size; } else { free(gamma_table); } return ok; } static bool drm_connector_export_dmabuf(struct wlr_output *output, struct wlr_dmabuf_attributes *attribs) { struct wlr_drm_connector *conn = get_drm_connector_from_output(output); struct wlr_drm_backend *drm = get_drm_backend_from_backend(output->backend); if (!drm->session->active) { return false; } struct wlr_drm_crtc *crtc = conn->crtc; if (!crtc) { return false; } struct wlr_drm_plane *plane = crtc->primary; struct wlr_drm_surface *surf = &plane->surf; return export_drm_bo(surf->back, attribs); } static bool drm_connector_pageflip_renderer(struct wlr_drm_connector *conn, struct wlr_drm_mode *mode) { struct wlr_drm_backend *drm = get_drm_backend_from_backend(conn->output.backend); struct wlr_drm_crtc *crtc = conn->crtc; if (!crtc) { wlr_log(WLR_ERROR, "Page-flip failed on connector '%s': no CRTC", conn->output.name); return false; } struct wlr_drm_plane *plane = crtc->primary; struct gbm_bo *bo = get_drm_surface_front( drm->parent ? &plane->mgpu_surf : &plane->surf); uint32_t fb_id = get_fb_for_bo(bo, plane->drm_format, drm->addfb2_modifiers); return drm->iface->crtc_pageflip(drm, conn, crtc, fb_id, &mode->drm_mode); } static void drm_connector_start_renderer(struct wlr_drm_connector *conn) { if (conn->state != WLR_DRM_CONN_CONNECTED) { return; } wlr_log(WLR_DEBUG, "Starting renderer on output '%s'", conn->output.name); struct wlr_drm_mode *mode = (struct wlr_drm_mode *)conn->output.current_mode; if (drm_connector_pageflip_renderer(conn, mode)) { conn->pageflip_pending = true; wlr_output_update_enabled(&conn->output, true); } else { wl_event_source_timer_update(conn->retry_pageflip, 1000000.0f / conn->output.current_mode->refresh); } } static bool drm_connector_init_renderer(struct wlr_drm_connector *conn, struct wlr_drm_mode *mode) { struct wlr_drm_backend *drm = get_drm_backend_from_backend(conn->output.backend); if (conn->state != WLR_DRM_CONN_CONNECTED && conn->state != WLR_DRM_CONN_NEEDS_MODESET) { return false; } wlr_log(WLR_DEBUG, "Initializing renderer on connector '%s'", conn->output.name); struct wlr_drm_crtc *crtc = conn->crtc; if (!crtc) { wlr_log(WLR_ERROR, "Failed to initialize renderer on connector '%s': " "no CRTC", conn->output.name); return false; } struct wlr_drm_plane *plane = crtc->primary; int width = mode->wlr_mode.width; int height = mode->wlr_mode.height; uint32_t format = drm->renderer.gbm_format; if (!init_drm_plane_surfaces(plane, drm, width, height, format, true) || !drm_connector_pageflip_renderer(conn, mode)) { // If page-flipping with modifiers enabled doesn't work, retry without // modifiers wlr_log(WLR_INFO, "Page-flip failed with primary FB modifiers enabled, " "retrying without modifiers"); finish_drm_surface(&plane->surf); finish_drm_surface(&plane->mgpu_surf); if (!init_drm_plane_surfaces(plane, drm, width, height, format, false)) { return false; } if (!drm_connector_pageflip_renderer(conn, mode)) { wlr_log(WLR_ERROR, "Failed to initialize renderer " "on connector '%s': initial page-flip failed", conn->output.name); return false; } } return true; } static void realloc_crtcs(struct wlr_drm_backend *drm); static void attempt_enable_needs_modeset(struct wlr_drm_backend *drm) { // Try to modeset any output that has a desired mode and a CRTC (ie. was // lacking a CRTC on last modeset) struct wlr_drm_connector *conn; wl_list_for_each(conn, &drm->outputs, link) { if (conn->state == WLR_DRM_CONN_NEEDS_MODESET && conn->crtc != NULL && conn->desired_mode != NULL && conn->desired_enabled) { wlr_log(WLR_DEBUG, "Output %s has a desired mode and a CRTC, " "attempting a modeset", conn->output.name); drm_connector_set_mode(&conn->output, conn->desired_mode); } } } bool enable_drm_connector(struct wlr_output *output, bool enable) { struct wlr_drm_connector *conn = get_drm_connector_from_output(output); struct wlr_drm_backend *drm = get_drm_backend_from_backend(output->backend); if (conn->state != WLR_DRM_CONN_CONNECTED && conn->state != WLR_DRM_CONN_NEEDS_MODESET) { return false; } conn->desired_enabled = enable; if (enable && conn->crtc == NULL) { // Maybe we can steal a CRTC from a disabled output realloc_crtcs(drm); } bool ok = drm->iface->conn_enable(drm, conn, enable); if (!ok) { return false; } if (enable) { drm_connector_start_renderer(conn); } else { realloc_crtcs(drm); attempt_enable_needs_modeset(drm); } wlr_output_update_enabled(&conn->output, enable); return true; } static void drm_connector_cleanup(struct wlr_drm_connector *conn); bool drm_connector_set_mode(struct wlr_output *output, struct wlr_output_mode *wlr_mode) { struct wlr_drm_connector *conn = get_drm_connector_from_output(output); struct wlr_drm_backend *drm = get_drm_backend_from_backend(output->backend); if (conn->crtc == NULL) { // Maybe we can steal a CRTC from a disabled output realloc_crtcs(drm); } if (conn->crtc == NULL) { wlr_log(WLR_ERROR, "Cannot modeset '%s': no CRTC for this connector", conn->output.name); // Save the desired mode for later, when we'll get a proper CRTC conn->desired_mode = wlr_mode; return false; } wlr_log(WLR_INFO, "Modesetting '%s' with '%ux%u@%u mHz'", conn->output.name, wlr_mode->width, wlr_mode->height, wlr_mode->refresh); struct wlr_drm_mode *mode = (struct wlr_drm_mode *)wlr_mode; if (!drm_connector_init_renderer(conn, mode)) { wlr_log(WLR_ERROR, "Failed to initialize renderer for plane"); return false; } conn->state = WLR_DRM_CONN_CONNECTED; conn->desired_mode = NULL; wlr_output_update_mode(&conn->output, wlr_mode); wlr_output_update_enabled(&conn->output, true); conn->desired_enabled = true; // When switching VTs, the mode is not updated but the buffers become // invalid, so we need to manually damage the output here wlr_output_damage_whole(&conn->output); return true; } static bool drm_connector_set_custom_mode(struct wlr_output *output, int32_t width, int32_t height, int32_t refresh) { drmModeModeInfo mode = {0}; generate_cvt_mode(&mode, width, height, (float)refresh / 1000, false, false); mode.type = DRM_MODE_TYPE_USERDEF; struct wlr_output_mode *wlr_mode = wlr_drm_connector_add_mode(output, &mode); if (wlr_mode == NULL) { return false; } return drm_connector_set_mode(output, wlr_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 = (struct wlr_drm_mode *)wlr_mode; if (memcmp(&mode->drm_mode, modeinfo, sizeof(*modeinfo)) == 0) { return wlr_mode; } } struct wlr_drm_mode *mode = calloc(1, sizeof(*mode)); if (!mode) { return NULL; } memcpy(&mode->drm_mode, modeinfo, sizeof(*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); wlr_log(WLR_INFO, "Registered custom mode " "%"PRId32"x%"PRId32"@%"PRId32, mode->wlr_mode.width, mode->wlr_mode.height, mode->wlr_mode.refresh); wl_list_insert(&conn->output.modes, &mode->wlr_mode.link); return &mode->wlr_mode; } static bool drm_connector_set_cursor(struct wlr_output *output, struct wlr_texture *texture, int32_t scale, enum wl_output_transform transform, int32_t hotspot_x, int32_t hotspot_y, bool update_texture) { struct wlr_drm_connector *conn = get_drm_connector_from_output(output); struct wlr_drm_backend *drm = get_drm_backend_from_backend(output->backend); struct wlr_drm_crtc *crtc = conn->crtc; if (!crtc) { return false; } struct wlr_drm_plane *plane = crtc->cursor; if (!plane) { // We don't have a real cursor plane, so we make a fake one plane = calloc(1, sizeof(*plane)); if (!plane) { wlr_log_errno(WLR_ERROR, "Allocation failed"); return false; } crtc->cursor = plane; } if (!plane->surf.gbm) { int ret; uint64_t w, h; ret = drmGetCap(drm->fd, DRM_CAP_CURSOR_WIDTH, &w); w = ret ? 64 : w; ret = drmGetCap(drm->fd, DRM_CAP_CURSOR_HEIGHT, &h); h = ret ? 64 : h; if (!drm->parent) { if (!init_drm_surface(&plane->surf, &drm->renderer, w, h, drm->renderer.gbm_format, NULL, GBM_BO_USE_LINEAR | GBM_BO_USE_SCANOUT)) { wlr_log(WLR_ERROR, "Cannot allocate cursor resources"); return false; } } else { if (!init_drm_surface(&plane->surf, &drm->parent->renderer, w, h, drm->parent->renderer.gbm_format, NULL, GBM_BO_USE_LINEAR)) { wlr_log(WLR_ERROR, "Cannot allocate cursor resources"); return false; } if (!init_drm_surface(&plane->mgpu_surf, &drm->renderer, w, h, drm->renderer.gbm_format, NULL, GBM_BO_USE_LINEAR | GBM_BO_USE_SCANOUT)) { wlr_log(WLR_ERROR, "Cannot allocate cursor resources"); return false; } } } wlr_matrix_projection(plane->matrix, plane->surf.width, plane->surf.height, output->transform); struct wlr_box hotspot = { .x = hotspot_x, .y = hotspot_y }; wlr_box_transform(&hotspot, &hotspot, wlr_output_transform_invert(output->transform), plane->surf.width, plane->surf.height); if (plane->cursor_hotspot_x != hotspot.x || plane->cursor_hotspot_y != hotspot.y) { // Update cursor hotspot conn->cursor_x -= hotspot.x - plane->cursor_hotspot_x; conn->cursor_y -= hotspot.y - plane->cursor_hotspot_y; plane->cursor_hotspot_x = hotspot.x; plane->cursor_hotspot_y = hotspot.y; if (!drm->iface->crtc_move_cursor(drm, conn->crtc, conn->cursor_x, conn->cursor_y)) { return false; } wlr_output_update_needs_frame(output); } if (!update_texture) { // Don't update cursor image return true; } plane->cursor_enabled = false; if (texture != NULL) { int width, height; wlr_texture_get_size(texture, &width, &height); width = width * output->scale / scale; height = height * output->scale / scale; if (width > (int)plane->surf.width || height > (int)plane->surf.height) { wlr_log(WLR_ERROR, "Cursor too large (max %dx%d)", (int)plane->surf.width, (int)plane->surf.height); return false; } make_drm_surface_current(&plane->surf, NULL); struct wlr_renderer *rend = plane->surf.renderer->wlr_rend; struct wlr_box cursor_box = { .width = width, .height = height }; float matrix[9]; wlr_matrix_project_box(matrix, &cursor_box, transform, 0, plane->matrix); wlr_renderer_begin(rend, plane->surf.width, plane->surf.height); wlr_renderer_clear(rend, (float[]){ 0.0, 0.0, 0.0, 0.0 }); wlr_render_texture_with_matrix(rend, texture, matrix, 1.0); wlr_renderer_end(rend); swap_drm_surface_buffers(&plane->surf, NULL); plane->cursor_enabled = true; } if (!drm->session->active) { return true; // will be committed when session is resumed } struct gbm_bo *bo = plane->cursor_enabled ? plane->surf.back : NULL; if (bo && drm->parent) { bo = copy_drm_surface_mgpu(&plane->mgpu_surf, bo); } if (bo) { // workaround for nouveau // Buffers created with GBM_BO_USER_LINEAR are placed in NOUVEAU_GEM_DOMAIN_GART. // When the bo is attached to the cursor plane it is moved to NOUVEAU_GEM_DOMAIN_VRAM. // However, this does not wait for the render operations to complete, leaving an empty surface. // see https://bugs.freedesktop.org/show_bug.cgi?id=109631 // The render operations can be waited for using: glFinish(); } bool ok = drm->iface->crtc_set_cursor(drm, crtc, bo); if (ok) { wlr_output_update_needs_frame(output); } return ok; } 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); struct wlr_drm_backend *drm = get_drm_backend_from_backend(output->backend); if (!conn->crtc) { return false; } struct wlr_drm_plane *plane = conn->crtc->cursor; 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); if (plane != NULL) { box.x -= plane->cursor_hotspot_x; box.y -= plane->cursor_hotspot_y; } conn->cursor_x = box.x; conn->cursor_y = box.y; if (!drm->session->active) { return true; // will be committed when session is resumed } bool ok = drm->iface->crtc_move_cursor(drm, conn->crtc, box.x, box.y); if (ok) { wlr_output_update_needs_frame(output); } return ok; } static bool drm_connector_schedule_frame(struct wlr_output *output) { struct wlr_drm_connector *conn = get_drm_connector_from_output(output); struct wlr_drm_backend *drm = get_drm_backend_from_backend(output->backend); if (!drm->session->active) { return false; } // We need to figure out where we are in the vblank cycle // TODO: try using drmWaitVBlank and fallback to pageflipping struct wlr_drm_crtc *crtc = conn->crtc; if (!crtc) { return false; } struct wlr_drm_plane *plane = crtc->primary; struct gbm_bo *bo = plane->surf.back; if (!bo) { // We haven't swapped buffers yet -- can't do a pageflip wlr_output_send_frame(output); return true; } if (drm->parent) { bo = copy_drm_surface_mgpu(&plane->mgpu_surf, bo); } if (conn->pageflip_pending) { wlr_log(WLR_ERROR, "Skipping pageflip on output '%s'", conn->output.name); return true; } uint32_t fb_id = get_fb_for_bo(bo, plane->drm_format, drm->addfb2_modifiers); if (!drm->iface->crtc_pageflip(drm, conn, crtc, fb_id, NULL)) { return false; } conn->pageflip_pending = true; wlr_output_update_enabled(output, true); return true; } static uint32_t strip_alpha_channel(uint32_t format) { switch (format) { case DRM_FORMAT_ARGB8888: return DRM_FORMAT_XRGB8888; default: return DRM_FORMAT_INVALID; } } static bool drm_connector_attach_buffer(struct wlr_output *output, struct wlr_buffer *buffer) { struct wlr_drm_connector *conn = get_drm_connector_from_output(output); struct wlr_drm_backend *drm = get_drm_backend_from_backend(output->backend); if (!drm->session->active) { return false; } struct wlr_drm_crtc *crtc = conn->crtc; if (!crtc) { return false; } struct wlr_dmabuf_attributes attribs; if (!wlr_buffer_get_dmabuf(buffer, &attribs)) { return false; } if (attribs.flags != 0) { return false; } if (attribs.width != output->width || attribs.height != output->height) { return false; } if (!wlr_drm_format_set_has(&crtc->primary->formats, attribs.format, attribs.modifier)) { // The format isn't supported by the plane. Try stripping the alpha // channel, if any. uint32_t format = strip_alpha_channel(attribs.format); if (format != DRM_FORMAT_INVALID && wlr_drm_format_set_has( &crtc->primary->formats, format, attribs.modifier)) { attribs.format = format; } else { return false; } } memcpy(&conn->pending_dmabuf, &attribs, sizeof(attribs)); return true; } static void drm_connector_destroy(struct wlr_output *output) { struct wlr_drm_connector *conn = get_drm_connector_from_output(output); drm_connector_cleanup(conn); drmModeFreeCrtc(conn->old_crtc); wl_event_source_remove(conn->retry_pageflip); wl_list_remove(&conn->link); free(conn); } static const struct wlr_output_impl output_impl = { .set_cursor = drm_connector_set_cursor, .move_cursor = drm_connector_move_cursor, .destroy = drm_connector_destroy, .attach_render = drm_connector_attach_render, .commit = drm_connector_commit, .set_gamma = set_drm_connector_gamma, .get_gamma_size = drm_connector_get_gamma_size, .export_dmabuf = drm_connector_export_dmabuf, .schedule_frame = drm_connector_schedule_frame, .attach_buffer = drm_connector_attach_buffer, }; bool wlr_output_is_drm(struct wlr_output *output) { return output->impl == &output_impl; } static int retry_pageflip(void *data) { struct wlr_drm_connector *conn = data; wlr_log(WLR_INFO, "%s: Retrying pageflip", conn->output.name); drm_connector_start_renderer(conn); return 0; } 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) { struct wlr_drm_backend *drm = get_drm_backend_from_backend(conn->output.backend); if (conn->crtc == NULL) { return; } wlr_log(WLR_DEBUG, "De-allocating CRTC %zu for output '%s'", conn->crtc - drm->crtcs, conn->output.name); set_drm_connector_gamma(&conn->output, 0, NULL, NULL, NULL); finish_drm_surface(&conn->crtc->primary->surf); finish_drm_surface(&conn->crtc->cursor->surf); drm->iface->conn_enable(drm, conn, false); conn->crtc = NULL; } static void realloc_crtcs(struct wlr_drm_backend *drm) { assert(drm->num_crtcs > 0); size_t num_outputs = wl_list_length(&drm->outputs); if (num_outputs == 0) { return; } wlr_log(WLR_DEBUG, "Reallocating CRTCs"); struct wlr_drm_connector *connectors[num_outputs]; uint32_t connector_constraints[num_outputs]; 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->outputs, link) { connectors[i] = conn; wlr_log(WLR_DEBUG, " '%s' crtc=%d state=%d desired_enabled=%d", conn->output.name, conn->crtc ? (int)(conn->crtc - drm->crtcs) : -1, conn->state, conn->desired_enabled); if (conn->crtc) { previous_match[conn->crtc - drm->crtcs] = i; } // Only search CRTCs for user-enabled outputs (that are already // connected or in need of a modeset) if ((conn->state == WLR_DRM_CONN_CONNECTED || conn->state == WLR_DRM_CONN_NEEDS_MODESET) && conn->desired_enabled) { connector_constraints[i] = conn->possible_crtc; } else { // Will always fail to match anything connector_constraints[i] = 0; } ++i; } match_obj(num_outputs, connector_constraints, drm->num_crtcs, previous_match, new_match); // Converts our crtc=>connector result into a connector=>crtc one. ssize_t connector_match[num_outputs]; for (size_t i = 0 ; i < num_outputs; ++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; } } /* * In the case that we add a new connector (hotplug) and we fail to * match everything, we prefer to fail the new connector and keep all * of the old mappings instead. */ for (size_t i = 0; i < num_outputs; ++i) { struct wlr_drm_connector *conn = connectors[i]; if (conn->state == WLR_DRM_CONN_CONNECTED && conn->desired_enabled && connector_match[i] == -1) { wlr_log(WLR_DEBUG, "Could not match a CRTC for previously connected output; " "keeping old configuration"); return; } } wlr_log(WLR_DEBUG, "State after reallocation:"); // Apply new configuration for (size_t i = 0; i < num_outputs; ++i) { struct wlr_drm_connector *conn = connectors[i]; bool prev_enabled = conn->crtc; wlr_log(WLR_DEBUG, " '%s' crtc=%zd state=%d desired_enabled=%d", conn->output.name, connector_match[i], conn->state, conn->desired_enabled); // We don't need to change anything. if (prev_enabled && connector_match[i] == conn->crtc - drm->crtcs) { continue; } dealloc_crtc(conn); if (connector_match[i] == -1) { if (prev_enabled) { wlr_log(WLR_DEBUG, "Output has %s lost its CRTC", conn->output.name); conn->state = WLR_DRM_CONN_NEEDS_MODESET; wlr_output_update_enabled(&conn->output, false); conn->desired_mode = conn->output.current_mode; wlr_output_update_mode(&conn->output, NULL); } continue; } conn->crtc = &drm->crtcs[connector_match[i]]; // Only realloc buffers if we have actually been modeset if (conn->state != WLR_DRM_CONN_CONNECTED) { continue; } struct wlr_drm_mode *mode = (struct wlr_drm_mode *)conn->output.current_mode; if (!drm_connector_init_renderer(conn, mode)) { wlr_log(WLR_ERROR, "Failed to initialize renderer on output %s", conn->output.name); wlr_output_update_enabled(&conn->output, false); continue; } wlr_output_damage_whole(&conn->output); } } static uint32_t get_possible_crtcs(int fd, drmModeRes *res, drmModeConnector *conn, bool is_mst) { uint32_t ret = 0; for (int i = 0; i < conn->count_encoders; ++i) { drmModeEncoder *enc = drmModeGetEncoder(fd, conn->encoders[i]); if (!enc) { continue; } ret |= enc->possible_crtcs; drmModeFreeEncoder(enc); } // Sometimes DP MST connectors report no encoders, so we'll loop though // all of the encoders of the MST type instead. // TODO: See if there is a better solution. if (!is_mst || ret) { return ret; } for (int i = 0; i < res->count_encoders; ++i) { drmModeEncoder *enc = drmModeGetEncoder(fd, res->encoders[i]); if (!enc) { continue; } if (enc->encoder_type == DRM_MODE_ENCODER_DPMST) { ret |= enc->possible_crtcs; } drmModeFreeEncoder(enc); } return ret; } void scan_drm_connectors(struct wlr_drm_backend *drm) { /* * This GPU is not really a modesetting device. * It's just being used as a renderer. */ if (drm->num_crtcs == 0) { return; } wlr_log(WLR_INFO, "Scanning DRM connectors"); 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->outputs); // +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) { drmModeConnector *drm_conn = drmModeGetConnector(drm->fd, res->connectors[i]); if (!drm_conn) { wlr_log_errno(WLR_ERROR, "Failed to get DRM connector"); continue; } drmModeEncoder *curr_enc = drmModeGetEncoder(drm->fd, drm_conn->encoder_id); ssize_t index = -1; struct wlr_drm_connector *c, *wlr_conn = NULL; wl_list_for_each(c, &drm->outputs, link) { index++; if (c->id == drm_conn->connector_id) { wlr_conn = c; break; } } if (!wlr_conn) { wlr_conn = calloc(1, sizeof(*wlr_conn)); if (!wlr_conn) { wlr_log_errno(WLR_ERROR, "Allocation failed"); drmModeFreeEncoder(curr_enc); drmModeFreeConnector(drm_conn); continue; } wlr_output_init(&wlr_conn->output, &drm->backend, &output_impl, drm->display); struct wl_event_loop *ev = wl_display_get_event_loop(drm->display); wlr_conn->retry_pageflip = wl_event_loop_add_timer(ev, retry_pageflip, wlr_conn); wlr_conn->state = WLR_DRM_CONN_DISCONNECTED; wlr_conn->id = drm_conn->connector_id; snprintf(wlr_conn->output.name, sizeof(wlr_conn->output.name), "%s-%"PRIu32, conn_get_name(drm_conn->connector_type), drm_conn->connector_type_id); if (curr_enc) { wlr_conn->old_crtc = drmModeGetCrtc(drm->fd, curr_enc->crtc_id); } wl_list_insert(drm->outputs.prev, &wlr_conn->link); wlr_log(WLR_INFO, "Found connector '%s'", wlr_conn->output.name); } else { seen[index] = true; } if (curr_enc) { for (size_t i = 0; i < drm->num_crtcs; ++i) { if (drm->crtcs[i].id == curr_enc->crtc_id) { wlr_conn->crtc = &drm->crtcs[i]; break; } } } else { wlr_conn->crtc = NULL; } // 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_log(WLR_ERROR, "Failed to get link status for '%s'", wlr_conn->output.name); continue; } if (link_status == DRM_MODE_LINK_STATUS_BAD) { // We need to reload our list of modes and force a modeset wlr_log(WLR_INFO, "Bad link for '%s'", wlr_conn->output.name); drm_connector_cleanup(wlr_conn); } } if (wlr_conn->state == WLR_DRM_CONN_DISCONNECTED && drm_conn->connection == DRM_MODE_CONNECTED) { wlr_log(WLR_INFO, "'%s' connected", wlr_conn->output.name); wlr_log(WLR_DEBUG, "Current CRTC: %d", wlr_conn->crtc ? (int)wlr_conn->crtc->id : -1); wlr_conn->output.phys_width = drm_conn->mmWidth; wlr_conn->output.phys_height = drm_conn->mmHeight; wlr_log(WLR_INFO, "Physical size: %"PRId32"x%"PRId32, wlr_conn->output.phys_width, wlr_conn->output.phys_height); wlr_conn->output.subpixel = subpixel_map[drm_conn->subpixel]; get_drm_connector_props(drm->fd, wlr_conn->id, &wlr_conn->props); 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->output, edid_len, edid); free(edid); struct wlr_output *output = &wlr_conn->output; char description[128]; snprintf(description, sizeof(description), "%s %s %s (%s)", output->make, output->model, output->serial, output->name); wlr_output_set_description(output, description); wlr_log(WLR_INFO, "Detected modes:"); for (int i = 0; i < drm_conn->count_modes; ++i) { struct wlr_drm_mode *mode = calloc(1, sizeof(*mode)); if (!mode) { wlr_log_errno(WLR_ERROR, "Allocation failed"); continue; } if (drm_conn->modes[i].flags & DRM_MODE_FLAG_INTERLACE) { free(mode); continue; } mode->drm_mode = drm_conn->modes[i]; mode->wlr_mode.width = mode->drm_mode.hdisplay; mode->wlr_mode.height = mode->drm_mode.vdisplay; mode->wlr_mode.refresh = calculate_refresh_rate(&mode->drm_mode); if (mode->drm_mode.type & DRM_MODE_TYPE_PREFERRED) { mode->wlr_mode.preferred = true; } wlr_log(WLR_INFO, " %"PRId32"x%"PRId32"@%"PRId32" %s", mode->wlr_mode.width, mode->wlr_mode.height, mode->wlr_mode.refresh, mode->wlr_mode.preferred ? "(preferred)" : ""); wl_list_insert(&wlr_conn->output.modes, &mode->wlr_mode.link); } size_t path_len; bool is_mst = false; char *path = get_drm_prop_blob(drm->fd, wlr_conn->id, wlr_conn->props.path, &path_len); if (path_len > 4 && path && strncmp(path, "mst:", 4) == 0) { is_mst = true; } free(path); wlr_conn->possible_crtc = get_possible_crtcs(drm->fd, res, drm_conn, is_mst); if (wlr_conn->possible_crtc == 0) { wlr_log(WLR_ERROR, "No CRTC possible for connector '%s'", wlr_conn->output.name); } // TODO: this results in connectors being enabled without a mode // set wlr_output_update_enabled(&wlr_conn->output, wlr_conn->crtc != NULL); wlr_conn->desired_enabled = true; wlr_conn->state = WLR_DRM_CONN_NEEDS_MODESET; new_outputs[new_outputs_len++] = wlr_conn; } else if ((wlr_conn->state == WLR_DRM_CONN_CONNECTED || wlr_conn->state == WLR_DRM_CONN_NEEDS_MODESET) && drm_conn->connection != DRM_MODE_CONNECTED) { wlr_log(WLR_INFO, "'%s' disconnected", wlr_conn->output.name); drm_connector_cleanup(wlr_conn); } drmModeFreeEncoder(curr_enc); 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->outputs); wl_list_for_each_reverse_safe(conn, tmp_conn, &drm->outputs, link) { index--; if (index >= seen_len || seen[index]) { continue; } wlr_log(WLR_INFO, "'%s' disappeared", conn->output.name); drm_connector_cleanup(conn); wlr_output_destroy(&conn->output); } realloc_crtcs(drm); for (size_t i = 0; i < new_outputs_len; ++i) { struct wlr_drm_connector *conn = new_outputs[i]; wlr_log(WLR_INFO, "Requesting modeset for '%s'", conn->output.name); wlr_signal_emit_safe(&drm->backend.events.new_output, &conn->output); } attempt_enable_needs_modeset(drm); } static int mhz_to_nsec(int mhz) { return 1000000000000LL / mhz; } static void page_flip_handler(int fd, unsigned seq, unsigned tv_sec, unsigned tv_usec, unsigned crtc_id, void *data) { struct wlr_drm_backend *drm = data; struct wlr_drm_connector *conn = NULL; struct wlr_drm_connector *search; wl_list_for_each(search, &drm->outputs, link) { if (search->crtc && search->crtc->id == crtc_id) { conn = search; } } if (!conn) { wlr_log(WLR_DEBUG, "No connector for crtc_id %u", crtc_id); return; } conn->pageflip_pending = false; if (conn->state != WLR_DRM_CONN_CONNECTED || conn->crtc == NULL) { return; } // Release the old buffer as it's not displayed anymore. The pending // buffer becomes the current buffer. wlr_buffer_unref(conn->current_buffer); conn->current_buffer = conn->pending_buffer; conn->pending_buffer = NULL; if (conn->current_bo != NULL) { gbm_bo_destroy(conn->current_bo); } conn->current_bo = conn->pending_bo; conn->pending_bo = NULL; uint32_t present_flags = WLR_OUTPUT_PRESENT_VSYNC | WLR_OUTPUT_PRESENT_HW_CLOCK | WLR_OUTPUT_PRESENT_HW_COMPLETION; if (conn->current_buffer != NULL) { present_flags |= WLR_OUTPUT_PRESENT_ZERO_COPY; } else { post_drm_surface(&conn->crtc->primary->surf); if (drm->parent) { post_drm_surface(&conn->crtc->primary->mgpu_surf); } } 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, .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) { drmEventContext event = { .version = 3, .page_flip_handler2 = page_flip_handler, }; drmHandleEvent(fd, &event); return 1; } void restore_drm_outputs(struct wlr_drm_backend *drm) { uint64_t to_close = (UINT64_C(1) << wl_list_length(&drm->outputs)) - 1; struct wlr_drm_connector *conn; wl_list_for_each(conn, &drm->outputs, link) { if (conn->state == WLR_DRM_CONN_CONNECTED) { conn->state = WLR_DRM_CONN_CLEANUP; } } time_t timeout = time(NULL) + 5; while (to_close && time(NULL) < timeout) { handle_drm_event(drm->fd, 0, NULL); size_t i = 0; struct wlr_drm_connector *conn; wl_list_for_each(conn, &drm->outputs, link) { if (conn->state != WLR_DRM_CONN_CLEANUP || !conn->pageflip_pending) { to_close &= ~(UINT64_C(1) << i); } i++; } } if (to_close) { wlr_log(WLR_ERROR, "Timed out stopping output renderers"); } wl_list_for_each(conn, &drm->outputs, link) { drmModeCrtc *crtc = conn->old_crtc; if (!crtc) { continue; } drmModeSetCrtc(drm->fd, crtc->crtc_id, crtc->buffer_id, crtc->x, crtc->y, &conn->id, 1, &crtc->mode); drmModeSetCursor(drm->fd, crtc->crtc_id, 0, 0, 0); } } static void drm_connector_cleanup(struct wlr_drm_connector *conn) { if (!conn) { return; } switch (conn->state) { case WLR_DRM_CONN_CONNECTED: case WLR_DRM_CONN_CLEANUP: conn->output.current_mode = NULL; conn->desired_mode = NULL; struct wlr_drm_mode *mode, *tmp; wl_list_for_each_safe(mode, tmp, &conn->output.modes, wlr_mode.link) { wl_list_remove(&mode->wlr_mode.link); free(mode); } conn->output.enabled = false; conn->output.width = conn->output.height = conn->output.refresh = 0; memset(&conn->output.make, 0, sizeof(conn->output.make)); memset(&conn->output.model, 0, sizeof(conn->output.model)); memset(&conn->output.serial, 0, sizeof(conn->output.serial)); if (conn->output.idle_frame != NULL) { wl_event_source_remove(conn->output.idle_frame); conn->output.idle_frame = NULL; } conn->output.needs_frame = false; conn->output.frame_pending = false; wlr_buffer_unref(conn->pending_buffer); wlr_buffer_unref(conn->current_buffer); conn->pending_buffer = conn->current_buffer = NULL; /* Fallthrough */ case WLR_DRM_CONN_NEEDS_MODESET: wlr_log(WLR_INFO, "Emitting destruction signal for '%s'", conn->output.name); dealloc_crtc(conn); conn->possible_crtc = 0; conn->desired_mode = NULL; wlr_signal_emit_safe(&conn->output.events.destroy, &conn->output); break; case WLR_DRM_CONN_DISCONNECTED: break; } conn->state = WLR_DRM_CONN_DISCONNECTED; } wlroots-0.10.0/backend/drm/legacy.c000066400000000000000000000047201361211131400170560ustar00rootroot00000000000000#include #include #include #include #include "backend/drm/drm.h" #include "backend/drm/iface.h" #include "backend/drm/util.h" static bool legacy_crtc_pageflip(struct wlr_drm_backend *drm, struct wlr_drm_connector *conn, struct wlr_drm_crtc *crtc, uint32_t fb_id, drmModeModeInfo *mode) { if (mode) { if (drmModeSetCrtc(drm->fd, crtc->id, fb_id, 0, 0, &conn->id, 1, mode)) { wlr_log_errno(WLR_ERROR, "%s: Failed to set CRTC", conn->output.name); return false; } } if (drmModePageFlip(drm->fd, crtc->id, fb_id, DRM_MODE_PAGE_FLIP_EVENT, drm)) { wlr_log_errno(WLR_ERROR, "%s: Failed to page flip", conn->output.name); return false; } return true; } static bool legacy_conn_enable(struct wlr_drm_backend *drm, struct wlr_drm_connector *conn, bool enable) { int ret = drmModeConnectorSetProperty(drm->fd, conn->id, conn->props.dpms, enable ? DRM_MODE_DPMS_ON : DRM_MODE_DPMS_OFF); if (!enable) { drmModeSetCrtc(drm->fd, conn->crtc->id, 0, 0, 0, NULL, 0, NULL); } return ret >= 0; } bool legacy_crtc_set_cursor(struct wlr_drm_backend *drm, struct wlr_drm_crtc *crtc, struct gbm_bo *bo) { if (!crtc || !crtc->cursor) { return true; } if (!bo) { if (drmModeSetCursor(drm->fd, crtc->id, 0, 0, 0)) { wlr_log_errno(WLR_DEBUG, "Failed to clear hardware cursor"); return false; } return true; } struct wlr_drm_plane *plane = crtc->cursor; if (drmModeSetCursor(drm->fd, crtc->id, gbm_bo_get_handle(bo).u32, plane->surf.width, plane->surf.height)) { wlr_log_errno(WLR_DEBUG, "Failed to set hardware cursor"); return false; } return true; } bool legacy_crtc_move_cursor(struct wlr_drm_backend *drm, struct wlr_drm_crtc *crtc, int x, int y) { return !drmModeMoveCursor(drm->fd, crtc->id, x, y); } static bool legacy_crtc_set_gamma(struct wlr_drm_backend *drm, struct wlr_drm_crtc *crtc, size_t size, uint16_t *r, uint16_t *g, uint16_t *b) { return !drmModeCrtcSetGamma(drm->fd, crtc->id, (uint32_t)size, r, g, b); } static size_t legacy_crtc_get_gamma_size(struct wlr_drm_backend *drm, struct wlr_drm_crtc *crtc) { return (size_t)crtc->legacy_crtc->gamma_size; } const struct wlr_drm_interface legacy_iface = { .conn_enable = legacy_conn_enable, .crtc_pageflip = legacy_crtc_pageflip, .crtc_set_cursor = legacy_crtc_set_cursor, .crtc_move_cursor = legacy_crtc_move_cursor, .crtc_set_gamma = legacy_crtc_set_gamma, .crtc_get_gamma_size = legacy_crtc_get_gamma_size, }; wlroots-0.10.0/backend/drm/meson.build000066400000000000000000000001751361211131400176100ustar00rootroot00000000000000wlr_files += files( 'atomic.c', 'backend.c', 'cvt.c', 'drm.c', 'legacy.c', 'properties.c', 'renderer.c', 'util.c', ) wlroots-0.10.0/backend/drm/properties.c000066400000000000000000000100031361211131400177750ustar00rootroot00000000000000#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) }, { "link-status", INDEX(link_status) }, #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) }, { "rotation", INDEX(rotation) }, { "scaling mode", INDEX(scaling_mode) }, #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_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) }, { "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; } wlroots-0.10.0/backend/drm/renderer.c000066400000000000000000000177201361211131400174240ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include "backend/drm/drm.h" bool init_drm_renderer(struct wlr_drm_backend *drm, struct wlr_drm_renderer *renderer, wlr_renderer_create_func_t create_renderer_func) { renderer->gbm = gbm_create_device(drm->fd); if (!renderer->gbm) { wlr_log(WLR_ERROR, "Failed to create GBM device"); return false; } if (!create_renderer_func) { create_renderer_func = wlr_renderer_autocreate; } static EGLint config_attribs[] = { EGL_RED_SIZE, 1, EGL_GREEN_SIZE, 1, EGL_BLUE_SIZE, 1, EGL_ALPHA_SIZE, 1, EGL_NONE, }; renderer->gbm_format = GBM_FORMAT_ARGB8888; renderer->wlr_rend = create_renderer_func(&renderer->egl, EGL_PLATFORM_GBM_MESA, renderer->gbm, config_attribs, renderer->gbm_format); if (!renderer->wlr_rend) { wlr_log(WLR_ERROR, "Failed to create EGL/WLR renderer"); goto error_gbm; } renderer->fd = drm->fd; return true; error_gbm: gbm_device_destroy(renderer->gbm); return false; } void finish_drm_renderer(struct wlr_drm_renderer *renderer) { if (!renderer) { return; } wlr_renderer_destroy(renderer->wlr_rend); wlr_egl_finish(&renderer->egl); gbm_device_destroy(renderer->gbm); } bool init_drm_surface(struct wlr_drm_surface *surf, struct wlr_drm_renderer *renderer, uint32_t width, uint32_t height, uint32_t format, struct wlr_drm_format_set *set, uint32_t flags) { if (surf->width == width && surf->height == height) { return true; } surf->renderer = renderer; surf->width = width; surf->height = height; if (surf->gbm) { if (surf->front) { gbm_surface_release_buffer(surf->gbm, surf->front); surf->front = NULL; } if (surf->back) { gbm_surface_release_buffer(surf->gbm, surf->back); surf->back = NULL; } gbm_surface_destroy(surf->gbm); surf->gbm = NULL; } wlr_egl_destroy_surface(&surf->renderer->egl, surf->egl); if (!(flags & GBM_BO_USE_LINEAR) && set != NULL) { const struct wlr_drm_format *drm_format = wlr_drm_format_set_get(set, format); if (drm_format != NULL) { surf->gbm = gbm_surface_create_with_modifiers(renderer->gbm, width, height, format, drm_format->modifiers, drm_format->len); } } if (surf->gbm == NULL) { surf->gbm = gbm_surface_create(renderer->gbm, width, height, format, GBM_BO_USE_RENDERING | flags); } if (!surf->gbm) { wlr_log_errno(WLR_ERROR, "Failed to create GBM surface"); goto error_zero; } surf->egl = wlr_egl_create_surface(&renderer->egl, surf->gbm); if (surf->egl == EGL_NO_SURFACE) { wlr_log(WLR_ERROR, "Failed to create EGL surface"); goto error_gbm; } return true; error_gbm: gbm_surface_destroy(surf->gbm); error_zero: memset(surf, 0, sizeof(*surf)); return false; } void finish_drm_surface(struct wlr_drm_surface *surf) { if (!surf || !surf->renderer) { return; } if (surf->front) { gbm_surface_release_buffer(surf->gbm, surf->front); } if (surf->back) { gbm_surface_release_buffer(surf->gbm, surf->back); } wlr_egl_destroy_surface(&surf->renderer->egl, surf->egl); if (surf->gbm) { gbm_surface_destroy(surf->gbm); } memset(surf, 0, sizeof(*surf)); } bool make_drm_surface_current(struct wlr_drm_surface *surf, int *buffer_damage) { return wlr_egl_make_current(&surf->renderer->egl, surf->egl, buffer_damage); } struct gbm_bo *swap_drm_surface_buffers(struct wlr_drm_surface *surf, pixman_region32_t *damage) { if (surf->front) { gbm_surface_release_buffer(surf->gbm, surf->front); } wlr_egl_swap_buffers(&surf->renderer->egl, surf->egl, damage); surf->front = surf->back; surf->back = gbm_surface_lock_front_buffer(surf->gbm); return surf->back; } struct gbm_bo *get_drm_surface_front(struct wlr_drm_surface *surf) { if (surf->front) { return surf->front; } make_drm_surface_current(surf, NULL); struct wlr_renderer *renderer = surf->renderer->wlr_rend; wlr_renderer_begin(renderer, surf->width, surf->height); wlr_renderer_clear(renderer, (float[]){ 0.0, 0.0, 0.0, 1.0 }); wlr_renderer_end(renderer); return swap_drm_surface_buffers(surf, NULL); } void post_drm_surface(struct wlr_drm_surface *surf) { if (surf->front) { gbm_surface_release_buffer(surf->gbm, surf->front); surf->front = NULL; } } struct gbm_bo *import_gbm_bo(struct wlr_drm_renderer *renderer, struct wlr_dmabuf_attributes *attribs) { if (attribs->modifier == DRM_FORMAT_MOD_INVALID && attribs->n_planes == 1 && attribs->offset[0] == 0) { struct gbm_import_fd_data data = { .fd = attribs->fd[0], .width = attribs->width, .height = attribs->height, .stride = attribs->stride[0], .format = attribs->format, }; return gbm_bo_import(renderer->gbm, GBM_BO_IMPORT_FD, &data, GBM_BO_USE_SCANOUT); } else { struct gbm_import_fd_modifier_data data = { .width = attribs->width, .height = attribs->height, .format = attribs->format, .num_fds = attribs->n_planes, .modifier = attribs->modifier, }; if ((size_t)attribs->n_planes > sizeof(data.fds) / sizeof(data.fds[0])) { return NULL; } for (size_t i = 0; i < (size_t)attribs->n_planes; ++i) { data.fds[i] = attribs->fd[i]; data.strides[i] = attribs->stride[i]; data.offsets[i] = attribs->offset[i]; } return gbm_bo_import(renderer->gbm, GBM_BO_IMPORT_FD_MODIFIER, &data, GBM_BO_USE_SCANOUT); } } bool export_drm_bo(struct gbm_bo *bo, struct wlr_dmabuf_attributes *attribs) { memset(attribs, 0, sizeof(struct wlr_dmabuf_attributes)); attribs->n_planes = gbm_bo_get_plane_count(bo); if (attribs->n_planes > WLR_DMABUF_MAX_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); for (int i = 0; i < attribs->n_planes; ++i) { attribs->offset[i] = gbm_bo_get_offset(bo, i); attribs->stride[i] = gbm_bo_get_stride_for_plane(bo, i); attribs->fd[i] = gbm_bo_get_fd(bo); if (attribs->fd[i] < 0) { for (int j = 0; j < i; ++j) { close(attribs->fd[j]); } return false; } } return true; } static void free_tex(struct gbm_bo *bo, void *data) { struct wlr_texture *tex = data; wlr_texture_destroy(tex); } static struct wlr_texture *get_tex_for_bo(struct wlr_drm_renderer *renderer, struct gbm_bo *bo) { struct wlr_texture *tex = gbm_bo_get_user_data(bo); if (tex) { return tex; } struct wlr_dmabuf_attributes attribs; if (!export_drm_bo(bo, &attribs)) { return NULL; } tex = wlr_texture_from_dmabuf(renderer->wlr_rend, &attribs); if (tex) { gbm_bo_set_user_data(bo, tex, free_tex); } return tex; } struct gbm_bo *copy_drm_surface_mgpu(struct wlr_drm_surface *dest, struct gbm_bo *src) { make_drm_surface_current(dest, NULL); struct wlr_texture *tex = get_tex_for_bo(dest->renderer, src); assert(tex); float mat[9]; wlr_matrix_projection(mat, 1, 1, WL_OUTPUT_TRANSFORM_NORMAL); struct wlr_renderer *renderer = dest->renderer->wlr_rend; wlr_renderer_begin(renderer, dest->width, dest->height); wlr_renderer_clear(renderer, (float[]){ 0.0, 0.0, 0.0, 0.0 }); wlr_render_texture_with_matrix(renderer, tex, mat, 1.0f); wlr_renderer_end(renderer); return swap_drm_surface_buffers(dest, NULL); } bool init_drm_plane_surfaces(struct wlr_drm_plane *plane, struct wlr_drm_backend *drm, int32_t width, uint32_t height, uint32_t format, bool with_modifiers) { struct wlr_drm_format_set *format_set = with_modifiers ? &plane->formats : NULL; if (!drm->parent) { return init_drm_surface(&plane->surf, &drm->renderer, width, height, format, format_set, GBM_BO_USE_SCANOUT); } if (!init_drm_surface(&plane->surf, &drm->parent->renderer, width, height, format, NULL, GBM_BO_USE_LINEAR)) { return false; } if (!init_drm_surface(&plane->mgpu_surf, &drm->renderer, width, height, format, format_set, GBM_BO_USE_SCANOUT)) { finish_drm_surface(&plane->surf); return false; } return true; } wlroots-0.10.0/backend/drm/util.c000066400000000000000000000272241361211131400165730ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #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; } // Constructed from http://edid.tv/manufacturer static const char *get_manufacturer(uint16_t id) { #define ID(a, b, c) ((a & 0x1f) << 10) | ((b & 0x1f) << 5) | (c & 0x1f) switch (id) { case ID('A', 'A', 'A'): return "Avolites Ltd"; case ID('A', 'C', 'I'): return "Ancor Communications Inc"; case ID('A', 'C', 'R'): return "Acer Technologies"; case ID('A', 'D', 'A'): return "Addi-Data GmbH"; case ID('A', 'P', 'P'): return "Apple Computer Inc"; case ID('A', 'S', 'K'): return "Ask A/S"; case ID('A', 'V', 'T'): return "Avtek (Electronics) Pty Ltd"; case ID('B', 'N', 'O'): return "Bang & Olufsen"; case ID('B', 'N', 'Q'): return "BenQ Corporation"; case ID('C', 'M', 'N'): return "Chimei Innolux Corporation"; case ID('C', 'M', 'O'): return "Chi Mei Optoelectronics corp."; case ID('C', 'R', 'O'): return "Extraordinary Technologies PTY Limited"; case ID('D', 'E', 'L'): return "Dell Inc."; case ID('D', 'G', 'C'): return "Data General Corporation"; case ID('D', 'O', 'N'): return "DENON, Ltd."; case ID('E', 'N', 'C'): return "Eizo Nanao Corporation"; case ID('E', 'P', 'H'): return "Epiphan Systems Inc."; case ID('E', 'X', 'P'): return "Data Export Corporation"; case ID('F', 'N', 'I'): return "Funai Electric Co., Ltd."; case ID('F', 'U', 'S'): return "Fujitsu Siemens Computers GmbH"; case ID('G', 'S', 'M'): return "Goldstar Company Ltd"; case ID('H', 'I', 'Q'): return "Kaohsiung Opto Electronics Americas, Inc."; case ID('H', 'S', 'D'): return "HannStar Display Corp"; case ID('H', 'T', 'C'): return "Hitachi Ltd"; case ID('H', 'W', 'P'): return "Hewlett Packard"; case ID('I', 'N', 'T'): return "Interphase Corporation"; case ID('I', 'N', 'X'): return "Communications Supply Corporation (A division of WESCO)"; case ID('I', 'T', 'E'): return "Integrated Tech Express Inc"; case ID('I', 'V', 'M'): return "Iiyama North America"; case ID('L', 'E', 'N'): return "Lenovo Group Limited"; case ID('M', 'A', 'X'): return "Rogen Tech Distribution Inc"; case ID('M', 'E', 'G'): return "Abeam Tech Ltd"; case ID('M', 'E', 'I'): return "Panasonic Industry Company"; case ID('M', 'T', 'C'): return "Mars-Tech Corporation"; case ID('M', 'T', 'X'): return "Matrox"; case ID('N', 'E', 'C'): return "NEC Corporation"; case ID('N', 'E', 'X'): return "Nexgen Mediatech Inc."; case ID('O', 'N', 'K'): return "ONKYO Corporation"; case ID('O', 'R', 'N'): return "ORION ELECTRIC CO., LTD."; case ID('O', 'T', 'M'): return "Optoma Corporation"; case ID('O', 'V', 'R'): return "Oculus VR, Inc."; case ID('P', 'H', 'L'): return "Philips Consumer Electronics Company"; case ID('P', 'I', 'O'): return "Pioneer Electronic Corporation"; case ID('P', 'N', 'R'): return "Planar Systems, Inc."; case ID('Q', 'D', 'S'): return "Quanta Display Inc."; case ID('R', 'A', 'T'): return "Rent-A-Tech"; case ID('R', 'E', 'N'): return "Renesas Technology Corp."; case ID('S', 'A', 'M'): return "Samsung Electric Company"; case ID('S', 'A', 'N'): return "Sanyo Electric Co., Ltd."; case ID('S', 'E', 'C'): return "Seiko Epson Corporation"; case ID('S', 'H', 'P'): return "Sharp Corporation"; case ID('S', 'I', 'I'): return "Silicon Image, Inc."; case ID('S', 'N', 'Y'): return "Sony"; case ID('S', 'T', 'D'): return "STD Computer Inc"; case ID('S', 'V', 'S'): return "SVSI"; case ID('S', 'Y', 'N'): return "Synaptics Inc"; case ID('T', 'C', 'L'): return "Technical Concepts Ltd"; case ID('T', 'O', 'P'): return "Orion Communications Co., Ltd."; case ID('T', 'S', 'B'): return "Toshiba America Info Systems Inc"; case ID('T', 'S', 'T'): return "Transtream Inc"; case ID('U', 'N', 'K'): return "Unknown"; case ID('V', 'E', 'S'): return "Vestel Elektronik Sanayi ve Ticaret A. S."; case ID('V', 'I', 'T'): return "Visitech AS"; case ID('V', 'I', 'Z'): return "VIZIO, Inc"; case ID('V', 'S', 'C'): return "ViewSonic Corporation"; case ID('Y', 'M', 'H'): return "Yamaha Corporation"; default: return "Unknown"; } #undef ID } /* See https://en.wikipedia.org/wiki/Extended_Display_Identification_Data for layout of EDID data. * We don't parse the EDID properly. We just expect to receive valid data. */ void parse_edid(struct wlr_output *restrict output, size_t len, const uint8_t *data) { if (!data || len < 128) { snprintf(output->make, sizeof(output->make), ""); snprintf(output->model, sizeof(output->model), ""); return; } uint16_t id = (data[8] << 8) | data[9]; snprintf(output->make, sizeof(output->make), "%s", get_manufacturer(id)); uint16_t model = data[10] | (data[11] << 8); snprintf(output->model, sizeof(output->model), "0x%04X", model); uint32_t serial = data[12] | (data[13] << 8) | (data[14] << 8) | (data[15] << 8); snprintf(output->serial, sizeof(output->serial), "0x%08X", serial); for (size_t i = 72; i <= 108; i += 18) { uint16_t flag = (data[i] << 8) | data[i + 1]; if (flag == 0 && data[i + 3] == 0xFC) { sprintf(output->model, "%.13s", &data[i + 5]); // Monitor names are terminated by newline if they're too short char *nl = strchr(output->model, '\n'); if (nl) { *nl = '\0'; } } else if (flag == 0 && data[i + 3] == 0xFF) { sprintf(output->serial, "%.13s", &data[i + 5]); // Monitor serial numbers are terminated by newline if they're too // short char *nl = strchr(output->serial, '\n'); if (nl) { *nl = '\0'; } } } } const char *conn_get_name(uint32_t type_id) { switch (type_id) { case DRM_MODE_CONNECTOR_Unknown: return "Unknown"; case DRM_MODE_CONNECTOR_VGA: return "VGA"; case DRM_MODE_CONNECTOR_DVII: return "DVI-I"; case DRM_MODE_CONNECTOR_DVID: return "DVI-D"; case DRM_MODE_CONNECTOR_DVIA: return "DVI-A"; case DRM_MODE_CONNECTOR_Composite: return "Composite"; case DRM_MODE_CONNECTOR_SVIDEO: return "SVIDEO"; case DRM_MODE_CONNECTOR_LVDS: return "LVDS"; case DRM_MODE_CONNECTOR_Component: return "Component"; case DRM_MODE_CONNECTOR_9PinDIN: return "DIN"; case DRM_MODE_CONNECTOR_DisplayPort: return "DP"; case DRM_MODE_CONNECTOR_HDMIA: return "HDMI-A"; case DRM_MODE_CONNECTOR_HDMIB: return "HDMI-B"; case DRM_MODE_CONNECTOR_TV: return "TV"; case DRM_MODE_CONNECTOR_eDP: return "eDP"; case DRM_MODE_CONNECTOR_VIRTUAL: return "Virtual"; case DRM_MODE_CONNECTOR_DSI: return "DSI"; #ifdef DRM_MODE_CONNECTOR_DPI case DRM_MODE_CONNECTOR_DPI: return "DPI"; #endif default: return "Unknown"; } } static void free_fb(struct gbm_bo *bo, void *data) { uint32_t id = (uintptr_t)data; if (id) { struct gbm_device *gbm = gbm_bo_get_device(bo); drmModeRmFB(gbm_device_get_fd(gbm), id); } } uint32_t get_fb_for_bo(struct gbm_bo *bo, uint32_t drm_format, bool with_modifiers) { uint32_t id = (uintptr_t)gbm_bo_get_user_data(bo); if (id) { return id; } struct gbm_device *gbm = gbm_bo_get_device(bo); int fd = gbm_device_get_fd(gbm); uint32_t width = gbm_bo_get_width(bo); uint32_t height = gbm_bo_get_height(bo); uint32_t handles[4] = {0}; uint32_t strides[4] = {0}; uint32_t offsets[4] = {0}; uint64_t modifiers[4] = {0}; for (int i = 0; i < gbm_bo_get_plane_count(bo); i++) { handles[i] = gbm_bo_get_handle_for_plane(bo, i).u32; strides[i] = gbm_bo_get_stride_for_plane(bo, i); offsets[i] = gbm_bo_get_offset(bo, i); // KMS requires all BO planes to have the same modifier modifiers[i] = gbm_bo_get_modifier(bo); } if (with_modifiers && gbm_bo_get_modifier(bo) != DRM_FORMAT_MOD_INVALID) { if (drmModeAddFB2WithModifiers(fd, width, height, drm_format, handles, strides, offsets, modifiers, &id, DRM_MODE_FB_MODIFIERS)) { wlr_log_errno(WLR_ERROR, "Unable to add DRM framebuffer"); } } else { if (drmModeAddFB2(fd, width, height, drm_format, handles, strides, offsets, &id, 0)) { wlr_log_errno(WLR_ERROR, "Unable to add DRM framebuffer"); } } gbm_bo_set_user_data(bo, (void *)(uintptr_t)id, free_fb); return id; } static inline 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; } wlroots-0.10.0/backend/headless/000077500000000000000000000000001361211131400164515ustar00rootroot00000000000000wlroots-0.10.0/backend/headless/backend.c000066400000000000000000000075151361211131400202140ustar00rootroot00000000000000#include #include #include #include #include #include #include #include "backend/headless.h" #include "util/signal.h" struct wlr_headless_backend *headless_backend_from_backend( struct wlr_backend *wlr_backend) { assert(wlr_backend_is_headless(wlr_backend)); return (struct wlr_headless_backend *)wlr_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_event_source_timer_update(output->frame_timer, output->frame_delay); wlr_output_update_enabled(&output->wlr_output, true); wlr_signal_emit_safe(&backend->backend.events.new_output, &output->wlr_output); } struct wlr_headless_input_device *input_device; wl_list_for_each(input_device, &backend->input_devices, wlr_input_device.link) { wlr_signal_emit_safe(&backend->backend.events.new_input, &input_device->wlr_input_device); } 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; } wl_list_remove(&backend->display_destroy.link); struct wlr_headless_output *output, *output_tmp; wl_list_for_each_safe(output, output_tmp, &backend->outputs, link) { wlr_output_destroy(&output->wlr_output); } struct wlr_headless_input_device *input_device, *input_device_tmp; wl_list_for_each_safe(input_device, input_device_tmp, &backend->input_devices, wlr_input_device.link) { wlr_input_device_destroy(&input_device->wlr_input_device); } wlr_signal_emit_safe(&wlr_backend->events.destroy, backend); wlr_renderer_destroy(backend->renderer); wlr_egl_finish(&backend->egl); free(backend); } static struct wlr_renderer *backend_get_renderer( struct wlr_backend *wlr_backend) { struct wlr_headless_backend *backend = headless_backend_from_backend(wlr_backend); return backend->renderer; } static const struct wlr_backend_impl backend_impl = { .start = backend_start, .destroy = backend_destroy, .get_renderer = backend_get_renderer, }; 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_renderer_create_func_t create_renderer_func) { wlr_log(WLR_INFO, "Creating headless backend"); struct wlr_headless_backend *backend = calloc(1, sizeof(struct wlr_headless_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); wl_list_init(&backend->input_devices); static const EGLint config_attribs[] = { EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, EGL_ALPHA_SIZE, 0, EGL_BLUE_SIZE, 1, EGL_GREEN_SIZE, 1, EGL_RED_SIZE, 1, EGL_NONE, }; if (!create_renderer_func) { create_renderer_func = wlr_renderer_autocreate; } backend->renderer = create_renderer_func(&backend->egl, EGL_PLATFORM_SURFACELESS_MESA, EGL_DEFAULT_DISPLAY, (EGLint*)config_attribs, 0); if (!backend->renderer) { wlr_log(WLR_ERROR, "Failed to create renderer"); free(backend); return NULL; } 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.10.0/backend/headless/input_device.c000066400000000000000000000057461361211131400213070ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include "backend/headless.h" #include "util/signal.h" static const struct wlr_input_device_impl input_device_impl = { 0 }; bool wlr_input_device_is_headless(struct wlr_input_device *wlr_dev) { return wlr_dev->impl == &input_device_impl; } struct wlr_input_device *wlr_headless_add_input_device( struct wlr_backend *wlr_backend, enum wlr_input_device_type type) { struct wlr_headless_backend *backend = headless_backend_from_backend(wlr_backend); struct wlr_headless_input_device *device = calloc(1, sizeof(struct wlr_headless_input_device)); if (device == NULL) { return NULL; } device->backend = backend; int vendor = 0; int product = 0; const char *name = "headless"; struct wlr_input_device *wlr_device = &device->wlr_input_device; wlr_input_device_init(wlr_device, type, &input_device_impl, name, vendor, product); switch (type) { case WLR_INPUT_DEVICE_KEYBOARD: wlr_device->keyboard = calloc(1, sizeof(struct wlr_keyboard)); if (wlr_device->keyboard == NULL) { wlr_log(WLR_ERROR, "Unable to allocate wlr_keyboard"); goto error; } wlr_keyboard_init(wlr_device->keyboard, NULL); break; case WLR_INPUT_DEVICE_POINTER: wlr_device->pointer = calloc(1, sizeof(struct wlr_pointer)); if (wlr_device->pointer == NULL) { wlr_log(WLR_ERROR, "Unable to allocate wlr_pointer"); goto error; } wlr_pointer_init(wlr_device->pointer, NULL); break; case WLR_INPUT_DEVICE_TOUCH: wlr_device->touch = calloc(1, sizeof(struct wlr_touch)); if (wlr_device->touch == NULL) { wlr_log(WLR_ERROR, "Unable to allocate wlr_touch"); goto error; } wlr_touch_init(wlr_device->touch, NULL); break; case WLR_INPUT_DEVICE_TABLET_TOOL: wlr_device->tablet = calloc(1, sizeof(struct wlr_tablet)); if (wlr_device->tablet == NULL) { wlr_log(WLR_ERROR, "Unable to allocate wlr_tablet"); goto error; } wlr_tablet_init(wlr_device->tablet, NULL); break; case WLR_INPUT_DEVICE_TABLET_PAD: wlr_device->tablet_pad = calloc(1, sizeof(struct wlr_tablet_pad)); if (wlr_device->tablet_pad == NULL) { wlr_log(WLR_ERROR, "Unable to allocate wlr_tablet_pad"); goto error; } wlr_tablet_pad_init(wlr_device->tablet_pad, NULL); break; case WLR_INPUT_DEVICE_SWITCH: wlr_device->switch_device = calloc(1, sizeof(struct wlr_switch)); if (wlr_device->switch_device == NULL) { wlr_log(WLR_ERROR, "Unable to allocate wlr_switch"); goto error; } wlr_switch_init(wlr_device->switch_device, NULL); } wl_list_insert(&backend->input_devices, &wlr_device->link); if (backend->started) { wlr_signal_emit_safe(&backend->backend.events.new_input, wlr_device); } return wlr_device; error: free(device); return NULL; } wlroots-0.10.0/backend/headless/meson.build000066400000000000000000000001041361211131400206060ustar00rootroot00000000000000wlr_files += files( 'backend.c', 'input_device.c', 'output.c', ) wlroots-0.10.0/backend/headless/output.c000066400000000000000000000122761361211131400201650ustar00rootroot00000000000000#include #include #include #include #include #include "backend/headless.h" #include "util/signal.h" static struct wlr_headless_output *headless_output_from_output( struct wlr_output *wlr_output) { assert(wlr_output_is_headless(wlr_output)); return (struct wlr_headless_output *)wlr_output; } static EGLSurface egl_create_surface(struct wlr_egl *egl, unsigned int width, unsigned int height) { EGLint attribs[] = {EGL_WIDTH, width, EGL_HEIGHT, height, EGL_NONE}; EGLSurface surf = eglCreatePbufferSurface(egl->display, egl->config, attribs); if (surf == EGL_NO_SURFACE) { wlr_log(WLR_ERROR, "Failed to create EGL surface"); return EGL_NO_SURFACE; } return surf; } static bool output_set_custom_mode(struct wlr_output *wlr_output, int32_t width, int32_t height, int32_t refresh) { struct wlr_headless_output *output = headless_output_from_output(wlr_output); struct wlr_headless_backend *backend = output->backend; if (refresh <= 0) { refresh = HEADLESS_DEFAULT_REFRESH; } wlr_egl_destroy_surface(&backend->egl, output->egl_surface); output->egl_surface = egl_create_surface(&backend->egl, width, height); if (output->egl_surface == EGL_NO_SURFACE) { wlr_log(WLR_ERROR, "Failed to recreate EGL surface"); wlr_output_destroy(wlr_output); return false; } output->frame_delay = 1000000 / refresh; wlr_output_update_custom_mode(&output->wlr_output, width, height, refresh); return true; } static bool output_attach_render(struct wlr_output *wlr_output, int *buffer_age) { struct wlr_headless_output *output = headless_output_from_output(wlr_output); return wlr_egl_make_current(&output->backend->egl, output->egl_surface, buffer_age); } static bool output_commit(struct wlr_output *wlr_output) { if (wlr_output->pending.committed & WLR_OUTPUT_STATE_ENABLED) { wlr_log(WLR_DEBUG, "Cannot disable a headless output"); return false; } if (wlr_output->pending.committed & WLR_OUTPUT_STATE_MODE) { assert(wlr_output->pending.mode_type == WLR_OUTPUT_STATE_MODE_CUSTOM); if (!output_set_custom_mode(wlr_output, wlr_output->pending.custom_mode.width, wlr_output->pending.custom_mode.height, wlr_output->pending.custom_mode.refresh)) { return false; } } if (wlr_output->pending.committed & WLR_OUTPUT_STATE_BUFFER) { // Nothing needs to be done for pbuffers wlr_output_send_present(wlr_output, NULL); } 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); wlr_egl_destroy_surface(&output->backend->egl, output->egl_surface); free(output); } static const struct wlr_output_impl output_impl = { .destroy = output_destroy, .attach_render = output_attach_render, .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); wl_event_source_timer_update(output->frame_timer, output->frame_delay); 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(struct wlr_headless_output)); if (output == NULL) { wlr_log(WLR_ERROR, "Failed to allocate wlr_headless_output"); return NULL; } output->backend = backend; wlr_output_init(&output->wlr_output, &backend->backend, &output_impl, backend->display); struct wlr_output *wlr_output = &output->wlr_output; output->egl_surface = egl_create_surface(&backend->egl, width, height); if (output->egl_surface == EGL_NO_SURFACE) { wlr_log(WLR_ERROR, "Failed to create EGL surface"); goto error; } output_set_custom_mode(wlr_output, width, height, 0); strncpy(wlr_output->make, "headless", sizeof(wlr_output->make)); strncpy(wlr_output->model, "headless", sizeof(wlr_output->model)); snprintf(wlr_output->name, sizeof(wlr_output->name), "HEADLESS-%zd", ++backend->last_output_num); char description[128]; snprintf(description, sizeof(description), "Headless output %zd", backend->last_output_num); wlr_output_set_description(wlr_output, description); if (!wlr_egl_make_current(&output->backend->egl, output->egl_surface, NULL)) { goto error; } wlr_renderer_begin(backend->renderer, wlr_output->width, wlr_output->height); wlr_renderer_clear(backend->renderer, (float[]){ 1.0, 1.0, 1.0, 1.0 }); wlr_renderer_end(backend->renderer); 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_event_source_timer_update(output->frame_timer, output->frame_delay); wlr_output_update_enabled(wlr_output, true); wlr_signal_emit_safe(&backend->backend.events.new_output, wlr_output); } return wlr_output; error: wlr_output_destroy(&output->wlr_output); return NULL; } wlroots-0.10.0/backend/libinput/000077500000000000000000000000001361211131400165075ustar00rootroot00000000000000wlroots-0.10.0/backend/libinput/backend.c000066400000000000000000000146631361211131400202540ustar00rootroot00000000000000#include #include #include #include #include #include #include "backend/libinput.h" #include "util/signal.h" static struct wlr_libinput_backend *get_libinput_backend_from_backend( struct wlr_backend *wlr_backend) { assert(wlr_backend_is_libinput(wlr_backend)); return (struct wlr_libinput_backend *)wlr_backend; } static int libinput_open_restricted(const char *path, int flags, void *_backend) { struct wlr_libinput_backend *backend = _backend; return wlr_session_open_file(backend->session, path); } static void libinput_close_restricted(int fd, void *_backend) { struct wlr_libinput_backend *backend = _backend; wlr_session_close_file(backend->session, fd); } 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; if (libinput_dispatch(backend->libinput_context) != 0) { wlr_log(WLR_ERROR, "Failed to dispatch libinput"); // TODO: some kind of abort? 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 void log_libinput(struct libinput *libinput_context, enum libinput_log_priority priority, const char *fmt, va_list args) { _wlr_vlog(WLR_ERROR, 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, "Initializing libinput"); 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); char *no_devs = getenv("WLR_LIBINPUT_NO_DEVICES"); if (no_devs) { if (strcmp(no_devs, "1") != 0) { no_devs = NULL; } } if (!no_devs && backend->wlr_device_lists.length == 0) { handle_libinput_readable(libinput_fd, WL_EVENT_READABLE, backend); if (backend->wlr_device_lists.length == 0) { 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); for (size_t i = 0; i < backend->wlr_device_lists.length; i++) { struct wl_list *wlr_devices = backend->wlr_device_lists.items[i]; struct wlr_input_device *wlr_dev, *next; wl_list_for_each_safe(wlr_dev, next, wlr_devices, link) { wlr_input_device_destroy(wlr_dev); } free(wlr_devices); } wlr_signal_emit_safe(&wlr_backend->events.destroy, wlr_backend); wl_list_remove(&backend->display_destroy.link); wl_list_remove(&backend->session_destroy.link); wl_list_remove(&backend->session_signal.link); wlr_list_finish(&backend->wlr_device_lists); 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 = data; 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(struct wlr_libinput_backend)); if (!backend) { wlr_log(WLR_ERROR, "Allocation failed: %s", strerror(errno)); return NULL; } wlr_backend_init(&backend->backend, &backend_impl); if (!wlr_list_init(&backend->wlr_device_lists)) { wlr_log(WLR_ERROR, "Allocation failed: %s", strerror(errno)); goto error_backend; } backend->session = session; backend->display = display; backend->session_signal.notify = session_signal; wl_signal_add(&session->session_signal, &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; error_backend: free(backend); return NULL; } struct libinput_device *wlr_libinput_get_device_handle( struct wlr_input_device *wlr_dev) { struct wlr_libinput_input_device *dev = (struct wlr_libinput_input_device *)wlr_dev; return dev->handle; } uint32_t usec_to_msec(uint64_t usec) { return (uint32_t)(usec / 1000); } wlroots-0.10.0/backend/libinput/events.c000066400000000000000000000233351361211131400201650ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include "backend/libinput.h" #include "util/signal.h" static struct wlr_libinput_input_device *get_libinput_device_from_device( struct wlr_input_device *wlr_dev) { assert(wlr_input_device_is_libinput(wlr_dev)); return (struct wlr_libinput_input_device *)wlr_dev; } struct wlr_input_device *get_appropriate_device( enum wlr_input_device_type desired_type, struct libinput_device *libinput_dev) { struct wl_list *wlr_devices = libinput_device_get_user_data(libinput_dev); if (!wlr_devices) { return NULL; } struct wlr_input_device *dev; wl_list_for_each(dev, wlr_devices, link) { if (dev->type == desired_type) { return dev; } } return NULL; } static void input_device_destroy(struct wlr_input_device *wlr_dev) { struct wlr_libinput_input_device *dev = get_libinput_device_from_device(wlr_dev); libinput_device_unref(dev->handle); wl_list_remove(&dev->wlr_input_device.link); free(dev); } static const struct wlr_input_device_impl input_device_impl = { .destroy = input_device_destroy, }; static struct wlr_input_device *allocate_device( struct wlr_libinput_backend *backend, struct libinput_device *libinput_dev, struct wl_list *wlr_devices, enum wlr_input_device_type type) { 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); struct wlr_libinput_input_device *dev = calloc(1, sizeof(struct wlr_libinput_input_device)); if (dev == NULL) { return NULL; } struct wlr_input_device *wlr_dev = &dev->wlr_input_device; libinput_device_get_size(libinput_dev, &wlr_dev->width_mm, &wlr_dev->height_mm); const char *output_name = libinput_device_get_output_name(libinput_dev); if (output_name != NULL) { wlr_dev->output_name = strdup(output_name); } wl_list_insert(wlr_devices, &wlr_dev->link); dev->handle = libinput_dev; libinput_device_ref(libinput_dev); wlr_input_device_init(wlr_dev, type, &input_device_impl, name, vendor, product); return wlr_dev; } bool wlr_input_device_is_libinput(struct wlr_input_device *wlr_dev) { return wlr_dev->impl == &input_device_impl; } static void handle_device_added(struct wlr_libinput_backend *backend, struct libinput_device *libinput_dev) { /* * Note: the wlr API exposes only devices with a single capability, because * that meshes better with how Wayland does things and is a bit simpler. * However, libinput devices often have multiple capabilities - in such * cases we have to create several devices. */ 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); struct wl_list *wlr_devices = calloc(1, sizeof(struct wl_list)); if (!wlr_devices) { wlr_log(WLR_ERROR, "Allocation failed"); return; } wl_list_init(wlr_devices); wlr_log(WLR_DEBUG, "Added %s [%d:%d]", name, vendor, product); if (libinput_device_has_capability( libinput_dev, LIBINPUT_DEVICE_CAP_KEYBOARD)) { struct wlr_input_device *wlr_dev = allocate_device(backend, libinput_dev, wlr_devices, WLR_INPUT_DEVICE_KEYBOARD); if (!wlr_dev) { goto fail; } wlr_dev->keyboard = create_libinput_keyboard(libinput_dev); if (!wlr_dev->keyboard) { free(wlr_dev); goto fail; } wlr_signal_emit_safe(&backend->backend.events.new_input, wlr_dev); } if (libinput_device_has_capability( libinput_dev, LIBINPUT_DEVICE_CAP_POINTER)) { struct wlr_input_device *wlr_dev = allocate_device(backend, libinput_dev, wlr_devices, WLR_INPUT_DEVICE_POINTER); if (!wlr_dev) { goto fail; } wlr_dev->pointer = create_libinput_pointer(libinput_dev); if (!wlr_dev->pointer) { free(wlr_dev); goto fail; } wlr_signal_emit_safe(&backend->backend.events.new_input, wlr_dev); } if (libinput_device_has_capability( libinput_dev, LIBINPUT_DEVICE_CAP_TOUCH)) { struct wlr_input_device *wlr_dev = allocate_device(backend, libinput_dev, wlr_devices, WLR_INPUT_DEVICE_TOUCH); if (!wlr_dev) { goto fail; } wlr_dev->touch = create_libinput_touch(libinput_dev); if (!wlr_dev->touch) { free(wlr_dev); goto fail; } wlr_signal_emit_safe(&backend->backend.events.new_input, wlr_dev); } if (libinput_device_has_capability(libinput_dev, LIBINPUT_DEVICE_CAP_TABLET_TOOL)) { struct wlr_input_device *wlr_dev = allocate_device(backend, libinput_dev, wlr_devices, WLR_INPUT_DEVICE_TABLET_TOOL); if (!wlr_dev) { goto fail; } wlr_dev->tablet = create_libinput_tablet(libinput_dev); if (!wlr_dev->tablet) { free(wlr_dev); goto fail; } wlr_signal_emit_safe(&backend->backend.events.new_input, wlr_dev); } if (libinput_device_has_capability( libinput_dev, LIBINPUT_DEVICE_CAP_TABLET_PAD)) { struct wlr_input_device *wlr_dev = allocate_device(backend, libinput_dev, wlr_devices, WLR_INPUT_DEVICE_TABLET_PAD); if (!wlr_dev) { goto fail; } wlr_dev->tablet_pad = create_libinput_tablet_pad(libinput_dev); if (!wlr_dev->tablet_pad) { free(wlr_dev); goto fail; } wlr_signal_emit_safe(&backend->backend.events.new_input, wlr_dev); } if (libinput_device_has_capability( libinput_dev, LIBINPUT_DEVICE_CAP_GESTURE)) { // TODO } if (libinput_device_has_capability( libinput_dev, LIBINPUT_DEVICE_CAP_SWITCH)) { struct wlr_input_device *wlr_dev = allocate_device(backend, libinput_dev, wlr_devices, WLR_INPUT_DEVICE_SWITCH); if (!wlr_dev) { goto fail; } wlr_dev->switch_device = create_libinput_switch(libinput_dev); if (!wlr_dev->switch_device) { free(wlr_dev); goto fail; } wlr_signal_emit_safe(&backend->backend.events.new_input, wlr_dev); } if (!wl_list_empty(wlr_devices)) { libinput_device_set_user_data(libinput_dev, wlr_devices); wlr_list_push(&backend->wlr_device_lists, wlr_devices); } else { free(wlr_devices); } return; fail: wlr_log(WLR_ERROR, "Could not allocate new device"); struct wlr_input_device *dev, *tmp_dev; wl_list_for_each_safe(dev, tmp_dev, wlr_devices, link) { free(dev); } free(wlr_devices); } static void handle_device_removed(struct wlr_libinput_backend *backend, struct libinput_device *libinput_dev) { struct wl_list *wlr_devices = libinput_device_get_user_data(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, "Removing %s [%d:%d]", name, vendor, product); if (!wlr_devices) { return; } struct wlr_input_device *dev, *tmp_dev; wl_list_for_each_safe(dev, tmp_dev, wlr_devices, link) { wlr_input_device_destroy(dev); } for (size_t i = 0; i < backend->wlr_device_lists.length; i++) { if (backend->wlr_device_lists.items[i] == wlr_devices) { wlr_list_del(&backend->wlr_device_lists, i); break; } } free(wlr_devices); } void handle_libinput_event(struct wlr_libinput_backend *backend, struct libinput_event *event) { struct libinput_device *libinput_dev = libinput_event_get_device(event); enum libinput_event_type event_type = libinput_event_get_type(event); switch (event_type) { case LIBINPUT_EVENT_DEVICE_ADDED: handle_device_added(backend, libinput_dev); break; case LIBINPUT_EVENT_DEVICE_REMOVED: handle_device_removed(backend, libinput_dev); break; case LIBINPUT_EVENT_KEYBOARD_KEY: handle_keyboard_key(event, libinput_dev); break; case LIBINPUT_EVENT_POINTER_MOTION: handle_pointer_motion(event, libinput_dev); break; case LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE: handle_pointer_motion_abs(event, libinput_dev); break; case LIBINPUT_EVENT_POINTER_BUTTON: handle_pointer_button(event, libinput_dev); break; case LIBINPUT_EVENT_POINTER_AXIS: handle_pointer_axis(event, libinput_dev); break; case LIBINPUT_EVENT_TOUCH_DOWN: handle_touch_down(event, libinput_dev); break; case LIBINPUT_EVENT_TOUCH_UP: handle_touch_up(event, libinput_dev); break; case LIBINPUT_EVENT_TOUCH_MOTION: handle_touch_motion(event, libinput_dev); break; case LIBINPUT_EVENT_TOUCH_CANCEL: handle_touch_cancel(event, libinput_dev); break; case LIBINPUT_EVENT_TOUCH_FRAME: // no-op (at least for now) break; case LIBINPUT_EVENT_TABLET_TOOL_AXIS: handle_tablet_tool_axis(event, libinput_dev); break; case LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY: handle_tablet_tool_proximity(event, libinput_dev); break; case LIBINPUT_EVENT_TABLET_TOOL_TIP: handle_tablet_tool_tip(event, libinput_dev); break; case LIBINPUT_EVENT_TABLET_TOOL_BUTTON: handle_tablet_tool_button(event, libinput_dev); break; case LIBINPUT_EVENT_TABLET_PAD_BUTTON: handle_tablet_pad_button(event, libinput_dev); break; case LIBINPUT_EVENT_TABLET_PAD_RING: handle_tablet_pad_ring(event, libinput_dev); break; case LIBINPUT_EVENT_TABLET_PAD_STRIP: handle_tablet_pad_strip(event, libinput_dev); break; case LIBINPUT_EVENT_SWITCH_TOGGLE: handle_switch_toggle(event, libinput_dev); break; case LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN: handle_pointer_swipe_begin(event, libinput_dev); break; case LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE: handle_pointer_swipe_update(event, libinput_dev); break; case LIBINPUT_EVENT_GESTURE_SWIPE_END: handle_pointer_swipe_end(event, libinput_dev); break; case LIBINPUT_EVENT_GESTURE_PINCH_BEGIN: handle_pointer_pinch_begin(event, libinput_dev); break; case LIBINPUT_EVENT_GESTURE_PINCH_UPDATE: handle_pointer_pinch_update(event, libinput_dev); break; case LIBINPUT_EVENT_GESTURE_PINCH_END: handle_pointer_pinch_end(event, libinput_dev); break; default: wlr_log(WLR_DEBUG, "Unknown libinput event %d", event_type); break; } } wlroots-0.10.0/backend/libinput/keyboard.c000066400000000000000000000046751361211131400204670ustar00rootroot00000000000000#include #include #include #include #include #include #include #include "backend/libinput.h" struct wlr_libinput_keyboard { struct wlr_keyboard wlr_keyboard; struct libinput_device *libinput_dev; }; static const struct wlr_keyboard_impl impl; static struct wlr_libinput_keyboard *get_libinput_keyboard_from_keyboard( struct wlr_keyboard *wlr_kb) { assert(wlr_kb->impl == &impl); return (struct wlr_libinput_keyboard *)wlr_kb; } static void keyboard_set_leds(struct wlr_keyboard *wlr_kb, uint32_t leds) { struct wlr_libinput_keyboard *kb = get_libinput_keyboard_from_keyboard(wlr_kb); libinput_device_led_update(kb->libinput_dev, leds); } static void keyboard_destroy(struct wlr_keyboard *wlr_kb) { struct wlr_libinput_keyboard *kb = get_libinput_keyboard_from_keyboard(wlr_kb); libinput_device_unref(kb->libinput_dev); free(kb); } static const struct wlr_keyboard_impl impl = { .destroy = keyboard_destroy, .led_update = keyboard_set_leds }; struct wlr_keyboard *create_libinput_keyboard( struct libinput_device *libinput_dev) { struct wlr_libinput_keyboard *kb = calloc(1, sizeof(struct wlr_libinput_keyboard)); if (kb == NULL) { return NULL; } kb->libinput_dev = libinput_dev; libinput_device_ref(libinput_dev); libinput_device_led_update(libinput_dev, 0); struct wlr_keyboard *wlr_kb = &kb->wlr_keyboard; wlr_keyboard_init(wlr_kb, &impl); return wlr_kb; } void handle_keyboard_key(struct libinput_event *event, struct libinput_device *libinput_dev) { struct wlr_input_device *wlr_dev = get_appropriate_device(WLR_INPUT_DEVICE_KEYBOARD, libinput_dev); if (!wlr_dev) { wlr_log(WLR_DEBUG, "Got a keyboard event for a device with no keyboards?"); return; } struct libinput_event_keyboard *kbevent = libinput_event_get_keyboard_event(event); struct wlr_event_keyboard_key wlr_event = { 0 }; wlr_event.time_msec = usec_to_msec(libinput_event_keyboard_get_time_usec(kbevent)); wlr_event.keycode = libinput_event_keyboard_get_key(kbevent); enum libinput_key_state state = libinput_event_keyboard_get_key_state(kbevent); switch (state) { case LIBINPUT_KEY_STATE_RELEASED: wlr_event.state = WLR_KEY_RELEASED; break; case LIBINPUT_KEY_STATE_PRESSED: wlr_event.state = WLR_KEY_PRESSED; break; } wlr_event.update_state = true; wlr_keyboard_notify_key(wlr_dev->keyboard, &wlr_event); } wlroots-0.10.0/backend/libinput/meson.build000066400000000000000000000002121361211131400206440ustar00rootroot00000000000000wlr_files += files( 'backend.c', 'events.c', 'keyboard.c', 'pointer.c', 'switch.c', 'tablet_pad.c', 'tablet_tool.c', 'touch.c', ) wlroots-0.10.0/backend/libinput/pointer.c000066400000000000000000000231201361211131400203310ustar00rootroot00000000000000#include #include #include #include #include #include #include #include "backend/libinput.h" #include "util/signal.h" struct wlr_pointer *create_libinput_pointer( struct libinput_device *libinput_dev) { assert(libinput_dev); struct wlr_pointer *wlr_pointer = calloc(1, sizeof(struct wlr_pointer)); if (!wlr_pointer) { wlr_log(WLR_ERROR, "Unable to allocate wlr_pointer"); return NULL; } wlr_pointer_init(wlr_pointer, NULL); return wlr_pointer; } void handle_pointer_motion(struct libinput_event *event, struct libinput_device *libinput_dev) { struct wlr_input_device *wlr_dev = get_appropriate_device(WLR_INPUT_DEVICE_POINTER, libinput_dev); if (!wlr_dev) { wlr_log(WLR_DEBUG, "Got a pointer event for a device with no pointers?"); return; } struct libinput_event_pointer *pevent = libinput_event_get_pointer_event(event); struct wlr_event_pointer_motion wlr_event = { 0 }; wlr_event.device = wlr_dev; wlr_event.time_msec = usec_to_msec(libinput_event_pointer_get_time_usec(pevent)); wlr_event.delta_x = libinput_event_pointer_get_dx(pevent); wlr_event.delta_y = libinput_event_pointer_get_dy(pevent); wlr_event.unaccel_dx = libinput_event_pointer_get_dx_unaccelerated(pevent); wlr_event.unaccel_dy = libinput_event_pointer_get_dy_unaccelerated(pevent); wlr_signal_emit_safe(&wlr_dev->pointer->events.motion, &wlr_event); wlr_signal_emit_safe(&wlr_dev->pointer->events.frame, wlr_dev->pointer); } void handle_pointer_motion_abs(struct libinput_event *event, struct libinput_device *libinput_dev) { struct wlr_input_device *wlr_dev = get_appropriate_device(WLR_INPUT_DEVICE_POINTER, libinput_dev); if (!wlr_dev) { wlr_log(WLR_DEBUG, "Got a pointer event for a device with no pointers?"); return; } struct libinput_event_pointer *pevent = libinput_event_get_pointer_event(event); struct wlr_event_pointer_motion_absolute wlr_event = { 0 }; wlr_event.device = wlr_dev; wlr_event.time_msec = usec_to_msec(libinput_event_pointer_get_time_usec(pevent)); wlr_event.x = libinput_event_pointer_get_absolute_x_transformed(pevent, 1); wlr_event.y = libinput_event_pointer_get_absolute_y_transformed(pevent, 1); wlr_signal_emit_safe(&wlr_dev->pointer->events.motion_absolute, &wlr_event); wlr_signal_emit_safe(&wlr_dev->pointer->events.frame, wlr_dev->pointer); } void handle_pointer_button(struct libinput_event *event, struct libinput_device *libinput_dev) { struct wlr_input_device *wlr_dev = get_appropriate_device(WLR_INPUT_DEVICE_POINTER, libinput_dev); if (!wlr_dev) { wlr_log(WLR_DEBUG, "Got a pointer event for a device with no pointers?"); return; } struct libinput_event_pointer *pevent = libinput_event_get_pointer_event(event); struct wlr_event_pointer_button wlr_event = { 0 }; wlr_event.device = wlr_dev; wlr_event.time_msec = usec_to_msec(libinput_event_pointer_get_time_usec(pevent)); wlr_event.button = libinput_event_pointer_get_button(pevent); switch (libinput_event_pointer_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; } wlr_signal_emit_safe(&wlr_dev->pointer->events.button, &wlr_event); wlr_signal_emit_safe(&wlr_dev->pointer->events.frame, wlr_dev->pointer); } void handle_pointer_axis(struct libinput_event *event, struct libinput_device *libinput_dev) { struct wlr_input_device *wlr_dev = get_appropriate_device(WLR_INPUT_DEVICE_POINTER, libinput_dev); if (!wlr_dev) { wlr_log(WLR_DEBUG, "Got a pointer event for a device with no pointers?"); return; } struct libinput_event_pointer *pevent = libinput_event_get_pointer_event(event); struct wlr_event_pointer_axis wlr_event = { 0 }; wlr_event.device = wlr_dev; wlr_event.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])) { 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_signal_emit_safe(&wlr_dev->pointer->events.axis, &wlr_event); } } wlr_signal_emit_safe(&wlr_dev->pointer->events.frame, wlr_dev->pointer); } void handle_pointer_swipe_begin(struct libinput_event *event, struct libinput_device *libinput_dev) { struct wlr_input_device *wlr_dev = get_appropriate_device(WLR_INPUT_DEVICE_POINTER, libinput_dev); if (!wlr_dev) { wlr_log(WLR_DEBUG, "Got a pointer gesture event for a device with no pointers?"); return; } struct libinput_event_gesture *gevent = libinput_event_get_gesture_event(event); struct wlr_event_pointer_swipe_begin wlr_event = { .device = wlr_dev, .time_msec = usec_to_msec(libinput_event_gesture_get_time_usec(gevent)), .fingers = libinput_event_gesture_get_finger_count(gevent), }; wlr_signal_emit_safe(&wlr_dev->pointer->events.swipe_begin, &wlr_event); } void handle_pointer_swipe_update(struct libinput_event *event, struct libinput_device *libinput_dev) { struct wlr_input_device *wlr_dev = get_appropriate_device(WLR_INPUT_DEVICE_POINTER, libinput_dev); if (!wlr_dev) { wlr_log(WLR_DEBUG, "Got a pointer gesture event for a device with no pointers?"); return; } struct libinput_event_gesture *gevent = libinput_event_get_gesture_event(event); struct wlr_event_pointer_swipe_update wlr_event = { .device = wlr_dev, .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), }; wlr_signal_emit_safe(&wlr_dev->pointer->events.swipe_update, &wlr_event); } void handle_pointer_swipe_end(struct libinput_event *event, struct libinput_device *libinput_dev) { struct wlr_input_device *wlr_dev = get_appropriate_device(WLR_INPUT_DEVICE_POINTER, libinput_dev); if (!wlr_dev) { wlr_log(WLR_DEBUG, "Got a pointer gesture event for a device with no pointers?"); return; } struct libinput_event_gesture *gevent = libinput_event_get_gesture_event(event); struct wlr_event_pointer_swipe_end wlr_event = { .device = wlr_dev, .time_msec = usec_to_msec(libinput_event_gesture_get_time_usec(gevent)), .cancelled = libinput_event_gesture_get_cancelled(gevent), }; wlr_signal_emit_safe(&wlr_dev->pointer->events.swipe_end, &wlr_event); } void handle_pointer_pinch_begin(struct libinput_event *event, struct libinput_device *libinput_dev) { struct wlr_input_device *wlr_dev = get_appropriate_device(WLR_INPUT_DEVICE_POINTER, libinput_dev); if (!wlr_dev) { wlr_log(WLR_DEBUG, "Got a pointer gesture event for a device with no pointers?"); return; } struct libinput_event_gesture *gevent = libinput_event_get_gesture_event(event); struct wlr_event_pointer_pinch_begin wlr_event = { .device = wlr_dev, .time_msec = usec_to_msec(libinput_event_gesture_get_time_usec(gevent)), .fingers = libinput_event_gesture_get_finger_count(gevent), }; wlr_signal_emit_safe(&wlr_dev->pointer->events.pinch_begin, &wlr_event); } void handle_pointer_pinch_update(struct libinput_event *event, struct libinput_device *libinput_dev) { struct wlr_input_device *wlr_dev = get_appropriate_device(WLR_INPUT_DEVICE_POINTER, libinput_dev); if (!wlr_dev) { wlr_log(WLR_DEBUG, "Got a pointer gesture event for a device with no pointers?"); return; } struct libinput_event_gesture *gevent = libinput_event_get_gesture_event(event); struct wlr_event_pointer_pinch_update wlr_event = { .device = wlr_dev, .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), }; wlr_signal_emit_safe(&wlr_dev->pointer->events.pinch_update, &wlr_event); } void handle_pointer_pinch_end(struct libinput_event *event, struct libinput_device *libinput_dev) { struct wlr_input_device *wlr_dev = get_appropriate_device(WLR_INPUT_DEVICE_POINTER, libinput_dev); if (!wlr_dev) { wlr_log(WLR_DEBUG, "Got a pointer gesture event for a device with no pointers?"); return; } struct libinput_event_gesture *gevent = libinput_event_get_gesture_event(event); struct wlr_event_pointer_pinch_end wlr_event = { .device = wlr_dev, .time_msec = usec_to_msec(libinput_event_gesture_get_time_usec(gevent)), .cancelled = libinput_event_gesture_get_cancelled(gevent), }; wlr_signal_emit_safe(&wlr_dev->pointer->events.pinch_end, &wlr_event); } wlroots-0.10.0/backend/libinput/switch.c000066400000000000000000000033721361211131400201610ustar00rootroot00000000000000#include #include #include #include #include #include #include #include "backend/libinput.h" #include "util/signal.h" struct wlr_switch *create_libinput_switch( struct libinput_device *libinput_dev) { assert(libinput_dev); struct wlr_switch *wlr_switch = calloc(1, sizeof(struct wlr_switch)); if (!wlr_switch) { wlr_log(WLR_ERROR, "Unable to allocate wlr_switch"); return NULL; } wlr_switch_init(wlr_switch, NULL); wlr_log(WLR_DEBUG, "Created switch for device %s", libinput_device_get_name(libinput_dev)); return wlr_switch; } void handle_switch_toggle(struct libinput_event *event, struct libinput_device *libinput_dev) { struct wlr_input_device *wlr_dev = get_appropriate_device(WLR_INPUT_DEVICE_SWITCH, libinput_dev); if (!wlr_dev) { wlr_log(WLR_DEBUG, "Got a switch event for a device with no switch?"); return; } struct libinput_event_switch *sevent = libinput_event_get_switch_event (event); struct wlr_event_switch_toggle wlr_event = { 0 }; wlr_event.device = wlr_dev; 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; } wlr_event.time_msec = usec_to_msec(libinput_event_switch_get_time_usec(sevent)); wlr_signal_emit_safe(&wlr_dev->switch_device->events.toggle, &wlr_event); } wlroots-0.10.0/backend/libinput/tablet_pad.c000066400000000000000000000143171361211131400207600ustar00rootroot00000000000000#ifndef _POSIX_C_SOURCE #define _POSIX_C_SOURCE 200809L #endif #include #include #include #include #include #include #include #include #include "backend/libinput.h" #include "util/signal.h" // FIXME: Decide on how to alloc/count here 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(struct wlr_tablet_pad_group)); if (!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); 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); 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); 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); wl_list_insert(&pad->groups, &group->link); } struct wlr_tablet_pad *create_libinput_tablet_pad( struct libinput_device *libinput_dev) { assert(libinput_dev); struct wlr_tablet_pad *wlr_tablet_pad = calloc(1, sizeof(struct wlr_tablet_pad)); if (!wlr_tablet_pad) { wlr_log(WLR_ERROR, "Unable to allocate wlr_tablet_pad"); return NULL; } wlr_tablet_pad_init(wlr_tablet_pad, NULL); wlr_tablet_pad->button_count = libinput_device_tablet_pad_get_num_buttons(libinput_dev); wlr_tablet_pad->ring_count = libinput_device_tablet_pad_get_num_rings(libinput_dev); wlr_tablet_pad->strip_count = libinput_device_tablet_pad_get_num_strips(libinput_dev); struct udev_device *udev = libinput_device_get_udev_device(libinput_dev); wlr_list_push(&wlr_tablet_pad->paths, strdup(udev_device_get_syspath(udev))); int groups = libinput_device_tablet_pad_get_num_mode_groups(libinput_dev); for (int i = 0; i < groups; ++i) { add_pad_group_from_libinput(wlr_tablet_pad, libinput_dev, i); } return wlr_tablet_pad; } void handle_tablet_pad_button(struct libinput_event *event, struct libinput_device *libinput_dev) { struct wlr_input_device *wlr_dev = get_appropriate_device(WLR_INPUT_DEVICE_TABLET_PAD, libinput_dev); if (!wlr_dev) { wlr_log(WLR_DEBUG, "Got a tablet pad event for a device with no tablet pad?"); return; } struct libinput_event_tablet_pad *pevent = libinput_event_get_tablet_pad_event(event); struct wlr_event_tablet_pad_button wlr_event = { 0 }; wlr_event.time_msec = usec_to_msec(libinput_event_tablet_pad_get_time_usec(pevent)); wlr_event.button = libinput_event_tablet_pad_get_button_number(pevent); wlr_event.mode = libinput_event_tablet_pad_get_mode(pevent); wlr_event.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; } wlr_signal_emit_safe(&wlr_dev->tablet_pad->events.button, &wlr_event); } void handle_tablet_pad_ring(struct libinput_event *event, struct libinput_device *libinput_dev) { struct wlr_input_device *wlr_dev = get_appropriate_device(WLR_INPUT_DEVICE_TABLET_PAD, libinput_dev); if (!wlr_dev) { wlr_log(WLR_DEBUG, "Got a tablet pad event for a device with no tablet pad?"); return; } struct libinput_event_tablet_pad *pevent = libinput_event_get_tablet_pad_event(event); struct wlr_event_tablet_pad_ring wlr_event = { 0 }; wlr_event.time_msec = usec_to_msec(libinput_event_tablet_pad_get_time_usec(pevent)); wlr_event.ring = libinput_event_tablet_pad_get_ring_number(pevent); wlr_event.position = libinput_event_tablet_pad_get_ring_position(pevent); wlr_event.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; } wlr_signal_emit_safe(&wlr_dev->tablet_pad->events.ring, &wlr_event); } void handle_tablet_pad_strip(struct libinput_event *event, struct libinput_device *libinput_dev) { struct wlr_input_device *wlr_dev = get_appropriate_device(WLR_INPUT_DEVICE_TABLET_PAD, libinput_dev); if (!wlr_dev) { wlr_log(WLR_DEBUG, "Got a tablet pad event for a device with no tablet pad?"); return; } struct libinput_event_tablet_pad *pevent = libinput_event_get_tablet_pad_event(event); struct wlr_event_tablet_pad_strip wlr_event = { 0 }; wlr_event.time_msec = usec_to_msec(libinput_event_tablet_pad_get_time_usec(pevent)); wlr_event.strip = libinput_event_tablet_pad_get_strip_number(pevent); wlr_event.position = libinput_event_tablet_pad_get_strip_position(pevent); wlr_event.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; } wlr_signal_emit_safe(&wlr_dev->tablet_pad->events.strip, &wlr_event); } wlroots-0.10.0/backend/libinput/tablet_tool.c000066400000000000000000000304621361211131400211700ustar00rootroot00000000000000#ifndef _POSIX_C_SOURCE #define _POSIX_C_SOURCE 200809L #endif #include #include #include #include #include #include #include #include #include #include "backend/libinput.h" #include "util/signal.h" static struct wlr_tablet_impl tablet_impl; static bool tablet_is_libinput(struct wlr_tablet *tablet) { return tablet->impl == &tablet_impl; } struct wlr_libinput_tablet_tool { struct wlr_tablet_tool wlr_tool; struct libinput_tablet_tool *libinput_tool; bool unique; // Refcount for destroy + release size_t pad_refs; }; // TODO: Maybe this should be a wlr_list? Do we keep it, or want to get rid of // it? struct tablet_tool_list_elem { struct wl_list link; struct wlr_libinput_tablet_tool *tool; }; struct wlr_libinput_tablet { struct wlr_tablet wlr_tablet; struct wl_list tools; // tablet_tool_list_elem::link }; static void destroy_tool(struct wlr_libinput_tablet_tool *tool) { wlr_signal_emit_safe(&tool->wlr_tool.events.destroy, &tool->wlr_tool); libinput_tablet_tool_ref(tool->libinput_tool); libinput_tablet_tool_set_user_data(tool->libinput_tool, NULL); free(tool); } static void destroy_tablet(struct wlr_tablet *wlr_tablet) { assert(tablet_is_libinput(wlr_tablet)); struct wlr_libinput_tablet *tablet = wl_container_of(wlr_tablet, tablet, wlr_tablet); struct tablet_tool_list_elem *pos; struct tablet_tool_list_elem *tmp; wl_list_for_each_safe(pos, tmp, &tablet->tools, link) { struct wlr_libinput_tablet_tool *tool = pos->tool; wl_list_remove(&pos->link); free(pos); if (--tool->pad_refs == 0) { destroy_tool(tool); } } free(tablet); } static struct wlr_tablet_impl tablet_impl = { .destroy = destroy_tablet, }; struct wlr_tablet *create_libinput_tablet( struct libinput_device *libinput_dev) { assert(libinput_dev); struct wlr_libinput_tablet *libinput_tablet = calloc(1, sizeof(struct wlr_libinput_tablet)); if (!libinput_tablet) { wlr_log(WLR_ERROR, "Unable to allocate wlr_tablet_tool"); return NULL; } struct wlr_tablet *wlr_tablet = &libinput_tablet->wlr_tablet; wlr_list_init(&wlr_tablet->paths); struct udev_device *udev = libinput_device_get_udev_device(libinput_dev); wlr_list_push(&wlr_tablet->paths, strdup(udev_device_get_syspath(udev))); wlr_tablet->name = strdup(libinput_device_get_name(libinput_dev)); wl_list_init(&libinput_tablet->tools); wlr_tablet_init(wlr_tablet, &tablet_impl); return wlr_tablet; } 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; #if LIBINPUT_MINOR >= 14 case LIBINPUT_TABLET_TOOL_TYPE_TOTEM: return WLR_TABLET_TOOL_TYPE_TOTEM; #endif } assert(false && "UNREACHABLE"); } static struct wlr_libinput_tablet_tool *get_wlr_tablet_tool( struct libinput_tablet_tool *tool) { struct wlr_libinput_tablet_tool *ret = libinput_tablet_tool_get_user_data(tool); if (ret) { return ret; } ret = calloc(1, sizeof(struct wlr_libinput_tablet_tool)); if (!ret) { return NULL; } ret->libinput_tool = libinput_tablet_tool_ref(tool); ret->wlr_tool.pressure = libinput_tablet_tool_has_pressure(tool); ret->wlr_tool.distance = libinput_tablet_tool_has_distance(tool); ret->wlr_tool.tilt = libinput_tablet_tool_has_tilt(tool); ret->wlr_tool.rotation = libinput_tablet_tool_has_rotation(tool); ret->wlr_tool.slider = libinput_tablet_tool_has_slider(tool); ret->wlr_tool.wheel = libinput_tablet_tool_has_wheel(tool); ret->wlr_tool.hardware_serial = libinput_tablet_tool_get_serial(tool); ret->wlr_tool.hardware_wacom = libinput_tablet_tool_get_tool_id(tool); ret->wlr_tool.type = wlr_type_from_libinput_type( libinput_tablet_tool_get_type(tool)); ret->unique = libinput_tablet_tool_is_unique(tool); wl_signal_init(&ret->wlr_tool.events.destroy); libinput_tablet_tool_set_user_data(tool, ret); return ret; } static void ensure_tool_reference(struct wlr_libinput_tablet_tool *tool, struct wlr_tablet *wlr_dev) { assert(tablet_is_libinput(wlr_dev)); struct wlr_libinput_tablet *tablet = wl_container_of(wlr_dev, tablet, wlr_tablet); struct tablet_tool_list_elem *pos; wl_list_for_each(pos, &tablet->tools, link) { if (pos->tool == tool) { // We already have a ref // XXX: We *could* optimize the tool to the front of // the list here, since we will probably get the next // couple of events from the same tool. // BUT the list should always be rather short (probably // single digit amount of tools) so it might be more // work than it saves return; } } struct tablet_tool_list_elem *new = calloc(1, sizeof(struct tablet_tool_list_elem)); if (!new) { wlr_log(WLR_ERROR, "Failed to allocate memory for tracking tablet tool"); return; } new->tool = tool; wl_list_insert(&tablet->tools, &new->link); ++tool->pad_refs; } void handle_tablet_tool_axis(struct libinput_event *event, struct libinput_device *libinput_dev) { struct wlr_input_device *wlr_dev = get_appropriate_device(WLR_INPUT_DEVICE_TABLET_TOOL, libinput_dev); if (!wlr_dev) { wlr_log(WLR_DEBUG, "Got a tablet tool event for a device with no tablet tools?"); return; } struct libinput_event_tablet_tool *tevent = libinput_event_get_tablet_tool_event(event); struct wlr_event_tablet_tool_axis wlr_event = { 0 }; struct wlr_libinput_tablet_tool *tool = get_wlr_tablet_tool( libinput_event_tablet_tool_get_tool(tevent)); ensure_tool_reference(tool, wlr_dev->tablet); wlr_event.device = wlr_dev; wlr_event.tool = &tool->wlr_tool; wlr_event.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); } wlr_signal_emit_safe(&wlr_dev->tablet->events.axis, &wlr_event); } void handle_tablet_tool_proximity(struct libinput_event *event, struct libinput_device *libinput_dev) { struct wlr_input_device *wlr_dev = get_appropriate_device(WLR_INPUT_DEVICE_TABLET_TOOL, libinput_dev); if (!wlr_dev) { wlr_log(WLR_DEBUG, "Got a tablet tool event for a device with no tablet tools?"); return; } struct libinput_event_tablet_tool *tevent = libinput_event_get_tablet_tool_event(event); struct wlr_event_tablet_tool_proximity wlr_event = { 0 }; struct wlr_libinput_tablet_tool *tool = get_wlr_tablet_tool( libinput_event_tablet_tool_get_tool(tevent)); ensure_tool_reference(tool, wlr_dev->tablet); wlr_event.tool = &tool->wlr_tool; wlr_event.device = wlr_dev; wlr_event.time_msec = usec_to_msec(libinput_event_tablet_tool_get_time_usec(tevent)); 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; } wlr_signal_emit_safe(&wlr_dev->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, libinput_dev); } // If the tool is not unique, libinput will not find it again after the // proximity out, so we should destroy it if (!tool->unique && 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 assert(tool->pad_refs == 1); assert(tablet_is_libinput(wlr_dev->tablet)); struct wlr_libinput_tablet *tablet = wl_container_of(wlr_dev->tablet, tablet, wlr_tablet); struct tablet_tool_list_elem *pos; struct tablet_tool_list_elem *tmp; wl_list_for_each_safe(pos, tmp, &tablet->tools, link) { if (pos->tool == tool) { wl_list_remove(&pos->link); free(pos); break; } } destroy_tool(tool); } } void handle_tablet_tool_tip(struct libinput_event *event, struct libinput_device *libinput_dev) { struct wlr_input_device *wlr_dev = get_appropriate_device(WLR_INPUT_DEVICE_TABLET_TOOL, libinput_dev); if (!wlr_dev) { wlr_log(WLR_DEBUG, "Got a tablet tool event for a device with no tablet tools?"); return; } handle_tablet_tool_axis(event, libinput_dev); struct libinput_event_tablet_tool *tevent = libinput_event_get_tablet_tool_event(event); struct wlr_event_tablet_tool_tip wlr_event = { 0 }; struct wlr_libinput_tablet_tool *tool = get_wlr_tablet_tool( libinput_event_tablet_tool_get_tool(tevent)); ensure_tool_reference(tool, wlr_dev->tablet); wlr_event.device = wlr_dev; wlr_event.tool = &tool->wlr_tool; wlr_event.time_msec = usec_to_msec(libinput_event_tablet_tool_get_time_usec(tevent)); 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; } wlr_signal_emit_safe(&wlr_dev->tablet->events.tip, &wlr_event); } void handle_tablet_tool_button(struct libinput_event *event, struct libinput_device *libinput_dev) { struct wlr_input_device *wlr_dev = get_appropriate_device(WLR_INPUT_DEVICE_TABLET_TOOL, libinput_dev); if (!wlr_dev) { wlr_log(WLR_DEBUG, "Got a tablet tool event for a device with no tablet tools?"); return; } handle_tablet_tool_axis(event, libinput_dev); struct libinput_event_tablet_tool *tevent = libinput_event_get_tablet_tool_event(event); struct wlr_event_tablet_tool_button wlr_event = { 0 }; struct wlr_libinput_tablet_tool *tool = get_wlr_tablet_tool( libinput_event_tablet_tool_get_tool(tevent)); ensure_tool_reference(tool, wlr_dev->tablet); wlr_event.device = wlr_dev; wlr_event.tool = &tool->wlr_tool; wlr_event.time_msec = usec_to_msec(libinput_event_tablet_tool_get_time_usec(tevent)); wlr_event.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; } wlr_signal_emit_safe(&wlr_dev->tablet->events.button, &wlr_event); } wlroots-0.10.0/backend/libinput/touch.c000066400000000000000000000067051361211131400200050ustar00rootroot00000000000000#include #include #include #include #include #include #include #include "backend/libinput.h" #include "util/signal.h" struct wlr_touch *create_libinput_touch( struct libinput_device *libinput_dev) { assert(libinput_dev); struct wlr_touch *wlr_touch = calloc(1, sizeof(struct wlr_touch)); if (!wlr_touch) { wlr_log(WLR_ERROR, "Unable to allocate wlr_touch"); return NULL; } wlr_touch_init(wlr_touch, NULL); return wlr_touch; } void handle_touch_down(struct libinput_event *event, struct libinput_device *libinput_dev) { struct wlr_input_device *wlr_dev = get_appropriate_device(WLR_INPUT_DEVICE_TOUCH, libinput_dev); if (!wlr_dev) { wlr_log(WLR_DEBUG, "Got a touch event for a device with no touch?"); return; } struct libinput_event_touch *tevent = libinput_event_get_touch_event(event); struct wlr_event_touch_down wlr_event = { 0 }; wlr_event.device = wlr_dev; 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); wlr_signal_emit_safe(&wlr_dev->touch->events.down, &wlr_event); } void handle_touch_up(struct libinput_event *event, struct libinput_device *libinput_dev) { struct wlr_input_device *wlr_dev = get_appropriate_device(WLR_INPUT_DEVICE_TOUCH, libinput_dev); if (!wlr_dev) { wlr_log(WLR_DEBUG, "Got a touch event for a device with no touch?"); return; } struct libinput_event_touch *tevent = libinput_event_get_touch_event(event); struct wlr_event_touch_up wlr_event = { 0 }; wlr_event.device = wlr_dev; 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_signal_emit_safe(&wlr_dev->touch->events.up, &wlr_event); } void handle_touch_motion(struct libinput_event *event, struct libinput_device *libinput_dev) { struct wlr_input_device *wlr_dev = get_appropriate_device(WLR_INPUT_DEVICE_TOUCH, libinput_dev); if (!wlr_dev) { wlr_log(WLR_DEBUG, "Got a touch event for a device with no touch?"); return; } struct libinput_event_touch *tevent = libinput_event_get_touch_event(event); struct wlr_event_touch_motion wlr_event = { 0 }; wlr_event.device = wlr_dev; 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); wlr_signal_emit_safe(&wlr_dev->touch->events.motion, &wlr_event); } void handle_touch_cancel(struct libinput_event *event, struct libinput_device *libinput_dev) { struct wlr_input_device *wlr_dev = get_appropriate_device(WLR_INPUT_DEVICE_TOUCH, libinput_dev); if (!wlr_dev) { wlr_log(WLR_DEBUG, "Got a touch event for a device with no touch?"); return; } struct libinput_event_touch *tevent = libinput_event_get_touch_event(event); struct wlr_event_touch_cancel wlr_event = { 0 }; wlr_event.device = wlr_dev; 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_signal_emit_safe(&wlr_dev->touch->events.cancel, &wlr_event); } wlroots-0.10.0/backend/meson.build000066400000000000000000000002471361211131400170260ustar00rootroot00000000000000wlr_files += files('backend.c') subdir('drm') subdir('headless') subdir('libinput') subdir('multi') subdir('noop') subdir('wayland') subdir('x11') subdir('session') wlroots-0.10.0/backend/multi/000077500000000000000000000000001361211131400160135ustar00rootroot00000000000000wlroots-0.10.0/backend/multi/backend.c000066400000000000000000000156461361211131400175620ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200112L #include #include #include #include #include #include #include #include "backend/multi.h" #include "util/signal.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)); return (struct wlr_multi_backend *)wlr_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); struct subbackend_state *sub, *next; wl_list_for_each_safe(sub, next, &backend->backends, link) { wlr_backend_destroy(sub->backend); } // Destroy this backend only after removing all sub-backends wlr_signal_emit_safe(&wlr_backend->events.destroy, backend); free(backend); } static struct wlr_renderer *multi_backend_get_renderer( 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) { struct wlr_renderer *rend = wlr_backend_get_renderer(sub->backend); if (rend != NULL) { return rend; } } return NULL; } static struct wlr_session *multi_backend_get_session( struct wlr_backend *_backend) { struct wlr_multi_backend *backend = multi_backend_from_backend(_backend); return backend->session; } static clockid_t multi_backend_get_presentation_clock( 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_presentation_clock) { return wlr_backend_get_presentation_clock(sub->backend); } } return CLOCK_MONOTONIC; } struct wlr_backend_impl backend_impl = { .start = multi_backend_start, .destroy = multi_backend_destroy, .get_renderer = multi_backend_get_renderer, .get_session = multi_backend_get_session, .get_presentation_clock = multi_backend_get_presentation_clock, }; 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(struct wlr_multi_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); wlr_signal_emit_safe(&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); wlr_signal_emit_safe(&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) { struct wlr_multi_backend *multi = multi_backend_from_backend(_multi); if (multi_backend_get_subbackend(multi, backend)) { // already added return true; } struct wlr_renderer *multi_renderer = multi_backend_get_renderer(&multi->backend); struct wlr_renderer *backend_renderer = wlr_backend_get_renderer(backend); if (multi_renderer != NULL && backend_renderer != NULL && multi_renderer != backend_renderer) { wlr_log(WLR_ERROR, "Could not add backend: multiple renderers at the " "same time aren't supported"); return false; } struct subbackend_state *sub = calloc(1, sizeof(struct subbackend_state)); if (sub == NULL) { wlr_log(WLR_ERROR, "Could not add backend: allocation failed"); return false; } wl_list_insert(&multi->backends, &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; wlr_signal_emit_safe(&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) { wlr_signal_emit_safe(&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.10.0/backend/multi/meson.build000066400000000000000000000000401361211131400201470ustar00rootroot00000000000000wlr_files += files('backend.c') wlroots-0.10.0/backend/noop/000077500000000000000000000000001361211131400156345ustar00rootroot00000000000000wlroots-0.10.0/backend/noop/backend.c000066400000000000000000000035171361211131400173750ustar00rootroot00000000000000#include #include #include #include #include "backend/noop.h" #include "util/signal.h" struct wlr_noop_backend *noop_backend_from_backend( struct wlr_backend *wlr_backend) { assert(wlr_backend_is_noop(wlr_backend)); return (struct wlr_noop_backend *)wlr_backend; } static bool backend_start(struct wlr_backend *wlr_backend) { struct wlr_noop_backend *backend = noop_backend_from_backend(wlr_backend); wlr_log(WLR_INFO, "Starting noop backend"); struct wlr_noop_output *output; wl_list_for_each(output, &backend->outputs, link) { wlr_output_update_enabled(&output->wlr_output, true); wlr_signal_emit_safe(&backend->backend.events.new_output, &output->wlr_output); } backend->started = true; return true; } static void backend_destroy(struct wlr_backend *wlr_backend) { struct wlr_noop_backend *backend = noop_backend_from_backend(wlr_backend); if (!wlr_backend) { return; } struct wlr_noop_output *output, *output_tmp; wl_list_for_each_safe(output, output_tmp, &backend->outputs, link) { wlr_output_destroy(&output->wlr_output); } wlr_signal_emit_safe(&wlr_backend->events.destroy, backend); free(backend); } static const struct wlr_backend_impl backend_impl = { .start = backend_start, .destroy = backend_destroy, }; struct wlr_backend *wlr_noop_backend_create(struct wl_display *display) { wlr_log(WLR_INFO, "Creating noop backend"); struct wlr_noop_backend *backend = calloc(1, sizeof(struct wlr_noop_backend)); if (!backend) { wlr_log(WLR_ERROR, "Failed to allocate wlr_noop_backend"); return NULL; } wlr_backend_init(&backend->backend, &backend_impl); backend->display = display; wl_list_init(&backend->outputs); return &backend->backend; } bool wlr_backend_is_noop(struct wlr_backend *backend) { return backend->impl == &backend_impl; } wlroots-0.10.0/backend/noop/meson.build000066400000000000000000000000611361211131400177730ustar00rootroot00000000000000wlr_files += files( 'backend.c', 'output.c', ) wlroots-0.10.0/backend/noop/output.c000066400000000000000000000046351361211131400173500ustar00rootroot00000000000000#include #include #include #include #include #include "backend/noop.h" #include "util/signal.h" static struct wlr_noop_output *noop_output_from_output( struct wlr_output *wlr_output) { assert(wlr_output_is_noop(wlr_output)); return (struct wlr_noop_output *)wlr_output; } static bool output_attach_render(struct wlr_output *wlr_output, int *buffer_age) { return false; } static bool output_commit(struct wlr_output *wlr_output) { if (wlr_output->pending.committed & WLR_OUTPUT_STATE_ENABLED) { wlr_log(WLR_DEBUG, "Cannot disable a noop output"); return false; } if (wlr_output->pending.committed & WLR_OUTPUT_STATE_MODE) { assert(wlr_output->pending.mode_type == WLR_OUTPUT_STATE_MODE_CUSTOM); wlr_output_update_custom_mode(wlr_output, wlr_output->pending.custom_mode.width, wlr_output->pending.custom_mode.height, wlr_output->pending.custom_mode.refresh); } if (wlr_output->pending.committed & WLR_OUTPUT_STATE_BUFFER) { return false; } return true; } static void output_destroy(struct wlr_output *wlr_output) { struct wlr_noop_output *output = noop_output_from_output(wlr_output); wl_list_remove(&output->link); free(output); } static const struct wlr_output_impl output_impl = { .destroy = output_destroy, .attach_render = output_attach_render, .commit = output_commit, }; bool wlr_output_is_noop(struct wlr_output *wlr_output) { return wlr_output->impl == &output_impl; } struct wlr_output *wlr_noop_add_output(struct wlr_backend *wlr_backend) { struct wlr_noop_backend *backend = noop_backend_from_backend(wlr_backend); struct wlr_noop_output *output = calloc(1, sizeof(struct wlr_noop_output)); if (output == NULL) { wlr_log(WLR_ERROR, "Failed to allocate wlr_noop_output"); return NULL; } output->backend = backend; wlr_output_init(&output->wlr_output, &backend->backend, &output_impl, backend->display); struct wlr_output *wlr_output = &output->wlr_output; strncpy(wlr_output->make, "noop", sizeof(wlr_output->make)); strncpy(wlr_output->model, "noop", sizeof(wlr_output->model)); snprintf(wlr_output->name, sizeof(wlr_output->name), "NOOP-%zd", ++backend->last_output_num); wl_list_insert(&backend->outputs, &output->link); if (backend->started) { wlr_output_update_enabled(wlr_output, true); wlr_signal_emit_safe(&backend->backend.events.new_output, wlr_output); } return wlr_output; } wlroots-0.10.0/backend/session/000077500000000000000000000000001361211131400163445ustar00rootroot00000000000000wlroots-0.10.0/backend/session/direct-freebsd.c000066400000000000000000000171071361211131400214000ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "backend/session/direct-ipc.h" #include "util/signal.h" const struct session_impl session_direct; struct direct_session { struct wlr_session base; int tty_fd; int old_tty; int old_kbmode; int sock; pid_t child; struct wl_event_source *vt_source; }; static struct direct_session *direct_session_from_session( struct wlr_session *base) { assert(base->impl == &session_direct); return (struct direct_session *)base; } static int direct_session_open(struct wlr_session *base, const char *path) { struct direct_session *session = direct_session_from_session(base); int fd = direct_ipc_open(session->sock, path); if (fd < 0) { wlr_log(WLR_ERROR, "Failed to open %s: %s%s", path, strerror(-fd), fd == -EINVAL ? "; is another display server running?" : ""); return fd; } return fd; } static void direct_session_close(struct wlr_session *base, int fd) { struct direct_session *session = direct_session_from_session(base); int ev; struct drm_version dv = {0}; if (ioctl(fd, DRM_IOCTL_VERSION, &dv) == 0) { direct_ipc_dropmaster(session->sock, fd); } else if (ioctl(fd, EVIOCGVERSION, &ev) == 0) { ioctl(fd, EVIOCREVOKE, 0); } close(fd); } static bool direct_change_vt(struct wlr_session *base, unsigned vt) { struct direct_session *session = direct_session_from_session(base); // Only seat0 has VTs associated with it if (strcmp(session->base.seat, "seat0") != 0) { return true; } return ioctl(session->tty_fd, VT_ACTIVATE, (int)vt) == 0; } static void direct_session_destroy(struct wlr_session *base) { struct direct_session *session = direct_session_from_session(base); if (strcmp(session->base.seat, "seat0") == 0) { struct vt_mode mode = { .mode = VT_AUTO, }; errno = 0; ioctl(session->tty_fd, KDSKBMODE, session->old_kbmode); ioctl(session->tty_fd, KDSETMODE, KD_TEXT); ioctl(session->tty_fd, VT_SETMODE, &mode); ioctl(session->tty_fd, VT_ACTIVATE, session->old_tty); if (errno) { wlr_log(WLR_ERROR, "Failed to restore tty"); } wl_event_source_remove(session->vt_source); close(session->tty_fd); } direct_ipc_finish(session->sock, session->child); close(session->sock); free(session); } static int vt_handler(int signo, void *data) { struct direct_session *session = data; struct drm_version dv = {0}; struct wlr_device *dev; if (session->base.active) { session->base.active = false; wlr_signal_emit_safe(&session->base.session_signal, session); wl_list_for_each(dev, &session->base.devices, link) { if (ioctl(dev->fd, DRM_IOCTL_VERSION, &dv) == 0) { direct_ipc_dropmaster(session->sock, dev->fd); } } ioctl(session->tty_fd, VT_RELDISP, 1); } else { ioctl(session->tty_fd, VT_RELDISP, VT_ACKACQ); wl_list_for_each(dev, &session->base.devices, link) { if (ioctl(dev->fd, DRM_IOCTL_VERSION, &dv) == 0) { direct_ipc_setmaster(session->sock, dev->fd); } } session->base.active = true; wlr_signal_emit_safe(&session->base.session_signal, session); } return 1; } static bool get_tty_path(int tty, char path[static 11], size_t len) { assert(tty > 0); const char prefix[] = "/dev/ttyv"; static_assert(sizeof(prefix) + 1 <= 11, "TTY path prefix is too long"); const size_t prefix_len = sizeof(prefix) - 1; strcpy(path, prefix); size_t offset = prefix_len; const int num = tty - 1; if (num == 0) { path[offset++] = '0'; path[offset++] = '\0'; return true; } const int base = 32; for (int remaning = num; remaning > 0; remaning /= base, offset++) { // Return early if the buffer is too small. if (offset + 1 >= len) { return false; } const int value = remaning % base; if (value >= 10) { path[offset] = 'a' + value - 10; } else { path[offset] = '0' + value; } } const size_t num_len = offset - prefix_len; for (size_t i = 0; i < num_len / 2; i++) { const size_t p1 = prefix_len + i; const size_t p2 = offset - 1 - i; const char tmp = path[p1]; path[p1] = path[p2]; path[p2] = tmp; } path[offset++] = '\0'; return true; } static bool setup_tty(struct direct_session *session, struct wl_display *display) { int fd = -1, tty = -1, tty0_fd = -1, old_tty = 1; if ((tty0_fd = open("/dev/ttyv0", O_RDWR | O_CLOEXEC)) < 0) { wlr_log_errno(WLR_ERROR, "Could not open /dev/ttyv0 to find a free vt"); goto error; } if (ioctl(tty0_fd, VT_GETACTIVE, &old_tty) != 0) { wlr_log_errno(WLR_ERROR, "Could not get active vt"); goto error; } if (ioctl(tty0_fd, VT_OPENQRY, &tty) != 0) { wlr_log_errno(WLR_ERROR, "Could not find a free vt"); goto error; } close(tty0_fd); char tty_path[64]; if (!get_tty_path(tty, tty_path, sizeof(tty_path))) { wlr_log(WLR_ERROR, "Could not get tty %d path", tty); goto error; } wlr_log(WLR_INFO, "Using tty %s", tty_path); fd = open(tty_path, O_RDWR | O_NOCTTY | O_CLOEXEC); if (fd == -1) { wlr_log_errno(WLR_ERROR, "Cannot open tty"); return false; } ioctl(fd, VT_ACTIVATE, tty); ioctl(fd, VT_WAITACTIVE, tty); int old_kbmode; if (ioctl(fd, KDGKBMODE, &old_kbmode)) { wlr_log_errno(WLR_ERROR, "Failed to read tty %d keyboard mode", tty); goto error; } if (ioctl(fd, KDSKBMODE, K_CODE)) { wlr_log_errno(WLR_ERROR, "Failed to set keyboard mode K_CODE on tty %d", tty); goto error; } if (ioctl(fd, KDSETMODE, KD_GRAPHICS)) { wlr_log_errno(WLR_ERROR, "Failed to set graphics mode on tty %d", tty); goto error; } struct vt_mode mode = { .mode = VT_PROCESS, .relsig = SIGUSR2, .acqsig = SIGUSR2, .frsig = SIGIO, // has to be set }; if (ioctl(fd, VT_SETMODE, &mode) < 0) { wlr_log(WLR_ERROR, "Failed to take control of tty %d", tty); goto error; } struct wl_event_loop *loop = wl_display_get_event_loop(display); session->vt_source = wl_event_loop_add_signal(loop, SIGUSR2, vt_handler, session); if (!session->vt_source) { goto error; } session->base.vtnr = tty; session->tty_fd = fd; session->old_tty = old_tty; session->old_kbmode = old_kbmode; return true; error: // In case we could not get the last active one, drop back to tty 1, // better than hanging in a useless blank console. Otherwise activate the // last active. ioctl(fd, VT_ACTIVATE, old_tty); close(fd); return false; } static struct wlr_session *direct_session_create(struct wl_display *disp) { struct direct_session *session = calloc(1, sizeof(*session)); if (!session) { wlr_log_errno(WLR_ERROR, "Allocation failed"); return NULL; } session->sock = direct_ipc_init(&session->child); if (session->sock == -1) { goto error_session; } const char *seat = getenv("XDG_SEAT"); if (!seat) { seat = "seat0"; } if (strcmp(seat, "seat0") == 0) { if (!setup_tty(session, disp)) { goto error_ipc; } } else { session->base.vtnr = 0; session->tty_fd = -1; } wlr_log(WLR_INFO, "Successfully loaded direct session"); snprintf(session->base.seat, sizeof(session->base.seat), "%s", seat); session->base.impl = &session_direct; return &session->base; error_ipc: direct_ipc_finish(session->sock, session->child); close(session->sock); error_session: free(session); return NULL; } const struct session_impl session_direct = { .create = direct_session_create, .destroy = direct_session_destroy, .open = direct_session_open, .close = direct_session_close, .change_vt = direct_change_vt, }; wlroots-0.10.0/backend/session/direct-ipc.c000066400000000000000000000123631361211131400205400ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200809L #ifdef __FreeBSD__ #define __BSD_VISIBLE 1 #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __linux__ #include #include #endif #include "backend/session/direct-ipc.h" enum { DRM_MAJOR = 226 }; #if WLR_HAS_LIBCAP #include static bool have_permissions(void) { cap_t cap = cap_get_proc(); cap_flag_value_t val; if (!cap || cap_get_flag(cap, CAP_SYS_ADMIN, CAP_PERMITTED, &val) || val != CAP_SET) { wlr_log(WLR_ERROR, "Do not have CAP_SYS_ADMIN; cannot become DRM master"); cap_free(cap); return false; } cap_free(cap); return true; } #else static bool have_permissions(void) { #ifdef __linux__ if (geteuid() != 0) { wlr_log(WLR_ERROR, "Do not have root privileges; cannot become DRM master"); return false; } #endif return true; } #endif static void send_msg(int sock, int fd, void *buf, size_t buf_len) { char control[CMSG_SPACE(sizeof(fd))] = {0}; struct iovec iovec = { .iov_base = buf, .iov_len = buf_len }; struct msghdr msghdr = {0}; if (buf) { msghdr.msg_iov = &iovec; msghdr.msg_iovlen = 1; } if (fd >= 0) { msghdr.msg_control = &control; msghdr.msg_controllen = sizeof(control); struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msghdr); *cmsg = (struct cmsghdr) { .cmsg_level = SOL_SOCKET, .cmsg_type = SCM_RIGHTS, .cmsg_len = CMSG_LEN(sizeof(fd)), }; memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd)); } ssize_t ret; do { ret = sendmsg(sock, &msghdr, 0); } while (ret < 0 && errno == EINTR); } static ssize_t recv_msg(int sock, int *fd_out, void *buf, size_t buf_len) { char control[CMSG_SPACE(sizeof(*fd_out))] = {0}; struct iovec iovec = { .iov_base = buf, .iov_len = buf_len }; struct msghdr msghdr = {0}; if (buf) { msghdr.msg_iov = &iovec; msghdr.msg_iovlen = 1; } if (fd_out) { msghdr.msg_control = &control; msghdr.msg_controllen = sizeof(control); } ssize_t ret; do { ret = recvmsg(sock, &msghdr, MSG_CMSG_CLOEXEC); } while (ret < 0 && errno == EINTR); if (fd_out) { struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msghdr); if (cmsg) { memcpy(fd_out, CMSG_DATA(cmsg), sizeof(*fd_out)); } else { *fd_out = -1; } } return ret; } enum msg_type { MSG_OPEN, MSG_SETMASTER, MSG_DROPMASTER, MSG_END, }; struct msg { enum msg_type type; char path[256]; }; static void communicate(int sock) { struct msg msg; int drm_fd = -1; bool running = true; while (running && recv_msg(sock, &drm_fd, &msg, sizeof(msg)) > 0) { switch (msg.type) { case MSG_OPEN: errno = 0; // These are the same flags that logind opens files with int fd = open(msg.path, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK); int ret = errno; if (fd == -1) { goto error; } #ifndef __FreeBSD__ struct stat st; if (fstat(fd, &st) < 0) { ret = errno; goto error; } uint32_t maj = major(st.st_rdev); if (maj != INPUT_MAJOR && maj != DRM_MAJOR) { ret = ENOTSUP; goto error; } if (maj == DRM_MAJOR && drmSetMaster(fd)) { ret = errno; } #else int ev; struct drm_version dv = {0}; if (ioctl(fd, EVIOCGVERSION, &ev) == -1 && ioctl(fd, DRM_IOCTL_VERSION, &dv) == -1) { ret = ENOTSUP; goto error; } if (dv.version_major != 0 && drmSetMaster(fd)) { ret = errno; } #endif error: send_msg(sock, ret ? -1 : fd, &ret, sizeof(ret)); if (fd >= 0) { close(fd); } break; case MSG_SETMASTER: drmSetMaster(drm_fd); close(drm_fd); send_msg(sock, -1, NULL, 0); break; case MSG_DROPMASTER: drmDropMaster(drm_fd); close(drm_fd); send_msg(sock, -1, NULL, 0); break; case MSG_END: running = false; send_msg(sock, -1, NULL, 0); break; } } close(sock); } int direct_ipc_open(int sock, const char *path) { struct msg msg = { .type = MSG_OPEN }; snprintf(msg.path, sizeof(msg.path), "%s", path); send_msg(sock, -1, &msg, sizeof(msg)); int fd, err, ret; int retry = 0; do { ret = recv_msg(sock, &fd, &err, sizeof(err)); } while (ret == 0 && retry++ < 3); return err ? -err : fd; } void direct_ipc_setmaster(int sock, int fd) { struct msg msg = { .type = MSG_SETMASTER }; send_msg(sock, fd, &msg, sizeof(msg)); recv_msg(sock, NULL, NULL, 0); } void direct_ipc_dropmaster(int sock, int fd) { struct msg msg = { .type = MSG_DROPMASTER }; send_msg(sock, fd, &msg, sizeof(msg)); recv_msg(sock, NULL, NULL, 0); } void direct_ipc_finish(int sock, pid_t pid) { struct msg msg = { .type = MSG_END }; send_msg(sock, -1, &msg, sizeof(msg)); recv_msg(sock, NULL, NULL, 0); waitpid(pid, NULL, 0); } int direct_ipc_init(pid_t *pid_out) { if (!have_permissions()) { return -1; } int sock[2]; if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sock) < 0) { wlr_log_errno(WLR_ERROR, "Failed to create socket pair"); return -1; } pid_t pid = fork(); if (pid < 0) { wlr_log_errno(WLR_ERROR, "Fork failed"); close(sock[0]); close(sock[1]); return -1; } else if (pid == 0) { close(sock[0]); communicate(sock[1]); _Exit(0); } close(sock[1]); *pid_out = pid; return sock[0]; } wlroots-0.10.0/backend/session/direct.c000066400000000000000000000146331361211131400177710ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "backend/session/direct-ipc.h" #include "util/signal.h" enum { DRM_MAJOR = 226 }; const struct session_impl session_direct; struct direct_session { struct wlr_session base; int tty_fd; int old_kbmode; int sock; pid_t child; struct wl_event_source *vt_source; }; static struct direct_session *direct_session_from_session( struct wlr_session *base) { assert(base->impl == &session_direct); return (struct direct_session *)base; } static int direct_session_open(struct wlr_session *base, const char *path) { struct direct_session *session = direct_session_from_session(base); int fd = direct_ipc_open(session->sock, path); if (fd < 0) { wlr_log(WLR_ERROR, "Failed to open %s: %s%s", path, strerror(-fd), fd == -EINVAL ? "; is another display server running?" : ""); return fd; } struct stat st; if (fstat(fd, &st) < 0) { close(fd); return -errno; } if (major(st.st_rdev) == DRM_MAJOR) { direct_ipc_setmaster(session->sock, fd); } return fd; } static void direct_session_close(struct wlr_session *base, int fd) { struct direct_session *session = direct_session_from_session(base); struct stat st; if (fstat(fd, &st) < 0) { wlr_log_errno(WLR_ERROR, "Stat failed"); close(fd); return; } if (major(st.st_rdev) == DRM_MAJOR) { direct_ipc_dropmaster(session->sock, fd); } else if (major(st.st_rdev) == INPUT_MAJOR) { ioctl(fd, EVIOCREVOKE, 0); } close(fd); } static bool direct_change_vt(struct wlr_session *base, unsigned vt) { struct direct_session *session = direct_session_from_session(base); // Only seat0 has VTs associated with it if (strcmp(session->base.seat, "seat0") != 0) { return true; } return ioctl(session->tty_fd, VT_ACTIVATE, (int)vt) == 0; } static void direct_session_destroy(struct wlr_session *base) { struct direct_session *session = direct_session_from_session(base); if (strcmp(session->base.seat, "seat0") == 0) { struct vt_mode mode = { .mode = VT_AUTO, }; errno = 0; ioctl(session->tty_fd, KDSKBMODE, session->old_kbmode); ioctl(session->tty_fd, KDSETMODE, KD_TEXT); ioctl(session->tty_fd, VT_SETMODE, &mode); if (errno) { wlr_log(WLR_ERROR, "Failed to restore tty"); } wl_event_source_remove(session->vt_source); close(session->tty_fd); } direct_ipc_finish(session->sock, session->child); close(session->sock); free(session); } static int vt_handler(int signo, void *data) { struct direct_session *session = data; if (session->base.active) { session->base.active = false; wlr_signal_emit_safe(&session->base.session_signal, session); struct wlr_device *dev; wl_list_for_each(dev, &session->base.devices, link) { if (major(dev->dev) == DRM_MAJOR) { direct_ipc_dropmaster(session->sock, dev->fd); } } ioctl(session->tty_fd, VT_RELDISP, 1); } else { ioctl(session->tty_fd, VT_RELDISP, VT_ACKACQ); struct wlr_device *dev; wl_list_for_each(dev, &session->base.devices, link) { if (major(dev->dev) == DRM_MAJOR) { direct_ipc_setmaster(session->sock, dev->fd); } } session->base.active = true; wlr_signal_emit_safe(&session->base.session_signal, session); } return 1; } static bool setup_tty(struct direct_session *session, struct wl_display *display) { bool default_tty = false; const char *tty_path = getenv("WLR_DIRECT_TTY"); if (!tty_path) { tty_path = "/dev/tty"; default_tty = true; } int fd = open(tty_path, O_RDWR | O_CLOEXEC); if (fd == -1) { wlr_log_errno(WLR_ERROR, "Cannot open %s", tty_path); return false; } struct vt_stat vt_stat; if (ioctl(fd, VT_GETSTATE, &vt_stat)) { wlr_log_errno(WLR_ERROR, "Could not get current tty number"); goto error; } int tty = vt_stat.v_active; int ret, kd_mode, old_kbmode; ret = ioctl(fd, KDGETMODE, &kd_mode); if (ret) { wlr_log_errno(WLR_ERROR, "Failed to get tty mode"); goto error; } if (default_tty && kd_mode != KD_TEXT) { wlr_log(WLR_ERROR, "tty already in graphics mode; is another display server running?"); goto error; } ioctl(fd, VT_ACTIVATE, tty); ioctl(fd, VT_WAITACTIVE, tty); if (ioctl(fd, KDGKBMODE, &old_kbmode)) { wlr_log_errno(WLR_ERROR, "Failed to read keyboard mode"); goto error; } if (ioctl(fd, KDSKBMODE, K_OFF)) { wlr_log_errno(WLR_ERROR, "Failed to set keyboard mode"); goto error; } if (ioctl(fd, KDSETMODE, KD_GRAPHICS)) { wlr_log_errno(WLR_ERROR, "Failed to set graphics mode on tty"); goto error; } struct vt_mode mode = { .mode = VT_PROCESS, .relsig = SIGUSR2, .acqsig = SIGUSR2, }; if (ioctl(fd, VT_SETMODE, &mode) < 0) { wlr_log(WLR_ERROR, "Failed to take control of tty"); goto error; } struct wl_event_loop *loop = wl_display_get_event_loop(display); session->vt_source = wl_event_loop_add_signal(loop, SIGUSR2, vt_handler, session); if (!session->vt_source) { goto error; } session->base.vtnr = tty; session->tty_fd = fd; session->old_kbmode = old_kbmode; return true; error: close(fd); return false; } static struct wlr_session *direct_session_create(struct wl_display *disp) { struct direct_session *session = calloc(1, sizeof(*session)); if (!session) { wlr_log_errno(WLR_ERROR, "Allocation failed"); return NULL; } session->sock = direct_ipc_init(&session->child); if (session->sock == -1) { goto error_session; } const char *seat = getenv("XDG_SEAT"); if (!seat) { seat = "seat0"; } if (strcmp(seat, "seat0") == 0) { if (!setup_tty(session, disp)) { goto error_ipc; } } else { session->base.vtnr = 0; session->tty_fd = -1; } snprintf(session->base.seat, sizeof(session->base.seat), "%s", seat); session->base.impl = &session_direct; wlr_log(WLR_INFO, "Successfully loaded direct session"); return &session->base; error_ipc: direct_ipc_finish(session->sock, session->child); close(session->sock); error_session: free(session); return NULL; } const struct session_impl session_direct = { .create = direct_session_create, .destroy = direct_session_destroy, .open = direct_session_open, .close = direct_session_close, .change_vt = direct_change_vt, }; wlroots-0.10.0/backend/session/logind.c000066400000000000000000000504631361211131400177740ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "util/signal.h" #if WLR_HAS_SYSTEMD #include #include #elif WLR_HAS_ELOGIND #include #include #endif enum { DRM_MAJOR = 226 }; const struct session_impl session_logind; struct logind_session { struct wlr_session base; sd_bus *bus; struct wl_event_source *event; char *id; char *path; char *seat_path; bool can_graphical; // specifies whether a drm device was taken // if so, the session will be (de)activated with the drm fd, // otherwise with the dbus PropertiesChanged on "active" signal bool has_drm; }; static struct logind_session *logind_session_from_session( struct wlr_session *base) { assert(base->impl == &session_logind); return (struct logind_session *)base; } static int logind_take_device(struct wlr_session *base, const char *path) { struct logind_session *session = logind_session_from_session(base); int fd = -1; int ret; sd_bus_message *msg = NULL; sd_bus_error error = SD_BUS_ERROR_NULL; struct stat st; if (stat(path, &st) < 0) { wlr_log(WLR_ERROR, "Failed to stat '%s'", path); return -1; } if (major(st.st_rdev) == DRM_MAJOR) { session->has_drm = true; } ret = sd_bus_call_method(session->bus, "org.freedesktop.login1", session->path, "org.freedesktop.login1.Session", "TakeDevice", &error, &msg, "uu", major(st.st_rdev), minor(st.st_rdev)); if (ret < 0) { wlr_log(WLR_ERROR, "Failed to take device '%s': %s", path, error.message); goto out; } int paused = 0; ret = sd_bus_message_read(msg, "hb", &fd, &paused); if (ret < 0) { wlr_log(WLR_ERROR, "Failed to parse D-Bus response for '%s': %s", path, strerror(-ret)); goto out; } // The original fd seems to be closed when the message is freed // so we just clone it. fd = fcntl(fd, F_DUPFD_CLOEXEC, 0); if (fd < 0) { wlr_log(WLR_ERROR, "Failed to clone file descriptor for '%s': %s", path, strerror(errno)); goto out; } out: sd_bus_error_free(&error); sd_bus_message_unref(msg); return fd; } static void logind_release_device(struct wlr_session *base, int fd) { struct logind_session *session = logind_session_from_session(base); struct stat st; if (fstat(fd, &st) < 0) { wlr_log(WLR_ERROR, "Failed to stat device '%d': %s", fd, strerror(errno)); return; } sd_bus_message *msg = NULL; sd_bus_error error = SD_BUS_ERROR_NULL; int ret = sd_bus_call_method(session->bus, "org.freedesktop.login1", session->path, "org.freedesktop.login1.Session", "ReleaseDevice", &error, &msg, "uu", major(st.st_rdev), minor(st.st_rdev)); if (ret < 0) { wlr_log(WLR_ERROR, "Failed to release device '%d': %s", fd, error.message); } sd_bus_error_free(&error); sd_bus_message_unref(msg); close(fd); } static bool logind_change_vt(struct wlr_session *base, unsigned vt) { struct logind_session *session = logind_session_from_session(base); // Only seat0 has VTs associated with it if (strcmp(session->base.seat, "seat0") != 0) { return true; } int ret; sd_bus_message *msg = NULL; sd_bus_error error = SD_BUS_ERROR_NULL; ret = sd_bus_call_method(session->bus, "org.freedesktop.login1", "/org/freedesktop/login1/seat/seat0", "org.freedesktop.login1.Seat", "SwitchTo", &error, &msg, "u", (uint32_t)vt); if (ret < 0) { wlr_log(WLR_ERROR, "Failed to change to vt '%d'", vt); } sd_bus_error_free(&error); sd_bus_message_unref(msg); return ret >= 0; } static bool find_session_path(struct logind_session *session) { int ret; sd_bus_message *msg = NULL; sd_bus_error error = SD_BUS_ERROR_NULL; ret = sd_bus_call_method(session->bus, "org.freedesktop.login1", "/org/freedesktop/login1", "org.freedesktop.login1.Manager", "GetSession", &error, &msg, "s", session->id); if (ret < 0) { wlr_log(WLR_ERROR, "Failed to get session path: %s", error.message); goto out; } const char *path; ret = sd_bus_message_read(msg, "o", &path); if (ret < 0) { wlr_log(WLR_ERROR, "Could not parse session path: %s", error.message); goto out; } session->path = strdup(path); out: sd_bus_error_free(&error); sd_bus_message_unref(msg); return ret >= 0; } static bool find_seat_path(struct logind_session *session) { int ret; sd_bus_message *msg = NULL; sd_bus_error error = SD_BUS_ERROR_NULL; ret = sd_bus_call_method(session->bus, "org.freedesktop.login1", "/org/freedesktop/login1", "org.freedesktop.login1.Manager", "GetSeat", &error, &msg, "s", session->base.seat); if (ret < 0) { wlr_log(WLR_ERROR, "Failed to get seat path: %s", error.message); goto out; } const char *path; ret = sd_bus_message_read(msg, "o", &path); if (ret < 0) { wlr_log(WLR_ERROR, "Could not parse seat path: %s", error.message); goto out; } session->seat_path = strdup(path); out: sd_bus_error_free(&error); sd_bus_message_unref(msg); return ret >= 0; } static bool session_activate(struct logind_session *session) { int ret; sd_bus_message *msg = NULL; sd_bus_error error = SD_BUS_ERROR_NULL; ret = sd_bus_call_method(session->bus, "org.freedesktop.login1", session->path, "org.freedesktop.login1.Session", "Activate", &error, &msg, ""); if (ret < 0) { wlr_log(WLR_ERROR, "Failed to activate session: %s", error.message); } sd_bus_error_free(&error); sd_bus_message_unref(msg); return ret >= 0; } static bool take_control(struct logind_session *session) { int ret; sd_bus_message *msg = NULL; sd_bus_error error = SD_BUS_ERROR_NULL; ret = sd_bus_call_method(session->bus, "org.freedesktop.login1", session->path, "org.freedesktop.login1.Session", "TakeControl", &error, &msg, "b", false); if (ret < 0) { wlr_log(WLR_ERROR, "Failed to take control of session: %s", error.message); } sd_bus_error_free(&error); sd_bus_message_unref(msg); return ret >= 0; } static void release_control(struct logind_session *session) { int ret; sd_bus_message *msg = NULL; sd_bus_error error = SD_BUS_ERROR_NULL; ret = sd_bus_call_method(session->bus, "org.freedesktop.login1", session->path, "org.freedesktop.login1.Session", "ReleaseControl", &error, &msg, ""); if (ret < 0) { wlr_log(WLR_ERROR, "Failed to release control of session: %s", error.message); } sd_bus_error_free(&error); sd_bus_message_unref(msg); } static void logind_session_destroy(struct wlr_session *base) { struct logind_session *session = logind_session_from_session(base); release_control(session); wl_event_source_remove(session->event); sd_bus_unref(session->bus); free(session->id); free(session->path); free(session->seat_path); free(session); } static int session_removed(sd_bus_message *msg, void *userdata, sd_bus_error *ret_error) { wlr_log(WLR_INFO, "SessionRemoved signal received"); return 0; } static struct wlr_device *find_device(struct wlr_session *session, dev_t devnum) { struct wlr_device *dev; wl_list_for_each(dev, &session->devices, link) { if (dev->dev == devnum) { return dev; } } wlr_log(WLR_ERROR, "Tried to use dev_t %lu not opened by session", (unsigned long)devnum); assert(0); return NULL; } static int pause_device(sd_bus_message *msg, void *userdata, sd_bus_error *ret_error) { struct logind_session *session = userdata; int ret; uint32_t major, minor; const char *type; ret = sd_bus_message_read(msg, "uus", &major, &minor, &type); if (ret < 0) { wlr_log(WLR_ERROR, "Failed to parse D-Bus response for PauseDevice: %s", strerror(-ret)); goto error; } if (major == DRM_MAJOR && strcmp(type, "gone") != 0) { assert(session->has_drm); session->base.active = false; wlr_signal_emit_safe(&session->base.session_signal, session); } if (strcmp(type, "pause") == 0) { ret = sd_bus_call_method(session->bus, "org.freedesktop.login1", session->path, "org.freedesktop.login1.Session", "PauseDeviceComplete", ret_error, &msg, "uu", major, minor); if (ret < 0) { wlr_log(WLR_ERROR, "Failed to send PauseDeviceComplete signal: %s", strerror(-ret)); } } error: return 0; } static int resume_device(sd_bus_message *msg, void *userdata, sd_bus_error *ret_error) { struct logind_session *session = userdata; int ret; int fd; uint32_t major, minor; ret = sd_bus_message_read(msg, "uuh", &major, &minor, &fd); if (ret < 0) { wlr_log(WLR_ERROR, "Failed to parse D-Bus response for ResumeDevice: %s", strerror(-ret)); goto error; } if (major == DRM_MAJOR) { struct wlr_device *dev = find_device(&session->base, makedev(major, minor)); close(dev->fd); if (fcntl(fd, F_DUPFD_CLOEXEC, dev->fd) < 0) { wlr_log_errno(WLR_ERROR, "Failed to duplicate file descriptor"); goto error; } if (!session->base.active) { session->base.active = true; wlr_signal_emit_safe(&session->base.session_signal, session); } } error: return 0; } static int session_properties_changed(sd_bus_message *msg, void *userdata, sd_bus_error *ret_error) { struct logind_session *session = userdata; int ret = 0; // if we have a drm fd we don't depend on this if (session->has_drm) { return 0; } // PropertiesChanged arg 1: interface const char *interface; ret = sd_bus_message_read_basic(msg, 's', &interface); // skip path if (ret < 0) { goto error; } if (strcmp(interface, "org.freedesktop.login1.Session") != 0) { // not interesting for us; ignore wlr_log(WLR_DEBUG, "ignoring PropertiesChanged from %s", interface); return 0; } // PropertiesChanged arg 2: changed properties with values ret = sd_bus_message_enter_container(msg, 'a', "{sv}"); if (ret < 0) { goto error; } const char *s; while ((ret = sd_bus_message_enter_container(msg, 'e', "sv")) > 0) { ret = sd_bus_message_read_basic(msg, 's', &s); if (ret < 0) { goto error; } if (strcmp(s, "Active") == 0) { int ret; ret = sd_bus_message_enter_container(msg, 'v', "b"); if (ret < 0) { goto error; } bool active; ret = sd_bus_message_read_basic(msg, 'b', &active); if (ret < 0) { goto error; } if (session->base.active != active) { session->base.active = active; wlr_signal_emit_safe(&session->base.session_signal, session); } return 0; } else { sd_bus_message_skip(msg, "{sv}"); } ret = sd_bus_message_exit_container(msg); if (ret < 0) { goto error; } } if (ret < 0) { goto error; } ret = sd_bus_message_exit_container(msg); if (ret < 0) { goto error; } // PropertiesChanged arg 3: changed properties without values sd_bus_message_enter_container(msg, 'a', "s"); while ((ret = sd_bus_message_read_basic(msg, 's', &s)) > 0) { if (strcmp(s, "Active") == 0) { sd_bus_error error = SD_BUS_ERROR_NULL; bool active; ret = sd_bus_get_property_trivial(session->bus, "org.freedesktop.login1", session->path, "org.freedesktop.login1.Session", "Active", &error, 'b', &active); if (ret < 0) { wlr_log(WLR_ERROR, "Failed to get 'Active' property: %s", error.message); return 0; } if (session->base.active != active) { session->base.active = active; wlr_signal_emit_safe(&session->base.session_signal, session); } return 0; } } if (ret < 0) { goto error; } return 0; error: wlr_log(WLR_ERROR, "Failed to parse D-Bus PropertiesChanged: %s", strerror(-ret)); return 0; } static int seat_properties_changed(sd_bus_message *msg, void *userdata, sd_bus_error *ret_error) { struct logind_session *session = userdata; int ret = 0; // if we have a drm fd we don't depend on this if (session->has_drm) { return 0; } // PropertiesChanged arg 1: interface const char *interface; ret = sd_bus_message_read_basic(msg, 's', &interface); // skip path if (ret < 0) { goto error; } if (strcmp(interface, "org.freedesktop.login1.Seat") != 0) { // not interesting for us; ignore wlr_log(WLR_DEBUG, "ignoring PropertiesChanged from %s", interface); return 0; } // PropertiesChanged arg 2: changed properties with values ret = sd_bus_message_enter_container(msg, 'a', "{sv}"); if (ret < 0) { goto error; } const char *s; while ((ret = sd_bus_message_enter_container(msg, 'e', "sv")) > 0) { ret = sd_bus_message_read_basic(msg, 's', &s); if (ret < 0) { goto error; } if (strcmp(s, "CanGraphical") == 0) { int ret; ret = sd_bus_message_enter_container(msg, 'v', "b"); if (ret < 0) { goto error; } ret = sd_bus_message_read_basic(msg, 'b', &session->can_graphical); if (ret < 0) { goto error; } return 0; } else { sd_bus_message_skip(msg, "{sv}"); } ret = sd_bus_message_exit_container(msg); if (ret < 0) { goto error; } } if (ret < 0) { goto error; } ret = sd_bus_message_exit_container(msg); if (ret < 0) { goto error; } // PropertiesChanged arg 3: changed properties without values sd_bus_message_enter_container(msg, 'a', "s"); while ((ret = sd_bus_message_read_basic(msg, 's', &s)) > 0) { if (strcmp(s, "CanGraphical") == 0) { session->can_graphical = sd_seat_can_graphical(session->base.seat); return 0; } } if (ret < 0) { goto error; } return 0; error: wlr_log(WLR_ERROR, "Failed to parse D-Bus PropertiesChanged: %s", strerror(-ret)); return 0; } static bool add_signal_matches(struct logind_session *session) { static const char *logind = "org.freedesktop.login1"; static const char *logind_path = "/org/freedesktop/login1"; static const char *manager_interface = "org.freedesktop.login1.Manager"; static const char *session_interface = "org.freedesktop.login1.Session"; static const char *property_interface = "org.freedesktop.DBus.Properties"; int ret; ret = sd_bus_match_signal(session->bus, NULL, logind, logind_path, manager_interface, "SessionRemoved", session_removed, session); if (ret < 0) { wlr_log(WLR_ERROR, "Failed to add D-Bus match: %s", strerror(-ret)); return false; } ret = sd_bus_match_signal(session->bus, NULL, logind, session->path, session_interface, "PauseDevice", pause_device, session); if (ret < 0) { wlr_log(WLR_ERROR, "Failed to add D-Bus match: %s", strerror(-ret)); return false; } ret = sd_bus_match_signal(session->bus, NULL, logind, session->path, session_interface, "ResumeDevice", resume_device, session); if (ret < 0) { wlr_log(WLR_ERROR, "Failed to add D-Bus match: %s", strerror(-ret)); return false; } ret = sd_bus_match_signal(session->bus, NULL, logind, session->path, property_interface, "PropertiesChanged", session_properties_changed, session); if (ret < 0) { wlr_log(WLR_ERROR, "Failed to add D-Bus match: %s", strerror(-ret)); return false; } ret = sd_bus_match_signal(session->bus, NULL, logind, session->seat_path, property_interface, "PropertiesChanged", seat_properties_changed, session); if (ret < 0) { wlr_log(WLR_ERROR, "Failed to add D-Bus match: %s", strerror(-ret)); return false; } return true; } static int dbus_event(int fd, uint32_t mask, void *data) { sd_bus *bus = data; while (sd_bus_process(bus, NULL) > 0) { // Do nothing. } return 1; } static bool contains_str(const char *needle, const char **haystack) { for (int i = 0; haystack[i]; i++) { if (strcmp(haystack[i], needle) == 0) { return true; } } return false; } static bool get_greeter_session(char **session_id) { char *class = NULL; char **user_sessions = NULL; int user_session_count = sd_uid_get_sessions(getuid(), 1, &user_sessions); if (user_session_count < 0) { wlr_log(WLR_ERROR, "Failed to get sessions"); goto out; } if (user_session_count == 0) { wlr_log(WLR_ERROR, "User has no sessions"); goto out; } for (int i = 0; i < user_session_count; ++i) { int ret = sd_session_get_class(user_sessions[i], &class); if (ret < 0) { continue; } if (strcmp(class, "greeter") == 0) { *session_id = strdup(user_sessions[i]); goto out; } } out: free(class); for (int i = 0; i < user_session_count; ++i) { free(user_sessions[i]); } free(user_sessions); return *session_id != NULL; } static bool get_display_session(char **session_id) { assert(session_id != NULL); int ret; char *type = NULL; char *state = NULL; char *xdg_session_id = getenv("XDG_SESSION_ID"); if (xdg_session_id) { // This just checks whether the supplied session ID is valid if (sd_session_is_active(xdg_session_id) < 0) { wlr_log(WLR_ERROR, "Invalid XDG_SESSION_ID: '%s'", xdg_session_id); goto error; } *session_id = strdup(xdg_session_id); return true; } // If there's a session active for the current process then just use that ret = sd_pid_get_session(getpid(), session_id); if (ret == 0) { return true; } // Find any active sessions for the user if the process isn't part of an // active session itself ret = sd_uid_get_display(getuid(), session_id); if (ret < 0 && ret != -ENODATA) { wlr_log(WLR_ERROR, "Failed to get display: %s", strerror(-ret)); goto error; } if (ret != 0 && !get_greeter_session(session_id)) { wlr_log(WLR_ERROR, "Couldn't find an active session or a greeter session"); goto error; } assert(*session_id != NULL); // Check that the available session is graphical ret = sd_session_get_type(*session_id, &type); if (ret < 0) { wlr_log(WLR_ERROR, "Couldn't get a type for session '%s': %s", *session_id, strerror(-ret)); goto error; } const char *graphical_session_types[] = {"wayland", "x11", "mir", NULL}; if (!contains_str(type, graphical_session_types)) { wlr_log(WLR_ERROR, "Session '%s' isn't a graphical session (type: '%s')", *session_id, type); goto error; } // Check that the session is active ret = sd_session_get_state(*session_id, &state); if (ret < 0) { wlr_log(WLR_ERROR, "Couldn't get state for session '%s': %s", *session_id, strerror(-ret)); goto error; } const char *active_states[] = {"active", "online", NULL}; if (!contains_str(state, active_states)) { wlr_log(WLR_ERROR, "Session '%s' is not active", *session_id); goto error; } free(type); free(state); return true; error: free(type); free(state); free(*session_id); *session_id = NULL; return false; } static struct wlr_session *logind_session_create(struct wl_display *disp) { int ret; struct logind_session *session = calloc(1, sizeof(*session)); if (!session) { wlr_log(WLR_ERROR, "Allocation failed: %s", strerror(errno)); return NULL; } if (!get_display_session(&session->id)) { goto error; } char *seat; ret = sd_session_get_seat(session->id, &seat); if (ret < 0) { wlr_log(WLR_ERROR, "Failed to get seat id: %s", strerror(-ret)); goto error; } snprintf(session->base.seat, sizeof(session->base.seat), "%s", seat); if (strcmp(seat, "seat0") == 0) { ret = sd_session_get_vt(session->id, &session->base.vtnr); if (ret < 0) { wlr_log(WLR_ERROR, "Session not running in virtual terminal"); goto error; } } free(seat); ret = sd_bus_default_system(&session->bus); if (ret < 0) { wlr_log(WLR_ERROR, "Failed to open D-Bus connection: %s", strerror(-ret)); goto error; } if (!find_session_path(session)) { sd_bus_unref(session->bus); goto error; } if (!find_seat_path(session)) { sd_bus_unref(session->bus); free(session->path); goto error; } if (!add_signal_matches(session)) { goto error_bus; } struct wl_event_loop *event_loop = wl_display_get_event_loop(disp); session->event = wl_event_loop_add_fd(event_loop, sd_bus_get_fd(session->bus), WL_EVENT_READABLE, dbus_event, session->bus); if (!session_activate(session)) { goto error_bus; } if (!take_control(session)) { goto error_bus; } // Check for CanGraphical first session->can_graphical = sd_seat_can_graphical(session->base.seat); if (!session->can_graphical) { wlr_log(WLR_INFO, "Waiting for 'CanGraphical' on seat %s", session->base.seat); } while (!session->can_graphical) { ret = wl_event_loop_dispatch(event_loop, -1); if (ret < 0) { wlr_log(WLR_ERROR, "Polling error waiting for 'CanGraphical' on seat %s", session->base.seat); goto error_bus; } } wlr_log(WLR_INFO, "Successfully loaded logind session"); session->base.impl = &session_logind; return &session->base; error_bus: sd_bus_unref(session->bus); free(session->path); free(session->seat_path); error: free(session->id); return NULL; } const struct session_impl session_logind = { .create = logind_session_create, .destroy = logind_session_destroy, .open = logind_take_device, .close = logind_release_device, .change_vt = logind_change_vt, }; wlroots-0.10.0/backend/session/meson.build000066400000000000000000000024211361211131400205050ustar00rootroot00000000000000wlr_files += files( 'direct-ipc.c', 'noop.c', 'session.c', ) if host_machine.system().startswith('freebsd') wlr_files += files('direct-freebsd.c') else wlr_files += files('direct.c') endif # logind msg = [] if get_option('logind').enabled() msg += 'Install "lib@0@" or pass "-Dlogind=disabled".' endif if not get_option('logind').disabled() msg += 'Required for logind support.' msg += 'You may need to pass "-Dlogind-provider=elogind" or "-Dlogind-provider=systemd" to ensure the correct library is detected.' endif logind = dependency('lib' + get_option('logind-provider'), required: get_option('logind'), not_found_message: '\n'.join(msg).format(get_option('logind-provider')), version: '>=237', ) if logind.found() wlr_files += files('logind.c') wlr_deps += logind conf_data.set10('WLR_HAS_' + get_option('logind-provider').to_upper(), true) endif # libcap msg = [] if get_option('libcap').enabled() msg += 'Install "libcap" or pass "-Dlibcap=disabled".' endif if not get_option('libcap').disabled() msg += 'Required for POSIX capability support (Not needed if using logind).' endif libcap = dependency('libcap', required: get_option('libcap'), not_found_message: '\n'.join(msg), ) if libcap.found() conf_data.set10('WLR_HAS_LIBCAP', true) wlr_deps += libcap endif wlroots-0.10.0/backend/session/noop.c000066400000000000000000000022311361211131400174610ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include "util/signal.h" const struct session_impl session_noop; static int noop_session_open(struct wlr_session *base, const char *path) { return open(path, O_RDWR | O_CLOEXEC); } static void noop_session_close(struct wlr_session *base, int fd) { close(fd); } static bool noop_change_vt(struct wlr_session *base, unsigned vt) { return false; } static void noop_session_destroy(struct wlr_session *base) { free(base); } static struct wlr_session *noop_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; } session->impl = &session_noop; wlr_log(WLR_INFO, "Successfully initialized noop session"); return session; } const struct session_impl session_noop = { .create = noop_session_create, .destroy = noop_session_destroy, .open = noop_session_open, .close = noop_session_close, .change_vt = noop_change_vt, }; wlroots-0.10.0/backend/session/session.c000066400000000000000000000205101361211131400201710ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "util/signal.h" extern const struct session_impl session_logind; extern const struct session_impl session_direct; extern const struct session_impl session_noop; static const struct session_impl *impls[] = { #if WLR_HAS_SYSTEMD || WLR_HAS_ELOGIND &session_logind, #endif &session_direct, NULL, }; static int 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 *action = udev_device_get_action(udev_dev); wlr_log(WLR_DEBUG, "udev event for %s (%s)", udev_device_get_sysname(udev_dev), action); if (!action || strcmp(action, "change") != 0) { goto out; } 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) { wlr_signal_emit_safe(&dev->signal, session); 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 = NULL; const char *env_wlr_session = getenv("WLR_SESSION"); if (env_wlr_session) { if (strcmp(env_wlr_session, "logind") == 0 || strcmp(env_wlr_session, "systemd") == 0) { #if WLR_HAS_SYSTEMD || WLR_HAS_ELOGIND session = session_logind.create(disp); #else wlr_log(WLR_ERROR, "wlroots is not compiled with logind support"); #endif } else if (strcmp(env_wlr_session, "direct") == 0) { session = session_direct.create(disp); } else if (strcmp(env_wlr_session, "noop") == 0) { session = session_noop.create(disp); } else { wlr_log(WLR_ERROR, "Unsupported WLR_SESSION: %s", env_wlr_session); } } else { const struct session_impl **iter; for (iter = impls; !session && *iter; ++iter) { session = (*iter)->create(disp); } } if (!session) { wlr_log(WLR_ERROR, "Failed to load session backend"); return NULL; } session->active = true; wl_signal_init(&session->session_signal); wl_signal_init(&session->events.destroy); wl_list_init(&session->devices); 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, udev_event, session); if (!session->udev_event) { wlr_log_errno(WLR_ERROR, "Failed to create udev event source"); goto error_mon; } 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: session->impl->destroy(session); return NULL; } void wlr_session_destroy(struct wlr_session *session) { if (!session) { return; } wlr_signal_emit_safe(&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); session->impl->destroy(session); } int wlr_session_open_file(struct wlr_session *session, const char *path) { int fd = session->impl->open(session, path); if (fd < 0) { return fd; } 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; wl_signal_init(&dev->signal); wl_list_insert(&session->devices, &dev->link); return fd; error: free(dev); return fd; } static struct wlr_device *find_device(struct wlr_session *session, int fd) { struct wlr_device *dev; wl_list_for_each(dev, &session->devices, link) { if (dev->fd == fd) { return dev; } } wlr_log(WLR_ERROR, "Tried to use fd %d not opened by session", fd); assert(0); return NULL; } void wlr_session_close_file(struct wlr_session *session, int fd) { struct wlr_device *dev = find_device(session, fd); session->impl->close(session, fd); wl_list_remove(&dev->link); free(dev); } void wlr_session_signal_add(struct wlr_session *session, int fd, struct wl_listener *listener) { struct wlr_device *dev = find_device(session, fd); wl_signal_add(&dev->signal, listener); } bool wlr_session_change_vt(struct wlr_session *session, unsigned vt) { if (!session) { return false; } return session->impl->change_vt(session, vt); } /* Tests if 'path' is KMS compatible by trying to open it. * It leaves the open device in *fd_out it it succeeds. */ static int open_if_kms(struct wlr_session *restrict session, const char *restrict path) { if (!path) { return -1; } int fd = wlr_session_open_file(session, path); if (fd < 0) { return -1; } drmVersion *ver = drmGetVersion(fd); if (!ver) { goto out_fd; } drmFreeVersion(ver); return fd; out_fd: wlr_session_close_file(session, fd); return -1; } static size_t explicit_find_gpus(struct wlr_session *session, size_t ret_len, int ret[static ret_len], const char *str) { char *gpus = strdup(str); if (!gpus) { wlr_log_errno(WLR_ERROR, "Allocation failed"); return 0; } size_t i = 0; char *save; char *ptr = strtok_r(gpus, ":", &save); do { if (i >= ret_len) { break; } ret[i] = open_if_kms(session, ptr); if (ret[i] < 0) { wlr_log(WLR_ERROR, "Unable to open %s as DRM device", ptr); } else { ++i; } } while ((ptr = strtok_r(NULL, ":", &save))); free(gpus); return i; } /* 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. */ size_t wlr_session_find_gpus(struct wlr_session *session, size_t ret_len, int *ret) { const char *explicit = getenv("WLR_DRM_DEVICES"); if (explicit) { return explicit_find_gpus(session, ret_len, ret, explicit); } #ifdef __FreeBSD__ // XXX: libudev-devd does not return any GPUs (yet?) return explicit_find_gpus(session, ret_len, ret, "/dev/drm/0"); #else struct udev_enumerate *en = udev_enumerate_new(session->udev); if (!en) { wlr_log(WLR_ERROR, "Failed to create udev enumeration"); return -1; } udev_enumerate_add_match_subsystem(en, "drm"); udev_enumerate_add_match_sysname(en, "card[0-9]*"); udev_enumerate_scan_devices(en); 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; } } int fd = open_if_kms(session, udev_device_get_devnode(dev)); if (fd < 0) { udev_device_unref(dev); continue; } udev_device_unref(dev); ret[i] = fd; if (is_boot_vga) { int tmp = ret[0]; ret[0] = ret[i]; ret[i] = tmp; } ++i; } udev_enumerate_unref(en); return i; #endif } wlroots-0.10.0/backend/wayland/000077500000000000000000000000001361211131400163205ustar00rootroot00000000000000wlroots-0.10.0/backend/wayland/backend.c000066400000000000000000000246761361211131400200720ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include "backend/wayland.h" #include "util/signal.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-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" struct wlr_wl_backend *get_wl_backend_from_backend(struct wlr_backend *backend) { assert(wlr_backend_is_wl(backend)); return (struct wlr_wl_backend *)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 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 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%d", 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) { wl->seat = wl_registry_bind(registry, name, &wl_seat_interface, 5); wl_seat_add_listener(wl->seat, &seat_listener, wl); } 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, 1); } else if (strcmp(iface, wp_presentation_interface.name) == 0) { wl->presentation = wl_registry_bind(registry, name, &wp_presentation_interface, 1); } 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, 3); 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); } } static void registry_global_remove(void *data, struct wl_registry *registry, uint32_t name) { // TODO } 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, "Initializating wayland backend"); wl->started = true; if (wl->keyboard) { create_wl_keyboard(wl->keyboard, wl); } if (wl->tablet_manager && wl->seat) { wl_add_tablet_seat(wl->tablet_manager, wl->seat, wl); } 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); } struct wlr_input_device *input_device, *tmp_input_device; wl_list_for_each_safe(input_device, tmp_input_device, &wl->devices, link) { wlr_input_device_destroy(input_device); } wlr_signal_emit_safe(&wl->backend.events.destroy, &wl->backend); wl_list_remove(&wl->local_display_destroy.link); free(wl->seat_name); wl_event_source_remove(wl->remote_display_src); wlr_renderer_destroy(wl->renderer); wlr_egl_finish(&wl->egl); wlr_drm_format_set_finish(&wl->linux_dmabuf_v1_formats); if (wl->pointer) { wl_pointer_destroy(wl->pointer); } if (wl->seat) { wl_seat_destroy(wl->seat); } 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->presentation) { wp_presentation_destroy(wl->presentation); } if (wl->zwp_linux_dmabuf_v1) { zwp_linux_dmabuf_v1_destroy(wl->zwp_linux_dmabuf_v1); } if (wl->zwp_relative_pointer_manager_v1) { zwp_relative_pointer_manager_v1_destroy(wl->zwp_relative_pointer_manager_v1); } xdg_wm_base_destroy(wl->xdg_wm_base); wl_compositor_destroy(wl->compositor); wl_registry_destroy(wl->registry); wl_display_disconnect(wl->remote_display); free(wl); } static struct wlr_renderer *backend_get_renderer(struct wlr_backend *backend) { struct wlr_wl_backend *wl = get_wl_backend_from_backend(backend); return wl->renderer; } static struct wlr_backend_impl backend_impl = { .start = backend_start, .destroy = backend_destroy, .get_renderer = backend_get_renderer, }; 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, const char *remote, wlr_renderer_create_func_t create_renderer_func) { 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->devices); wl_list_init(&wl->outputs); wl->remote_display = wl_display_connect(remote); if (!wl->remote_display) { wlr_log_errno(WLR_ERROR, "Could not connect to remote display"); goto error_wl; } 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_dispatch(wl->remote_display); wl_display_roundtrip(wl->remote_display); 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 wl_event_loop *loop = wl_display_get_event_loop(wl->local_display); int fd = wl_display_get_fd(wl->remote_display); int events = WL_EVENT_READABLE | WL_EVENT_ERROR | WL_EVENT_HANGUP; wl->remote_display_src = wl_event_loop_add_fd(loop, fd, events, 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); static EGLint config_attribs[] = { EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_RED_SIZE, 1, EGL_GREEN_SIZE, 1, EGL_BLUE_SIZE, 1, EGL_ALPHA_SIZE, 1, EGL_NONE, }; if (!create_renderer_func) { create_renderer_func = wlr_renderer_autocreate; } wl->renderer = create_renderer_func(&wl->egl, EGL_PLATFORM_WAYLAND_EXT, wl->remote_display, config_attribs, WL_SHM_FORMAT_ARGB8888); if (!wl->renderer) { wlr_log(WLR_ERROR, "Could not create renderer"); goto error_event; } wl->local_display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &wl->local_display_destroy); return &wl->backend; error_event: wl_event_source_remove(wl->remote_display_src); error_registry: 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: wl_display_disconnect(wl->remote_display); error_wl: 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.10.0/backend/wayland/meson.build000066400000000000000000000005771361211131400204730ustar00rootroot00000000000000wlr_files += files( 'backend.c', 'output.c', 'wl_seat.c', 'tablet_v2.c', ) client_protos = [ 'linux-dmabuf-unstable-v1', 'pointer-gestures-unstable-v1', 'presentation-time', 'relative-pointer-unstable-v1', 'tablet-unstable-v2', 'xdg-decoration-unstable-v1', 'xdg-shell', ] foreach proto : client_protos wlr_files += get_variable(proto.underscorify() + '_h') endforeach wlroots-0.10.0/backend/wayland/output.c000066400000000000000000000446431361211131400200370ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include "backend/wayland.h" #include "util/signal.h" #include "linux-dmabuf-unstable-v1-client-protocol.h" #include "presentation-time-client-protocol.h" #include "xdg-decoration-unstable-v1-client-protocol.h" #include "xdg-shell-client-protocol.h" static struct wlr_wl_output *get_wl_output_from_output( struct wlr_output *wlr_output) { assert(wlr_output_is_wl(wlr_output)); return (struct wlr_wl_output *)wlr_output; } static void surface_frame_callback(void *data, struct wl_callback *cb, uint32_t time) { struct wlr_wl_output *output = data; assert(output); 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, .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; wlr_output_send_present(&feedback->output->wlr_output, NULL); 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, }; static bool output_set_custom_mode(struct wlr_output *wlr_output, int32_t width, int32_t height, int32_t refresh) { struct wlr_wl_output *output = get_wl_output_from_output(wlr_output); wlr_egl_swap_buffers(&output->backend->egl, output->egl_surface, NULL); wl_egl_window_resize(output->egl_window, width, height, 0, 0); wlr_output_update_custom_mode(&output->wlr_output, width, height, 0); return true; } static bool output_attach_render(struct wlr_output *wlr_output, int *buffer_age) { struct wlr_wl_output *output = get_wl_output_from_output(wlr_output); return wlr_egl_make_current(&output->backend->egl, output->egl_surface, buffer_age); } static void destroy_wl_buffer(struct wlr_wl_buffer *buffer) { if (buffer == NULL) { return; } wl_buffer_destroy(buffer->wl_buffer); wlr_buffer_unref(buffer->buffer); free(buffer); } static void buffer_handle_release(void *data, struct wl_buffer *wl_buffer) { struct wlr_wl_buffer *buffer = data; destroy_wl_buffer(buffer); } static const struct wl_buffer_listener buffer_listener = { .release = buffer_handle_release, }; static struct wlr_wl_buffer *create_wl_buffer(struct wlr_wl_backend *wl, struct wlr_buffer *wlr_buffer, int required_width, int required_height) { struct wlr_dmabuf_attributes attribs; if (!wlr_buffer_get_dmabuf(wlr_buffer, &attribs)) { return NULL; } if (attribs.width != required_width || attribs.height != required_height) { return NULL; } if (!wlr_drm_format_set_has(&wl->linux_dmabuf_v1_formats, attribs.format, attribs.modifier)) { return NULL; } uint32_t modifier_hi = attribs.modifier >> 32; uint32_t modifier_lo = (uint32_t)attribs.modifier; struct zwp_linux_buffer_params_v1 *params = zwp_linux_dmabuf_v1_create_params(wl->zwp_linux_dmabuf_v1); for (int i = 0; i < attribs.n_planes; i++) { zwp_linux_buffer_params_v1_add(params, attribs.fd[i], i, attribs.offset[i], attribs.stride[i], modifier_hi, modifier_lo); } uint32_t flags = 0; if (attribs.flags & WLR_DMABUF_ATTRIBUTES_FLAGS_Y_INVERT) { flags |= ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_Y_INVERT; } if (attribs.flags & WLR_DMABUF_ATTRIBUTES_FLAGS_INTERLACED) { flags |= ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_INTERLACED; } if (attribs.flags & WLR_DMABUF_ATTRIBUTES_FLAGS_BOTTOM_FIRST) { flags |= ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_BOTTOM_FIRST; } struct wl_buffer *wl_buffer = zwp_linux_buffer_params_v1_create_immed( params, attribs.width, attribs.height, attribs.format, flags); // TODO: handle create() errors struct wlr_wl_buffer *buffer = calloc(1, sizeof(struct wlr_wl_buffer)); if (buffer == NULL) { wl_buffer_destroy(wl_buffer); return NULL; } buffer->wl_buffer = wl_buffer; buffer->buffer = wlr_buffer_ref(wlr_buffer); wl_buffer_add_listener(wl_buffer, &buffer_listener, buffer); return buffer; } static bool output_attach_buffer(struct wlr_output *wlr_output, struct wlr_buffer *wlr_buffer) { struct wlr_wl_output *output = get_wl_output_from_output(wlr_output); struct wlr_wl_backend *wl = output->backend; struct wlr_wl_buffer *buffer = create_wl_buffer(wl, wlr_buffer, wlr_output->width, wlr_output->height); if (buffer == NULL) { return false; } destroy_wl_buffer(output->pending_buffer); output->pending_buffer = buffer; return true; } static bool output_commit(struct wlr_output *wlr_output) { struct wlr_wl_output *output = get_wl_output_from_output(wlr_output); if (wlr_output->pending.committed & WLR_OUTPUT_STATE_ENABLED) { wlr_log(WLR_DEBUG, "Cannot disable a Wayland output"); return false; } if (wlr_output->pending.committed & WLR_OUTPUT_STATE_MODE) { assert(wlr_output->pending.mode_type == WLR_OUTPUT_STATE_MODE_CUSTOM); if (!output_set_custom_mode(wlr_output, wlr_output->pending.custom_mode.width, wlr_output->pending.custom_mode.height, wlr_output->pending.custom_mode.refresh)) { return false; } } if (wlr_output->pending.committed & WLR_OUTPUT_STATE_BUFFER) { struct wp_presentation_feedback *wp_feedback = NULL; if (output->backend->presentation != NULL) { wp_feedback = wp_presentation_feedback(output->backend->presentation, output->surface); } pixman_region32_t *damage = NULL; if (wlr_output->pending.committed & WLR_OUTPUT_STATE_DAMAGE) { damage = &wlr_output->pending.damage; } if (output->frame_callback != NULL) { wlr_log(WLR_ERROR, "Skipping buffer swap"); return false; } output->frame_callback = wl_surface_frame(output->surface); wl_callback_add_listener(output->frame_callback, &frame_listener, output); switch (wlr_output->pending.buffer_type) { case WLR_OUTPUT_STATE_BUFFER_RENDER: if (!wlr_egl_swap_buffers(&output->backend->egl, output->egl_surface, damage)) { return false; } break; case WLR_OUTPUT_STATE_BUFFER_SCANOUT: assert(output->pending_buffer != NULL); wl_surface_attach(output->surface, output->pending_buffer->wl_buffer, 0, 0); if (damage == NULL) { wl_surface_damage_buffer(output->surface, 0, 0, INT32_MAX, INT32_MAX); } else { int rects_len; pixman_box32_t *rects = pixman_region32_rectangles(damage, &rects_len); for (int i = 0; i < rects_len; i++) { pixman_box32_t *r = &rects[i]; wl_surface_damage_buffer(output->surface, r->x1, r->y1, r->x2 - r->x1, r->y2 - r->y1); } } wl_surface_commit(output->surface); output->pending_buffer = NULL; break; } 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 { wlr_output_send_present(wlr_output, NULL); } } return true; } static bool output_set_cursor(struct wlr_output *wlr_output, struct wlr_texture *texture, int32_t scale, enum wl_output_transform transform, int32_t hotspot_x, int32_t hotspot_y, bool update_texture) { struct wlr_wl_output *output = get_wl_output_from_output(wlr_output); struct wlr_wl_backend *backend = output->backend; struct wlr_box hotspot = { .x = hotspot_x, .y = hotspot_y }; wlr_box_transform(&hotspot, &hotspot, wlr_output_transform_invert(wlr_output->transform), output->cursor.width, output->cursor.height); // TODO: use output->wlr_output.transform to transform pixels and hotpot output->cursor.hotspot_x = hotspot.x; output->cursor.hotspot_y = hotspot.y; if (!update_texture) { // Update hotspot without changing cursor image update_wl_output_cursor(output); return true; } if (output->cursor.surface == NULL) { output->cursor.surface = wl_compositor_create_surface(backend->compositor); } struct wl_surface *surface = output->cursor.surface; if (texture != NULL) { int width, height; wlr_texture_get_size(texture, &width, &height); width = width * wlr_output->scale / scale; height = height * wlr_output->scale / scale; output->cursor.width = width; output->cursor.height = height; if (output->cursor.egl_window == NULL) { output->cursor.egl_window = wl_egl_window_create(surface, width, height); } wl_egl_window_resize(output->cursor.egl_window, width, height, 0, 0); EGLSurface egl_surface = wlr_egl_create_surface(&backend->egl, output->cursor.egl_window); wlr_egl_make_current(&backend->egl, egl_surface, NULL); struct wlr_box cursor_box = { .width = width, .height = height, }; float projection[9]; wlr_matrix_projection(projection, width, height, wlr_output->transform); float matrix[9]; wlr_matrix_project_box(matrix, &cursor_box, transform, 0, projection); wlr_renderer_begin(backend->renderer, width, height); wlr_renderer_clear(backend->renderer, (float[]){ 0.0, 0.0, 0.0, 0.0 }); wlr_render_texture_with_matrix(backend->renderer, texture, matrix, 1.0); wlr_renderer_end(backend->renderer); wlr_egl_swap_buffers(&backend->egl, egl_surface, NULL); wlr_egl_destroy_surface(&backend->egl, egl_surface); } else { wl_surface_attach(surface, NULL, 0, 0); wl_surface_commit(surface); } update_wl_output_cursor(output); return true; } 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.egl_window != NULL) { wl_egl_window_destroy(output->cursor.egl_window); } 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); } destroy_wl_buffer(output->pending_buffer); wlr_egl_destroy_surface(&output->backend->egl, output->egl_surface); wl_egl_window_destroy(output->egl_window); if (output->zxdg_toplevel_decoration_v1) { zxdg_toplevel_decoration_v1_destroy(output->zxdg_toplevel_decoration_v1); } xdg_toplevel_destroy(output->xdg_toplevel); xdg_surface_destroy(output->xdg_surface); wl_surface_destroy(output->surface); free(output); } void update_wl_output_cursor(struct wlr_wl_output *output) { if (output->backend->pointer && output->enter_serial) { wl_pointer_set_cursor(output->backend->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 bool output_schedule_frame(struct wlr_output *wlr_output) { struct wlr_wl_output *output = get_wl_output_from_output(wlr_output); if (output->frame_callback != NULL) { wlr_log(WLR_ERROR, "Skipping frame scheduling"); return true; } output->frame_callback = wl_surface_frame(output->surface); wl_callback_add_listener(output->frame_callback, &frame_listener, output); wl_surface_commit(output->surface); return true; } static const struct wlr_output_impl output_impl = { .destroy = output_destroy, .attach_render = output_attach_render, .attach_buffer = output_attach_buffer, .commit = output_commit, .set_cursor = output_set_cursor, .move_cursor = output_move_cursor, .schedule_frame = output_schedule_frame, }; 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); xdg_surface_ack_configure(xdg_surface, serial); // nothing else? } static 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; } // loop over states for maximized etc? output_set_custom_mode(&output->wlr_output, width, height, 0); } 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((struct wlr_output *)output); } static struct xdg_toplevel_listener xdg_toplevel_listener = { .configure = xdg_toplevel_handle_configure, .close = xdg_toplevel_handle_close, }; 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 wlr_wl_output *output; if (!(output = calloc(sizeof(struct wlr_wl_output), 1))) { wlr_log(WLR_ERROR, "Failed to allocate wlr_wl_output"); return NULL; } wlr_output_init(&output->wlr_output, &backend->backend, &output_impl, backend->local_display); struct wlr_output *wlr_output = &output->wlr_output; wlr_output_update_custom_mode(wlr_output, 1280, 720, 0); strncpy(wlr_output->make, "wayland", sizeof(wlr_output->make)); strncpy(wlr_output->model, "wayland", sizeof(wlr_output->model)); snprintf(wlr_output->name, sizeof(wlr_output->name), "WL-%zd", ++backend->last_output_num); char description[128]; snprintf(description, sizeof(description), "Wayland output %zd", backend->last_output_num); wlr_output_set_description(wlr_output, description); output->backend = backend; wl_list_init(&output->presentation_feedbacks); output->surface = wl_compositor_create_surface(backend->compositor); if (!output->surface) { wlr_log_errno(WLR_ERROR, "Could not create output surface"); goto error; } wl_surface_set_user_data(output->surface, output); 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(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); output->egl_window = wl_egl_window_create(output->surface, wlr_output->width, wlr_output->height); output->egl_surface = wlr_egl_create_surface(&backend->egl, output->egl_window); wl_display_roundtrip(output->backend->remote_display); // start rendering loop per callbacks by rendering first frame if (!wlr_egl_make_current(&output->backend->egl, output->egl_surface, NULL)) { goto error; } wlr_renderer_begin(backend->renderer, wlr_output->width, wlr_output->height); wlr_renderer_clear(backend->renderer, (float[]){ 1.0, 1.0, 1.0, 1.0 }); wlr_renderer_end(backend->renderer); output->frame_callback = wl_surface_frame(output->surface); wl_callback_add_listener(output->frame_callback, &frame_listener, output); if (!wlr_egl_swap_buffers(&output->backend->egl, output->egl_surface, NULL)) { goto error; } wl_list_insert(&backend->outputs, &output->link); wlr_output_update_enabled(wlr_output, true); wlr_signal_emit_safe(&backend->backend.events.new_output, wlr_output); if (backend->pointer != NULL) { create_wl_pointer(backend->pointer, output); } return wlr_output; error: wlr_output_destroy(&output->wlr_output); return NULL; } 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); 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); } 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.10.0/backend/wayland/tablet_v2.c000066400000000000000000000640201361211131400203500ustar00rootroot00000000000000#ifndef _POSIX_C_SOURCE #define _POSIX_C_SOURCE 200809L #endif #include #include #include #include #include #include #include #include #include #include "util/signal.h" #include "wlr/util/log.h" #include "tablet-unstable-v2-client-protocol.h" #include "backend/wayland.h" struct wlr_wl_tablet_seat { struct zwp_tablet_seat_v2 *tablet_seat; }; struct wlr_wl_tablet_tool { /* static */ struct zwp_tablet_tool_v2 *tool; struct wlr_tablet_tool wlr_tool; /* semi-static */ struct wlr_wl_input_device *tablet; 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 wlr_wl_tablet_pad_ring { struct wl_list link; // wlr_wl_tablet_pad_group::rings /* static */ struct zwp_tablet_pad_ring_v2 *ring; struct wlr_wl_tablet_pad_group *group; size_t index; /* per frame */ enum wlr_tablet_pad_ring_source source; double angle; bool stopped; }; struct wlr_wl_tablet_pad_strip { struct wl_list link; // wlr_wl_tablet_pad_group::strips struct zwp_tablet_pad_strip_v2 *strip; struct wlr_wl_tablet_pad_group *group; size_t index; enum wlr_tablet_pad_strip_source source; double position; bool stopped; }; struct wlr_wl_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; // wlr_wl_tablet_pad_ring::link struct wl_list strips; // wlr_wl_tablet_pad_strips::link }; static uint32_t get_current_time_msec(void) { struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); return now.tv_nsec / (1000 * 1000) + now.tv_sec * 1000; } static void handle_tablet_pad_ring_source(void *data, struct zwp_tablet_pad_ring_v2 *zwp_tablet_pad_ring_v2, uint32_t source) { struct wlr_wl_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 wlr_wl_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 wlr_wl_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 wlr_wl_tablet_pad_ring *ring = data; struct wlr_event_tablet_pad_ring evt = { .time_msec = time, .source = ring->source, .ring = ring->index, .position = ring->angle, .mode = ring->group->mode, }; if (ring->angle >= 0) { wlr_signal_emit_safe(&ring->group->pad->events.ring, &evt); } if (ring->stopped) { evt.position = -1; wlr_signal_emit_safe(&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 wlr_wl_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 wlr_wl_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 wlr_wl_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 wlr_wl_tablet_pad_strip *strip = data; struct wlr_event_tablet_pad_strip evt = { .time_msec = time, .source = strip->source, .strip = strip->index, .position = strip->position, .mode = strip->group->mode, }; if (strip->position >= 0) { wlr_signal_emit_safe(&strip->group->pad->events.strip, &evt); } if (strip->stopped) { evt.position = -1; wlr_signal_emit_safe(&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 wlr_wl_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 wlr_wl_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 wlr_wl_tablet_pad_group *group = data; struct wlr_wl_tablet_pad_ring *tablet_ring = calloc(1, sizeof(struct wlr_wl_tablet_pad_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 wlr_wl_tablet_pad_group *group = data; struct wlr_wl_tablet_pad_strip *tablet_strip = calloc(1, sizeof(struct wlr_wl_tablet_pad_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 wlr_wl_tablet_pad_group *group = data; group->mode = mode; } /* This isn't in the listener, but keep the naming scheme around since the * other removed functions work like this, and pad sub-resources are just a bit * special */ static void handle_tablet_pad_group_removed( struct wlr_wl_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 wlr_wl_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 wlr_wl_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); /* While I'm pretty sure we have control over this as well, it's * outside the scope of a single function, so better be safe than * sorry */ 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_input_device *dev = data; struct wlr_tablet_pad *pad = dev->wlr_input_device.tablet_pad; struct wlr_wl_tablet_pad_group *group = calloc(1, sizeof(struct wlr_wl_tablet_pad_group)); if (!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_input_device *dev = data; struct wlr_tablet_pad *tablet_pad = dev->wlr_input_device.tablet_pad; wlr_list_push(&tablet_pad->paths, 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_input_device *dev = data; struct wlr_tablet_pad *tablet_pad = dev->wlr_input_device.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_input_device *dev = data; struct wlr_tablet_pad *tablet_pad = dev->wlr_input_device.tablet_pad; struct wlr_event_tablet_pad_button evt = { .time_msec = time, .button = button, .state = state, .mode = 0, .group = 0, }; wlr_signal_emit_safe(&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_input_device *dev = data; wlr_signal_emit_safe(&dev->backend->backend.events.new_input, &dev->wlr_input_device); } 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_input_device *dev = data; struct wlr_tablet_pad *tablet_pad = dev->wlr_input_device.tablet_pad; struct wlr_wl_input_device *tab_dev = zwp_tablet_v2_get_user_data(tablet_p); struct wlr_input_device *tablet = &tab_dev->wlr_input_device; wlr_log(WLR_DEBUG, "Tablet: %p\n", tablet); wlr_signal_emit_safe(&tablet_pad->events.attach_tablet, tablet); } 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_input_device *dev = data; struct wlr_tablet_pad *tablet_pad = dev->wlr_input_device.tablet_pad; /* This doesn't free anything, but emits the destroy signal */ wlr_input_device_destroy(&dev->wlr_input_device); /* This is a bit ugly, but we need to remove it from our list */ wl_list_remove(&dev->wlr_input_device.link); struct wlr_wl_tablet_pad_group *group, *it; wl_list_for_each_safe(group, it, &tablet_pad->groups, group.link) { handle_tablet_pad_group_removed(group); } /* This frees */ wlr_tablet_pad_destroy(tablet_pad); zwp_tablet_pad_v2_destroy(dev->resource); free(dev); } 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, }; static void handle_pad_added(void *data, struct zwp_tablet_seat_v2 *zwp_tablet_seat_v2, struct zwp_tablet_pad_v2 *id) { wlr_log(WLR_DEBUG, "New tablet pad"); struct wlr_wl_backend *backend = data; struct wlr_wl_input_device *dev = create_wl_input_device( backend, WLR_INPUT_DEVICE_TABLET_PAD); if (!dev) { /* This leaks a couple of server-sent resource ids. iirc this * shouldn't ever be a problem, but it isn't exactly nice * either. */ zwp_tablet_pad_v2_destroy(id); return; } dev->resource = id; struct wlr_input_device *wlr_dev = &dev->wlr_input_device; wlr_dev->tablet_pad = calloc(1, sizeof(*wlr_dev->tablet_pad)); if (!wlr_dev->tablet_pad) { /* This leaks a couple of server-sent resource ids. iirc this * shouldn't ever be a problem, but it isn't exactly nice * either. */ free(dev); zwp_tablet_pad_v2_destroy(id); return; } wlr_tablet_pad_init(wlr_dev->tablet_pad, NULL); zwp_tablet_pad_v2_add_listener(id, &tablet_pad_listener, dev); } 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; default: break; } assert(false && "Unreachable"); } static void handle_tablet_tool_type(void *data, struct zwp_tablet_tool_v2 *id, uint32_t tool_type) { struct wlr_wl_tablet_tool *tool = data; 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 wlr_wl_tablet_tool *tool = data; 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 wlr_wl_tablet_tool *tool = data; 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 wlr_wl_tablet_tool *tool = data; enum zwp_tablet_tool_v2_capability cap = capability; switch (cap) { case ZWP_TABLET_TOOL_V2_CAPABILITY_TILT: tool->wlr_tool.tilt = true; break; case ZWP_TABLET_TOOL_V2_CAPABILITY_PRESSURE: tool->wlr_tool.pressure = true; break; case ZWP_TABLET_TOOL_V2_CAPABILITY_DISTANCE: tool->wlr_tool.distance = true; break; case ZWP_TABLET_TOOL_V2_CAPABILITY_ROTATION: tool->wlr_tool.rotation = true; break; case ZWP_TABLET_TOOL_V2_CAPABILITY_SLIDER: tool->wlr_tool.slider = true; break; case ZWP_TABLET_TOOL_V2_CAPABILITY_WHEEL: tool->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 wlr_wl_tablet_tool *tool = data; tool->is_in = true; tool->tablet = zwp_tablet_v2_get_user_data(tablet_id); } static void handle_tablet_tool_proximity_out(void *data, struct zwp_tablet_tool_v2 *id) { struct wlr_wl_tablet_tool *tool = data; tool->is_out = true; } static void handle_tablet_tool_down(void *data, struct zwp_tablet_tool_v2 *id, unsigned int serial) { struct wlr_wl_tablet_tool *tool = data; tool->is_down = true; } static void handle_tablet_tool_up(void *data, struct zwp_tablet_tool_v2 *id) { struct wlr_wl_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 wlr_wl_tablet_tool *tool = data; tool->x = wl_fixed_to_double(x); tool->y = wl_fixed_to_double(y); } static void handle_tablet_tool_pressure(void *data, struct zwp_tablet_tool_v2 *id, uint32_t pressure) { struct wlr_wl_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 wlr_wl_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 wlr_wl_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 wlr_wl_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 wlr_wl_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 wlr_wl_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 wlr_wl_tablet_tool *tool = data; struct wlr_tablet *tablet = tool->tablet->wlr_input_device.tablet; struct wlr_event_tablet_tool_button evt = { .device = &tool->tablet->wlr_input_device, .tool = &tool->wlr_tool, .time_msec = get_current_time_msec(), .button = button, .state = state == ZWP_TABLET_TOOL_V2_BUTTON_STATE_RELEASED ? WLR_BUTTON_RELEASED : WLR_BUTTON_PRESSED, }; wlr_signal_emit_safe(&tablet->events.button, &evt); } static void clear_tablet_tool_values(struct wlr_wl_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 wlr_wl_tablet_tool *tool = data; 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 = tool->tablet->wlr_input_device.tablet; if (tool->is_in) { struct wlr_event_tablet_tool_proximity evt = { .device = &tool->tablet->wlr_input_device, .tool = &tool->wlr_tool, .time_msec = time, .x = tool->x, .y = tool->y, .state = WLR_TABLET_TOOL_PROXIMITY_IN, }; wlr_signal_emit_safe(&tablet->events.proximity, &evt); } { struct wlr_event_tablet_tool_axis evt = { .device = &tool->tablet->wlr_input_device, .tool = &tool->wlr_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) { wlr_signal_emit_safe(&tablet->events.proximity, &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_event_tablet_tool_tip evt = { .device = &tool->tablet->wlr_input_device, .tool = &tool->wlr_tool, .time_msec = time, .x = tool->x, .y = tool->y, .state = WLR_TABLET_TOOL_TIP_DOWN, }; wlr_signal_emit_safe(&tablet->events.tip, &evt); } if (tool->is_up) { struct wlr_event_tablet_tool_tip evt = { .device = &tool->tablet->wlr_input_device, .tool = &tool->wlr_tool, .time_msec = time, .x = tool->x, .y = tool->y, .state = WLR_TABLET_TOOL_TIP_UP, }; wlr_signal_emit_safe(&tablet->events.tip, &evt); } if (tool->is_out) { struct wlr_event_tablet_tool_proximity evt = { .device = &tool->tablet->wlr_input_device, .tool = &tool->wlr_tool, .time_msec = time, .x = tool->x, .y = tool->y, .state = WLR_TABLET_TOOL_PROXIMITY_OUT, }; wlr_signal_emit_safe(&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 wlr_wl_tablet_tool *tool = data; zwp_tablet_tool_v2_destroy(tool->tool); wlr_signal_emit_safe(&tool->wlr_tool.events.destroy, &tool->wlr_tool); 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 *id) { wlr_log(WLR_DEBUG, "New tablet tool"); struct wlr_wl_tablet_tool *tool = calloc(1, sizeof(*tool)); if (!tool) { zwp_tablet_tool_v2_destroy(id); return; } tool->tool = id; clear_tablet_tool_values(tool); wl_signal_init(&tool->wlr_tool.events.destroy); zwp_tablet_tool_v2_add_listener(id, &tablet_tool_listener, tool); } static void handle_tablet_name(void *data, struct zwp_tablet_v2 *zwp_tablet_v2, const char *name) { struct wlr_wl_input_device *dev = data; struct wlr_tablet *tablet = dev->wlr_input_device.tablet; free(tablet->name); tablet->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_input_device *dev = data; dev->wlr_input_device.vendor = vid; dev->wlr_input_device.product = pid; } static void handle_tablet_path(void *data, struct zwp_tablet_v2 *zwp_tablet_v2, const char *path) { struct wlr_wl_input_device *dev = data; struct wlr_tablet *tablet = dev->wlr_input_device.tablet; wlr_list_push(&tablet->paths, strdup(path)); } static void handle_tablet_done(void *data, struct zwp_tablet_v2 *zwp_tablet_v2) { struct wlr_wl_input_device *dev = data; wlr_signal_emit_safe(&dev->backend->backend.events.new_input, &dev->wlr_input_device); } static void handle_tablet_removed(void *data, struct zwp_tablet_v2 *zwp_tablet_v2) { struct wlr_wl_input_device *dev = data; /* This doesn't free anything, but emits the destroy signal */ wlr_input_device_destroy(&dev->wlr_input_device); /* This is a bit ugly, but we need to remove it from our list */ wl_list_remove(&dev->wlr_input_device.link); zwp_tablet_v2_destroy(dev->resource); free(dev); } 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, }; static void handle_tab_added(void *data, struct zwp_tablet_seat_v2 *zwp_tablet_seat_v2, struct zwp_tablet_v2 *id) { wlr_log(WLR_DEBUG, "New tablet"); struct wlr_wl_backend *backend = data; struct wlr_wl_input_device *dev = create_wl_input_device( backend, WLR_INPUT_DEVICE_TABLET_TOOL); if (!dev) { zwp_tablet_v2_destroy(id); return; } dev->resource = id; struct wlr_input_device *wlr_dev = &dev->wlr_input_device; wlr_dev->tablet = calloc(1, sizeof(*wlr_dev->tablet)); if (!wlr_dev->tablet) { zwp_tablet_v2_destroy(id); return; } zwp_tablet_v2_set_user_data(id, wlr_dev->tablet); wlr_tablet_init(wlr_dev->tablet, NULL); zwp_tablet_v2_add_listener(id, &tablet_listener, dev); } 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, }; struct wlr_wl_tablet_seat *wl_add_tablet_seat( struct zwp_tablet_manager_v2 *manager, struct wl_seat *seat, struct wlr_wl_backend *backend) { struct wlr_wl_tablet_seat *ret = calloc(1, sizeof(struct wlr_wl_tablet_seat)); if (!(ret->tablet_seat = zwp_tablet_manager_v2_get_tablet_seat(manager, seat))) { free(ret); return NULL; } zwp_tablet_seat_v2_add_listener(ret->tablet_seat, &tablet_seat_listener, backend); return ret; } wlroots-0.10.0/backend/wayland/wl_seat.c000066400000000000000000000454271361211131400201360ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include #include #include #include #include #include "pointer-gestures-unstable-v1-client-protocol.h" #include "relative-pointer-unstable-v1-client-protocol.h" #include "backend/wayland.h" #include "util/signal.h" static struct wlr_wl_pointer *output_get_pointer(struct wlr_wl_output *output) { struct wlr_input_device *wlr_dev; wl_list_for_each(wlr_dev, &output->backend->devices, link) { if (wlr_dev->type != WLR_INPUT_DEVICE_POINTER) { continue; } struct wlr_wl_pointer *pointer = pointer_get_wl(wlr_dev->pointer); 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_backend *backend = data; if (surface == NULL) { return; } struct wlr_wl_output *output = wl_surface_get_user_data(surface); assert(output); struct wlr_wl_pointer *pointer = output_get_pointer(output); output->enter_serial = serial; backend->current_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_backend *backend = data; if (surface == NULL) { return; } struct wlr_wl_output *output = wl_surface_get_user_data(surface); assert(output); output->enter_serial = 0; if (backend->current_pointer == NULL || backend->current_pointer->output != output) { return; } backend->current_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_backend *backend = data; struct wlr_wl_pointer *pointer = backend->current_pointer; if (pointer == NULL) { return; } struct wlr_output *wlr_output = &pointer->output->wlr_output; struct wlr_event_pointer_motion_absolute event = { .device = &pointer->input_device->wlr_input_device, .time_msec = time, .x = wl_fixed_to_double(sx) / wlr_output->width, .y = wl_fixed_to_double(sy) / wlr_output->height, }; wlr_signal_emit_safe(&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_backend *backend = data; struct wlr_wl_pointer *pointer = backend->current_pointer; if (pointer == NULL) { return; } struct wlr_event_pointer_button event = { .device = &pointer->input_device->wlr_input_device, .button = button, .state = state, .time_msec = time, }; wlr_signal_emit_safe(&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_backend *backend = data; struct wlr_wl_pointer *pointer = backend->current_pointer; if (pointer == NULL) { return; } struct wlr_event_pointer_axis event = { .device = &pointer->input_device->wlr_input_device, .delta = wl_fixed_to_double(value), .delta_discrete = pointer->axis_discrete, .orientation = axis, .time_msec = time, .source = pointer->axis_source, }; wlr_signal_emit_safe(&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_backend *backend = data; struct wlr_wl_pointer *pointer = backend->current_pointer; if (pointer == NULL) { return; } wlr_signal_emit_safe(&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_backend *backend = data; struct wlr_wl_pointer *pointer = backend->current_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_backend *backend = data; struct wlr_wl_pointer *pointer = backend->current_pointer; if (pointer == NULL) { return; } struct wlr_event_pointer_axis event = { .device = &pointer->input_device->wlr_input_device, .delta = 0, .delta_discrete = 0, .orientation = axis, .time_msec = time, .source = pointer->axis_source, }; wlr_signal_emit_safe(&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_backend *backend = data; struct wlr_wl_pointer *pointer = backend->current_pointer; if (pointer == NULL) { return; } pointer->axis_discrete = discrete; } 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, }; static void keyboard_handle_keymap(void *data, struct wl_keyboard *wl_keyboard, uint32_t format, int32_t fd, uint32_t size) { // TODO: set keymap } static uint32_t get_current_time_msec(void) { struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); return now.tv_nsec / 1000; } 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_input_device *dev = data; uint32_t time = get_current_time_msec(); uint32_t *keycode_ptr; wl_array_for_each(keycode_ptr, keys) { struct wlr_event_keyboard_key event = { .keycode = *keycode_ptr, .state = WLR_KEY_PRESSED, .time_msec = time, .update_state = false, }; wlr_keyboard_notify_key(dev->keyboard, &event); } } static void keyboard_handle_leave(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface) { struct wlr_input_device *dev = data; uint32_t time = get_current_time_msec(); uint32_t pressed[dev->keyboard->num_keycodes + 1]; memcpy(pressed, dev->keyboard->keycodes, dev->keyboard->num_keycodes * sizeof(uint32_t)); for (size_t i = 0; i < sizeof(pressed)/sizeof(pressed[0]); ++i) { uint32_t keycode = pressed[i]; struct wlr_event_keyboard_key event = { .keycode = keycode, .state = WLR_KEY_RELEASED, .time_msec = time, .update_state = false, }; wlr_keyboard_notify_key(dev->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_input_device *dev = data; assert(dev && dev->keyboard); struct wlr_event_keyboard_key wlr_event = { .keycode = key, .state = state, .time_msec = time, .update_state = false, }; wlr_keyboard_notify_key(dev->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_input_device *dev = data; assert(dev && dev->keyboard); wlr_keyboard_notify_modifiers(dev->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 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 struct wlr_wl_input_device *get_wl_input_device_from_input_device( struct wlr_input_device *wlr_dev) { assert(wlr_input_device_is_wl(wlr_dev)); return (struct wlr_wl_input_device *)wlr_dev; } static void input_device_destroy(struct wlr_input_device *wlr_dev) { struct wlr_wl_input_device *dev = get_wl_input_device_from_input_device(wlr_dev); if (dev->resource) { wl_proxy_destroy(dev->resource); } wl_list_remove(&dev->wlr_input_device.link); free(dev); } static struct wlr_input_device_impl input_device_impl = { .destroy = input_device_destroy, }; bool wlr_input_device_is_wl(struct wlr_input_device *dev) { return dev->impl == &input_device_impl; } struct wlr_wl_input_device *create_wl_input_device( struct wlr_wl_backend *backend, enum wlr_input_device_type type) { struct wlr_wl_input_device *dev = calloc(1, sizeof(struct wlr_wl_input_device)); if (dev == NULL) { wlr_log_errno(WLR_ERROR, "Allocation failed"); return NULL; } dev->backend = backend; struct wlr_input_device *wlr_dev = &dev->wlr_input_device; unsigned int vendor = 0, product = 0; const char *name = "wayland"; wlr_input_device_init(wlr_dev, type, &input_device_impl, name, vendor, product); wl_list_insert(&backend->devices, &wlr_dev->link); return dev; } static struct wlr_pointer_impl pointer_impl; struct wlr_wl_pointer *pointer_get_wl(struct wlr_pointer *wlr_pointer) { assert(wlr_pointer->impl == &pointer_impl); return (struct wlr_wl_pointer *)wlr_pointer; } static void pointer_destroy(struct wlr_pointer *wlr_pointer) { struct wlr_wl_pointer *pointer = pointer_get_wl(wlr_pointer); if (pointer->output->backend->current_pointer == pointer) { pointer->output->backend->current_pointer = NULL; } wl_list_remove(&pointer->output_destroy.link); free(pointer); } static struct wlr_pointer_impl pointer_impl = { .destroy = pointer_destroy, }; 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_input_device *input_device = (struct wlr_wl_input_device *)data; struct wlr_input_device *wlr_dev = &input_device->wlr_input_device; struct wlr_event_pointer_swipe_begin wlr_event = { .device = wlr_dev, .time_msec = time, .fingers = fingers, }; input_device->fingers = fingers; wlr_signal_emit_safe(&wlr_dev->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_input_device *input_device = (struct wlr_wl_input_device *)data; struct wlr_input_device *wlr_dev = &input_device->wlr_input_device; struct wlr_event_pointer_swipe_update wlr_event = { .device = wlr_dev, .time_msec = time, .fingers = input_device->fingers, .dx = wl_fixed_to_double(dx), .dy = wl_fixed_to_double(dy), }; wlr_signal_emit_safe(&wlr_dev->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_input_device *input_device = (struct wlr_wl_input_device *)data; struct wlr_input_device *wlr_dev = &input_device->wlr_input_device; struct wlr_event_pointer_swipe_end wlr_event = { .device = wlr_dev, .time_msec = time, .cancelled = cancelled, }; wlr_signal_emit_safe(&wlr_dev->pointer->events.swipe_end, &wlr_event); } static 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_input_device *input_device = (struct wlr_wl_input_device *)data; struct wlr_input_device *wlr_dev = &input_device->wlr_input_device; struct wlr_event_pointer_pinch_begin wlr_event = { .device = wlr_dev, .time_msec = time, .fingers = fingers, }; input_device->fingers = fingers; wlr_signal_emit_safe(&wlr_dev->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_input_device *input_device = (struct wlr_wl_input_device *)data; struct wlr_input_device *wlr_dev = &input_device->wlr_input_device; struct wlr_event_pointer_pinch_update wlr_event = { .device = wlr_dev, .time_msec = time, .fingers = input_device->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), }; wlr_signal_emit_safe(&wlr_dev->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_input_device *input_device = (struct wlr_wl_input_device *)data; struct wlr_input_device *wlr_dev = &input_device->wlr_input_device; struct wlr_event_pointer_pinch_end wlr_event = { .device = wlr_dev, .time_msec = time, .cancelled = cancelled, }; wlr_signal_emit_safe(&wlr_dev->pointer->events.pinch_end, &wlr_event); } static struct zwp_pointer_gesture_pinch_v1_listener gesture_pinch_impl = { .begin = gesture_pinch_begin, .update = gesture_pinch_update, .end = gesture_pinch_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_input_device *input_device = data; struct wlr_input_device *wlr_dev = &input_device->wlr_input_device; uint64_t time_usec = (uint64_t)utime_hi << 32 | utime_lo; struct wlr_event_pointer_motion wlr_event = { .device = wlr_dev, .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), }; wlr_signal_emit_safe(&wlr_dev->pointer->events.motion, &wlr_event); } static const struct zwp_relative_pointer_v1_listener relative_pointer_listener = { .relative_motion = relative_pointer_handle_relative_motion, }; static void pointer_handle_output_destroy(struct wl_listener *listener, void *data) { struct wlr_wl_pointer *pointer = wl_container_of(listener, pointer, output_destroy); wlr_input_device_destroy(&pointer->input_device->wlr_input_device); } void create_wl_pointer(struct wl_pointer *wl_pointer, struct wlr_wl_output *output) { struct wlr_wl_backend *backend = output->backend; struct wlr_input_device *wlr_dev; wl_list_for_each(wlr_dev, &output->backend->devices, link) { if (wlr_dev->type != WLR_INPUT_DEVICE_POINTER) { continue; } struct wlr_wl_pointer *pointer = pointer_get_wl(wlr_dev->pointer); if (pointer->output == output) { return; } } struct wlr_wl_pointer *pointer = calloc(1, sizeof(struct wlr_wl_pointer)); if (pointer == NULL) { wlr_log(WLR_ERROR, "Allocation failed"); return; } pointer->wl_pointer = wl_pointer; pointer->output = output; wl_signal_add(&output->wlr_output.events.destroy, &pointer->output_destroy); pointer->output_destroy.notify = pointer_handle_output_destroy; struct wlr_wl_input_device *dev = create_wl_input_device(backend, WLR_INPUT_DEVICE_POINTER); if (dev == NULL) { free(pointer); wlr_log(WLR_ERROR, "Allocation failed"); return; } pointer->input_device = dev; wlr_dev = &dev->wlr_input_device; wlr_dev->pointer = &pointer->wlr_pointer; wlr_dev->output_name = strdup(output->wlr_output.name); wlr_pointer_init(wlr_dev->pointer, &pointer_impl); if (backend->zwp_pointer_gestures_v1) { pointer->gesture_swipe = zwp_pointer_gestures_v1_get_swipe_gesture( backend->zwp_pointer_gestures_v1, wl_pointer); zwp_pointer_gesture_swipe_v1_add_listener(pointer->gesture_swipe, &gesture_swipe_impl, dev); pointer->gesture_pinch = zwp_pointer_gestures_v1_get_pinch_gesture( backend->zwp_pointer_gestures_v1, wl_pointer); zwp_pointer_gesture_pinch_v1_add_listener(pointer->gesture_pinch, &gesture_pinch_impl, dev); } if (backend->zwp_relative_pointer_manager_v1) { pointer->relative_pointer = zwp_relative_pointer_manager_v1_get_relative_pointer( backend->zwp_relative_pointer_manager_v1, wl_pointer); zwp_relative_pointer_v1_add_listener(pointer->relative_pointer, &relative_pointer_listener, dev); } wlr_signal_emit_safe(&backend->backend.events.new_input, wlr_dev); } void create_wl_keyboard(struct wl_keyboard *wl_keyboard, struct wlr_wl_backend *wl) { struct wlr_wl_input_device *dev = create_wl_input_device(wl, WLR_INPUT_DEVICE_KEYBOARD); if (!dev) { return; } struct wlr_input_device *wlr_dev = &dev->wlr_input_device; wlr_dev->keyboard = calloc(1, sizeof(*wlr_dev->keyboard)); if (!wlr_dev->keyboard) { wlr_log_errno(WLR_ERROR, "Allocation failed"); free(dev); return; } wlr_keyboard_init(wlr_dev->keyboard, NULL); wl_keyboard_add_listener(wl_keyboard, &keyboard_listener, wlr_dev); dev->resource = wl_keyboard; wlr_signal_emit_safe(&wl->backend.events.new_input, wlr_dev); } static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat, enum wl_seat_capability caps) { struct wlr_wl_backend *backend = data; assert(backend->seat == wl_seat); if ((caps & WL_SEAT_CAPABILITY_POINTER)) { wlr_log(WLR_DEBUG, "seat %p offered pointer", (void*) wl_seat); struct wl_pointer *wl_pointer = wl_seat_get_pointer(wl_seat); backend->pointer = wl_pointer; struct wlr_wl_output *output; wl_list_for_each(output, &backend->outputs, link) { create_wl_pointer(wl_pointer, output); } wl_pointer_add_listener(wl_pointer, &pointer_listener, backend); } if ((caps & WL_SEAT_CAPABILITY_KEYBOARD)) { wlr_log(WLR_DEBUG, "seat %p offered keyboard", (void*) wl_seat); struct wl_keyboard *wl_keyboard = wl_seat_get_keyboard(wl_seat); backend->keyboard = wl_keyboard; if (backend->started) { create_wl_keyboard(wl_keyboard, backend); } } } static void seat_handle_name(void *data, struct wl_seat *wl_seat, const char *name) { struct wlr_wl_backend *backend = data; assert(backend->seat == wl_seat); // Do we need to check if seatName was previously set for name change? free(backend->seat_name); backend->seat_name = strdup(name); } const struct wl_seat_listener seat_listener = { .capabilities = seat_handle_capabilities, .name = seat_handle_name, }; struct wl_seat *wlr_wl_input_device_get_seat(struct wlr_input_device *wlr_dev) { struct wlr_wl_input_device *dev = get_wl_input_device_from_input_device(wlr_dev); return dev->backend->seat; } wlroots-0.10.0/backend/x11/000077500000000000000000000000001361211131400152725ustar00rootroot00000000000000wlroots-0.10.0/backend/x11/backend.c000066400000000000000000000204631361211131400170320ustar00rootroot00000000000000#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 "backend/x11.h" #include "util/signal.h" 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_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) { 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); } } 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); } } } } 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)) { 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); } return 0; } struct wlr_x11_backend *get_x11_backend_from_backend( struct wlr_backend *wlr_backend) { assert(wlr_backend_is_x11(wlr_backend)); return (struct wlr_x11_backend *)wlr_backend; } static bool backend_start(struct wlr_backend *backend) { struct wlr_x11_backend *x11 = get_x11_backend_from_backend(backend); x11->started = true; wlr_signal_emit_safe(&x11->backend.events.new_input, &x11->keyboard_dev); 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_input_device_destroy(&x11->keyboard_dev); wlr_signal_emit_safe(&backend->events.destroy, backend); if (x11->event_source) { wl_event_source_remove(x11->event_source); } wl_list_remove(&x11->display_destroy.link); wlr_renderer_destroy(x11->renderer); wlr_egl_finish(&x11->egl); if (x11->xlib_conn) { XCloseDisplay(x11->xlib_conn); } free(x11); } static struct wlr_renderer *backend_get_renderer( struct wlr_backend *backend) { struct wlr_x11_backend *x11 = get_x11_backend_from_backend(backend); return x11->renderer; } static const struct wlr_backend_impl backend_impl = { .start = backend_start, .destroy = backend_destroy, .get_renderer = backend_get_renderer, }; 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); } struct wlr_backend *wlr_x11_backend_create(struct wl_display *display, const char *x11_display, wlr_renderer_create_func_t create_renderer_func) { 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->xlib_conn = XOpenDisplay(x11_display); if (!x11->xlib_conn) { wlr_log(WLR_ERROR, "Failed to open X connection"); goto error_x11; } x11->xcb = XGetXCBConnection(x11->xlib_conn); if (!x11->xcb || xcb_connection_has_error(x11->xcb)) { wlr_log(WLR_ERROR, "Failed to open xcb connection"); goto error_display; } XSetEventQueueOwner(x11->xlib_conn, XCBOwnsEventQueue); 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 }, }; 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; 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"); free(fixes_reply); goto error_display; } free(fixes_reply); 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"); 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 (!create_renderer_func) { create_renderer_func = wlr_renderer_autocreate; } static EGLint config_attribs[] = { EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_RED_SIZE, 1, EGL_GREEN_SIZE, 1, EGL_BLUE_SIZE, 1, EGL_ALPHA_SIZE, 0, EGL_NONE, }; x11->renderer = create_renderer_func(&x11->egl, EGL_PLATFORM_X11_KHR, x11->xlib_conn, config_attribs, x11->screen->root_visual); if (x11->renderer == NULL) { wlr_log(WLR_ERROR, "Failed to create renderer"); goto error_event; } wlr_input_device_init(&x11->keyboard_dev, WLR_INPUT_DEVICE_KEYBOARD, &input_device_impl, "X11 keyboard", 0, 0); wlr_keyboard_init(&x11->keyboard, &keyboard_impl); x11->keyboard_dev.keyboard = &x11->keyboard; x11->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &x11->display_destroy); return &x11->backend; error_event: wl_event_source_remove(x11->event_source); error_display: XCloseDisplay(x11->xlib_conn); error_x11: free(x11); return NULL; } wlroots-0.10.0/backend/x11/input_device.c000066400000000000000000000230321361211131400201140ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include "backend/x11.h" #include "util/signal.h" static void send_key_event(struct wlr_x11_backend *x11, uint32_t key, enum wlr_key_state st, xcb_timestamp_t time) { struct wlr_event_keyboard_key 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_event_pointer_button ev = { .device = &output->pointer_dev, .time_msec = time, .button = key, .state = st, }; wlr_signal_emit_safe(&output->pointer.events.button, &ev); wlr_signal_emit_safe(&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_event_pointer_axis ev = { .device = &output->pointer_dev, .time_msec = time, .source = WLR_AXIS_SOURCE_WHEEL, .orientation = WLR_AXIS_ORIENTATION_VERTICAL, // 15 is a typical value libinput sends for one scroll .delta = delta * 15, .delta_discrete = delta, }; wlr_signal_emit_safe(&output->pointer.events.axis, &ev); wlr_signal_emit_safe(&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_event_pointer_motion_absolute ev = { .device = &output->pointer_dev, .time_msec = time, .x = (double)x / output->wlr_output.width, .y = (double)y / output->wlr_output.height, }; wlr_signal_emit_safe(&output->pointer.events.motion_absolute, &ev); wlr_signal_emit_safe(&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_event_touch_down ev = { .device = &output->touch_dev, .time_msec = time, .x = (double)x / output->wlr_output.width, .y = (double)y / output->wlr_output.height, .touch_id = touch_id, }; wlr_signal_emit_safe(&output->touch.events.down, &ev); } 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_event_touch_motion ev = { .device = &output->touch_dev, .time_msec = time, .x = (double)x / output->wlr_output.width, .y = (double)y / output->wlr_output.height, .touch_id = touch_id, }; wlr_signal_emit_safe(&output->touch.events.motion, &ev); } static void send_touch_up_event(struct wlr_x11_output *output, int32_t touch_id, xcb_timestamp_t time) { struct wlr_event_touch_up ev = { .device = &output->touch_dev, .time_msec = time, .touch_id = touch_id, }; wlr_signal_emit_safe(&output->touch.events.up, &ev); } 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; 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, WLR_KEY_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, WLR_KEY_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_ENTER: { xcb_input_enter_event_t *ev = (xcb_input_enter_event_t *)event; output = get_x11_output_from_window_id(x11, ev->event); if (!output) { return; } if (!output->cursor_hidden) { xcb_xfixes_hide_cursor(x11->xcb, output->win); xcb_flush(x11->xcb); output->cursor_hidden = true; } break; } case XCB_INPUT_LEAVE: { xcb_input_leave_event_t *ev = (xcb_input_leave_event_t *)event; output = get_x11_output_from_window_id(x11, ev->event); if (!output) { return; } if (output->cursor_hidden) { xcb_xfixes_show_cursor(x11->xcb, output->win); xcb_flush(x11->xcb); output->cursor_hidden = false; } 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(struct wlr_x11_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; } } } static void input_device_destroy(struct wlr_input_device *wlr_device) { // Don't free the input device, it's on the stack } const struct wlr_input_device_impl input_device_impl = { .destroy = input_device_destroy, }; static void keyboard_destroy(struct wlr_keyboard *wlr_keyboard) { // Don't free the keyboard, it's on the stack } const struct wlr_keyboard_impl keyboard_impl = { .destroy = keyboard_destroy, }; static void pointer_destroy(struct wlr_pointer *wlr_pointer) { // Don't free the pointer, it's on the stack } const struct wlr_pointer_impl pointer_impl = { .destroy = pointer_destroy, }; static void touch_destroy(struct wlr_touch *wlr_touch) { // Don't free the touch, it's on the stack } const struct wlr_touch_impl touch_impl = { .destroy = touch_destroy, }; 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) { return wlr_dev->impl == &input_device_impl; } wlroots-0.10.0/backend/x11/meson.build000066400000000000000000000012151361211131400174330ustar00rootroot00000000000000x11_libs = [] x11_required = [ 'x11-xcb', 'xcb', 'xcb-xinput', 'xcb-xfixes', ] msg = [] if get_option('x11-backend').enabled() msg += 'Install "@0@" or pass "-Dx11-backend=disabled" to disable it.' endif if not get_option('x11-backend').disabled() msg += 'Required for X11 backend support.' endif foreach lib : x11_required dep = dependency(lib, required: get_option('x11-backend'), 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 conf_data.set10('WLR_HAS_X11_BACKEND', true) wlroots-0.10.0/backend/x11/output.c000066400000000000000000000207771361211131400170130ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include #include #include "backend/x11.h" #include "util/signal.h" static int signal_frame(void *data) { struct wlr_x11_output *output = data; wlr_output_send_frame(&output->wlr_output); wl_event_source_timer_update(output->frame_timer, output->frame_delay); return 0; } static void parse_xcb_setup(struct wlr_output *output, xcb_connection_t *xcb) { const xcb_setup_t *xcb_setup = xcb_get_setup(xcb); snprintf(output->make, sizeof(output->make), "%.*s", xcb_setup_vendor_length(xcb_setup), xcb_setup_vendor(xcb_setup)); snprintf(output->model, sizeof(output->model), "%"PRIu16".%"PRIu16, xcb_setup->protocol_major_version, xcb_setup->protocol_minor_version); } static struct wlr_x11_output *get_x11_output_from_output( struct wlr_output *wlr_output) { assert(wlr_output_is_x11(wlr_output)); return (struct wlr_x11_output *)wlr_output; } static void output_set_refresh(struct wlr_output *wlr_output, int32_t refresh) { struct wlr_x11_output *output = get_x11_output_from_output(wlr_output); if (refresh <= 0) { refresh = X11_DEFAULT_REFRESH; } wlr_output_update_custom_mode(&output->wlr_output, wlr_output->width, wlr_output->height, refresh); output->frame_delay = 1000000 / refresh; } 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; output_set_refresh(&output->wlr_output, refresh); 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; } return true; } 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; wlr_input_device_destroy(&output->pointer_dev); wlr_input_device_destroy(&output->touch_dev); wl_list_remove(&output->link); wl_event_source_remove(output->frame_timer); wlr_egl_destroy_surface(&x11->egl, output->surf); xcb_destroy_window(x11->xcb, output->win); xcb_flush(x11->xcb); free(output); } static bool output_attach_render(struct wlr_output *wlr_output, int *buffer_age) { struct wlr_x11_output *output = get_x11_output_from_output(wlr_output); struct wlr_x11_backend *x11 = output->x11; return wlr_egl_make_current(&x11->egl, output->surf, buffer_age); } static bool output_commit(struct wlr_output *wlr_output) { struct wlr_x11_output *output = get_x11_output_from_output(wlr_output); struct wlr_x11_backend *x11 = output->x11; if (wlr_output->pending.committed & WLR_OUTPUT_STATE_ENABLED) { wlr_log(WLR_DEBUG, "Cannot disable an X11 output"); return false; } if (wlr_output->pending.committed & WLR_OUTPUT_STATE_MODE) { assert(wlr_output->pending.mode_type == WLR_OUTPUT_STATE_MODE_CUSTOM); if (!output_set_custom_mode(wlr_output, wlr_output->pending.custom_mode.width, wlr_output->pending.custom_mode.height, wlr_output->pending.custom_mode.refresh)) { return false; } } if (wlr_output->pending.committed & WLR_OUTPUT_STATE_BUFFER) { pixman_region32_t *damage = NULL; if (wlr_output->pending.committed & WLR_OUTPUT_STATE_DAMAGE) { damage = &wlr_output->pending.damage; } if (!wlr_egl_swap_buffers(&x11->egl, output->surf, damage)) { return false; } wlr_output_send_present(wlr_output, NULL); } return true; } static const struct wlr_output_impl output_impl = { .destroy = output_destroy, .attach_render = output_attach_render, .commit = output_commit, }; 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(struct wlr_x11_output)); if (output == NULL) { return NULL; } output->x11 = x11; struct wlr_output *wlr_output = &output->wlr_output; wlr_output_init(wlr_output, &x11->backend, &output_impl, x11->wl_display); wlr_output->width = 1024; wlr_output->height = 768; output_set_refresh(&output->wlr_output, 0); snprintf(wlr_output->name, sizeof(wlr_output->name), "X11-%zd", ++x11->last_output_num); parse_xcb_setup(wlr_output, x11->xcb); char description[128]; snprintf(description, sizeof(description), "X11 output %zd", x11->last_output_num); wlr_output_set_description(wlr_output, description); uint32_t mask = XCB_CW_EVENT_MASK; uint32_t values[] = { XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_STRUCTURE_NOTIFY }; output->win = xcb_generate_id(x11->xcb); xcb_create_window(x11->xcb, XCB_COPY_FROM_PARENT, output->win, x11->screen->root, 0, 0, wlr_output->width, wlr_output->height, 1, XCB_WINDOW_CLASS_INPUT_OUTPUT, x11->screen->root_visual, mask, values); 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_ENTER | XCB_INPUT_XI_EVENT_MASK_LEAVE | 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); output->surf = wlr_egl_create_surface(&x11->egl, &output->win); if (!output->surf) { wlr_log(WLR_ERROR, "Failed to create EGL surface"); free(output); return NULL; } 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); wlr_x11_output_set_title(wlr_output, NULL); xcb_map_window(x11->xcb, output->win); xcb_flush(x11->xcb); struct wl_event_loop *ev = wl_display_get_event_loop(x11->wl_display); output->frame_timer = wl_event_loop_add_timer(ev, signal_frame, output); wl_list_insert(&x11->outputs, &output->link); wl_event_source_timer_update(output->frame_timer, output->frame_delay); wlr_output_update_enabled(wlr_output, true); wlr_input_device_init(&output->pointer_dev, WLR_INPUT_DEVICE_POINTER, &input_device_impl, "X11 pointer", 0, 0); wlr_pointer_init(&output->pointer, &pointer_impl); output->pointer_dev.pointer = &output->pointer; output->pointer_dev.output_name = strdup(wlr_output->name); wlr_input_device_init(&output->touch_dev, WLR_INPUT_DEVICE_TOUCH, &input_device_impl, "X11 touch", 0, 0); wlr_touch_init(&output->touch, &touch_impl); output->touch_dev.touch = &output->touch; output->touch_dev.output_name = strdup(wlr_output->name); wl_list_init(&output->touchpoints); wlr_signal_emit_safe(&x11->backend.events.new_output, wlr_output); wlr_signal_emit_safe(&x11->backend.events.new_input, &output->pointer_dev); wlr_signal_emit_safe(&x11->backend.events.new_input, &output->touch_dev); return wlr_output; } void handle_x11_configure_notify(struct wlr_x11_output *output, xcb_configure_notify_event_t *ev) { // ignore events that set an invalid size: if (ev->width > 0 && ev->height > 0) { wlr_output_update_custom_mode(&output->wlr_output, ev->width, ev->height, output->wlr_output.refresh); // Move the pointer to its new location update_x11_pointer_position(output, output->x11->time); } else { wlr_log(WLR_DEBUG, "Ignoring X11 configure event for height=%d, width=%d", ev->width, ev->height); } } 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); } wlroots-0.10.0/contrib/000077500000000000000000000000001361211131400147325ustar00rootroot00000000000000wlroots-0.10.0/contrib/_incr_version000077500000000000000000000016351361211131400175240ustar00rootroot00000000000000#!/bin/sh -eu old_version="$1" new_version="$2" sed -i meson.build -e "s/version: '$old_version'/version: '$new_version'/g" printf "Backwards-incompatible ABI changes? (y/n) " read inc_age if [ "$inc_age" = 'n' ] then printf "Interface(s) added, removed, or changed? (y/n) " read inc_current fi so_version=$(egrep '^so_version =' meson.build | cut -d'[' -f2- | cut -d']' -f1) current=$(echo "$so_version" | cut -d',' -f1 | sed -e "s/'//g" -e "s/ //g") revision=$(echo "$so_version" | cut -d',' -f2 | sed -e "s/'//g" -e "s/ //g") age=$(echo "$so_version" | cut -d',' -f3 | sed -e "s/'//g" -e "s/ //g") if [ "$inc_age" = 'y' ] then age=$((age+1)) current=$((current+1)) elif [ "$inc_current" = 'y' ] then current=$((current+1)) fi revision=$((revision+1)) sed -i meson.build \ -e "s/so_version = .*/so_version = ['$current', '$revision', '$age']/g" git add meson.build git commit -m "Update version to $new_version" wlroots-0.10.0/docs/000077500000000000000000000000001361211131400142225ustar00rootroot00000000000000wlroots-0.10.0/docs/env_vars.md000066400000000000000000000027161361211131400163750ustar00rootroot00000000000000wlroots reads these environment variables # wlroots specific * *WLR_BACKENDS*: comma-separated list of backends to use (available backends: libinput, drm, wayland, x11, headless, noop) * *WLR_NO_HARDWARE_CURSORS*: set to 1 to use software cursors instead of hardware cursors * *WLR_SESSION*: specifies the wlr\_session to be used (available sessions: logind/systemd, direct) * *WLR_DIRECT_TTY*: specifies the tty to be used (instead of using /dev/tty) ## 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 ## 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 # Generic * *DISPLAY*: if set probe X11 backend in *wlr_backend_autocreate* * *WAYLAND_DISPLAY*, *_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.10.0/examples/000077500000000000000000000000001361211131400151105ustar00rootroot00000000000000wlroots-0.10.0/examples/.gitignore000066400000000000000000000000261361211131400170760ustar00rootroot00000000000000/compositor/protocols wlroots-0.10.0/examples/cat.c000066400000000000000000006677231361211131400160500ustar00rootroot00000000000000/* 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.10.0/examples/cat.h000066400000000000000000000004141361211131400160270ustar00rootroot00000000000000#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.10.0/examples/dmabuf-capture.c000066400000000000000000000526551361211131400201700ustar00rootroot00000000000000#define _POSIX_C_SOURCE 199309L #include #include #include #include #include #include #include #include #include #include #include #include #include #include "wlr-export-dmabuf-unstable-v1-client-protocol.h" struct wayland_output { struct wl_list link; uint32_t id; struct wl_output *output; char *make; char *model; int width; int height; AVRational framerate; }; struct fifo_buffer { AVFrame **queued_frames; int num_queued_frames; int max_queued_frames; pthread_mutex_t lock; pthread_cond_t cond; pthread_mutex_t cond_lock; }; struct capture_context { AVClass *class; /* For pretty logging */ struct wl_display *display; struct wl_registry *registry; struct zwlr_export_dmabuf_manager_v1 *export_manager; struct wl_list output_list; /* Target */ struct wl_output *target_output; bool with_cursor; /* Main frame callback */ struct zwlr_export_dmabuf_frame_v1 *frame_callback; /* If something happens during capture */ int err; bool quit; /* FFmpeg specific parts */ pthread_t vid_thread; AVFrame *current_frame; AVFormatContext *avf; AVCodecContext *avctx; AVBufferRef *drm_device_ref; AVBufferRef *drm_frames_ref; AVBufferRef *mapped_device_ref; AVBufferRef *mapped_frames_ref; /* Sync stuff */ struct fifo_buffer vid_frames; int64_t start_pts; /* Config */ enum AVPixelFormat software_format; enum AVHWDeviceType hw_device_type; AVDictionary *encoder_opts; int is_software_encoder; char *hardware_device; char *out_filename; char *encoder_name; float out_bitrate; }; static int init_fifo(struct fifo_buffer *buf, int max_queued_frames) { pthread_mutex_init(&buf->lock, NULL); pthread_cond_init(&buf->cond, NULL); pthread_mutex_init(&buf->cond_lock, NULL); buf->num_queued_frames = 0; buf->max_queued_frames = max_queued_frames; buf->queued_frames = av_mallocz(buf->max_queued_frames * sizeof(AVFrame)); return !buf->queued_frames ? AVERROR(ENOMEM) : 0; } static int get_fifo_size(struct fifo_buffer *buf) { pthread_mutex_lock(&buf->lock); int ret = buf->num_queued_frames; pthread_mutex_unlock(&buf->lock); return ret; } static int push_to_fifo(struct fifo_buffer *buf, AVFrame *f) { int ret; pthread_mutex_lock(&buf->lock); if ((buf->num_queued_frames + 1) > buf->max_queued_frames) { av_frame_free(&f); ret = 1; } else { buf->queued_frames[buf->num_queued_frames++] = f; ret = 0; } pthread_mutex_unlock(&buf->lock); pthread_cond_signal(&buf->cond); return ret; } static AVFrame *pop_from_fifo(struct fifo_buffer *buf) { pthread_mutex_lock(&buf->lock); if (!buf->num_queued_frames) { pthread_mutex_unlock(&buf->lock); pthread_cond_wait(&buf->cond, &buf->cond_lock); pthread_mutex_lock(&buf->lock); } AVFrame *rf = buf->queued_frames[0]; for (int i = 1; i < buf->num_queued_frames; i++) { buf->queued_frames[i - 1] = buf->queued_frames[i]; } buf->num_queued_frames--; buf->queued_frames[buf->num_queued_frames] = NULL; pthread_mutex_unlock(&buf->lock); return rf; } static void free_fifo(struct fifo_buffer *buf) { pthread_mutex_lock(&buf->lock); if (buf->num_queued_frames) { for (int i = 0; i < buf->num_queued_frames; i++) { av_frame_free(&buf->queued_frames[i]); } } av_freep(&buf->queued_frames); pthread_mutex_unlock(&buf->lock); } static void output_handle_geometry(void *data, struct wl_output *wl_output, int32_t x, int32_t y, int32_t phys_width, int32_t phys_height, int32_t subpixel, const char *make, const char *model, int32_t transform) { struct wayland_output *output = data; output->make = av_strdup(make); output->model = av_strdup(model); } static void output_handle_mode(void *data, struct wl_output *wl_output, uint32_t flags, int32_t width, int32_t height, int32_t refresh) { if (flags & WL_OUTPUT_MODE_CURRENT) { struct wayland_output *output = data; output->width = width; output->height = height; output->framerate = (AVRational){ refresh, 1000 }; } } static void output_handle_done(void* data, struct wl_output *wl_output) { /* Nothing to do */ } static void output_handle_scale(void* data, struct wl_output *wl_output, int32_t factor) { /* Nothing to do */ } static const struct wl_output_listener output_listener = { .geometry = output_handle_geometry, .mode = output_handle_mode, .done = output_handle_done, .scale = output_handle_scale, }; static void registry_handle_add(void *data, struct wl_registry *reg, uint32_t id, const char *interface, uint32_t ver) { struct capture_context *ctx = data; if (!strcmp(interface, wl_output_interface.name)) { struct wayland_output *output = av_mallocz(sizeof(*output)); output->id = id; output->output = wl_registry_bind(reg, id, &wl_output_interface, 1); wl_output_add_listener(output->output, &output_listener, output); wl_list_insert(&ctx->output_list, &output->link); } if (!strcmp(interface, zwlr_export_dmabuf_manager_v1_interface.name)) { ctx->export_manager = wl_registry_bind(reg, id, &zwlr_export_dmabuf_manager_v1_interface, 1); } } static void remove_output(struct wayland_output *out) { wl_list_remove(&out->link); av_free(out->make); av_free(out->model); av_free(out); } static struct wayland_output *find_output(struct capture_context *ctx, struct wl_output *out, uint32_t id) { struct wayland_output *output, *tmp; wl_list_for_each_safe(output, tmp, &ctx->output_list, link) { if ((output->output == out) || (output->id == id)) { return output; } } return NULL; } static void registry_handle_remove(void *data, struct wl_registry *reg, uint32_t id) { remove_output(find_output((struct capture_context *)data, NULL, id)); } static const struct wl_registry_listener registry_listener = { .global = registry_handle_add, .global_remove = registry_handle_remove, }; static void frame_free(void *opaque, uint8_t *data) { AVDRMFrameDescriptor *desc = (AVDRMFrameDescriptor *)data; if (desc) { for (int i = 0; i < desc->nb_objects; ++i) { close(desc->objects[i].fd); } av_free(data); } zwlr_export_dmabuf_frame_v1_destroy(opaque); } static void frame_start(void *data, struct zwlr_export_dmabuf_frame_v1 *frame, uint32_t width, uint32_t height, uint32_t offset_x, uint32_t offset_y, uint32_t buffer_flags, uint32_t flags, uint32_t format, uint32_t mod_high, uint32_t mod_low, uint32_t num_objects) { struct capture_context *ctx = data; int err = 0; /* Allocate DRM specific struct */ AVDRMFrameDescriptor *desc = av_mallocz(sizeof(*desc)); if (!desc) { err = AVERROR(ENOMEM); goto fail; } desc->nb_objects = num_objects; desc->objects[0].format_modifier = ((uint64_t)mod_high << 32) | mod_low; desc->nb_layers = 1; desc->layers[0].format = format; /* Allocate a frame */ AVFrame *f = av_frame_alloc(); if (!f) { err = AVERROR(ENOMEM); goto fail; } /* Set base frame properties */ ctx->current_frame = f; f->width = width; f->height = height; f->format = AV_PIX_FMT_DRM_PRIME; /* Set the frame data to the DRM specific struct */ f->buf[0] = av_buffer_create((uint8_t*)desc, sizeof(*desc), &frame_free, frame, 0); if (!f->buf[0]) { err = AVERROR(ENOMEM); goto fail; } f->data[0] = (uint8_t*)desc; return; fail: ctx->err = err; frame_free(frame, (uint8_t *)desc); } static void frame_object(void *data, struct zwlr_export_dmabuf_frame_v1 *frame, uint32_t index, int32_t fd, uint32_t size, uint32_t offset, uint32_t stride, uint32_t plane_index) { struct capture_context *ctx = data; AVFrame *f = ctx->current_frame; AVDRMFrameDescriptor *desc = (AVDRMFrameDescriptor *)f->data[0]; desc->objects[index].fd = fd; desc->objects[index].size = size; desc->layers[0].planes[plane_index].object_index = index; desc->layers[0].planes[plane_index].offset = offset; desc->layers[0].planes[plane_index].pitch = stride; } static enum AVPixelFormat drm_fmt_to_pixfmt(uint32_t fmt) { switch (fmt) { case DRM_FORMAT_NV12: return AV_PIX_FMT_NV12; case DRM_FORMAT_ARGB8888: return AV_PIX_FMT_BGRA; case DRM_FORMAT_XRGB8888: return AV_PIX_FMT_BGR0; case DRM_FORMAT_ABGR8888: return AV_PIX_FMT_RGBA; case DRM_FORMAT_XBGR8888: return AV_PIX_FMT_RGB0; case DRM_FORMAT_RGBA8888: return AV_PIX_FMT_ABGR; case DRM_FORMAT_RGBX8888: return AV_PIX_FMT_0BGR; case DRM_FORMAT_BGRA8888: return AV_PIX_FMT_ARGB; case DRM_FORMAT_BGRX8888: return AV_PIX_FMT_0RGB; default: return AV_PIX_FMT_NONE; }; } static int attach_drm_frames_ref(struct capture_context *ctx, AVFrame *f, enum AVPixelFormat sw_format) { int err = 0; AVHWFramesContext *hwfc; if (ctx->drm_frames_ref) { hwfc = (AVHWFramesContext*)ctx->drm_frames_ref->data; if (hwfc->width == f->width && hwfc->height == f->height && hwfc->sw_format == sw_format) { goto attach; } av_buffer_unref(&ctx->drm_frames_ref); } ctx->drm_frames_ref = av_hwframe_ctx_alloc(ctx->drm_device_ref); if (!ctx->drm_frames_ref) { err = AVERROR(ENOMEM); goto fail; } hwfc = (AVHWFramesContext*)ctx->drm_frames_ref->data; hwfc->format = f->format; hwfc->sw_format = sw_format; hwfc->width = f->width; hwfc->height = f->height; err = av_hwframe_ctx_init(ctx->drm_frames_ref); if (err) { av_log(ctx, AV_LOG_ERROR, "AVHWFramesContext init failed: %s!\n", av_err2str(err)); goto fail; } attach: /* Set frame hardware context referencce */ f->hw_frames_ctx = av_buffer_ref(ctx->drm_frames_ref); if (!f->hw_frames_ctx) { err = AVERROR(ENOMEM); goto fail; } return 0; fail: av_buffer_unref(&ctx->drm_frames_ref); return err; } static void register_cb(struct capture_context *ctx); static void frame_ready(void *data, struct zwlr_export_dmabuf_frame_v1 *frame, uint32_t tv_sec_hi, uint32_t tv_sec_lo, uint32_t tv_nsec) { struct capture_context *ctx = data; AVFrame *f = ctx->current_frame; AVDRMFrameDescriptor *desc = (AVDRMFrameDescriptor *)f->data[0]; enum AVPixelFormat pix_fmt = drm_fmt_to_pixfmt(desc->layers[0].format); int err = 0; /* Timestamp, nanoseconds timebase */ f->pts = ((((uint64_t)tv_sec_hi) << 32) | tv_sec_lo) * 1000000000 + tv_nsec; if (!ctx->start_pts) { ctx->start_pts = f->pts; } f->pts = av_rescale_q(f->pts - ctx->start_pts, (AVRational){ 1, 1000000000 }, ctx->avctx->time_base); /* Attach the hardware frame context to the frame */ err = attach_drm_frames_ref(ctx, f, pix_fmt); if (err) { goto end; } /* TODO: support multiplane stuff */ desc->layers[0].nb_planes = av_pix_fmt_count_planes(pix_fmt); AVFrame *mapped_frame = av_frame_alloc(); if (!mapped_frame) { err = AVERROR(ENOMEM); goto end; } AVHWFramesContext *mapped_hwfc; mapped_hwfc = (AVHWFramesContext *)ctx->mapped_frames_ref->data; mapped_frame->format = mapped_hwfc->format; mapped_frame->pts = f->pts; /* Set frame hardware context referencce */ mapped_frame->hw_frames_ctx = av_buffer_ref(ctx->mapped_frames_ref); if (!mapped_frame->hw_frames_ctx) { err = AVERROR(ENOMEM); goto end; } err = av_hwframe_map(mapped_frame, f, 0); if (err) { av_log(ctx, AV_LOG_ERROR, "Error mapping: %s!\n", av_err2str(err)); goto end; } if (push_to_fifo(&ctx->vid_frames, mapped_frame)) { av_log(ctx, AV_LOG_WARNING, "Dropped frame!\n"); } if (!ctx->quit && !ctx->err) { register_cb(ctx); } end: ctx->err = err; av_frame_free(&ctx->current_frame); } static void frame_cancel(void *data, struct zwlr_export_dmabuf_frame_v1 *frame, uint32_t reason) { struct capture_context *ctx = data; av_log(ctx, AV_LOG_WARNING, "Frame cancelled!\n"); av_frame_free(&ctx->current_frame); if (reason == ZWLR_EXPORT_DMABUF_FRAME_V1_CANCEL_REASON_PERMANENT) { av_log(ctx, AV_LOG_ERROR, "Permanent failure, exiting\n"); ctx->err = true; } else { register_cb(ctx); } } static const struct zwlr_export_dmabuf_frame_v1_listener frame_listener = { .frame = frame_start, .object = frame_object, .ready = frame_ready, .cancel = frame_cancel, }; static void register_cb(struct capture_context *ctx) { ctx->frame_callback = zwlr_export_dmabuf_manager_v1_capture_output( ctx->export_manager, ctx->with_cursor, ctx->target_output); zwlr_export_dmabuf_frame_v1_add_listener(ctx->frame_callback, &frame_listener, ctx); } static void *vid_encode_thread(void *arg) { int err = 0; struct capture_context *ctx = arg; do { AVFrame *f = NULL; if (get_fifo_size(&ctx->vid_frames) || !ctx->quit) { f = pop_from_fifo(&ctx->vid_frames); } if (ctx->is_software_encoder && f) { AVFrame *soft_frame = av_frame_alloc(); av_hwframe_transfer_data(soft_frame, f, 0); soft_frame->pts = f->pts; av_frame_free(&f); f = soft_frame; } err = avcodec_send_frame(ctx->avctx, f); av_frame_free(&f); if (err) { av_log(ctx, AV_LOG_ERROR, "Error encoding: %s!\n", av_err2str(err)); goto end; } while (1) { AVPacket pkt; av_init_packet(&pkt); int ret = avcodec_receive_packet(ctx->avctx, &pkt); if (ret == AVERROR(EAGAIN)) { break; } else if (ret == AVERROR_EOF) { av_log(ctx, AV_LOG_INFO, "Encoder flushed!\n"); goto end; } else if (ret) { av_log(ctx, AV_LOG_ERROR, "Error encoding: %s!\n", av_err2str(ret)); err = ret; goto end; } pkt.stream_index = 0; err = av_interleaved_write_frame(ctx->avf, &pkt); av_packet_unref(&pkt); if (err) { av_log(ctx, AV_LOG_ERROR, "Writing packet fail: %s!\n", av_err2str(err)); goto end; } }; av_log(ctx, AV_LOG_INFO, "Encoded frame %i (%i in queue)\n", ctx->avctx->frame_number, get_fifo_size(&ctx->vid_frames)); } while (!ctx->err); end: if (!ctx->err) { ctx->err = err; } return NULL; } static int init_lavu_hwcontext(struct capture_context *ctx) { /* DRM hwcontext */ ctx->drm_device_ref = av_hwdevice_ctx_alloc(AV_HWDEVICE_TYPE_DRM); if (!ctx->drm_device_ref) return AVERROR(ENOMEM); AVHWDeviceContext *ref_data = (AVHWDeviceContext*)ctx->drm_device_ref->data; AVDRMDeviceContext *hwctx = ref_data->hwctx; /* We don't need a device (we don't even know it and can't open it) */ hwctx->fd = -1; av_hwdevice_ctx_init(ctx->drm_device_ref); /* Mapped hwcontext */ int err = av_hwdevice_ctx_create(&ctx->mapped_device_ref, ctx->hw_device_type, ctx->hardware_device, NULL, 0); if (err < 0) { av_log(ctx, AV_LOG_ERROR, "Failed to create a hardware device: %s\n", av_err2str(err)); return err; } return 0; } static int set_hwframe_ctx(struct capture_context *ctx, AVBufferRef *hw_device_ctx) { AVHWFramesContext *frames_ctx = NULL; int err = 0; if (!(ctx->mapped_frames_ref = av_hwframe_ctx_alloc(hw_device_ctx))) { return AVERROR(ENOMEM); } AVHWFramesConstraints *cst = av_hwdevice_get_hwframe_constraints(ctx->mapped_device_ref, NULL); if (!cst) { av_log(ctx, AV_LOG_ERROR, "Failed to get hw device constraints!\n"); av_buffer_unref(&ctx->mapped_frames_ref); return AVERROR(ENOMEM); } frames_ctx = (AVHWFramesContext *)(ctx->mapped_frames_ref->data); frames_ctx->format = cst->valid_hw_formats[0]; frames_ctx->sw_format = ctx->avctx->pix_fmt; frames_ctx->width = ctx->avctx->width; frames_ctx->height = ctx->avctx->height; av_hwframe_constraints_free(&cst); if ((err = av_hwframe_ctx_init(ctx->mapped_frames_ref))) { av_log(ctx, AV_LOG_ERROR, "Failed to initialize hw frame context: %s!\n", av_err2str(err)); av_buffer_unref(&ctx->mapped_frames_ref); return err; } if (!ctx->is_software_encoder) { ctx->avctx->pix_fmt = frames_ctx->format; ctx->avctx->hw_frames_ctx = av_buffer_ref(ctx->mapped_frames_ref); if (!ctx->avctx->hw_frames_ctx) { av_buffer_unref(&ctx->mapped_frames_ref); err = AVERROR(ENOMEM); } } return err; } static int init_encoding(struct capture_context *ctx) { int err; /* lavf init */ err = avformat_alloc_output_context2(&ctx->avf, NULL, NULL, ctx->out_filename); if (err) { av_log(ctx, AV_LOG_ERROR, "Unable to init lavf context!\n"); return err; } AVStream *st = avformat_new_stream(ctx->avf, NULL); if (!st) { av_log(ctx, AV_LOG_ERROR, "Unable to alloc stream!\n"); return 1; } /* Find encoder */ AVCodec *out_codec = avcodec_find_encoder_by_name(ctx->encoder_name); if (!out_codec) { av_log(ctx, AV_LOG_ERROR, "Codec not found (not compiled in lavc?)!\n"); return AVERROR(EINVAL); } ctx->avf->oformat->video_codec = out_codec->id; ctx->is_software_encoder = !(out_codec->capabilities & AV_CODEC_CAP_HARDWARE); ctx->avctx = avcodec_alloc_context3(out_codec); if (!ctx->avctx) return 1; ctx->avctx->opaque = ctx; ctx->avctx->bit_rate = (int)ctx->out_bitrate*1000000.0f; ctx->avctx->pix_fmt = ctx->software_format; ctx->avctx->time_base = (AVRational){ 1, 1000 }; ctx->avctx->compression_level = 7; ctx->avctx->width = find_output(ctx, ctx->target_output, 0)->width; ctx->avctx->height = find_output(ctx, ctx->target_output, 0)->height; if (ctx->avf->oformat->flags & AVFMT_GLOBALHEADER) { ctx->avctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; } st->id = 0; st->time_base = ctx->avctx->time_base; st->avg_frame_rate = find_output(ctx, ctx->target_output, 0)->framerate; /* Init hw frames context */ err = set_hwframe_ctx(ctx, ctx->mapped_device_ref); if (err) { return err; } err = avcodec_open2(ctx->avctx, out_codec, &ctx->encoder_opts); if (err) { av_log(ctx, AV_LOG_ERROR, "Cannot open encoder: %s!\n", av_err2str(err)); return err; } if (avcodec_parameters_from_context(st->codecpar, ctx->avctx) < 0) { av_log(ctx, AV_LOG_ERROR, "Couldn't copy codec params: %s!\n", av_err2str(err)); return err; } /* Debug print */ av_dump_format(ctx->avf, 0, ctx->out_filename, 1); /* Open for writing */ err = avio_open(&ctx->avf->pb, ctx->out_filename, AVIO_FLAG_WRITE); if (err) { av_log(ctx, AV_LOG_ERROR, "Couldn't open %s: %s!\n", ctx->out_filename, av_err2str(err)); return err; } err = avformat_write_header(ctx->avf, NULL); if (err) { av_log(ctx, AV_LOG_ERROR, "Couldn't write header: %s!\n", av_err2str(err)); return err; } return err; } struct capture_context *q_ctx = NULL; static void on_quit_signal(int signo) { printf("\r"); av_log(q_ctx, AV_LOG_WARNING, "Quitting!\n"); q_ctx->quit = true; } static int main_loop(struct capture_context *ctx) { int err; q_ctx = ctx; if (signal(SIGINT, on_quit_signal) == SIG_ERR) { av_log(ctx, AV_LOG_ERROR, "Unable to install signal handler!\n"); return AVERROR(EINVAL); } err = init_lavu_hwcontext(ctx); if (err) { return err; } err = init_encoding(ctx); if (err) { return err; } /* Start video encoding thread */ err = init_fifo(&ctx->vid_frames, 16); if (err) { return err; } pthread_create(&ctx->vid_thread, NULL, vid_encode_thread, ctx); /* Start the frame callback */ register_cb(ctx); /* Run capture */ while (wl_display_dispatch(ctx->display) != -1 && !ctx->err && !ctx->quit); /* Join with encoder thread */ pthread_join(ctx->vid_thread, NULL); err = av_write_trailer(ctx->avf); if (err) { av_log(ctx, AV_LOG_ERROR, "Error writing trailer: %s!\n", av_err2str(err)); return err; } av_log(ctx, AV_LOG_INFO, "Wrote trailer!\n"); return ctx->err; } static int init(struct capture_context *ctx) { ctx->display = wl_display_connect(NULL); if (!ctx->display) { av_log(ctx, AV_LOG_ERROR, "Failed to connect to display!\n"); return AVERROR(EINVAL); } wl_list_init(&ctx->output_list); ctx->registry = wl_display_get_registry(ctx->display); wl_registry_add_listener(ctx->registry, ®istry_listener, ctx); wl_display_roundtrip(ctx->display); wl_display_dispatch(ctx->display); if (!ctx->export_manager) { av_log(ctx, AV_LOG_ERROR, "Compositor doesn't support %s!\n", zwlr_export_dmabuf_manager_v1_interface.name); return -1; } return 0; } static void uninit(struct capture_context *ctx); int main(int argc, char *argv[]) { int err; struct capture_context ctx = { 0 }; ctx.class = &((AVClass) { .class_name = "dmabuf-capture", .item_name = av_default_item_name, .version = LIBAVUTIL_VERSION_INT, }); err = init(&ctx); if (err) { goto end; } struct wayland_output *o, *tmp_o; wl_list_for_each_reverse_safe(o, tmp_o, &ctx.output_list, link) { printf("Capturable output: %s Model: %s: ID: %i\n", o->make, o->model, o->id); } if (argc != 8) { printf("Invalid number of arguments! Usage and example:\n" "./dmabuf-capture " " \n" "./dmabuf-capture 0 vaapi /dev/dri/renderD129 libx264 nv12 12 " "dmabuf_recording_01.mkv\n"); return 1; } const int o_id = strtol(argv[1], NULL, 10); o = find_output(&ctx, NULL, o_id); if (!o) { printf("Unable to find output with ID %i!\n", o_id); return 1; } ctx.target_output = o->output; ctx.with_cursor = true; ctx.hw_device_type = av_hwdevice_find_type_by_name(argv[2]); ctx.hardware_device = argv[3]; ctx.encoder_name = argv[4]; ctx.software_format = av_get_pix_fmt(argv[5]); ctx.out_bitrate = strtof(argv[6], NULL); ctx.out_filename = argv[7]; av_dict_set(&ctx.encoder_opts, "preset", "veryfast", 0); err = main_loop(&ctx); if (err) { goto end; } end: uninit(&ctx); return err; } static void uninit(struct capture_context *ctx) { struct wayland_output *output, *tmp_o; wl_list_for_each_safe(output, tmp_o, &ctx->output_list, link) { remove_output(output); } if (ctx->export_manager) { zwlr_export_dmabuf_manager_v1_destroy(ctx->export_manager); } free_fifo(&ctx->vid_frames); av_buffer_unref(&ctx->drm_frames_ref); av_buffer_unref(&ctx->drm_device_ref); av_buffer_unref(&ctx->mapped_frames_ref); av_buffer_unref(&ctx->mapped_device_ref); av_dict_free(&ctx->encoder_opts); avcodec_close(ctx->avctx); if (ctx->avf) { avio_closep(&ctx->avf->pb); } avformat_free_context(ctx->avf); } wlroots-0.10.0/examples/foreign-toplevel.c000066400000000000000000000254201361211131400205400ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include "wlr-foreign-toplevel-management-unstable-v1-client-protocol.h" #define WLR_FOREIGN_TOPLEVEL_MANAGEMENT_VERSION 2 /** * Usage: * 1. foreign-toplevel * Prints a list of opened toplevels * 2. foreign-toplevel -f * Focus the toplevel with the given id * 3. foreign-toplevel -a * Maximize the toplevel with the given id * 4. foreign-toplevel -u * Unmaximize the toplevel with the given id * 5. foreign-toplevel -i * Minimize the toplevel with the given id * 6. foreign-toplevel -r * Restore(unminimize) the toplevel with the given id * 7. foreign-toplevel -c * Close the toplevel with the given id * 8. foreign-toplevel -m * Continuously print changes to the list of opened toplevels. * Can be used together with some of the previous options. */ enum toplevel_state_field { TOPLEVEL_STATE_MAXIMIZED = (1 << 0), TOPLEVEL_STATE_MINIMIZED = (1 << 1), TOPLEVEL_STATE_ACTIVATED = (1 << 2), TOPLEVEL_STATE_FULLSCREEN = (1 << 3), TOPLEVEL_STATE_INVALID = (1 << 4), }; struct toplevel_state { char *title; char *app_id; uint32_t state; }; static void copy_state(struct toplevel_state *current, struct toplevel_state *pending) { if (current->title && pending->title) { free(current->title); } if (current->app_id && pending->app_id) { free(current->app_id); } if (pending->title) { current->title = pending->title; pending->title = NULL; } if (pending->app_id) { current->app_id = pending->app_id; pending->app_id = NULL; } if (!(pending->state & TOPLEVEL_STATE_INVALID)) { current->state = pending->state; } pending->state = TOPLEVEL_STATE_INVALID; } static uint32_t global_id = 0; struct toplevel_v1 { struct wl_list link; struct zwlr_foreign_toplevel_handle_v1 *zwlr_toplevel; uint32_t id; struct toplevel_state current, pending; }; static void print_toplevel(struct toplevel_v1 *toplevel, bool print_endl) { printf("-> %d. title=%s app_id=%s", toplevel->id, toplevel->current.title ?: "(nil)", toplevel->current.app_id ?: "(nil)"); if (print_endl) { printf("\n"); } } static void print_toplevel_state(struct toplevel_v1 *toplevel, bool print_endl) { if (toplevel->current.state & TOPLEVEL_STATE_MAXIMIZED) { printf(" maximized"); } else { printf(" unmaximized"); } if (toplevel->current.state & TOPLEVEL_STATE_MINIMIZED) { printf(" minimized"); } else { printf(" unminimized"); } if (toplevel->current.state & TOPLEVEL_STATE_ACTIVATED) { printf(" active"); } else { printf(" inactive"); } if (toplevel->current.state & TOPLEVEL_STATE_FULLSCREEN) { printf(" fullscreen"); } if (print_endl) { printf("\n"); } } static void toplevel_handle_title(void *data, struct zwlr_foreign_toplevel_handle_v1 *zwlr_toplevel, const char *title) { struct toplevel_v1 *toplevel = data; free(toplevel->pending.title); toplevel->pending.title = strdup(title); } static void toplevel_handle_app_id(void *data, struct zwlr_foreign_toplevel_handle_v1 *zwlr_toplevel, const char *app_id) { struct toplevel_v1 *toplevel = data; free(toplevel->pending.app_id); toplevel->pending.app_id = strdup(app_id); } static void toplevel_handle_output_enter(void *data, struct zwlr_foreign_toplevel_handle_v1 *zwlr_toplevel, struct wl_output *output) { struct toplevel_v1 *toplevel = data; print_toplevel(toplevel, false); printf(" enter output %u\n", (uint32_t)(size_t)wl_output_get_user_data(output)); } static void toplevel_handle_output_leave(void *data, struct zwlr_foreign_toplevel_handle_v1 *zwlr_toplevel, struct wl_output *output) { struct toplevel_v1 *toplevel = data; print_toplevel(toplevel, false); printf(" leave output %u\n", (uint32_t)(size_t)wl_output_get_user_data(output)); } static uint32_t array_to_state(struct wl_array *array) { uint32_t state = 0; uint32_t *entry; wl_array_for_each(entry, array) { if (*entry == ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MAXIMIZED) state |= TOPLEVEL_STATE_MAXIMIZED; if (*entry == ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MINIMIZED) state |= TOPLEVEL_STATE_MINIMIZED; if (*entry == ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED) state |= TOPLEVEL_STATE_ACTIVATED; if (*entry == ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN) state |= TOPLEVEL_STATE_FULLSCREEN; } return state; } static void toplevel_handle_state(void *data, struct zwlr_foreign_toplevel_handle_v1 *zwlr_toplevel, struct wl_array *state) { struct toplevel_v1 *toplevel = data; toplevel->pending.state = array_to_state(state); } static void toplevel_handle_done(void *data, struct zwlr_foreign_toplevel_handle_v1 *zwlr_toplevel) { struct toplevel_v1 *toplevel = data; bool state_changed = toplevel->current.state != toplevel->pending.state; copy_state(&toplevel->current, &toplevel->pending); print_toplevel(toplevel, !state_changed); if (state_changed) { print_toplevel_state(toplevel, true); } } static void toplevel_handle_closed(void *data, struct zwlr_foreign_toplevel_handle_v1 *zwlr_toplevel) { struct toplevel_v1 *toplevel = data; print_toplevel(toplevel, false); printf(" closed\n"); zwlr_foreign_toplevel_handle_v1_destroy(zwlr_toplevel); } static const struct zwlr_foreign_toplevel_handle_v1_listener toplevel_impl = { .title = toplevel_handle_title, .app_id = toplevel_handle_app_id, .output_enter = toplevel_handle_output_enter, .output_leave = toplevel_handle_output_leave, .state = toplevel_handle_state, .done = toplevel_handle_done, .closed = toplevel_handle_closed, }; static struct zwlr_foreign_toplevel_manager_v1 *toplevel_manager = NULL; static struct wl_list toplevel_list; static void toplevel_manager_handle_toplevel(void *data, struct zwlr_foreign_toplevel_manager_v1 *toplevel_manager, struct zwlr_foreign_toplevel_handle_v1 *zwlr_toplevel) { struct toplevel_v1 *toplevel = calloc(1, sizeof(struct toplevel_v1)); if (!toplevel) { fprintf(stderr, "Failed to allocate memory for toplevel\n"); return; } toplevel->id = global_id++; toplevel->zwlr_toplevel = zwlr_toplevel; wl_list_insert(&toplevel_list, &toplevel->link); zwlr_foreign_toplevel_handle_v1_add_listener(zwlr_toplevel, &toplevel_impl, toplevel); } static void toplevel_manager_handle_finished(void *data, struct zwlr_foreign_toplevel_manager_v1 *toplevel_manager) { zwlr_foreign_toplevel_manager_v1_destroy(toplevel_manager); } static const struct zwlr_foreign_toplevel_manager_v1_listener toplevel_manager_impl = { .toplevel = toplevel_manager_handle_toplevel, .finished = toplevel_manager_handle_finished, }; struct wl_seat *seat = NULL; static void handle_global(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { if (strcmp(interface, wl_output_interface.name) == 0) { struct wl_output *output = wl_registry_bind(registry, name, &wl_output_interface, version); wl_output_set_user_data(output, (void*)(size_t)name); // assign some ID to the output } else if (strcmp(interface, zwlr_foreign_toplevel_manager_v1_interface.name) == 0) { toplevel_manager = wl_registry_bind(registry, name, &zwlr_foreign_toplevel_manager_v1_interface, WLR_FOREIGN_TOPLEVEL_MANAGEMENT_VERSION); wl_list_init(&toplevel_list); zwlr_foreign_toplevel_manager_v1_add_listener(toplevel_manager, &toplevel_manager_impl, NULL); } else if (strcmp(interface, wl_seat_interface.name) == 0 && seat == NULL) { seat = wl_registry_bind(registry, name, &wl_seat_interface, version); } } 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, }; /* return NULL when id == -1 * exit if the given ID cannot be found in the list of toplevels */ static struct toplevel_v1 *toplevel_by_id_or_bail(int32_t id) { if (id == -1) { return NULL; } struct toplevel_v1 *toplevel; wl_list_for_each(toplevel, &toplevel_list, link) { if (toplevel->id == (uint32_t)id) { return toplevel; } } fprintf(stderr, "No toplevel with the given id: %d\n", id); exit(EXIT_FAILURE); } int main(int argc, char **argv) { int focus_id = -1, close_id = -1; int maximize_id = -1, unmaximize_id = -1; int minimize_id = -1, restore_id = -1; int fullscreen_id = -1, unfullscreen_id = -1; int one_shot = 1; int c; // TODO maybe print usage with -h? while ((c = getopt(argc, argv, "f:a:u:i:r:c:s:S:m")) != -1) { switch (c) { case 'f': focus_id = atoi(optarg); break; case 'a': maximize_id = atoi(optarg); break; case 'u': unmaximize_id = atoi(optarg); break; case 'i': minimize_id = atoi(optarg); break; case 'r': restore_id = atoi(optarg); break; case 'c': close_id = atoi(optarg); break; case 's': fullscreen_id = atoi(optarg); break; case 'S': unfullscreen_id = atoi(optarg); break; case 'm': one_shot = 0; break; } } struct wl_display *display = wl_display_connect(NULL); if (display == NULL) { fprintf(stderr, "Failed to create display\n"); return EXIT_FAILURE; } struct wl_registry *registry = wl_display_get_registry(display); wl_registry_add_listener(registry, ®istry_listener, NULL); wl_display_dispatch(display); wl_display_roundtrip(display); if (toplevel_manager == NULL) { fprintf(stderr, "wlr-foreign-toplevel not available\n"); return EXIT_FAILURE; } wl_display_roundtrip(display); // load list of toplevels wl_display_roundtrip(display); // load toplevel details struct toplevel_v1 *toplevel; if ((toplevel = toplevel_by_id_or_bail(focus_id))) { zwlr_foreign_toplevel_handle_v1_activate(toplevel->zwlr_toplevel, seat); } if ((toplevel = toplevel_by_id_or_bail(maximize_id))) { zwlr_foreign_toplevel_handle_v1_set_maximized(toplevel->zwlr_toplevel); } if ((toplevel = toplevel_by_id_or_bail(unmaximize_id))) { zwlr_foreign_toplevel_handle_v1_unset_maximized(toplevel->zwlr_toplevel); } if ((toplevel = toplevel_by_id_or_bail(minimize_id))) { zwlr_foreign_toplevel_handle_v1_set_minimized(toplevel->zwlr_toplevel); } if ((toplevel = toplevel_by_id_or_bail(restore_id))) { zwlr_foreign_toplevel_handle_v1_unset_minimized(toplevel->zwlr_toplevel); } if ((toplevel = toplevel_by_id_or_bail(fullscreen_id))) { zwlr_foreign_toplevel_handle_v1_set_fullscreen(toplevel->zwlr_toplevel, NULL); } if ((toplevel = toplevel_by_id_or_bail(unfullscreen_id))) { zwlr_foreign_toplevel_handle_v1_unset_fullscreen(toplevel->zwlr_toplevel); } if ((toplevel = toplevel_by_id_or_bail(close_id))) { zwlr_foreign_toplevel_handle_v1_close(toplevel->zwlr_toplevel); } wl_display_flush(display); if (one_shot == 0) { while (wl_display_dispatch(display) != -1) { // This space intentionally left blank } } return EXIT_SUCCESS; } wlroots-0.10.0/examples/fullscreen-shell.c000066400000000000000000000154531361211131400205330ustar00rootroot00000000000000#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_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_renderer *renderer; struct tinywl_view *view; 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, }; float matrix[9]; enum wl_output_transform transform = wlr_output_transform_invert(surface->current.transform); wlr_matrix_project_box(matrix, &box, transform, 0, output->transform_matrix); wlr_render_texture_with_matrix(rdata->renderer, texture, matrix, 1); 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 wlr_renderer *renderer = output->server->renderer; struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); int width, height; wlr_output_effective_resolution(output->wlr_output, &width, &height); if (!wlr_output_attach_render(output->wlr_output, NULL)) { return; } wlr_renderer_begin(renderer, width, height); float color[4] = {0.3, 0.3, 0.3, 1.0}; wlr_renderer_clear(renderer, color); if (output->surface != NULL) { struct render_data rdata = { .output = output->wlr_output, .renderer = renderer, .when = &now, }; wlr_surface_for_each_surface(output->surface, render_surface, &rdata); } wlr_renderer_end(renderer); wlr_output_commit(output->wlr_output); } 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; if (!wl_list_empty(&wlr_output->modes)) { struct wlr_output_mode *mode = wl_container_of(wlr_output->modes.prev, mode, link); wlr_output_set_mode(wlr_output, mode); } struct fullscreen_output *output = calloc(1, sizeof(struct fullscreen_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); } 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_backend_get_renderer(server.backend); wlr_renderer_init_wl_display(server.renderer, server.wl_display); wlr_compositor_create(server.wl_display, server.renderer); wlr_linux_dmabuf_v1_create(server.wl_display, 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.10.0/examples/gamma-control.c000066400000000000000000000121661361211131400200220ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include #include #include "wlr-gamma-control-unstable-v1-client-protocol.h" struct output { struct wl_output *wl_output; struct zwlr_gamma_control_v1 *gamma_control; uint32_t ramp_size; int table_fd; uint16_t *table; struct wl_list link; }; static struct wl_list outputs; static struct zwlr_gamma_control_manager_v1 *gamma_control_manager = NULL; static int create_anonymous_file(off_t size) { char template[] = "/tmp/wlroots-shared-XXXXXX"; int fd = mkstemp(template); if (fd < 0) { return -1; } int ret; do { errno = 0; ret = ftruncate(fd, size); } while (errno == EINTR); if (ret < 0) { close(fd); return -1; } unlink(template); return fd; } static int create_gamma_table(uint32_t ramp_size, uint16_t **table) { size_t table_size = ramp_size * 3 * sizeof(uint16_t); int fd = create_anonymous_file(table_size); if (fd < 0) { fprintf(stderr, "failed to create anonymous file\n"); return -1; } void *data = mmap(NULL, table_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (data == MAP_FAILED) { fprintf(stderr, "failed to mmap()\n"); close(fd); return -1; } *table = data; return fd; } static void gamma_control_handle_gamma_size(void *data, struct zwlr_gamma_control_v1 *gamma_control, uint32_t ramp_size) { struct output *output = data; output->ramp_size = ramp_size; output->table_fd = create_gamma_table(ramp_size, &output->table); if (output->table_fd < 0) { exit(EXIT_FAILURE); } } static void gamma_control_handle_failed(void *data, struct zwlr_gamma_control_v1 *gamma_control) { fprintf(stderr, "failed to set gamma table\n"); exit(EXIT_FAILURE); } static const struct zwlr_gamma_control_v1_listener gamma_control_listener = { .gamma_size = gamma_control_handle_gamma_size, .failed = gamma_control_handle_failed, }; static void registry_handle_global(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { if (strcmp(interface, wl_output_interface.name) == 0) { struct output *output = calloc(1, sizeof(struct output)); output->wl_output = wl_registry_bind(registry, name, &wl_output_interface, 1); wl_list_insert(&outputs, &output->link); } else if (strcmp(interface, zwlr_gamma_control_manager_v1_interface.name) == 0) { gamma_control_manager = wl_registry_bind(registry, name, &zwlr_gamma_control_manager_v1_interface, 1); } } static void registry_handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) { // Who cares? } static const struct wl_registry_listener registry_listener = { .global = registry_handle_global, .global_remove = registry_handle_global_remove, }; static void fill_gamma_table(uint16_t *table, uint32_t ramp_size, double contrast, double brightness, double gamma) { uint16_t *r = table; uint16_t *g = table + ramp_size; uint16_t *b = table + 2 * ramp_size; for (uint32_t i = 0; i < ramp_size; ++i) { double val = (double)i / (ramp_size - 1); val = contrast * pow(val, 1.0 / gamma) + (brightness - 1); if (val > 1.0) { val = 1.0; } else if (val < 0.0) { val = 0.0; } r[i] = g[i] = b[i] = (uint16_t)(UINT16_MAX * val); } } static const char usage[] = "usage: gamma-control [options]\n" " -h show this help message\n" " -c set contrast (default: 1)\n" " -b set brightness (default: 1)\n" " -g set gamma (default: 1)\n"; int main(int argc, char *argv[]) { wl_list_init(&outputs); double contrast = 1, brightness = 1, gamma = 1; int opt; while ((opt = getopt(argc, argv, "hc:b:g:")) != -1) { switch (opt) { case 'c': contrast = strtod(optarg, NULL); break; case 'b': brightness = strtod(optarg, NULL); break; case 'g': gamma = strtod(optarg, NULL); break; case 'h': default: fprintf(stderr, usage); return opt == 'h' ? EXIT_SUCCESS : EXIT_FAILURE; } } struct wl_display *display = wl_display_connect(NULL); if (display == NULL) { fprintf(stderr, "failed to create display\n"); return -1; } struct wl_registry *registry = wl_display_get_registry(display); wl_registry_add_listener(registry, ®istry_listener, NULL); wl_display_dispatch(display); wl_display_roundtrip(display); if (gamma_control_manager == NULL) { fprintf(stderr, "compositor doesn't support wlr-gamma-control-unstable-v1\n"); return EXIT_FAILURE; } struct output *output; wl_list_for_each(output, &outputs, link) { output->gamma_control = zwlr_gamma_control_manager_v1_get_gamma_control( gamma_control_manager, output->wl_output); zwlr_gamma_control_v1_add_listener(output->gamma_control, &gamma_control_listener, output); } wl_display_roundtrip(display); wl_list_for_each(output, &outputs, link) { fill_gamma_table(output->table, output->ramp_size, contrast, brightness, gamma); zwlr_gamma_control_v1_set_gamma(output->gamma_control, output->table_fd); } while (wl_display_dispatch(display) != -1) { // This space is intentionnally left blank } return EXIT_SUCCESS; } wlroots-0.10.0/examples/idle-inhibit.c000066400000000000000000000154411361211131400176220ustar00rootroot00000000000000#include #include #include #include #include #include #include #include "idle-inhibit-unstable-v1-client-protocol.h" #include "xdg-shell-client-protocol.h" #include /** * Usage: idle-inhibit * Creates a xdg-toplevel using the idle-inhibit protocol. * It will be solid green, when it has an idle inhibitor, and solid yellow if * it does not. * Left click with a pointer will toggle this state. (Touch is not supported * for now). */ static int width = 500, height = 300; static struct wl_compositor *compositor = NULL; static struct wl_seat *seat = NULL; static struct xdg_wm_base *wm_base = NULL; static struct zwp_idle_inhibit_manager_v1 *idle_inhibit_manager = NULL; static struct zwp_idle_inhibitor_v1 *idle_inhibitor = NULL; struct wlr_egl egl; struct wl_egl_window *egl_window; struct wlr_egl_surface *egl_surface; static void draw(void) { eglMakeCurrent(egl.display, egl_surface, egl_surface, egl.context); float color[] = {1.0, 1.0, 0.0, 1.0}; if (idle_inhibitor) { color[0] = 0.0; } glViewport(0, 0, width, height); glClearColor(color[0], color[1], color[2], 1.0); glClear(GL_COLOR_BUFFER_BIT); eglSwapBuffers(egl.display, egl_surface); } static void pointer_handle_button(void *data, struct wl_pointer *pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t state_w) { struct wl_surface *surface = data; if (button == BTN_LEFT && state_w == WL_POINTER_BUTTON_STATE_PRESSED) { if (idle_inhibitor) { zwp_idle_inhibitor_v1_destroy(idle_inhibitor); idle_inhibitor = NULL; } else { idle_inhibitor = zwp_idle_inhibit_manager_v1_create_inhibitor( idle_inhibit_manager, surface); } } draw(); } static void pointer_handle_enter(void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface, wl_fixed_t surface_x, wl_fixed_t surface_y) { // This space intentionally left blank } static void pointer_handle_leave(void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface) { // This space intentionally left blank } static void pointer_handle_motion(void *data, struct wl_pointer *wl_pointer, uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) { // This space intentionally left blank } static void pointer_handle_axis(void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis, wl_fixed_t value) { // This space intentionally left blank } static void pointer_handle_frame(void *data, struct wl_pointer *wl_pointer) { // This space intentionally left blank } static void pointer_handle_axis_source(void *data, struct wl_pointer *wl_pointer, uint32_t axis_source) { // This space intentionally left blank } static void pointer_handle_axis_stop(void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis) { // This space intentionally left blank } static void pointer_handle_axis_discrete(void *data, struct wl_pointer *wl_pointer, uint32_t axis, int32_t discrete) { // This space intentionally left blank } 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, }; static void xdg_surface_handle_configure(void *data, struct xdg_surface *xdg_surface, uint32_t serial) { xdg_surface_ack_configure(xdg_surface, serial); wl_egl_window_resize(egl_window, width, height, 0, 0); draw(); } 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) { width = w; height = h; } static void xdg_toplevel_handle_close(void *data, struct xdg_toplevel *xdg_toplevel) { exit(EXIT_SUCCESS); } static const struct xdg_toplevel_listener xdg_toplevel_listener = { .configure = xdg_toplevel_handle_configure, .close = xdg_toplevel_handle_close, }; static void handle_global(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { if (strcmp(interface, "wl_compositor") == 0) { compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 1); } else if (strcmp(interface, xdg_wm_base_interface.name) == 0) { wm_base = wl_registry_bind(registry, name, &xdg_wm_base_interface, 1); } else if (strcmp(interface, zwp_idle_inhibit_manager_v1_interface.name) == 0) { idle_inhibit_manager = wl_registry_bind(registry, name, &zwp_idle_inhibit_manager_v1_interface, 1); } else if (strcmp(interface, wl_seat_interface.name) == 0) { seat = wl_registry_bind(registry, name, &wl_seat_interface, version); } } 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, }; int main(int argc, char **argv) { struct wl_display *display = wl_display_connect(NULL); if (display == NULL) { fprintf(stderr, "Failed to create display\n"); return EXIT_FAILURE; } struct wl_registry *registry = wl_display_get_registry(display); wl_registry_add_listener(registry, ®istry_listener, NULL); wl_display_dispatch(display); wl_display_roundtrip(display); if (compositor == NULL) { fprintf(stderr, "wl-compositor not available\n"); return EXIT_FAILURE; } if (wm_base == NULL) { fprintf(stderr, "xdg-shell not available\n"); return EXIT_FAILURE; } if (idle_inhibit_manager == NULL) { fprintf(stderr, "idle-inhibit not available\n"); return EXIT_FAILURE; } wlr_egl_init(&egl, EGL_PLATFORM_WAYLAND_EXT, display, NULL, WL_SHM_FORMAT_ARGB8888); struct wl_surface *surface = wl_compositor_create_surface(compositor); struct xdg_surface *xdg_surface = xdg_wm_base_get_xdg_surface(wm_base, surface); struct xdg_toplevel *xdg_toplevel = xdg_surface_get_toplevel(xdg_surface); idle_inhibitor = zwp_idle_inhibit_manager_v1_create_inhibitor(idle_inhibit_manager, surface); struct wl_pointer *pointer = wl_seat_get_pointer(seat); wl_pointer_add_listener(pointer, &pointer_listener, surface); xdg_surface_add_listener(xdg_surface, &xdg_surface_listener, NULL); xdg_toplevel_add_listener(xdg_toplevel, &xdg_toplevel_listener, NULL); wl_surface_commit(surface); egl_window = wl_egl_window_create(surface, width, height); egl_surface = wlr_egl_create_surface(&egl, egl_window); wl_display_roundtrip(display); draw(); while (wl_display_dispatch(display) != -1) { // This space intentionally left blank } return EXIT_SUCCESS; } wlroots-0.10.0/examples/idle.c000066400000000000000000000113511361211131400161720ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include "idle-client-protocol.h" static struct org_kde_kwin_idle *idle_manager = NULL; static struct wl_seat *seat = NULL; static uint32_t timeout = 0, simulate_activity_timeout = 0, close_timeout = 0; static int run = 1; struct thread_args { struct wl_display *display; struct org_kde_kwin_idle_timeout *timer; }; static void handle_global(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { fprintf(stdout, "interfaces found: %s\n", interface); if (strcmp(interface, "org_kde_kwin_idle") == 0) { idle_manager = wl_registry_bind(registry, name, &org_kde_kwin_idle_interface, 1); } else if (strcmp(interface, "wl_seat") == 0) { seat = wl_registry_bind(registry, name, &wl_seat_interface, 1); } } static void handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) { //TODO } static const struct wl_registry_listener registry_listener = { .global = handle_global, .global_remove = handle_global_remove, }; static void handle_idle(void* data, struct org_kde_kwin_idle_timeout *timer) { fprintf(stdout, "idle state\n"); } static void handle_resume(void* data, struct org_kde_kwin_idle_timeout *timer) { fprintf(stdout, "active state\n"); } static const struct org_kde_kwin_idle_timeout_listener idle_timer_listener = { .idle = handle_idle, .resumed = handle_resume, }; static int parse_args(int argc, char *argv[]) { int c; while ((c = getopt(argc, argv, "c:hs:t:")) != -1) { switch(c) { case 'c': close_timeout = strtoul(optarg, NULL, 10); break; case 's': simulate_activity_timeout = strtoul(optarg, NULL, 10); break; case 't': timeout = strtoul(optarg, NULL, 10); break; case 'h': case '?': printf("Usage: %s [OPTIONS]\n", argv[0]); printf(" -t seconds\t\t\tidle timeout in seconds\n"); printf(" -s seconds optional\t\tsimulate user activity after x seconds\n"); printf(" -c seconds optional\t\tclose program after x seconds\n"); printf(" -h\t\t\t\tthis help menu\n"); return 1; default: return 1; } } return 0; } static void *simulate_activity(void *data) { sleep(simulate_activity_timeout); fprintf(stdout, "simulate user activity\n"); struct thread_args *arg = data; org_kde_kwin_idle_timeout_simulate_user_activity(arg->timer); wl_display_roundtrip(arg->display); return NULL; } static void *close_program(void *data) { sleep(close_timeout); struct thread_args *arg = data; org_kde_kwin_idle_timeout_release(arg->timer); wl_display_roundtrip(arg->display); fprintf(stdout, "close program\n"); run = 0; return NULL; } static void *main_loop(void *data) { struct wl_display *display = data; while (wl_display_dispatch(display) != -1) { ; } return NULL; } int main(int argc, char *argv[]) { if (parse_args(argc, argv) != 0) { return -1; } if (timeout == 0) { printf("idle timeout 0 is invalid\n"); return -1; } struct wl_display *display = wl_display_connect(NULL); if (display == NULL) { fprintf(stderr, "failed to create display\n"); return -1; } struct wl_registry *registry = wl_display_get_registry(display); wl_registry_add_listener(registry, ®istry_listener, NULL); wl_display_dispatch(display); wl_display_roundtrip(display); free(registry); if (idle_manager == NULL) { fprintf(stderr, "display doesn't support idle protocol\n"); return -1; } if (seat== NULL) { fprintf(stderr, "seat error\n"); return -1; } struct org_kde_kwin_idle_timeout *timer = org_kde_kwin_idle_get_idle_timeout(idle_manager, seat, timeout * 1000); if (timer == NULL) { fprintf(stderr, "Could not create idle_timeout\n"); return -1; } pthread_t t1, t2, t3; struct thread_args arg = { .timer = timer, .display = display, }; bool create_t1 = (simulate_activity_timeout != 0) && (simulate_activity_timeout < close_timeout); if (create_t1) { if (pthread_create(&t1, NULL, &simulate_activity, (void *)&arg) != 0) { return -1; } } bool create_t2 = (close_timeout != 0); if (create_t2) { if (pthread_create(&t2, NULL, &close_program, (void *)&arg) != 0) { if (create_t1) { pthread_cancel(t1); } return -1; } } org_kde_kwin_idle_timeout_add_listener(timer, &idle_timer_listener, timer); fprintf(stdout, "waiting\n"); if (pthread_create(&t3, NULL, &main_loop, (void *)display) != 0) { if (create_t1) { pthread_cancel(t1); } if (create_t2) { pthread_cancel(t2); } return -1; } if (create_t1) { pthread_join(t1, NULL); } if (create_t2) { pthread_join(t2, NULL); } pthread_cancel(t3); wl_display_disconnect(display); return 0; } wlroots-0.10.0/examples/input-inhibitor.c000066400000000000000000000133571361211131400204110ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include "wlr-input-inhibitor-unstable-v1-client-protocol.h" #include "xdg-shell-client-protocol.h" static int width = 500, height = 300; static int keys = 0; static struct wl_compositor *compositor = NULL; static struct wl_seat *seat = NULL; static struct xdg_wm_base *wm_base = NULL; static struct zwlr_input_inhibit_manager_v1 *input_inhibit_manager = NULL; static struct zwlr_input_inhibitor_v1 *input_inhibitor = NULL; struct wlr_egl egl; struct wl_egl_window *egl_window; struct wlr_egl_surface *egl_surface; static void render_frame(void) { eglMakeCurrent(egl.display, egl_surface, egl_surface, egl.context); glViewport(0, 0, width, height); if (keys) { glClearColor(1.0, 1.0, 1.0, 1.0); } else { glClearColor(0.8, 0.4, 1.0, 1.0); } glClear(GL_COLOR_BUFFER_BIT); eglSwapBuffers(egl.display, egl_surface); } static void xdg_surface_handle_configure(void *data, struct xdg_surface *xdg_surface, uint32_t serial) { xdg_surface_ack_configure(xdg_surface, serial); wl_egl_window_resize(egl_window, width, height, 0, 0); render_frame(); } 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) { width = w; height = h; } static void xdg_toplevel_handle_close(void *data, struct xdg_toplevel *xdg_toplevel) { exit(0); } static const struct xdg_toplevel_listener xdg_toplevel_listener = { .configure = xdg_toplevel_handle_configure, .close = xdg_toplevel_handle_close, }; static void wl_keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard, uint32_t format, int32_t fd, uint32_t size) { } static void wl_keyboard_enter(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface, struct wl_array *keys) { } static void wl_keyboard_leave(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface) { } static void wl_keyboard_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) { } static void wl_keyboard_repeat_info(void *data, struct wl_keyboard *wl_keyboard, int32_t rate, int32_t delay) { } static void wl_keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state) { if (state == WL_KEYBOARD_KEY_STATE_PRESSED) { ++keys; } else { --keys; } render_frame(); } static struct wl_keyboard_listener keyboard_listener = { .keymap = wl_keyboard_keymap, .enter = wl_keyboard_enter, .leave = wl_keyboard_leave, .key = wl_keyboard_key, .modifiers = wl_keyboard_modifiers, .repeat_info = wl_keyboard_repeat_info, }; static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat, enum wl_seat_capability caps) { if ((caps & WL_SEAT_CAPABILITY_KEYBOARD)) { struct wl_keyboard *keyboard = wl_seat_get_keyboard(wl_seat); wl_keyboard_add_listener(keyboard, &keyboard_listener, NULL); } } static void seat_handle_name(void *data, struct wl_seat *wl_seat, const char *name) { // Who cares } const struct wl_seat_listener seat_listener = { .capabilities = seat_handle_capabilities, .name = seat_handle_name, }; 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, 1); } else if (strcmp(interface, xdg_wm_base_interface.name) == 0) { wm_base = wl_registry_bind(registry, name, &xdg_wm_base_interface, 1); } else if (strcmp(interface, zwlr_input_inhibit_manager_v1_interface.name) == 0) { input_inhibit_manager = wl_registry_bind(registry, name, &zwlr_input_inhibit_manager_v1_interface, 1); } else if (strcmp(interface, wl_seat_interface.name) == 0) { seat = wl_registry_bind(registry, name, &wl_seat_interface, version); wl_seat_add_listener(seat, &seat_listener, seat); } } 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, }; int main(int argc, char **argv) { struct wl_display *display = wl_display_connect(NULL); assert(display); struct wl_registry *registry = wl_display_get_registry(display); assert(registry); wl_registry_add_listener(registry, ®istry_listener, NULL); wl_display_dispatch(display); wl_display_roundtrip(display); assert(compositor && seat && wm_base && input_inhibit_manager); input_inhibitor = zwlr_input_inhibit_manager_v1_get_inhibitor( input_inhibit_manager); assert(input_inhibitor); wlr_egl_init(&egl, EGL_PLATFORM_WAYLAND_EXT, display, NULL, WL_SHM_FORMAT_ARGB8888); struct wl_surface *surface = wl_compositor_create_surface(compositor); assert(surface); struct xdg_surface *xdg_surface = xdg_wm_base_get_xdg_surface(wm_base, surface); assert(xdg_surface); struct xdg_toplevel *xdg_toplevel = xdg_surface_get_toplevel(xdg_surface); assert(xdg_toplevel); xdg_surface_add_listener(xdg_surface, &xdg_surface_listener, NULL); xdg_toplevel_add_listener(xdg_toplevel, &xdg_toplevel_listener, NULL); wl_surface_commit(surface); egl_window = wl_egl_window_create(surface, width, height); egl_surface = wlr_egl_create_surface(&egl, egl_window); wl_display_roundtrip(display); render_frame(); while (wl_display_dispatch(display) != -1) { // This space intentionally left blank } return 0; } wlroots-0.10.0/examples/input-method.c000066400000000000000000000264741361211131400177060ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include #include #include #include #include "input-method-unstable-v2-client-protocol.h" #include "text-input-unstable-v3-client-protocol.h" #include "xdg-shell-client-protocol.h" const char usage[] = "Usage: input-method [seconds]\n\ \n\ Creates an input method using the input-method protocol.\n\ \n\ Whenever a text input is activated, this program sends a few sequences of\n\ commands and checks the validity of the responses, relying on returned\n\ surrounding text.\n\ \n\ The \"seconds\" argument is optional and defines the maximum delay between\n\ stages."; struct input_method_state { enum zwp_text_input_v3_change_cause change_cause; struct { enum zwp_text_input_v3_content_hint hint; enum zwp_text_input_v3_content_purpose purpose; } content_type; struct { char *text; uint32_t cursor; uint32_t anchor; } surrounding; }; static int sleeptime = 0; static struct wl_display *display = NULL; static struct wl_compositor *compositor = NULL; static struct wl_seat *seat = NULL; static struct zwp_input_method_manager_v2 *input_method_manager = NULL; static struct zwp_input_method_v2 *input_method = NULL; struct input_method_state pending; struct input_method_state current; static uint32_t serial = 0; bool active = false; bool pending_active = false; bool unavailable = false; bool running = false; uint32_t update_stage = 0; int timer_fd = 0; static void print_state_diff(struct input_method_state previous, struct input_method_state future) { if (previous.content_type.hint != future.content_type.hint) { char *strs[] = { "COMPLETION", "SPELLCHECK", "AUTO_CAPITALIZATION", "LOWERCASE", "UPPERCASE", "TITLECASE", "HIDDEN_TEXT", "SENSITIVE_DATA", "LATIN", "MULTILINE"}; printf("content_type.hint:"); uint32_t hint = future.content_type.hint; if (!hint) { printf(" NONE"); } for (unsigned i = 0; i < sizeof(strs) / sizeof(*strs); i++) { if (hint & 1 << i) { printf(" %s", strs[i]); } } printf("\n"); } if (previous.content_type.purpose != future.content_type.purpose) { char *strs[] = { "NORMAL", "ALPHA", "DIGITS", "NUMBER", "PHONE", "URL", "EMAIL", "NAME", "PASSWORD", "PIN", "DATE", "TIME", "DATETIME", "TERMINAL" }; printf("content_type.purpose: %s\n", strs[future.content_type.purpose]); } if (!!previous.surrounding.text != !!future.surrounding.text || (previous.surrounding.text && future.surrounding.text && strcmp(previous.surrounding.text, future.surrounding.text) != 0) || previous.surrounding.anchor != future.surrounding.anchor || previous.surrounding.cursor != future.surrounding.cursor) { char *text = future.surrounding.text; if (!text) { printf("Removed surrounding text\n"); } else { printf("Surrounding text: %s\n", text); uint32_t anchor = future.surrounding.anchor; uint32_t cursor = future.surrounding.cursor; if (cursor == anchor) { char *temp = strndup(text, cursor); printf("Cursor after %d: %s\n", cursor, temp); free(temp); } else { if (cursor > anchor) { uint32_t tmp = anchor; anchor = cursor; cursor = tmp; } char *temp = strndup(&text[cursor], anchor - cursor); printf("Selection: %s\n", temp); free(temp); } } } if (previous.change_cause != future.change_cause) { char *strs[] = { "INPUT_METHOD", "OTHER" }; printf("Change cause: %s\n", strs[future.change_cause]); } } static void handle_content_type(void *data, struct zwp_input_method_v2 *zwp_input_method_v2, uint32_t hint, uint32_t purpose) { pending.content_type.hint = hint; pending.content_type.purpose = purpose; } static void handle_surrounding_text(void *data, struct zwp_input_method_v2 *zwp_input_method_v2, const char *text, uint32_t cursor, uint32_t anchor) { free(pending.surrounding.text); pending.surrounding.text = strdup(text); pending.surrounding.cursor = cursor; pending.surrounding.anchor = anchor; } static void handle_text_change_cause(void *data, struct zwp_input_method_v2 *zwp_input_method_v2, uint32_t cause) { pending.change_cause = cause; } static void handle_activate(void *data, struct zwp_input_method_v2 *zwp_input_method_v2) { pending_active = true; } static void handle_deactivate(void *data, struct zwp_input_method_v2 *zwp_input_method_v2) { pending_active = false; } static void handle_unavailable(void *data, struct zwp_input_method_v2 *zwp_input_method_v2) { printf("IM disappeared\n"); zwp_input_method_v2_destroy(zwp_input_method_v2); input_method = NULL; running = false; } static void im_activate(void *data, struct zwp_input_method_v2 *id) { update_stage = 0; } static void timer_arm(unsigned seconds) { printf("Timer armed\n"); struct itimerspec spec = { .it_interval = {0}, .it_value = { .tv_sec = seconds, .tv_nsec = 0 } }; if (timerfd_settime(timer_fd, 0, &spec, NULL)) { fprintf(stderr, "Failed to arm timer: %s\n", strerror(errno)); } } static void do_updates(void) { printf("Update %d\n", update_stage); switch (update_stage) { case 0: // TODO: remember initial surrounding text zwp_input_method_v2_set_preedit_string(input_method, "Preedit", 2, 4); zwp_input_method_v2_commit(input_method, serial); // don't expect an answer, preedit doesn't change anything visible timer_arm(sleeptime); update_stage++; return; case 1: zwp_input_method_v2_set_preedit_string(input_method, "Præedit2", strlen("Pr"), strlen("Præed")); zwp_input_method_v2_commit_string(input_method, "_Commit_"); zwp_input_method_v2_commit(input_method, serial); update_stage++; break; case 2: if (strcmp(current.surrounding.text, "_Commit_") != 0) { return; } zwp_input_method_v2_commit_string(input_method, "_CommitNoPreed_"); zwp_input_method_v2_commit(input_method, serial); timer_arm(sleeptime); update_stage++; break; case 3: if (strcmp(current.surrounding.text, "_Commit__CommitNoPreed_") != 0) { return; } zwp_input_method_v2_commit_string(input_method, "_WaitNo_"); zwp_input_method_v2_delete_surrounding_text(input_method, strlen("_CommitNoPreed_"), 0); zwp_input_method_v2_commit(input_method, serial); update_stage++; break; case 4: if (strcmp(current.surrounding.text, "_Commit__WaitNo_") != 0) { return; } zwp_input_method_v2_set_preedit_string(input_method, "PreedWithDel", strlen("Preed"), strlen("Preed")); zwp_input_method_v2_delete_surrounding_text(input_method, strlen("_WaitNo_"), 0); zwp_input_method_v2_commit(input_method, serial); update_stage++; break; case 5: if (strcmp(current.surrounding.text, "_Commit_") != 0) { return; } zwp_input_method_v2_delete_surrounding_text(input_method, strlen("mit_"), 0); zwp_input_method_v2_commit(input_method, serial); update_stage++; break; case 6: if (strcmp(current.surrounding.text, "_Com") != 0) { printf("Failed\n"); } update_stage++; break; default: printf("Submitted everything\n"); return; }; } static void handle_timer(void) { printf("Timer dispatched at %d\n", update_stage); do_updates(); } static void im_deactivate(void *data, struct zwp_input_method_v2 *context) { // No special action needed } static void handle_done(void *data, struct zwp_input_method_v2 *zwp_input_method_v2) { bool prev_active = active; serial++; printf("Handle serial %d\n", serial); if (active != pending_active) { printf("Now %s\n", pending_active ? "active" : "inactive"); } if (pending_active) { print_state_diff(current, pending); } active = pending_active; free(current.surrounding.text); struct input_method_state default_state = {0}; current = pending; pending = default_state; if (active && !prev_active) { im_activate(data, zwp_input_method_v2); } else if (!active && prev_active) { im_deactivate(data, zwp_input_method_v2); } do_updates(); } static const struct zwp_input_method_v2_listener im_listener = { .activate = handle_activate, .deactivate = handle_deactivate, .surrounding_text = handle_surrounding_text, .text_change_cause = handle_text_change_cause, .content_type = handle_content_type, .done = handle_done, .unavailable = handle_unavailable, }; static void handle_global(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { if (strcmp(interface, "wl_compositor") == 0) { compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 1); } else if (strcmp(interface, zwp_input_method_manager_v2_interface.name) == 0) { input_method_manager = wl_registry_bind(registry, name, &zwp_input_method_manager_v2_interface, 1); } else if (strcmp(interface, wl_seat_interface.name) == 0) { seat = wl_registry_bind(registry, name, &wl_seat_interface, version); } } 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, }; int main(int argc, char **argv) { if (argc > 1) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-h") == 0) { printf(usage); return 0; } sleeptime = atoi(argv[1]); } display = wl_display_connect(NULL); if (display == NULL) { fprintf(stderr, "Failed to create display\n"); return EXIT_FAILURE; } struct wl_registry *registry = wl_display_get_registry(display); wl_registry_add_listener(registry, ®istry_listener, NULL); wl_display_dispatch(display); wl_display_roundtrip(display); if (compositor == NULL) { fprintf(stderr, "wl-compositor not available\n"); return EXIT_FAILURE; } if (input_method_manager == NULL) { fprintf(stderr, "input-method not available\n"); return EXIT_FAILURE; } if (seat == NULL) { fprintf(stderr, "seat not available\n"); return EXIT_FAILURE; } input_method = zwp_input_method_manager_v2_get_input_method( input_method_manager, seat); running = true; zwp_input_method_v2_add_listener(input_method, &im_listener, NULL); int display_fd = wl_display_get_fd(display); timer_fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK); if (timer_fd < 0) { fprintf(stderr, "Failed to start timer\n"); return EXIT_FAILURE; } int epoll = epoll_create1(EPOLL_CLOEXEC); if (epoll < 0) { fprintf(stderr, "Failed to start epoll\n"); return EXIT_FAILURE; } struct epoll_event epoll_display = { .events = EPOLLIN | EPOLLOUT, .data = {.fd = display_fd}, }; if (epoll_ctl(epoll, EPOLL_CTL_ADD, display_fd, &epoll_display)) { fprintf(stderr, "Failed to epoll display\n"); return EXIT_FAILURE; } wl_display_roundtrip(display); // timer may be armed here struct epoll_event epoll_timer = { .events = EPOLLIN, .data = {.fd = timer_fd}, }; if (epoll_ctl(epoll, EPOLL_CTL_ADD, timer_fd, &epoll_timer)) { fprintf(stderr, "Failed to epoll timer\n"); return EXIT_FAILURE; } timer_arm(2); struct epoll_event caught; while (epoll_wait(epoll, &caught, 1, -1)) { if (!running) { printf("Exiting\n"); return EXIT_SUCCESS; } if (caught.data.fd == display_fd) { if (wl_display_dispatch(display) == -1) { break; } } else if (caught.data.fd == timer_fd) { uint64_t expirations; ssize_t n = read(timer_fd, &expirations, sizeof(expirations)); assert(n >= 0); handle_timer(); } else { printf("Unknown source\n"); } } return EXIT_SUCCESS; } wlroots-0.10.0/examples/layer-shell.c000066400000000000000000000436761361211131400175150ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200112L #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "wlr-layer-shell-unstable-v1-client-protocol.h" #include "xdg-shell-client-protocol.h" static struct wl_display *display; static struct wl_compositor *compositor; static struct wl_seat *seat; static struct wl_shm *shm; static struct wl_pointer *pointer; static struct wl_keyboard *keyboard; static struct xdg_wm_base *xdg_wm_base; static struct zwlr_layer_shell_v1 *layer_shell; struct zwlr_layer_surface_v1 *layer_surface; static struct wl_output *wl_output; struct wl_surface *wl_surface; struct wlr_egl egl; struct wl_egl_window *egl_window; struct wlr_egl_surface *egl_surface; struct wl_callback *frame_callback; static uint32_t output = UINT32_MAX; struct xdg_popup *popup; struct wl_surface *popup_wl_surface; struct wl_egl_window *popup_egl_window; static uint32_t popup_width = 256, popup_height = 256; struct wlr_egl_surface *popup_egl_surface; struct wl_callback *popup_frame_callback; float popup_alpha = 1.0, popup_red = 0.5f; static uint32_t layer = ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND; static uint32_t anchor = 0; static uint32_t width = 256, height = 256; static int32_t margin_top = 0; static double alpha = 1.0; static bool run_display = true; static bool animate = false; static bool keyboard_interactive = false; static double frame = 0; static int cur_x = -1, cur_y = -1; static int buttons = 0; struct wl_cursor_image *cursor_image; struct wl_cursor_image *popup_cursor_image; struct wl_surface *cursor_surface, *input_surface; static struct { struct timespec last_frame; float color[3]; int dec; } demo; static void draw(void); static void draw_popup(void); static void surface_frame_callback( void *data, struct wl_callback *cb, uint32_t time) { wl_callback_destroy(cb); frame_callback = NULL; draw(); } static struct wl_callback_listener frame_listener = { .done = surface_frame_callback }; static void popup_surface_frame_callback( void *data, struct wl_callback *cb, uint32_t time) { wl_callback_destroy(cb); popup_frame_callback = NULL; if (popup) { draw_popup(); } } static struct wl_callback_listener popup_frame_listener = { .done = popup_surface_frame_callback }; static void draw(void) { eglMakeCurrent(egl.display, egl_surface, egl_surface, egl.context); struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); long ms = (ts.tv_sec - demo.last_frame.tv_sec) * 1000 + (ts.tv_nsec - demo.last_frame.tv_nsec) / 1000000; int inc = (demo.dec + 1) % 3; if (!buttons) { demo.color[inc] += ms / 2000.0f; demo.color[demo.dec] -= ms / 2000.0f; if (demo.color[demo.dec] < 0.0f) { demo.color[inc] = 1.0f; demo.color[demo.dec] = 0.0f; demo.dec = inc; } } if (animate) { frame += ms / 50.0; int32_t old_top = margin_top; margin_top = -(20 - ((int)frame % 20)); if (old_top != margin_top) { zwlr_layer_surface_v1_set_margin(layer_surface, margin_top, 0, 0, 0); wl_surface_commit(wl_surface); } } glViewport(0, 0, width, height); if (buttons) { glClearColor(1, 1, 1, alpha); } else { glClearColor(demo.color[0], demo.color[1], demo.color[2], alpha); } glClear(GL_COLOR_BUFFER_BIT); if (cur_x != -1 && cur_y != -1) { glEnable(GL_SCISSOR_TEST); glScissor(cur_x, height - cur_y, 5, 5); glClearColor(0, 0, 0, 1); glClear(GL_COLOR_BUFFER_BIT); glDisable(GL_SCISSOR_TEST); } frame_callback = wl_surface_frame(wl_surface); wl_callback_add_listener(frame_callback, &frame_listener, NULL); eglSwapBuffers(egl.display, egl_surface); demo.last_frame = ts; } static void draw_popup(void) { static float alpha_mod = -0.01; eglMakeCurrent(egl.display, popup_egl_surface, popup_egl_surface, egl.context); glViewport(0, 0, popup_width, popup_height); glClearColor(popup_red, 0.5f, 0.5f, popup_alpha); popup_alpha += alpha_mod; if (popup_alpha < 0.01 || popup_alpha >= 1.0f) { alpha_mod *= -1.0; } glClear(GL_COLOR_BUFFER_BIT); popup_frame_callback = wl_surface_frame(popup_wl_surface); assert(popup_frame_callback); wl_callback_add_listener(popup_frame_callback, &popup_frame_listener, NULL); eglSwapBuffers(egl.display, popup_egl_surface); wl_surface_commit(popup_wl_surface); } static void xdg_surface_handle_configure(void *data, struct xdg_surface *xdg_surface, uint32_t serial) { xdg_surface_ack_configure(xdg_surface, serial); } static const struct xdg_surface_listener xdg_surface_listener = { .configure = xdg_surface_handle_configure, }; static void xdg_popup_configure(void *data, struct xdg_popup *xdg_popup, int32_t x, int32_t y, int32_t width, int32_t height) { wlr_log(WLR_DEBUG, "Popup configured %dx%d@%d,%d", width, height, x, y); popup_width = width; popup_height = height; if (popup_egl_window) { wl_egl_window_resize(popup_egl_window, width, height, 0, 0); } } static void popup_destroy(void) { wlr_egl_destroy_surface(&egl, popup_egl_surface); wl_egl_window_destroy(popup_egl_window); xdg_popup_destroy(popup); wl_surface_destroy(popup_wl_surface); popup_wl_surface = NULL; popup = NULL; popup_egl_window = NULL; } static void xdg_popup_done(void *data, struct xdg_popup *xdg_popup) { wlr_log(WLR_DEBUG, "Popup done"); popup_destroy(); } static const struct xdg_popup_listener xdg_popup_listener = { .configure = xdg_popup_configure, .popup_done = xdg_popup_done, }; static void create_popup(uint32_t serial) { if (popup) { return; } struct wl_surface *surface = wl_compositor_create_surface(compositor); assert(xdg_wm_base && surface); struct xdg_surface *xdg_surface = xdg_wm_base_get_xdg_surface(xdg_wm_base, surface); struct xdg_positioner *xdg_positioner = xdg_wm_base_create_positioner(xdg_wm_base); assert(xdg_surface && xdg_positioner); xdg_positioner_set_size(xdg_positioner, popup_width, popup_height); xdg_positioner_set_offset(xdg_positioner, 0, 0); xdg_positioner_set_anchor_rect(xdg_positioner, cur_x, cur_y, 1, 1); xdg_positioner_set_anchor(xdg_positioner, XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT); popup = xdg_surface_get_popup(xdg_surface, NULL, xdg_positioner); xdg_popup_grab(popup, seat, serial); assert(popup); zwlr_layer_surface_v1_get_popup(layer_surface, popup); xdg_surface_add_listener(xdg_surface, &xdg_surface_listener, NULL); xdg_popup_add_listener(popup, &xdg_popup_listener, NULL); wl_surface_commit(surface); wl_display_roundtrip(display); xdg_positioner_destroy(xdg_positioner); popup_wl_surface = surface; popup_egl_window = wl_egl_window_create(surface, popup_width, popup_height); assert(popup_egl_window); popup_egl_surface = wlr_egl_create_surface(&egl, popup_egl_window); assert(popup_egl_surface); draw_popup(); } static void layer_surface_configure(void *data, struct zwlr_layer_surface_v1 *surface, uint32_t serial, uint32_t w, uint32_t h) { width = w; height = h; if (egl_window) { wl_egl_window_resize(egl_window, width, height, 0, 0); } zwlr_layer_surface_v1_ack_configure(surface, serial); } static void layer_surface_closed(void *data, struct zwlr_layer_surface_v1 *surface) { wlr_egl_destroy_surface(&egl, egl_surface); wl_egl_window_destroy(egl_window); zwlr_layer_surface_v1_destroy(surface); wl_surface_destroy(wl_surface); run_display = false; } struct zwlr_layer_surface_v1_listener layer_surface_listener = { .configure = layer_surface_configure, .closed = layer_surface_closed, }; static void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface, wl_fixed_t surface_x, wl_fixed_t surface_y) { struct wl_cursor_image *image; if (surface == popup_wl_surface) { image = popup_cursor_image; } else { image = cursor_image; } wl_surface_attach(cursor_surface, wl_cursor_image_get_buffer(image), 0, 0); wl_surface_damage(cursor_surface, 1, 0, image->width, image->height); wl_surface_commit(cursor_surface); wl_pointer_set_cursor(wl_pointer, serial, cursor_surface, image->hotspot_x, image->hotspot_y); input_surface = surface; } static void wl_pointer_leave(void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface) { cur_x = cur_y = -1; buttons = 0; } static void wl_pointer_motion(void *data, struct wl_pointer *wl_pointer, uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) { cur_x = wl_fixed_to_int(surface_x); cur_y = wl_fixed_to_int(surface_y); } static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t state) { if (input_surface == wl_surface) { if (state == WL_POINTER_BUTTON_STATE_PRESSED) { if (button == BTN_RIGHT) { if (popup_wl_surface) { popup_destroy(); } else { create_popup(serial); } } else { buttons++; } } else { if (button != BTN_RIGHT) { buttons--; } } } else if (input_surface == popup_wl_surface) { if (state == WL_POINTER_BUTTON_STATE_PRESSED) { if (button == BTN_LEFT && popup_red <= 0.9f) { popup_red += 0.1; } else if (button == BTN_RIGHT && popup_red >= 0.1f) { popup_red -= 0.1; } } } else { assert(false && "Unknown surface"); } } static void wl_pointer_axis(void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis, wl_fixed_t value) { // Who cares } static void wl_pointer_frame(void *data, struct wl_pointer *wl_pointer) { // Who cares } static void wl_pointer_axis_source(void *data, struct wl_pointer *wl_pointer, uint32_t axis_source) { // Who cares } static void wl_pointer_axis_stop(void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis) { // Who cares } static void wl_pointer_axis_discrete(void *data, struct wl_pointer *wl_pointer, uint32_t axis, int32_t discrete) { // Who cares } struct wl_pointer_listener pointer_listener = { .enter = wl_pointer_enter, .leave = wl_pointer_leave, .motion = wl_pointer_motion, .button = wl_pointer_button, .axis = wl_pointer_axis, .frame = wl_pointer_frame, .axis_source = wl_pointer_axis_source, .axis_stop = wl_pointer_axis_stop, .axis_discrete = wl_pointer_axis_discrete, }; static void wl_keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard, uint32_t format, int32_t fd, uint32_t size) { // Who cares } static void wl_keyboard_enter(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface, struct wl_array *keys) { wlr_log(WLR_DEBUG, "Keyboard enter"); } static void wl_keyboard_leave(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface) { wlr_log(WLR_DEBUG, "Keyboard leave"); } static void wl_keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state) { wlr_log(WLR_DEBUG, "Key event: %d %d", key, state); } static void wl_keyboard_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) { // Who cares } static void wl_keyboard_repeat_info(void *data, struct wl_keyboard *wl_keyboard, int32_t rate, int32_t delay) { // Who cares } static struct wl_keyboard_listener keyboard_listener = { .keymap = wl_keyboard_keymap, .enter = wl_keyboard_enter, .leave = wl_keyboard_leave, .key = wl_keyboard_key, .modifiers = wl_keyboard_modifiers, .repeat_info = wl_keyboard_repeat_info, }; static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat, enum wl_seat_capability caps) { if ((caps & WL_SEAT_CAPABILITY_POINTER)) { pointer = wl_seat_get_pointer(wl_seat); wl_pointer_add_listener(pointer, &pointer_listener, NULL); } if ((caps & WL_SEAT_CAPABILITY_KEYBOARD)) { keyboard = wl_seat_get_keyboard(wl_seat); wl_keyboard_add_listener(keyboard, &keyboard_listener, NULL); } } static void seat_handle_name(void *data, struct wl_seat *wl_seat, const char *name) { // Who cares } const struct wl_seat_listener seat_listener = { .capabilities = seat_handle_capabilities, .name = seat_handle_name, }; 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, 1); } else if (strcmp(interface, wl_shm_interface.name) == 0) { shm = wl_registry_bind(registry, name, &wl_shm_interface, 1); } else if (strcmp(interface, "wl_output") == 0) { if (output != UINT32_MAX) { if (!wl_output) { wl_output = wl_registry_bind(registry, name, &wl_output_interface, 1); } else { output--; } } } else if (strcmp(interface, wl_seat_interface.name) == 0) { seat = wl_registry_bind(registry, name, &wl_seat_interface, 1); wl_seat_add_listener(seat, &seat_listener, NULL); } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) { layer_shell = wl_registry_bind( registry, name, &zwlr_layer_shell_v1_interface, 1); } else if (strcmp(interface, xdg_wm_base_interface.name) == 0) { xdg_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, }; int main(int argc, char **argv) { wlr_log_init(WLR_DEBUG, NULL); char *namespace = "wlroots"; int exclusive_zone = 0; int32_t margin_right = 0, margin_bottom = 0, margin_left = 0; bool found; int c; while ((c = getopt(argc, argv, "knw:h:o:l:a:x:m:t:")) != -1) { switch (c) { case 'o': output = atoi(optarg); break; case 'w': width = atoi(optarg); break; case 'h': height = atoi(optarg); break; case 'x': exclusive_zone = atoi(optarg); break; case 'l': { struct { char *name; enum zwlr_layer_shell_v1_layer value; } layers[] = { { "background", ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND }, { "bottom", ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM }, { "top", ZWLR_LAYER_SHELL_V1_LAYER_TOP }, { "overlay", ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY }, }; found = false; for (size_t i = 0; i < sizeof(layers) / sizeof(layers[0]); ++i) { if (strcmp(optarg, layers[i].name) == 0) { layer = layers[i].value; found = true; break; } } if (!found) { fprintf(stderr, "invalid layer %s\n", optarg); return 1; } break; } case 'a': { struct { char *name; uint32_t value; } anchors[] = { { "top", ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP }, { "bottom", ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM }, { "left", ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT }, { "right", ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT }, }; found = false; for (size_t i = 0; i < sizeof(anchors) / sizeof(anchors[0]); ++i) { if (strcmp(optarg, anchors[i].name) == 0) { anchor |= anchors[i].value; found = true; break; } } if (!found) { fprintf(stderr, "invalid anchor %s\n", optarg); return 1; } break; } case 't': alpha = atof(optarg); break; case 'm': { char *endptr = optarg; margin_top = strtol(endptr, &endptr, 10); assert(*endptr == ','); margin_right = strtol(endptr + 1, &endptr, 10); assert(*endptr == ','); margin_bottom = strtol(endptr + 1, &endptr, 10); assert(*endptr == ','); margin_left = strtol(endptr + 1, &endptr, 10); assert(!*endptr); break; } case 'n': animate = true; break; case 'k': keyboard_interactive = true; break; default: break; } } display = wl_display_connect(NULL); if (display == NULL) { fprintf(stderr, "Failed to create display\n"); return 1; } struct wl_registry *registry = wl_display_get_registry(display); wl_registry_add_listener(registry, ®istry_listener, NULL); wl_display_roundtrip(display); if (compositor == NULL) { fprintf(stderr, "wl_compositor not available\n"); return 1; } if (shm == NULL) { fprintf(stderr, "wl_shm not available\n"); return 1; } if (layer_shell == NULL) { fprintf(stderr, "layer_shell not available\n"); return 1; } struct wl_cursor_theme *cursor_theme = wl_cursor_theme_load(NULL, 16, shm); assert(cursor_theme); struct wl_cursor *cursor = wl_cursor_theme_get_cursor(cursor_theme, "crosshair"); if (cursor == NULL) { cursor = wl_cursor_theme_get_cursor(cursor_theme, "left_ptr"); } assert(cursor); cursor_image = cursor->images[0]; cursor = wl_cursor_theme_get_cursor(cursor_theme, "tcross"); if (cursor == NULL) { cursor = wl_cursor_theme_get_cursor(cursor_theme, "left_ptr"); } assert(cursor); popup_cursor_image = cursor->images[0]; cursor_surface = wl_compositor_create_surface(compositor); assert(cursor_surface); EGLint attribs[] = { EGL_ALPHA_SIZE, 8, EGL_NONE }; wlr_egl_init(&egl, EGL_PLATFORM_WAYLAND_EXT, display, attribs, WL_SHM_FORMAT_ARGB8888); wl_surface = wl_compositor_create_surface(compositor); assert(wl_surface); layer_surface = zwlr_layer_shell_v1_get_layer_surface(layer_shell, wl_surface, wl_output, layer, namespace); assert(layer_surface); zwlr_layer_surface_v1_set_size(layer_surface, width, height); zwlr_layer_surface_v1_set_anchor(layer_surface, anchor); zwlr_layer_surface_v1_set_exclusive_zone(layer_surface, exclusive_zone); zwlr_layer_surface_v1_set_margin(layer_surface, margin_top, margin_right, margin_bottom, margin_left); zwlr_layer_surface_v1_set_keyboard_interactivity( layer_surface, keyboard_interactive); zwlr_layer_surface_v1_add_listener(layer_surface, &layer_surface_listener, layer_surface); wl_surface_commit(wl_surface); wl_display_roundtrip(display); egl_window = wl_egl_window_create(wl_surface, width, height); assert(egl_window); egl_surface = wlr_egl_create_surface(&egl, egl_window); assert(egl_surface); wl_display_roundtrip(display); draw(); while (wl_display_dispatch(display) != -1 && run_display) { // This space intentionally left blank } wl_cursor_theme_destroy(cursor_theme); return 0; } wlroots-0.10.0/examples/meson.build000066400000000000000000000075101361211131400172550ustar00rootroot00000000000000threads = dependency('threads') wayland_cursor = dependency('wayland-cursor') libpng = dependency('libpng', required: false, disabler: true) # These versions correspond to ffmpeg 4.0 libavutil = dependency('libavutil', version: '>=56.14.100', required: false, disabler: true) libavcodec = dependency('libavcodec', version: '>=58.18.100', required: false, disabler: true) libavformat = dependency('libavformat', version: '>=58.12.100', required: false, disabler: true) # epoll is a separate library in FreeBSD if host_machine.system() == 'freebsd' libepoll = dependency('epoll-shim') else libepoll = dependency('', required: false) endif # Check if libavutil is found because of https://github.com/mesonbuild/meson/issues/6010 if libavutil.found() and not cc.has_header('libavutil/hwcontext_drm.h', dependencies: libavutil) libavutil = disabler() endif 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'], }, 'multi-pointer': { 'src': 'multi-pointer.c', }, 'output-layout': { 'src': ['output-layout.c', 'cat.c'], }, 'fullscreen-shell': { 'src': 'fullscreen-shell.c', }, } clients = { 'idle': { 'src': 'idle.c', 'dep': threads, 'proto': ['kde-idle'], }, 'idle-inhibit': { 'src': 'idle-inhibit.c', 'dep': wlroots, 'proto': [ 'idle-inhibit-unstable-v1', 'xdg-shell', ], }, 'layer-shell': { 'src': 'layer-shell.c', 'dep': [wayland_cursor, wlroots], 'proto': [ 'wlr-layer-shell-unstable-v1', 'xdg-shell', ], }, 'input-inhibitor': { 'src': 'input-inhibitor.c', 'dep': [wayland_cursor, wlroots], 'proto': [ 'wlr-input-inhibitor-unstable-v1', 'xdg-shell', ], }, 'gamma-control': { 'src': 'gamma-control.c', 'dep': [wayland_cursor, math], 'proto': ['wlr-gamma-control-unstable-v1'], }, 'pointer-constraints': { 'src': 'pointer-constraints.c', 'dep': wlroots, 'proto': [ 'pointer-constraints-unstable-v1', 'xdg-shell', ], }, 'relative-pointer': { 'src': 'relative-pointer-unstable-v1.c', 'dep': wlroots, 'proto': [ 'pointer-constraints-unstable-v1', 'relative-pointer-unstable-v1', 'xdg-shell', ], }, 'dmabuf-capture': { 'src': 'dmabuf-capture.c', 'dep': [ libavcodec, libavformat, libavutil, drm.partial_dependency(compile_args: true), # threads, ], 'proto': ['wlr-export-dmabuf-unstable-v1'], }, 'screencopy': { 'src': 'screencopy.c', 'dep': [libpng, rt], 'proto': ['wlr-screencopy-unstable-v1'], }, 'toplevel-decoration': { 'src': 'toplevel-decoration.c', 'dep': wlroots, 'proto': [ 'xdg-decoration-unstable-v1', 'xdg-shell', ], }, 'input-method': { 'src': 'input-method.c', 'dep': libepoll, 'proto': [ 'input-method-unstable-v2', 'text-input-unstable-v3', 'xdg-shell', ], }, 'text-input': { 'src': 'text-input.c', 'dep': [wayland_cursor, wlroots], 'proto': [ 'text-input-unstable-v3', 'xdg-shell', ], }, 'foreign-toplevel': { 'src': 'foreign-toplevel.c', 'dep': [wlroots], 'proto': ['wlr-foreign-toplevel-management-unstable-v1'], }, 'virtual-pointer': { 'src': 'virtual-pointer.c', 'dep': wlroots, 'proto': ['wlr-virtual-pointer-unstable-v1'], }, } foreach name, info : compositors executable( name, info.get('src'), dependencies: wlroots, include_directories: [wlr_inc, proto_inc], build_by_default: get_option('examples'), ) endforeach foreach name, info : clients extra_src = [] foreach p : info.get('proto') extra_src += get_variable(p.underscorify() + '_c') extra_src += get_variable(p.underscorify() + '_h') endforeach executable( name, [info.get('src'), extra_src], dependencies: [wayland_client, info.get('dep')], build_by_default: get_option('examples'), ) endforeach wlroots-0.10.0/examples/multi-pointer.c000066400000000000000000000244221361211131400200700ustar00rootroot00000000000000#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 struct sample_state { struct wl_display *display; struct wlr_xcursor *xcursor; float default_color[4]; float clear_color[4]; struct wlr_output_layout *layout; struct wl_list cursors; // sample_cursor::link struct wl_list pointers; // sample_pointer::link struct wl_list outputs; // sample_output::link struct timespec last_frame; struct wl_listener new_output; struct wl_listener new_input; }; struct sample_cursor { struct sample_state *sample; struct wlr_input_device *device; struct wlr_cursor *cursor; struct wl_list link; struct wl_listener cursor_motion; struct wl_listener cursor_motion_absolute; struct wl_listener cursor_button; struct wl_listener cursor_axis; struct wl_listener destroy; }; struct sample_pointer { struct wlr_input_device *device; struct wl_list link; }; struct sample_output { struct sample_state *sample; struct wlr_output *output; struct wl_listener frame; struct wl_listener destroy; struct wl_list link; }; struct sample_keyboard { struct sample_state *sample; struct wlr_input_device *device; struct wl_listener key; struct wl_listener destroy; }; static void configure_cursor(struct wlr_cursor *cursor, struct wlr_input_device *device, struct sample_state *sample) { struct sample_output *output; wlr_log(WLR_ERROR, "Configuring cursor %p for device %p", cursor, device); // reset mappings wlr_cursor_map_to_output(cursor, NULL); wlr_cursor_detach_input_device(cursor, device); wlr_cursor_map_input_to_output(cursor, device, NULL); wlr_cursor_attach_input_device(cursor, device); // configure device to output mappings wl_list_for_each(output, &sample->outputs, link) { wlr_cursor_map_to_output(cursor, output->output); wlr_cursor_map_input_to_output(cursor, device, output->output); } } 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; wlr_output_attach_render(wlr_output, NULL); glClearColor(sample->clear_color[0], sample->clear_color[1], sample->clear_color[2], sample->clear_color[3]); glClear(GL_COLOR_BUFFER_BIT); wlr_output_render_software_cursors(wlr_output, NULL); wlr_output_commit(wlr_output); } static void handle_cursor_motion(struct wl_listener *listener, void *data) { struct sample_cursor *cursor = wl_container_of(listener, cursor, cursor_motion); struct wlr_event_pointer_motion *event = data; wlr_cursor_move(cursor->cursor, event->device, event->delta_x, event->delta_y); } static void handle_cursor_motion_absolute(struct wl_listener *listener, void *data) { struct sample_cursor *cursor = wl_container_of(listener, cursor, cursor_motion_absolute); struct wlr_event_pointer_motion_absolute *event = data; wlr_cursor_warp_absolute(cursor->cursor, event->device, event->x, event->y); } static void cursor_destroy(struct sample_cursor *cursor) { wl_list_remove(&cursor->link); wl_list_remove(&cursor->cursor_motion.link); wl_list_remove(&cursor->cursor_motion_absolute.link); wlr_cursor_destroy(cursor->cursor); free(cursor); } 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; wl_list_remove(&sample_output->frame.link); wl_list_remove(&sample_output->destroy.link); wl_list_remove(&sample_output->link); free(sample_output); struct sample_cursor *cursor; wl_list_for_each(cursor, &sample->cursors, link) { configure_cursor(cursor->cursor, cursor->device, sample); } } 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); struct sample_output *sample_output = calloc(1, sizeof(struct sample_output)); if (!wl_list_empty(&output->modes)) { struct wlr_output_mode *mode = wl_container_of(output->modes.prev, mode, link); wlr_output_set_mode(output, mode); } 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; wlr_output_layout_add_auto(sample->layout, output); struct sample_cursor *cursor; wl_list_for_each(cursor, &sample->cursors, link) { configure_cursor(cursor->cursor, cursor->device, sample); struct wlr_xcursor_image *image = sample->xcursor->images[0]; wlr_cursor_set_image(cursor->cursor, image->buffer, image->width * 4, image->width, image->height, image->hotspot_x, image->hotspot_y, 0); wlr_cursor_warp(cursor->cursor, NULL, cursor->cursor->x, cursor->cursor->y); } wl_list_insert(&sample->outputs, &sample_output->link); } 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_event_keyboard_key *event = data; uint32_t keycode = event->keycode + 8; const xkb_keysym_t *syms; int nsyms = xkb_state_key_get_syms(keyboard->device->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(struct sample_keyboard)); keyboard->device = device; keyboard->sample = sample; wl_signal_add(&device->events.destroy, &keyboard->destroy); keyboard->destroy.notify = keyboard_destroy_notify; wl_signal_add(&device->keyboard->events.key, &keyboard->key); keyboard->key.notify = keyboard_key_notify; struct xkb_rule_names rules = { 0 }; rules.rules = getenv("XKB_DEFAULT_RULES"); rules.model = getenv("XKB_DEFAULT_MODEL"); rules.layout = getenv("XKB_DEFAULT_LAYOUT"); rules.variant = getenv("XKB_DEFAULT_VARIANT"); rules.options = getenv("XKB_DEFAULT_OPTIONS"); 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_map_new_from_names(context, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS); if (!keymap) { wlr_log(WLR_ERROR, "Failed to create XKB keymap"); exit(1); } wlr_keyboard_set_keymap(device->keyboard, keymap); xkb_keymap_unref(keymap); xkb_context_unref(context); break; case WLR_INPUT_DEVICE_POINTER:; struct sample_cursor *cursor = calloc(1, sizeof(struct sample_cursor)); struct sample_pointer *pointer = calloc(1, sizeof(struct sample_pointer)); pointer->device = device; cursor->sample = sample; cursor->device = device; cursor->cursor = wlr_cursor_create(); wlr_cursor_attach_output_layout(cursor->cursor, sample->layout); wl_signal_add(&cursor->cursor->events.motion, &cursor->cursor_motion); cursor->cursor_motion.notify = handle_cursor_motion; wl_signal_add(&cursor->cursor->events.motion_absolute, &cursor->cursor_motion_absolute); cursor->cursor_motion_absolute.notify = handle_cursor_motion_absolute; wlr_cursor_attach_input_device(cursor->cursor, device); configure_cursor(cursor->cursor, device, sample); struct wlr_xcursor_image *image = sample->xcursor->images[0]; wlr_cursor_set_image(cursor->cursor, image->buffer, image->width * 4, image->width, image->height, image->hotspot_x, image->hotspot_y, 0); wl_list_insert(&sample->cursors, &cursor->link); wl_list_insert(&sample->pointers, &pointer->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 = { .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); } wl_list_init(&state.cursors); wl_list_init(&state.pointers); wl_list_init(&state.outputs); state.layout = wlr_output_layout_create(); 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); struct wlr_xcursor_theme *theme = wlr_xcursor_theme_load("default", 16); if (!theme) { wlr_log(WLR_ERROR, "Failed to load cursor theme"); return 1; } state.xcursor = wlr_xcursor_theme_get_cursor(theme, "left_ptr"); if (!state.xcursor) { wlr_log(WLR_ERROR, "Failed to load left_ptr cursor"); return 1; } 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); struct sample_cursor *cursor, *tmp_cursor; wl_list_for_each_safe(cursor, tmp_cursor, &state.cursors, link) { cursor_destroy(cursor); } struct sample_pointer *pointer, *tmp_pointer; wl_list_for_each_safe(pointer, tmp_pointer, &state.pointers, link) { free(pointer); } wlr_xcursor_theme_destroy(theme); wlr_output_layout_destroy(state.layout); } wlroots-0.10.0/examples/output-layout.c000066400000000000000000000224411361211131400201320ustar00rootroot00000000000000#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 wl_listener new_output; struct wl_listener new_input; struct wlr_renderer *renderer; 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_input_device *device; 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 timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); struct wlr_output *wlr_output = output->output; wlr_output_attach_render(wlr_output, NULL); wlr_renderer_begin(sample->renderer, wlr_output->width, wlr_output->height); wlr_renderer_clear(sample->renderer, (float[]){0.25f, 0.25f, 0.25f, 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_texture(sample->renderer, sample->cat_texture, wlr_output->transform_matrix, local_x, local_y, 1.0f); } wlr_renderer_end(sample->renderer); wlr_output_commit(wlr_output); } 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); struct sample_output *sample_output = calloc(1, sizeof(struct sample_output)); if (!wl_list_empty(&output->modes)) { struct wlr_output_mode *mode = wl_container_of(output->modes.prev, mode, link); wlr_output_set_mode(output, mode); } 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; } 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_event_keyboard_key *event = data; uint32_t keycode = event->keycode + 8; const xkb_keysym_t *syms; int nsyms = xkb_state_key_get_syms(keyboard->device->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 == WLR_KEY_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(struct sample_keyboard)); keyboard->device = device; keyboard->sample = sample; wl_signal_add(&device->events.destroy, &keyboard->destroy); keyboard->destroy.notify = keyboard_destroy_notify; wl_signal_add(&device->keyboard->events.key, &keyboard->key); keyboard->key.notify = keyboard_key_notify; struct xkb_rule_names rules = { 0 }; rules.rules = getenv("XKB_DEFAULT_RULES"); rules.model = getenv("XKB_DEFAULT_MODEL"); rules.layout = getenv("XKB_DEFAULT_LAYOUT"); rules.variant = getenv("XKB_DEFAULT_VARIANT"); rules.options = getenv("XKB_DEFAULT_OPTIONS"); 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_map_new_from_names(context, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS); if (!keymap) { wlr_log(WLR_ERROR, "Failed to create XKB keymap"); exit(1); } wlr_keyboard_set_keymap(device->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_backend_get_renderer(wlr); state.cat_texture = wlr_texture_from_pixels(state.renderer, WL_SHM_FORMAT_ABGR8888, cat_tex.width * 4, cat_tex.width, cat_tex.height, cat_tex.pixel_data); 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.10.0/examples/pointer-constraints.c000066400000000000000000000176241361211131400213130ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include "xdg-shell-client-protocol.h" #include "pointer-constraints-unstable-v1-client-protocol.h" static int width = 512, height = 512; static struct wl_compositor *compositor = NULL; static struct wl_seat *seat = NULL; static struct xdg_wm_base *wm_base = NULL; static struct zwp_pointer_constraints_v1 *pointer_constraints = NULL; struct wlr_egl egl; struct wl_egl_window *egl_window; struct wlr_egl_surface *egl_surface; struct zwp_locked_pointer_v1* locked_pointer; struct zwp_confined_pointer_v1* confined_pointer; enum { REGION_TYPE_NONE, REGION_TYPE_DISJOINT, REGION_TYPE_JOINT, REGION_TYPE_MAX } region_type = REGION_TYPE_NONE; struct wl_region *regions[3]; static void draw(void) { eglMakeCurrent(egl.display, egl_surface, egl_surface, egl.context); float color[] = {1.0, 1.0, 0.0, 1.0}; glViewport(0, 0, width, height); glClearColor(color[0], color[1], color[2], 1.0); glClear(GL_COLOR_BUFFER_BIT); eglSwapBuffers(egl.display, egl_surface); } static void pointer_handle_button(void *data, struct wl_pointer *pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t state_w) { struct wl_surface *surface = data; if (button == BTN_LEFT && state_w == WL_POINTER_BUTTON_STATE_PRESSED) { region_type = (region_type + 1) % REGION_TYPE_MAX; if (locked_pointer) { zwp_locked_pointer_v1_set_region(locked_pointer, regions[region_type]); } else if (confined_pointer) { zwp_confined_pointer_v1_set_region(confined_pointer, regions[region_type]); } wl_surface_commit(surface); } } static void pointer_handle_enter(void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface, wl_fixed_t surface_x, wl_fixed_t surface_y) { // This space intentionally left blank } static void pointer_handle_leave(void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface) { // This space intentionally left blank } static void pointer_handle_motion(void *data, struct wl_pointer *wl_pointer, uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) { // This space intentionally left blank } static void pointer_handle_axis(void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis, wl_fixed_t value) { // This space intentionally left blank } static void pointer_handle_frame(void *data, struct wl_pointer *wl_pointer) { // This space intentionally left blank } static void pointer_handle_axis_source(void *data, struct wl_pointer *wl_pointer, uint32_t axis_source) { // This space intentionally left blank } static void pointer_handle_axis_stop(void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis) { // This space intentionally left blank } static void pointer_handle_axis_discrete(void *data, struct wl_pointer *wl_pointer, uint32_t axis, int32_t discrete) { // This space intentionally left blank } 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, }; static void xdg_surface_handle_configure(void *data, struct xdg_surface *xdg_surface, uint32_t serial) { xdg_surface_ack_configure(xdg_surface, serial); wl_egl_window_resize(egl_window, width, height, 0, 0); draw(); } 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) { width = w; height = h; } static void xdg_toplevel_handle_close(void *data, struct xdg_toplevel *xdg_toplevel) { exit(EXIT_SUCCESS); } static const struct xdg_toplevel_listener xdg_toplevel_listener = { .configure = xdg_toplevel_handle_configure, .close = xdg_toplevel_handle_close, }; 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, 1); } else if (strcmp(interface, xdg_wm_base_interface.name) == 0) { wm_base = wl_registry_bind(registry, name, &xdg_wm_base_interface, 1); } else if (strcmp(interface, wl_seat_interface.name) == 0) { seat = wl_registry_bind(registry, name, &wl_seat_interface, version); } else if (strcmp(interface, zwp_pointer_constraints_v1_interface.name) == 0) { pointer_constraints = wl_registry_bind(registry, name, &zwp_pointer_constraints_v1_interface, version); } } static const struct wl_registry_listener registry_listener = { .global = handle_global, .global_remove = NULL, }; int main(int argc, char **argv) { if (argc != 4) { goto invalid_args; } bool lock; if (strcmp(argv[1], "lock") == 0) { lock = true; } else if (strcmp(argv[1], "confine") == 0) { lock = false; } else { goto invalid_args; } enum zwp_pointer_constraints_v1_lifetime lifetime; if (strcmp(argv[2], "oneshot") == 0) { lifetime = ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_ONESHOT; } else if (strcmp(argv[2], "persistent") == 0) { lifetime = ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT; } else { goto invalid_args; } if (strcmp(argv[3], "no-region") == 0) { region_type = REGION_TYPE_NONE; } else if (strcmp(argv[3], "disjoint-region") == 0) { region_type = REGION_TYPE_DISJOINT; } else if (strcmp(argv[3], "joint-region") == 0) { region_type = REGION_TYPE_JOINT; } struct wl_display *display = wl_display_connect(NULL); struct wl_registry *registry = wl_display_get_registry(display); wl_registry_add_listener(registry, ®istry_listener, NULL); wl_display_dispatch(display); wl_display_roundtrip(display); struct wl_region *disjoint_region = wl_compositor_create_region(compositor); wl_region_add(disjoint_region, 0, 0, 255, 256); wl_region_add(disjoint_region, 257, 0, 255, 256); regions[REGION_TYPE_DISJOINT] = disjoint_region; struct wl_region *joint_region = wl_compositor_create_region(compositor); wl_region_add(joint_region, 0, 0, 256, 256); wl_region_add(joint_region, 256, 0, 256, 256); wl_region_add(joint_region, 256, 256, 256, 256); regions[REGION_TYPE_JOINT] = joint_region; wlr_egl_init(&egl, EGL_PLATFORM_WAYLAND_EXT, display, NULL, WL_SHM_FORMAT_ARGB8888); struct wl_surface *surface = wl_compositor_create_surface(compositor); struct xdg_surface *xdg_surface = xdg_wm_base_get_xdg_surface(wm_base, 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); struct wl_pointer *pointer = wl_seat_get_pointer(seat); wl_pointer_add_listener(pointer, &pointer_listener, surface); if (lock) { locked_pointer = zwp_pointer_constraints_v1_lock_pointer( pointer_constraints, surface, pointer, regions[region_type], lifetime); zwp_locked_pointer_v1_set_cursor_position_hint(locked_pointer, wl_fixed_from_int(128), wl_fixed_from_int(128)); } else { confined_pointer = zwp_pointer_constraints_v1_confine_pointer( pointer_constraints, surface, pointer, regions[region_type], lifetime); } wl_surface_commit(surface); egl_window = wl_egl_window_create(surface, width, height); egl_surface = wlr_egl_create_surface(&egl, egl_window); wl_display_roundtrip(display); draw(); while (wl_display_dispatch(display) != -1) {} return EXIT_SUCCESS; invalid_args: fprintf(stderr, "pointer-constraints " " " "\n"); return EXIT_FAILURE; } wlroots-0.10.0/examples/pointer.c000066400000000000000000000311061361211131400167350ustar00rootroot00000000000000#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 struct sample_state { struct wl_display *display; struct compositor_state *compositor; 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_input_device *device; 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 = wlr_backend_get_renderer(wlr_output->backend); assert(renderer); wlr_output_attach_render(wlr_output, NULL); wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height); wlr_renderer_clear(renderer, state->clear_color); wlr_output_render_software_cursors(wlr_output, NULL); wlr_output_commit(wlr_output); wlr_renderer_end(renderer); } static void handle_cursor_motion(struct wl_listener *listener, void *data) { struct sample_state *sample = wl_container_of(listener, sample, cursor_motion); struct wlr_event_pointer_motion *event = data; wlr_cursor_move(sample->cursor, event->device, 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_event_pointer_motion_absolute *event = data; sample->cur_x = event->x; sample->cur_y = event->y; wlr_cursor_warp_absolute(sample->cursor, event->device, 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_event_pointer_button *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_event_pointer_axis *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_event_touch_up *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->device); } static void handle_touch_down(struct wl_listener *listener, void *data) { struct sample_state *sample = wl_container_of(listener, sample, touch_down); struct wlr_event_touch_down *event = data; struct touch_point *point = calloc(1, sizeof(struct touch_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->device); } static void handle_touch_motion(struct wl_listener *listener, void *data) { struct sample_state *sample = wl_container_of(listener, sample, touch_motion); struct wlr_event_touch_motion *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->device); } 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_event_tablet_tool_axis *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->device, 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_event_keyboard_key *event = data; uint32_t keycode = event->keycode + 8; const xkb_keysym_t *syms; int nsyms = xkb_state_key_get_syms(keyboard->device->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); struct sample_output *sample_output = calloc(1, sizeof(struct sample_output)); if (!wl_list_empty(&output->modes)) { struct wlr_output_mode *mode = wl_container_of(output->modes.prev, mode, link); wlr_output_set_mode(output, mode); } 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); wlr_xcursor_manager_load(sample->xcursor_manager, output->scale); wlr_xcursor_manager_set_cursor_image(sample->xcursor_manager, "left_ptr", sample->cursor); } 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(struct sample_keyboard)); keyboard->device = device; keyboard->state = state; wl_signal_add(&device->events.destroy, &keyboard->destroy); keyboard->destroy.notify = keyboard_destroy_notify; wl_signal_add(&device->keyboard->events.key, &keyboard->key); keyboard->key.notify = keyboard_key_notify; struct xkb_rule_names rules = { 0 }; rules.rules = getenv("XKB_DEFAULT_RULES"); rules.model = getenv("XKB_DEFAULT_MODEL"); rules.layout = getenv("XKB_DEFAULT_LAYOUT"); rules.variant = getenv("XKB_DEFAULT_VARIANT"); rules.options = getenv("XKB_DEFAULT_OPTIONS"); 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_map_new_from_names(context, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS); if (!keymap) { wlr_log(WLR_ERROR, "Failed to create XKB keymap"); exit(1); } wlr_keyboard_set_keymap(device->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.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("default", 24); if (!state.xcursor_manager) { wlr_log(WLR_ERROR, "Failed to load left_ptr cursor"); return 1; } wlr_xcursor_manager_set_cursor_image(state.xcursor_manager, "left_ptr", state.cursor); 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.10.0/examples/relative-pointer-unstable-v1.c000066400000000000000000000352061361211131400227120ustar00rootroot00000000000000#include #include #include #include #include #include #include #include "pointer-constraints-unstable-v1-client-protocol.h" #include "relative-pointer-unstable-v1-client-protocol.h" #include "xdg-shell-client-protocol.h" /** * structs storing global state * * These are passed around as user data in the wayland globals. */ struct egl_info { struct wlr_egl *egl; struct wl_egl_window *egl_window; struct wlr_egl_surface *egl_surface; uint32_t width; uint32_t height; struct wl_surface *surface; struct wl_callback *frame_callback; }; struct window { struct egl_info *egl_info; struct wl_pointer *pointer; struct zwp_locked_pointer_v1 *locked_pointer; struct zwp_relative_pointer_v1 *relative_pointer; int32_t pointer_x, pointer_y; uint32_t last_draw; }; /** * surface handling and helpers * * draw_cursor and draw_relative_cursor draw a small 5px by 5px box around the * cursor and relative motion coordinates respectively. draw_background colors * the background black. * * The functions are somewhat duplicated, but it doesn't really matter. */ static void surface_callback_handle_done(void *data, struct wl_callback *callback, uint32_t time) { wl_callback_destroy(callback); struct egl_info *e = data; e->frame_callback = NULL; } static struct wl_callback_listener surface_callback_listener = { .done = surface_callback_handle_done, }; static void draw_init(struct egl_info *e) { eglMakeCurrent(e->egl->display, e->egl_surface, e->egl_surface, e->egl->context); glViewport(0, 0, e->width, e->height); } static void draw_cursor(struct egl_info *e, int32_t x, int32_t y) { glEnable(GL_SCISSOR_TEST); glScissor(x, e->height - y, 5, 5); glClearColor(1.0f, 1.0f, 1.0f, 1.0f); /* white */ glClear(GL_COLOR_BUFFER_BIT); glDisable(GL_SCISSOR_TEST); } static void draw_relative_cursor(struct egl_info *e, int32_t x, int32_t y) { glEnable(GL_SCISSOR_TEST); glScissor(x, e->height - y, 5, 5); glClearColor(1.0f, 0.0f, 0.0f, 1.0f); /* red */ glClear(GL_COLOR_BUFFER_BIT); glDisable(GL_SCISSOR_TEST); } static void draw_background(struct egl_info *e) { glClearColor(0.0f, 0.0f, 0.0f, 1.0f); /* black */ glClear(GL_COLOR_BUFFER_BIT); } static void draw_end(struct egl_info *e) { e->frame_callback = wl_surface_frame(e->surface); wl_callback_add_listener(e->frame_callback, &surface_callback_listener, e); eglSwapBuffers(e->egl->display, e->egl_surface); } /** * registry and globals handling */ static struct wl_compositor *compositor = NULL; static struct wl_seat *seat = NULL; static struct xdg_wm_base *wm_base = NULL; static struct zwp_pointer_constraints_v1 *pointer_constraints = NULL; static struct zwp_relative_pointer_manager_v1 *relative_pointer_manager = NULL; static void registry_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, version); } else if (strcmp(interface, xdg_wm_base_interface.name) == 0) { wm_base = wl_registry_bind(registry, name, &xdg_wm_base_interface, version); } else if (strcmp(interface, wl_seat_interface.name) == 0) { seat = wl_registry_bind(registry, name, &wl_seat_interface, version); } else if (strcmp(interface, zwp_pointer_constraints_v1_interface.name) == 0) { pointer_constraints = wl_registry_bind(registry, name, &zwp_pointer_constraints_v1_interface, version); } else if (strcmp(interface, zwp_relative_pointer_manager_v1_interface.name) == 0) { relative_pointer_manager = wl_registry_bind(registry, name, &zwp_relative_pointer_manager_v1_interface, version); } } static void registry_handle_global_remove(void *data, struct wl_registry *registry, uint32_t time) { /* This space intentionally left blank */ } static const struct wl_registry_listener registry_listener = { .global = registry_handle_global, .global_remove = registry_handle_global_remove, }; /** * xdg_surface handling */ static void xdg_surface_handle_configure(void *data, struct xdg_surface *xdg_surface, uint32_t serial) { struct egl_info *e = data; xdg_surface_ack_configure(xdg_surface, serial); wl_egl_window_resize(e->egl_window, e->width, e->height, 0, 0); draw_init(e); draw_background(e); draw_end(e); } static const struct xdg_surface_listener xdg_surface_listener = { .configure = xdg_surface_handle_configure, }; /** * xdg_toplevel handling */ static void xdg_toplevel_handle_configure(void *data, struct xdg_toplevel *xdg_toplevel, int32_t width, int32_t height, struct wl_array *states) { struct egl_info *e = data; // TODO: Why do we get 0,0 on initialization here? (in rootston) if (width == 0 && height == 0) { return; } e->width = width; e->height = height; } static void xdg_toplevel_handle_close(void *data, struct xdg_toplevel *xdg_toplevel) { struct egl_info *e = data; wlr_egl_finish(e->egl); exit(EXIT_SUCCESS); } static const struct xdg_toplevel_listener xdg_toplevel_listener = { .configure = xdg_toplevel_handle_configure, .close = xdg_toplevel_handle_close, }; /** * zwp_locked_pointer handling * * Pointer unlocks need to be handled properly since the relative pointer needs * to be released as well. Unlocks happen when the focus is changed, for * example. */ static void locked_pointer_handle_locked(void *data, struct zwp_locked_pointer_v1 *zwp_locked_pointer_v1) { /* This space intentionally left blank */ } static void locked_pointer_handle_unlocked(void *data, struct zwp_locked_pointer_v1 *zwp_locked_pointer_v1) { struct window *w = data; /* The locked pointer doesn't need to be destroyed since it was oneshot */ w->locked_pointer = NULL; if (w->relative_pointer) { /* Destroy the relative pointer */ zwp_relative_pointer_v1_destroy(w->relative_pointer); w->relative_pointer = NULL; } draw_init(w->egl_info); draw_background(w->egl_info); draw_end(w->egl_info); } static const struct zwp_locked_pointer_v1_listener locked_pointer_listener = { .locked = locked_pointer_handle_locked, .unlocked = locked_pointer_handle_unlocked, }; /** * zwp_relative_pointer handling * * Handling relative_motion events is the meat of the code. The handler simply * tries to indicate what the deltas look like. */ static void relative_pointer_handle_relative_motion(void *data, struct zwp_relative_pointer_v1 *zwp_relative_pointer_v1, 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 window *w = data; /** * This renders the last location of the pointer (before it as locked), as * well as what the position would have been after the given relative * motion. Note that, if the device sends absolute motion events, the * cursor location after relative motion is always identical to the actual * cursor position. */ uint64_t utime = (((uint64_t) utime_hi << 32) + utime_lo) / 1000; if (utime - w->last_draw > 30 && w->egl_info->frame_callback == NULL) { w->last_draw = utime; struct egl_info *e = w->egl_info; draw_init(e); draw_background(e); draw_cursor(e, w->pointer_x, w->pointer_y); draw_relative_cursor(e, w->pointer_x + wl_fixed_to_int(dx), w->pointer_y + wl_fixed_to_int(dy)); draw_end(e); } } static const struct zwp_relative_pointer_v1_listener relative_pointer_listener = { .relative_motion = relative_pointer_handle_relative_motion, }; /** * wl_pointer handling * * The client toggles between locking the pointer and receiving relative motion * events, and releasing the locked pointer and falling back to normal motion * events, on a mouse button one (left mouse button) click. * * It additionally removes the cursor image, and indicates the pointer location * via a small white box. */ static void pointer_handle_button(void *data, struct wl_pointer *pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t state_w) { struct window *w = data; struct egl_info *e = w->egl_info; if (button == BTN_LEFT && state_w == WL_POINTER_BUTTON_STATE_PRESSED) { if (w->locked_pointer == NULL && w->relative_pointer == NULL) { /* Get a locked pointer and add listener */ w->locked_pointer = zwp_pointer_constraints_v1_lock_pointer( pointer_constraints, w->egl_info->surface, w->pointer, NULL, ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_ONESHOT); zwp_locked_pointer_v1_add_listener(w->locked_pointer, &locked_pointer_listener, w); /* Get relative pointer and add listener */ w->relative_pointer = zwp_relative_pointer_manager_v1_get_relative_pointer( relative_pointer_manager, w->pointer); zwp_relative_pointer_v1_add_listener(w->relative_pointer, &relative_pointer_listener, w); /* Commit the surface and render */ wl_surface_commit(e->surface); draw_init(e); draw_background(e); draw_cursor(e, w->pointer_x, w->pointer_y); draw_end(e); } else if (w->locked_pointer && w->relative_pointer) { /* Destroy the locked pointer */ zwp_locked_pointer_v1_destroy(w->locked_pointer); w->locked_pointer = NULL; /* Destroy the relative pointer */ zwp_relative_pointer_v1_destroy(w->relative_pointer); w->relative_pointer = NULL; /* Render */ draw_init(e); draw_background(e); draw_cursor(e, w->pointer_x, w->pointer_y); draw_end(e); } else { fprintf(stderr, "Unknown state!\n"); exit(EXIT_FAILURE); } } } static void pointer_handle_enter(void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface, wl_fixed_t surface_x, wl_fixed_t surface_y) { struct window *w = data; wl_pointer_set_cursor(w->pointer, serial, NULL, 0, 0); } static void pointer_handle_leave(void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface) { /* This space intentionally left blank */ } static void pointer_handle_motion(void *data, struct wl_pointer *wl_pointer, uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) { struct window *w = data; struct egl_info *e = w->egl_info; w->pointer_x = wl_fixed_to_int(surface_x); w->pointer_y = wl_fixed_to_int(surface_y); if (time - w->last_draw > 30 && e->frame_callback == NULL) { w->last_draw = time; draw_init(e); draw_background(e); draw_cursor(e, w->pointer_x, w->pointer_y); draw_end(e); } } static void pointer_handle_axis(void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis, wl_fixed_t value) { /* This space intentionally left blank */ } static void pointer_handle_frame(void *data, struct wl_pointer *wl_pointer) { /* This space intentionally left blank */ } static void pointer_handle_axis_source(void *data, struct wl_pointer *wl_pointer, uint32_t axis_source) { /* This space intentionally left blank */ } static void pointer_handle_axis_stop(void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis) { /* This space intentionally left blank */ } static void pointer_handle_axis_discrete(void *data, struct wl_pointer *wl_pointer, uint32_t axis, int32_t discrete) { /* This space intentionally left blank */ } 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, }; /** * relative-pointer: * * This client servers as an example for the relative-pointer-v1 protocol, and * to some extent the pointer-constraints protocol as well (the locked_pointer * interface). * * The intended behavior is the following. In the default state, the client * shows a black background, and renders the pointer location as a small white * box. No cursor is shown. In the locked state, the pointer is locked and the * client only listens for relative motion events, which are rendered relative * to the last unlocked pointer location by a small red box. Clicking with the * left mouse button toggles the state. * * Most of the code is standard. The interesting stuff happens in "wl_pointer * handling" (toggling states), and "zwp_relative_pointer handling" (rendering * relative motion events). */ int main(int argc, char **argv) { /* Connect to the display */ struct wl_display *display = wl_display_connect(NULL); if (display == NULL) { fprintf(stderr, "Could not connect to a Wayland display\n"); return EXIT_FAILURE; } /* Get the registry and set listeners */ struct wl_registry *registry = wl_display_get_registry(display); wl_registry_add_listener(registry, ®istry_listener, NULL); wl_display_dispatch(display); wl_display_roundtrip(display); /* Check that all the global interfaces were captured */ if (compositor == NULL) { fprintf(stderr, "wl_compositor not available\n"); return EXIT_FAILURE; } if (seat == NULL) { fprintf(stderr, "wl_seat not available\n"); return EXIT_FAILURE; } if (wm_base == NULL) { fprintf(stderr, "xdg_wm_base not available\n"); return EXIT_FAILURE; } if (pointer_constraints == NULL) { fprintf(stderr, "zwp_pointer_constraints_v1 not available\n"); return EXIT_FAILURE; } if (relative_pointer_manager == NULL) { fprintf(stderr, "zwp_relative_pointer_manager_v1 not available\n"); return EXIT_FAILURE; } /* Initialize EGL context */ struct egl_info *e = calloc(1, sizeof(struct egl_info)); e->egl = calloc(1, sizeof(struct wlr_egl)); e->width = e->height = 512; wlr_egl_init(e->egl, EGL_PLATFORM_WAYLAND_EXT, display, NULL, WL_SHM_FORMAT_ARGB8888); /* Create the surface and xdg_toplevels, and set listeners */ struct wl_surface *surface = wl_compositor_create_surface(compositor); struct xdg_surface *xdg_surface = xdg_wm_base_get_xdg_surface(wm_base, surface); struct xdg_toplevel *xdg_toplevel = xdg_surface_get_toplevel(xdg_surface); xdg_surface_add_listener(xdg_surface, &xdg_surface_listener, e); xdg_toplevel_add_listener(xdg_toplevel, &xdg_toplevel_listener, e); /* Create the egl window and surface */ wl_surface_commit(surface); e->egl_window = wl_egl_window_create(surface, e->width, e->height); e->egl_surface = wlr_egl_create_surface(e->egl, e->egl_window); e->surface = surface; wl_display_roundtrip(display); /* Setup global state and render */ struct window *w = calloc(1, sizeof(struct window)); w->egl_info = e; draw_init(e); draw_background(e); draw_end(e); /* Setup the pointer */ struct wl_pointer *pointer = wl_seat_get_pointer(seat); wl_pointer_add_listener(pointer, &pointer_listener, w); w->pointer = pointer; /* Run display */ while (wl_display_dispatch(display) != -1) { /* This space intentionally left blank */ } return EXIT_SUCCESS; } wlroots-0.10.0/examples/rotation.c000066400000000000000000000212401361211131400171120ustar00rootroot00000000000000#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 wl_listener new_output; struct wl_listener new_input; struct timespec last_frame; struct wlr_renderer *renderer; 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_input_device *device; 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); wlr_output_attach_render(wlr_output, NULL); wlr_renderer_begin(sample->renderer, wlr_output->width, wlr_output->height); wlr_renderer_clear(sample->renderer, (float[]){0.25f, 0.25f, 0.25f, 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_texture(sample->renderer, sample->cat_texture, wlr_output->transform_matrix, x, y, 1.0f); } } wlr_renderer_end(sample->renderer); wlr_output_commit(wlr_output); 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); struct sample_output *sample_output = calloc(1, sizeof(struct sample_output)); if (!wl_list_empty(&output->modes)) { struct wlr_output_mode *mode = wl_container_of(output->modes.prev, mode, link); wlr_output_set_mode(output, mode); } sample_output->x_offs = sample_output->y_offs = 0; sample_output->x_vel = sample_output->y_vel = 128; wlr_output_set_transform(output, sample->transform); 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); } 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_event_keyboard_key *event = data; uint32_t keycode = event->keycode + 8; const xkb_keysym_t *syms; int nsyms = xkb_state_key_get_syms(keyboard->device->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 == WLR_KEY_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(struct sample_keyboard)); keyboard->device = device; keyboard->sample = sample; wl_signal_add(&device->events.destroy, &keyboard->destroy); keyboard->destroy.notify = keyboard_destroy_notify; wl_signal_add(&device->keyboard->events.key, &keyboard->key); keyboard->key.notify = keyboard_key_notify; struct xkb_rule_names rules = { 0 }; rules.rules = getenv("XKB_DEFAULT_RULES"); rules.model = getenv("XKB_DEFAULT_MODEL"); rules.layout = getenv("XKB_DEFAULT_LAYOUT"); rules.variant = getenv("XKB_DEFAULT_VARIANT"); rules.options = getenv("XKB_DEFAULT_OPTIONS"); 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_map_new_from_names(context, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS); if (!keymap) { wlr_log(WLR_ERROR, "Failed to create XKB keymap"); exit(1); } wlr_keyboard_set_keymap(device->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: break; } } 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_backend_get_renderer(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, WL_SHM_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); } 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.10.0/examples/screencopy.c000066400000000000000000000167531361211131400174420ustar00rootroot00000000000000/* * Copyright © 2008 Kristian Høgsberg * * 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 200112L #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "wlr-screencopy-unstable-v1-client-protocol.h" struct format { enum wl_shm_format wl_format; bool is_bgr; }; static struct wl_shm *shm = NULL; static struct zwlr_screencopy_manager_v1 *screencopy_manager = NULL; static struct wl_output *output = NULL; static struct { struct wl_buffer *wl_buffer; void *data; enum wl_shm_format format; int width, height, stride; bool y_invert; } buffer; bool buffer_copy_done = false; // wl_shm_format describes little-endian formats, libpng uses big-endian // formats (so Wayland's ABGR is libpng's RGBA). static const struct format formats[] = { {WL_SHM_FORMAT_XRGB8888, true}, {WL_SHM_FORMAT_ARGB8888, true}, {WL_SHM_FORMAT_XBGR8888, false}, {WL_SHM_FORMAT_ABGR8888, false}, }; static struct wl_buffer *create_shm_buffer(enum wl_shm_format fmt, int width, int height, int stride, void **data_out) { int size = stride * height; const char shm_name[] = "/wlroots-screencopy"; int fd = shm_open(shm_name, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); if (fd < 0) { fprintf(stderr, "shm_open failed\n"); return NULL; } shm_unlink(shm_name); int ret; while ((ret = ftruncate(fd, size)) == EINTR) { // No-op } if (ret < 0) { close(fd); fprintf(stderr, "ftruncate failed\n"); return NULL; } void *data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (data == MAP_FAILED) { fprintf(stderr, "mmap failed: %m\n"); close(fd); return NULL; } struct wl_shm_pool *pool = wl_shm_create_pool(shm, fd, size); close(fd); struct wl_buffer *buffer = wl_shm_pool_create_buffer(pool, 0, width, height, stride, fmt); wl_shm_pool_destroy(pool); *data_out = data; return buffer; } static void frame_handle_buffer(void *data, struct zwlr_screencopy_frame_v1 *frame, uint32_t format, uint32_t width, uint32_t height, uint32_t stride) { buffer.format = format; buffer.width = width; buffer.height = height; buffer.stride = stride; buffer.wl_buffer = create_shm_buffer(format, width, height, stride, &buffer.data); if (buffer.wl_buffer == NULL) { fprintf(stderr, "failed to create buffer\n"); exit(EXIT_FAILURE); } zwlr_screencopy_frame_v1_copy(frame, buffer.wl_buffer); } static void frame_handle_flags(void *data, struct zwlr_screencopy_frame_v1 *frame, uint32_t flags) { buffer.y_invert = flags & ZWLR_SCREENCOPY_FRAME_V1_FLAGS_Y_INVERT; } static void frame_handle_ready(void *data, struct zwlr_screencopy_frame_v1 *frame, uint32_t tv_sec_hi, uint32_t tv_sec_lo, uint32_t tv_nsec) { buffer_copy_done = true; } static void frame_handle_failed(void *data, struct zwlr_screencopy_frame_v1 *frame) { fprintf(stderr, "failed to copy frame\n"); exit(EXIT_FAILURE); } static const struct zwlr_screencopy_frame_v1_listener frame_listener = { .buffer = frame_handle_buffer, .flags = frame_handle_flags, .ready = frame_handle_ready, .failed = frame_handle_failed, }; static void handle_global(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { if (strcmp(interface, wl_output_interface.name) == 0 && output == NULL) { output = wl_registry_bind(registry, name, &wl_output_interface, 1); } else if (strcmp(interface, wl_shm_interface.name) == 0) { shm = wl_registry_bind(registry, name, &wl_shm_interface, 1); } else if (strcmp(interface, zwlr_screencopy_manager_v1_interface.name) == 0) { screencopy_manager = wl_registry_bind(registry, name, &zwlr_screencopy_manager_v1_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 write_image(char *filename, enum wl_shm_format wl_fmt, int width, int height, int stride, bool y_invert, png_bytep data) { const struct format *fmt = NULL; for (size_t i = 0; i < sizeof(formats) / sizeof(formats[0]); ++i) { if (formats[i].wl_format == wl_fmt) { fmt = &formats[i]; break; } } if (fmt == NULL) { fprintf(stderr, "unsupported format %"PRIu32"\n", wl_fmt); exit(EXIT_FAILURE); } FILE *f = fopen(filename, "wb"); if (f == NULL) { fprintf(stderr, "failed to open output file\n"); exit(EXIT_FAILURE); } png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); png_infop info = png_create_info_struct(png); png_init_io(png, f); png_set_IHDR(png, info, width, height, 8, PNG_COLOR_TYPE_RGBA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); if (fmt->is_bgr) { png_set_bgr(png); } png_write_info(png, info); for (size_t i = 0; i < (size_t)height; ++i) { png_bytep row; if (y_invert) { row = data + (height - i - 1) * stride; } else { row = data + i * stride; } png_write_row(png, row); } png_write_end(png, NULL); png_destroy_write_struct(&png, &info); fclose(f); } int main(int argc, char *argv[]) { struct wl_display * display = wl_display_connect(NULL); if (display == NULL) { fprintf(stderr, "failed to create display: %m\n"); return EXIT_FAILURE; } struct wl_registry *registry = wl_display_get_registry(display); wl_registry_add_listener(registry, ®istry_listener, NULL); wl_display_dispatch(display); wl_display_roundtrip(display); if (shm == NULL) { fprintf(stderr, "compositor is missing wl_shm\n"); return EXIT_FAILURE; } if (screencopy_manager == NULL) { fprintf(stderr, "compositor doesn't support wlr-screencopy-unstable-v1\n"); return EXIT_FAILURE; } if (output == NULL) { fprintf(stderr, "no output available\n"); return EXIT_FAILURE; } struct zwlr_screencopy_frame_v1 *frame = zwlr_screencopy_manager_v1_capture_output(screencopy_manager, 0, output); zwlr_screencopy_frame_v1_add_listener(frame, &frame_listener, NULL); while (!buffer_copy_done && wl_display_dispatch(display) != -1) { // This space is intentionally left blank } write_image("wayland-screenshot.png", buffer.format, buffer.width, buffer.height, buffer.stride, buffer.y_invert, buffer.data); wl_buffer_destroy(buffer.wl_buffer); return EXIT_SUCCESS; } wlroots-0.10.0/examples/simple.c000066400000000000000000000132241361211131400165470ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200112L #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 timespec last_frame; float color[3]; 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_input_device *device; 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); 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; } wlr_output_attach_render(sample_output->output, NULL); glClearColor(sample->color[0], sample->color[1], sample->color[2], 1.0); glClear(GL_COLOR_BUFFER_BIT); wlr_output_commit(sample_output->output); 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); struct sample_output *sample_output = calloc(1, sizeof(struct sample_output)); if (!wl_list_empty(&output->modes)) { struct wlr_output_mode *mode = wl_container_of(output->modes.prev, mode, link); wlr_output_set_mode(output, mode); } 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; } 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_event_keyboard_key *event = data; uint32_t keycode = event->keycode + 8; const xkb_keysym_t *syms; int nsyms = xkb_state_key_get_syms(keyboard->device->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(struct sample_keyboard)); keyboard->device = device; keyboard->sample = sample; wl_signal_add(&device->events.destroy, &keyboard->destroy); keyboard->destroy.notify = keyboard_destroy_notify; wl_signal_add(&device->keyboard->events.key, &keyboard->key); keyboard->key.notify = keyboard_key_notify; struct xkb_rule_names rules = { 0 }; rules.rules = getenv("XKB_DEFAULT_RULES"); rules.model = getenv("XKB_DEFAULT_MODEL"); rules.layout = getenv("XKB_DEFAULT_LAYOUT"); rules.variant = getenv("XKB_DEFAULT_VARIANT"); rules.options = getenv("XKB_DEFAULT_OPTIONS"); 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_map_new_from_names(context, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS); if (!keymap) { wlr_log(WLR_ERROR, "Failed to create XKB keymap"); exit(1); } wlr_keyboard_set_keymap(device->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 }, .dec = 0, .last_frame = { 0 }, .display = display }; struct wlr_backend *backend = wlr_backend_autocreate(display, NULL); if (!backend) { exit(1); } 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.10.0/examples/tablet.c000066400000000000000000000307221361211131400165330ustar00rootroot00000000000000#define _XOPEN_SOURCE 600 #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; 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_input_device *device; 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_input_device *device; 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_input_device *device; 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); wlr_output_attach_render(wlr_output, NULL); wlr_renderer_begin(sample->renderer, wlr_output->width, wlr_output->height); wlr_renderer_clear(sample->renderer, (float[]){0.25f, 0.25f, 0.25f, 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; const struct wlr_box box = { .x = left, .y = top, .width = pad_width, .height = pad_height, }; wlr_render_rect(sample->renderer, &box, sample->pad_color, wlr_output->transform_matrix); 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), }; float matrix[9]; wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL, sample->ring, wlr_output->transform_matrix); wlr_render_quad_with_matrix(sample->renderer, tool_color, matrix); box.x += sample->x_tilt; box.y += sample->y_tilt; box.width /= 2; box.height /= 2; wlr_render_rect(sample->renderer, &box, tool_color, wlr_output->transform_matrix); } wlr_renderer_end(sample->renderer); wlr_output_commit(wlr_output); 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_event_tablet_tool_axis *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_event_tablet_tool_proximity *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_event_tablet_tool_button *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_event_tablet_pad_button *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_event_tablet_pad_ring *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); struct sample_output *sample_output = calloc(1, sizeof(struct sample_output)); if (!wl_list_empty(&output->modes)) { struct wlr_output_mode *mode = wl_container_of(output->modes.prev, mode, link); wlr_output_set_mode(output, mode); } 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; } 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_event_keyboard_key *event = data; uint32_t keycode = event->keycode + 8; const xkb_keysym_t *syms; int nsyms = xkb_state_key_get_syms(keyboard->device->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(struct sample_keyboard)); keyboard->device = device; keyboard->sample = sample; wl_signal_add(&device->events.destroy, &keyboard->destroy); keyboard->destroy.notify = keyboard_destroy_notify; wl_signal_add(&device->keyboard->events.key, &keyboard->key); keyboard->key.notify = keyboard_key_notify; struct xkb_rule_names rules = { 0 }; rules.rules = getenv("XKB_DEFAULT_RULES"); rules.model = getenv("XKB_DEFAULT_MODEL"); rules.layout = getenv("XKB_DEFAULT_LAYOUT"); rules.variant = getenv("XKB_DEFAULT_VARIANT"); rules.options = getenv("XKB_DEFAULT_OPTIONS"); 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_map_new_from_names(context, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS); if (!keymap) { wlr_log(WLR_ERROR, "Failed to create XKB keymap"); exit(1); } wlr_keyboard_set_keymap(device->keyboard, keymap); xkb_keymap_unref(keymap); xkb_context_unref(context); break; case WLR_INPUT_DEVICE_TABLET_PAD:; struct tablet_pad_state *pstate = calloc(sizeof(struct tablet_pad_state), 1); pstate->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(&device->tablet_pad->events.button, &pstate->button); pstate->ring.notify = tablet_pad_ring_notify; wl_signal_add(&device->tablet_pad->events.ring, &pstate->ring); wl_list_insert(&sample->tablet_pads, &pstate->link); break; case WLR_INPUT_DEVICE_TABLET_TOOL: sample->width_mm = device->width_mm == 0 ? 20 : device->width_mm; sample->height_mm = device->height_mm == 0 ? 10 : device->height_mm; struct tablet_tool_state *tstate = calloc(sizeof(struct tablet_tool_state), 1); tstate->device = device; 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(&device->tablet->events.axis, &tstate->axis); tstate->proximity.notify = tablet_tool_proximity_notify; wl_signal_add(&device->tablet->events.proximity, &tstate->proximity); tstate->button.notify = tablet_tool_button_notify; wl_signal_add(&device->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_backend_get_renderer(wlr); if (!state.renderer) { wlr_log(WLR_ERROR, "Could not start compositor, OOM"); exit(EXIT_FAILURE); } 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.10.0/examples/text-input.c000066400000000000000000000262021361211131400173770ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include #include "text-input-unstable-v3-client-protocol.h" #include "xdg-shell-client-protocol.h" const char usage[] = "Usage: text-input [seconds [width height]]\n\ \n\ Creates a xdg-toplevel using the text-input protocol.\n\ It will be solid black when it has no text input focus, yellow when it\n\ has focus, and red when it was notified that the focus moved away\n\ but still didn't give up the text input ability.\n\ \n\ The \"seconds\" argument is optional and defines the delay between getting\n\ notified of lost focus and releasing text input.\n\ \n\ The \"width\" and \"height\" arguments define the window shape.\n\ \n\ The console will print the internal state of the text field:\n\ - the text in the 1st line\n\ - \".\" under each preedit character\n\ - \"_\" under each selected preedit character\n\ - \"|\" at the cursor position if there are no selected characters in the\n\ preedit.\n\ \n\ The cursor positions may be inaccurate, especially in presence of zero-width\n\ characters or non-monospaced fonts.\n"; struct text_input_state { char *commit; struct { char *text; int32_t cursor_begin; int32_t cursor_end; } preedit; struct { uint32_t after_length; uint32_t before_length; } delete_surrounding; }; static struct text_input_state pending = {0}; static struct text_input_state current = {0}; static bool entered = false; static uint32_t serial; static char *buffer; // text buffer // cursor is not present, there's no way to move it outside of preedit static int sleeptime = 0; static int width = 100, height = 200; static int enabled = 0; static struct wl_display *display = NULL; static struct wl_compositor *compositor = NULL; static struct wl_seat *seat = NULL; static struct xdg_wm_base *wm_base = NULL; static struct zwp_text_input_manager_v3 *text_input_manager = NULL; static struct zwp_text_input_v3 *text_input = NULL; struct wlr_egl egl; struct wl_egl_window *egl_window; struct wlr_egl_surface *egl_surface; static void draw(void) { eglMakeCurrent(egl.display, egl_surface, egl_surface, egl.context); float color[] = {1.0, 1.0, 0.0, 1.0}; color[0] = enabled * 1.0; color[1] = entered * 1.0; glViewport(0, 0, width, height); glClearColor(color[0], color[1], color[2], 1.0); glClear(GL_COLOR_BUFFER_BIT); eglSwapBuffers(egl.display, egl_surface); } static size_t utf8_strlen(char *str) { size_t cp_count = 0; for (; *str != '\0'; str++) { if ((*str & 0xc0) != 0x80) { cp_count++; } } return cp_count; } static size_t utf8_offset(char *utf8_str, size_t byte_offset) { size_t cp_count = 0; for (char *c = utf8_str; c < utf8_str + byte_offset; c++) { if ((*c & 0xc0) != 0x80) { cp_count++; } } return cp_count; } // TODO: would be nicer to have this text display inside the window static void show_status(void) { printf("State %d:", serial); if (!enabled) { printf(" disabled"); } char *preedit_text = current.preedit.text; if (!preedit_text) { preedit_text = ""; } printf("\n"); printf("%s", buffer); printf("%s\n", preedit_text); // Positioning of the cursor requires UTF8 offsets to match monospaced // glyphs for (unsigned i = 0; i < utf8_strlen(buffer); i++) { printf(" "); } char *cursor_mark = calloc(utf8_strlen(preedit_text) + 2, sizeof(char)); for (unsigned i = 0; i < utf8_strlen(preedit_text); i++) { cursor_mark[i] = '.'; } if (current.preedit.cursor_begin == -1 && current.preedit.cursor_end == -1) { goto end; } if (current.preedit.cursor_begin == -1 || current.preedit.cursor_end == -1) { printf("Only one cursor side is defined: %d to %d\n", current.preedit.cursor_begin, current.preedit.cursor_end); goto end; } if ((unsigned)current.preedit.cursor_begin > strlen(preedit_text)) { printf("Cursor out of bounds\n"); goto end; } if (current.preedit.cursor_begin == current.preedit.cursor_end) { cursor_mark[utf8_offset(preedit_text, current.preedit.cursor_begin)] = '|'; goto print; } if (current.preedit.cursor_begin > current.preedit.cursor_end) { printf("End cursor is before start cursor\n"); goto end; } // negative offsets already checked before for (unsigned i = utf8_offset(preedit_text, current.preedit.cursor_begin); i < utf8_offset(preedit_text, current.preedit.cursor_end); i++) { cursor_mark[i] = '_'; } print: printf("%s\n", cursor_mark); end: free(cursor_mark); } static void commit(struct zwp_text_input_v3 *text_input) { zwp_text_input_v3_commit(text_input); serial++; } static void send_status_update(struct zwp_text_input_v3 *text_input) { zwp_text_input_v3_set_surrounding_text(text_input, buffer, strlen(buffer), strlen(buffer)); zwp_text_input_v3_set_text_change_cause(text_input, ZWP_TEXT_INPUT_V3_CHANGE_CAUSE_INPUT_METHOD); commit(text_input); } static void text_input_handle_enter(void *data, struct zwp_text_input_v3 *zwp_text_input_v3, struct wl_surface *surface) { entered = true; zwp_text_input_v3_enable(zwp_text_input_v3); commit(zwp_text_input_v3); enabled = true; draw(); show_status(); } static void text_input_handle_leave(void *data, struct zwp_text_input_v3 *zwp_text_input_v3, struct wl_surface *surface) { entered = false; draw(); wl_display_roundtrip(display); sleep(sleeptime); zwp_text_input_v3_disable(zwp_text_input_v3); commit(zwp_text_input_v3); enabled = false; draw(); show_status(); } static void text_input_commit_string(void *data, struct zwp_text_input_v3 *zwp_text_input_v3, const char *text) { free(pending.commit); pending.commit = strdup(text); } static void text_input_delete_surrounding_text(void *data, struct zwp_text_input_v3 *zwp_text_input_v3, uint32_t before_length, uint32_t after_length) { pending.delete_surrounding.before_length = before_length; pending.delete_surrounding.after_length = after_length; } static void text_input_preedit_string(void *data, struct zwp_text_input_v3 *zwp_text_input_v3, const char *text, int32_t cursor_begin, int32_t cursor_end) { free(pending.preedit.text); pending.preedit.text = strdup(text); pending.preedit.cursor_begin = cursor_begin; pending.preedit.cursor_end = cursor_end; } static void text_input_handle_done(void *data, struct zwp_text_input_v3 *zwp_text_input_v3, uint32_t incoming_serial) { if (serial != incoming_serial) { fprintf(stderr, "Received serial %d while expecting %d\n", incoming_serial, serial); return; } free(current.preedit.text); free(current.commit); current = pending; struct text_input_state empty = {0}; pending = empty; if (current.delete_surrounding.after_length + current.delete_surrounding.before_length > 0) { // cursor is always after committed text, after_length != 0 will never happen unsigned delete_before = current.delete_surrounding.before_length; if (delete_before > strlen(buffer)) { delete_before = strlen(buffer); } buffer[strlen(buffer) - delete_before] = '\0'; } char *commit_string = current.commit; if (!commit_string) { commit_string = ""; } char *old_buffer = buffer; buffer = calloc(strlen(buffer) + strlen(commit_string) + 1, sizeof(char)); // realloc may fail anyway strcpy(buffer, old_buffer); free(old_buffer); strcat(buffer, commit_string); send_status_update(zwp_text_input_v3); show_status(); } static const struct zwp_text_input_v3_listener text_input_listener = { .enter = text_input_handle_enter, .leave = text_input_handle_leave, .commit_string = text_input_commit_string, .delete_surrounding_text = text_input_delete_surrounding_text, .preedit_string = text_input_preedit_string, .done = text_input_handle_done, }; static void xdg_surface_handle_configure(void *data, struct xdg_surface *xdg_surface, uint32_t serial) { xdg_surface_ack_configure(xdg_surface, serial); wl_egl_window_resize(egl_window, width, height, 0, 0); draw(); } 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) { width = w; height = h; } static void xdg_toplevel_handle_close(void *data, struct xdg_toplevel *xdg_toplevel) { exit(EXIT_SUCCESS); } static const struct xdg_toplevel_listener xdg_toplevel_listener = { .configure = xdg_toplevel_handle_configure, .close = xdg_toplevel_handle_close, }; static void handle_global(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { if (strcmp(interface, "wl_compositor") == 0) { compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 1); } else if (strcmp(interface, xdg_wm_base_interface.name) == 0) { wm_base = wl_registry_bind(registry, name, &xdg_wm_base_interface, 1); } else if (strcmp(interface, zwp_text_input_manager_v3_interface.name) == 0) { text_input_manager = wl_registry_bind(registry, name, &zwp_text_input_manager_v3_interface, 1); } else if (strcmp(interface, wl_seat_interface.name) == 0) { seat = wl_registry_bind(registry, name, &wl_seat_interface, version); } } 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, }; int main(int argc, char **argv) { if (argc > 1) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-h") == 0) { printf(usage); return 0; } sleeptime = atoi(argv[1]); if (argc > 3) { width = atoi(argv[2]); height = atoi(argv[3]); } } buffer = calloc(1, sizeof(char)); display = wl_display_connect(NULL); if (display == NULL) { fprintf(stderr, "Failed to create display\n"); return EXIT_FAILURE; } struct wl_registry *registry = wl_display_get_registry(display); wl_registry_add_listener(registry, ®istry_listener, NULL); wl_display_dispatch(display); wl_display_roundtrip(display); if (compositor == NULL) { fprintf(stderr, "wl-compositor not available\n"); return EXIT_FAILURE; } if (wm_base == NULL) { fprintf(stderr, "xdg-shell not available\n"); return EXIT_FAILURE; } if (text_input_manager == NULL) { fprintf(stderr, "text-input not available\n"); return EXIT_FAILURE; } text_input = zwp_text_input_manager_v3_get_text_input(text_input_manager, seat); zwp_text_input_v3_add_listener(text_input, &text_input_listener, NULL); wlr_egl_init(&egl, EGL_PLATFORM_WAYLAND_EXT, display, NULL, WL_SHM_FORMAT_ARGB8888); struct wl_surface *surface = wl_compositor_create_surface(compositor); struct xdg_surface *xdg_surface = xdg_wm_base_get_xdg_surface(wm_base, 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); wl_surface_commit(surface); egl_window = wl_egl_window_create(surface, width, height); egl_surface = wlr_egl_create_surface(&egl, egl_window); wl_display_roundtrip(display); draw(); while (wl_display_dispatch(display) != -1) { // This space intentionally left blank } return EXIT_SUCCESS; } wlroots-0.10.0/examples/toplevel-decoration.c000066400000000000000000000170561361211131400212440ustar00rootroot00000000000000#include #include #include #include #include #include #include #include "xdg-shell-client-protocol.h" #include "xdg-decoration-unstable-v1-client-protocol.h" /** * Usage: toplevel-decoration [mode] * Creates an xdg-toplevel supporting decoration negotiation. If `mode` is * specified, the client will prefer this decoration mode. */ static int width = 500, height = 300; static struct wl_compositor *compositor = NULL; static struct xdg_wm_base *wm_base = NULL; static struct zxdg_decoration_manager_v1 *decoration_manager = NULL; struct wlr_egl egl; struct wl_egl_window *egl_window; struct wlr_egl_surface *egl_surface; struct zxdg_toplevel_decoration_v1 *decoration; enum zxdg_toplevel_decoration_v1_mode client_preferred_mode, current_mode; static const char *get_mode_name(enum zxdg_toplevel_decoration_v1_mode mode) { switch (mode) { case ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE: return "client-side decorations"; case ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE: return "server-side decorations"; } abort(); } static void request_preferred_mode(void) { enum zxdg_toplevel_decoration_v1_mode mode = client_preferred_mode; if (mode == 0) { printf("Requesting compositor preferred mode\n"); zxdg_toplevel_decoration_v1_unset_mode(decoration); return; } if (mode == current_mode) { return; } printf("Requesting %s\n", get_mode_name(mode)); zxdg_toplevel_decoration_v1_set_mode(decoration, mode); } static void draw(void) { eglMakeCurrent(egl.display, egl_surface, egl_surface, egl.context); float color[] = {1.0, 1.0, 0.0, 1.0}; if (current_mode == ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE) { color[0] = 0.0; } glViewport(0, 0, width, height); glClearColor(color[0], color[1], color[2], 1.0); glClear(GL_COLOR_BUFFER_BIT); eglSwapBuffers(egl.display, egl_surface); } static void xdg_surface_handle_configure(void *data, struct xdg_surface *xdg_surface, uint32_t serial) { xdg_surface_ack_configure(xdg_surface, serial); wl_egl_window_resize(egl_window, width, height, 0, 0); draw(); } 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) { width = w; height = h; } static const struct xdg_toplevel_listener xdg_toplevel_listener = { .configure = xdg_toplevel_handle_configure, }; static void decoration_handle_configure(void *data, struct zxdg_toplevel_decoration_v1 *decoration, enum zxdg_toplevel_decoration_v1_mode mode) { printf("Using %s\n", get_mode_name(mode)); current_mode = mode; } static const struct zxdg_toplevel_decoration_v1_listener decoration_listener = { .configure = decoration_handle_configure, }; static void pointer_handle_enter(void *data, struct wl_pointer *pointer, uint32_t serial, struct wl_surface *surface, wl_fixed_t sx, wl_fixed_t sy) { // No-op } static void pointer_handle_leave(void *data, struct wl_pointer *pointer, uint32_t serial, struct wl_surface *surface) { // No-op } static void pointer_handle_motion(void *data, struct wl_pointer *pointer, uint32_t time, wl_fixed_t sx, wl_fixed_t sy) { // No-op } static void pointer_handle_button(void *data, struct wl_pointer *wl_pointer, uint32_t serial, uint32_t time, uint32_t button, enum wl_pointer_button_state state) { if (state == WL_POINTER_BUTTON_STATE_PRESSED) { // Toggle mode if (client_preferred_mode == ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE) { client_preferred_mode = ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE; } else { client_preferred_mode = ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE; } request_preferred_mode(); } } static void pointer_handle_axis(void *data, struct wl_pointer *wl_pointer, uint32_t time, enum wl_pointer_axis axis, wl_fixed_t value) { // No-op } 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, }; static void seat_handle_capabilities(void *data, struct wl_seat *seat, enum wl_seat_capability caps) { if (caps & WL_SEAT_CAPABILITY_POINTER) { struct wl_pointer *pointer = wl_seat_get_pointer(seat); wl_pointer_add_listener(pointer, &pointer_listener, NULL); } } static const struct wl_seat_listener seat_listener = { .capabilities = seat_handle_capabilities, }; 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, 1); } else if (strcmp(interface, xdg_wm_base_interface.name) == 0) { wm_base = wl_registry_bind(registry, name, &xdg_wm_base_interface, 1); } else if (strcmp(interface, zxdg_decoration_manager_v1_interface.name) == 0) { decoration_manager = wl_registry_bind(registry, name, &zxdg_decoration_manager_v1_interface, 1); } else if (strcmp(interface, wl_seat_interface.name) == 0) { struct wl_seat *seat = wl_registry_bind(registry, name, &wl_seat_interface, 1); wl_seat_add_listener(seat, &seat_listener, NULL); } } 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, }; int main(int argc, char **argv) { if (argc == 2) { char *mode = argv[1]; if (strcmp(mode, "client") == 0) { client_preferred_mode = ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE; } else if (strcmp(mode, "server") == 0) { client_preferred_mode = ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE; } else { fprintf(stderr, "Invalid decoration mode\n"); return EXIT_FAILURE; } } struct wl_display *display = wl_display_connect(NULL); if (display == NULL) { fprintf(stderr, "Failed to create display\n"); return EXIT_FAILURE; } struct wl_registry *registry = wl_display_get_registry(display); wl_registry_add_listener(registry, ®istry_listener, NULL); wl_display_dispatch(display); wl_display_roundtrip(display); if (compositor == NULL) { fprintf(stderr, "wl-compositor not available\n"); return EXIT_FAILURE; } if (wm_base == NULL) { fprintf(stderr, "xdg-shell not available\n"); return EXIT_FAILURE; } if (decoration_manager == NULL) { fprintf(stderr, "xdg-decoration not available\n"); return EXIT_FAILURE; } wlr_egl_init(&egl, EGL_PLATFORM_WAYLAND_EXT, display, NULL, WL_SHM_FORMAT_ARGB8888); struct wl_surface *surface = wl_compositor_create_surface(compositor); struct xdg_surface *xdg_surface = xdg_wm_base_get_xdg_surface(wm_base, surface); struct xdg_toplevel *xdg_toplevel = xdg_surface_get_toplevel(xdg_surface); decoration = zxdg_decoration_manager_v1_get_toplevel_decoration( decoration_manager, xdg_toplevel); wl_display_roundtrip(display); request_preferred_mode(); xdg_surface_add_listener(xdg_surface, &xdg_surface_listener, NULL); xdg_toplevel_add_listener(xdg_toplevel, &xdg_toplevel_listener, NULL); zxdg_toplevel_decoration_v1_add_listener(decoration, &decoration_listener, NULL); wl_surface_commit(surface); egl_window = wl_egl_window_create(surface, width, height); egl_surface = wlr_egl_create_surface(&egl, egl_window); wl_display_roundtrip(display); draw(); while (wl_display_dispatch(display) != -1) { // This space is intentionally left blank } return EXIT_SUCCESS; } wlroots-0.10.0/examples/touch.c000066400000000000000000000220561361211131400164030ustar00rootroot00000000000000#define _POSIX_C_SOURCE 200112L #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_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_input_device *device; struct wl_listener destroy; struct wl_listener down; struct wl_listener up; struct wl_listener motion; 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_input_device *device; 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); wlr_output_attach_render(wlr_output, NULL); wlr_renderer_begin(sample->renderer, wlr_output->width, wlr_output->height); wlr_renderer_clear(sample->renderer, (float[]){0.25f, 0.25f, 0.25f, 1}); int tex_width, tex_height; wlr_texture_get_size(sample->cat_texture, &tex_width, &tex_height); struct touch_point *p; wl_list_for_each(p, &sample->touch_points, link) { int x = (int)(p->x * width) - tex_width / 2; int y = (int)(p->y * height) - tex_height / 2; wlr_render_texture(sample->renderer, sample->cat_texture, wlr_output->transform_matrix, x, y, 1.0f); } wlr_renderer_end(sample->renderer); wlr_output_commit(wlr_output); sample->last_frame = now; } static void touch_down_notify(struct wl_listener *listener, void *data) { struct wlr_event_touch_motion *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(struct touch_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_event_touch_up *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_event_touch_motion *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_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); 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); struct sample_output *sample_output = calloc(1, sizeof(struct sample_output)); if (!wl_list_empty(&output->modes)) { struct wlr_output_mode *mode = wl_container_of(output->modes.prev, mode, link); wlr_output_set_mode(output, mode); } 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; } 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_event_keyboard_key *event = data; uint32_t keycode = event->keycode + 8; const xkb_keysym_t *syms; int nsyms = xkb_state_key_get_syms(keyboard->device->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(struct sample_keyboard)); keyboard->device = device; keyboard->sample = sample; wl_signal_add(&device->events.destroy, &keyboard->destroy); keyboard->destroy.notify = keyboard_destroy_notify; wl_signal_add(&device->keyboard->events.key, &keyboard->key); keyboard->key.notify = keyboard_key_notify; struct xkb_rule_names rules = { 0 }; rules.rules = getenv("XKB_DEFAULT_RULES"); rules.model = getenv("XKB_DEFAULT_MODEL"); rules.layout = getenv("XKB_DEFAULT_LAYOUT"); rules.variant = getenv("XKB_DEFAULT_VARIANT"); rules.options = getenv("XKB_DEFAULT_OPTIONS"); 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_map_new_from_names(context, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS); if (!keymap) { wlr_log(WLR_ERROR, "Failed to create XKB keymap"); exit(1); } wlr_keyboard_set_keymap(device->keyboard, keymap); xkb_keymap_unref(keymap); xkb_context_unref(context); break; case WLR_INPUT_DEVICE_TOUCH:; struct touch_state *tstate = calloc(sizeof(struct touch_state), 1); tstate->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(&device->touch->events.down, &tstate->down); tstate->motion.notify = touch_motion_notify; wl_signal_add(&device->touch->events.motion, &tstate->motion); tstate->up.notify = touch_up_notify; wl_signal_add(&device->touch->events.up, &tstate->up); 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_backend_get_renderer(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, WL_SHM_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); } 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.10.0/examples/virtual-pointer.c000066400000000000000000000116141361211131400204230ustar00rootroot00000000000000/* * 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. */ #define _POSIX_C_SOURCE 200112L #include #include #include #include #include #include "wlr-virtual-pointer-unstable-v1-client-protocol.h" static struct wl_seat *seat = NULL; static struct zwlr_virtual_pointer_manager_v1 *pointer_manager = NULL; static void handle_global(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { if (strcmp(interface, wl_seat_interface.name) == 0) { seat = wl_registry_bind(registry, name, &wl_seat_interface, version); } else if (strcmp(interface, zwlr_virtual_pointer_manager_v1_interface.name) == 0) { pointer_manager = wl_registry_bind(registry, name, &zwlr_virtual_pointer_manager_v1_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, }; int main(int argc, char *argv[]) { if (argc < 2) { fprintf(stderr, "Usage: ./virtual-pointer \n"); return EXIT_FAILURE; } struct wl_display * display = wl_display_connect(NULL); if (display == NULL) { fprintf(stderr, "failed to create display: %m\n"); return EXIT_FAILURE; } struct wl_registry *registry = wl_display_get_registry(display); wl_registry_add_listener(registry, ®istry_listener, NULL); wl_display_dispatch(display); wl_display_roundtrip(display); if (pointer_manager == NULL) { fprintf(stderr, "compositor does not support wlr-virtual-pointer-unstable-v1\n"); return EXIT_FAILURE; } struct zwlr_virtual_pointer_v1 *pointer = zwlr_virtual_pointer_manager_v1_create_virtual_pointer( pointer_manager, seat); const char *cmd = argv[1]; if (strcmp(cmd, "motion") == 0) { if (argc < 4) { fprintf(stderr, "Usage: ./virtual-pointer motion \n"); return EXIT_FAILURE; } wl_fixed_t dx = wl_fixed_from_double(atof(argv[2])); wl_fixed_t dy = wl_fixed_from_double(atof(argv[3])); zwlr_virtual_pointer_v1_motion(pointer, 0, dx, dy); } else if (strcmp(cmd, "absolute") == 0) { if (argc < 6) { fprintf(stderr, "Usage: ./virtual-pointer absolute \n"); return EXIT_FAILURE; } uint32_t x = atoi(argv[2]); uint32_t y = atoi(argv[3]); uint32_t x_extent = atoi(argv[4]); uint32_t y_extent = atoi(argv[5]); zwlr_virtual_pointer_v1_motion_absolute(pointer, 0, x, y, x_extent, y_extent); } else if (strcmp(cmd, "button") == 0) { if (argc < 4) { fprintf(stderr, "Usage: ./virtual-pointer button