From d1b75674d4d7c38147d3a527f319ee699837ace3 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 1 Nov 2021 16:51:18 +0100 Subject: [PATCH 001/188] ci: add .gitlab-ci.yml This is a glue file to allow integration with builds.sr.ht. --- .gitlab-ci.yml | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .gitlab-ci.yml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000000..b0942ab314 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,7 @@ +include: https://git.sr.ht/~emersion/dalligi/blob/master/templates/multi.yml +alpine: + extends: .dalligi +archlinux: + extends: .dalligi +freebsd: + extends: .dalligi From 8bc1086cac35f22356c91912576e332c7125e348 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 1 Nov 2021 17:28:16 +0100 Subject: [PATCH 002/188] s/GitHub/GitLab/ --- .builds/alpine.yml | 2 +- .builds/archlinux.yml | 2 +- .builds/freebsd.yml | 2 +- CONTRIBUTING.md | 32 ++++++++++++++++---------------- README.md | 4 ++-- 5 files changed, 21 insertions(+), 21 deletions(-) diff --git a/.builds/alpine.yml b/.builds/alpine.yml index e4c654b1dd..ab613b2888 100644 --- a/.builds/alpine.yml +++ b/.builds/alpine.yml @@ -18,7 +18,7 @@ packages: - xwayland - libseat-dev sources: - - https://github.com/swaywm/wlroots + - https://gitlab.freedesktop.org/wlroots/wlroots.git tasks: - setup: | cd wlroots diff --git a/.builds/archlinux.yml b/.builds/archlinux.yml index 1c89ca6f5c..4d8e3767b0 100644 --- a/.builds/archlinux.yml +++ b/.builds/archlinux.yml @@ -19,7 +19,7 @@ packages: - vulkan-headers - glslang sources: - - https://github.com/swaywm/wlroots + - https://gitlab.freedesktop.org/wlroots/wlroots.git tasks: - setup: | cd wlroots diff --git a/.builds/freebsd.yml b/.builds/freebsd.yml index 368167c845..dc79d561c8 100644 --- a/.builds/freebsd.yml +++ b/.builds/freebsd.yml @@ -26,7 +26,7 @@ packages: - sysutils/seatd - gmake sources: - - https://github.com/swaywm/wlroots + - https://gitlab.freedesktop.org/wlroots/wlroots.git tasks: - wlroots: | cd wlroots diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a4e53bbd9b..b63d8b4f17 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,21 +1,21 @@ # Contributing to wlroots -Contributing just involves sending a pull request. You will probably be more +Contributing just involves sending a merge request. You will probably be more successful with your contribution if you visit [#sway-devel on Libera Chat] upfront and discuss your plans. Note: rules are made to be broken. Adjust or ignore any/all of these as you see fit, but be prepared to justify it to your peers. -## Pull Requests +## Merge Requests -If you already have your own pull request habits, feel free to use them. If you +If you already have your own merge request habits, feel free to use them. If you don't, however, allow me to make a suggestion: feature branches pulled from upstream. Try this: 1. Fork wlroots -2. `git clone git@github.com:/wlroots.git && cd wlroots` -3. `git remote add upstream https://github.com/swaywm/wlroots` +2. `git clone git@gitlab.freedesktop.org:/wlroots.git && cd wlroots` +3. `git remote add upstream https://gitlab.freedesktop.org/wlroots/wlroots.git` You only need to do this once. You're never going to use your fork's master branch. Instead, when you start working on a feature, do this: @@ -24,11 +24,11 @@ branch. Instead, when you start working on a feature, do this: 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 +5. Make a merge request from your feature branch -When you submit your pull request, your commit log should do most of the talking +When you submit your merge request, your commit log should do most of the talking when it comes to describing your changes and their motivation. In addition to -this, your pull request's comments will ideally include a test plan that the +this, your merge request's comments will ideally include a test plan that the reviewers can use to (1) demonstrate the problem on master, if applicable and (2) verify that the problem no longer exists with your changes applied (or that your new features work correctly). Document all of the edge cases you're aware @@ -80,15 +80,15 @@ cmd_move"* or *"Improve performance of arrange_windows on ARM"* or similar. The subsequent lines should be separated from the subject line by a single blank line, and include optional details. In this you can give justification -for the change, [reference Github issues], or explain some of the subtler +for the change, [reference issues], or explain some of the subtler details of your patch. This is important because when someone finds a line of code they don't understand later, they can use the `git blame` command to find out what the author was thinking when they wrote it. It's also easier to review -your pull requests if they're separated into logical commits that have good +your merge requests if they're separated into logical commits that have good commit messages and justify themselves in the extended commit description. -As a good rule of thumb, anything you might put into the pull request -description on Github is probably fair game for going into the extended commit +As a good rule of thumb, anything you might put into the merge request +description on GitLab is probably fair game for going into the extended commit message as well. See [How to Write a Git Commit Message] for more details. @@ -101,16 +101,16 @@ 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 +1. **Triage** the merge request. Do the commit messages make sense? Is a test plan necessary and/or present? Add anyone as reviewers that you think should - be there (using the relevant GitHub feature, if you have the permissions, or + be there (using the relevant GitLab feature, if you have the permissions, or with an @mention if necessary). 2. **Review** the code. Look for code style violations, naming convention violations, buffer overflows, memory leaks, logic errors, non-portable code (including GNU-isms), etc. For significant changes to the public API, loop in a couple more people for discussion. 3. **Execute** the test plan, if present. -4. **Merge** the pull request when all reviewers approve. +4. **Merge** the merge request when all reviewers approve. 5. **File** follow-up tickets if appropriate. ## Style Reference @@ -396,6 +396,6 @@ static void subsurface_handle_surface_destroy(struct wl_listener *listener, [#sway-devel on Libera Chat]: https://web.libera.chat/gamja/?channels=#sway-devel [linear, "recipe" style]: https://www.bitsnbites.eu/git-history-work-log-vs-recipe/ [git-rebase.io]: https://git-rebase.io/ -[reference Github issues]: https://help.github.com/articles/closing-issues-via-commit-messages/ +[reference issues]: https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically [How to Write a Git Commit Message]: https://chris.beams.io/posts/git-commit/ [kernel style]: https://www.kernel.org/doc/Documentation/process/coding-style.rst diff --git a/README.md b/README.md index 6af4e17196..33c59104b8 100644 --- a/README.md +++ b/README.md @@ -75,9 +75,9 @@ Install like so: See [CONTRIBUTING.md]. [Wayland]: https://wayland.freedesktop.org/ -[wiki]: https://github.com/swaywm/wlroots/wiki/Getting-started +[wiki]: https://gitlab.freedesktop.org/wlroots/wlroots/-/wikis/Getting-started [#sway-devel on Libera Chat]: https://web.libera.chat/gamja/?channels=#sway-devel [Sway]: https://github.com/swaywm/sway [wrapper libraries]: https://github.com/search?q=topic%3Abindings+org%3Aswaywm&type=Repositories [libseat]: https://git.sr.ht/~kennylevinsen/seatd -[CONTRIBUTING.md]: https://github.com/swaywm/wlroots/blob/master/CONTRIBUTING.md +[CONTRIBUTING.md]: https://gitlab.freedesktop.org/wlroots/wlroots/-/blob/master/CONTRIBUTING.md From 760e1665789212c49adba8d50632787f49f88c09 Mon Sep 17 00:00:00 2001 From: Jan Beich Date: Tue, 5 Oct 2021 01:49:27 +0000 Subject: [PATCH 003/188] render: completely disable gles2 if requested but libEGL is found For `required` to disable search the value needs to be of `feature` type. Checking `gles2` via `in` keyword returns a `bool` but `required: false` makes the dependency optional instead of disabled. --- render/meson.build | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/render/meson.build b/render/meson.build index 2d65188762..a1289edee1 100644 --- a/render/meson.build +++ b/render/meson.build @@ -14,13 +14,12 @@ wlr_files += files( 'wlr_texture.c', ) -egl = dependency('egl', required: 'gles2' in renderers) -if egl.found() - wlr_deps += egl - wlr_files += files('egl.c') -endif - if 'gles2' in renderers or 'auto' in renderers + egl = dependency('egl', required: 'gles2' in renderers) + if egl.found() + wlr_deps += egl + wlr_files += files('egl.c') + endif subdir('gles2') endif From 3e801d68f2c6c64567b1f24c6d03893f32c81197 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Fri, 22 Oct 2021 14:12:49 +0200 Subject: [PATCH 004/188] xwayland: add support for -noTouchPointerEmulation This allows compositors to handle touch pointer emulation manually, instead of having Xwayland do it [1]. [1]: https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/691 --- include/wlr/xwayland.h | 1 + include/xwayland/meson.build | 7 ++++++- xwayland/server.c | 8 ++++++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/include/wlr/xwayland.h b/include/wlr/xwayland.h index 3689b8b3e5..aa9569fea1 100644 --- a/include/wlr/xwayland.h +++ b/include/wlr/xwayland.h @@ -22,6 +22,7 @@ struct wlr_xwayland_cursor; struct wlr_xwayland_server_options { bool lazy; bool enable_wm; + bool no_touch_pointer_emulation; }; struct wlr_xwayland_server { diff --git a/include/xwayland/meson.build b/include/xwayland/meson.build index a92234f1c7..1dad9259bb 100644 --- a/include/xwayland/meson.build +++ b/include/xwayland/meson.build @@ -1,7 +1,11 @@ have_listenfd = false +have_no_touch_pointer_emulation = false if xwayland.found() xwayland_path = xwayland.get_variable('xwayland') - have_listenfd = xwayland.get_variable('have_listenfd') == 'true' + have_listenfd = xwayland.get_variable('have_listenfd', + default_value: 'false') == 'true' + have_no_touch_pointer_emulation = xwayland.get_variable( + 'have_no_touch_pointer_emulation', default_value: 'false') == 'true' else xwayland_path = xwayland_prog.full_path() endif @@ -9,6 +13,7 @@ endif xwayland_config_data = configuration_data() xwayland_config_data.set_quoted('XWAYLAND_PATH', xwayland_path) xwayland_config_data.set10('HAVE_XWAYLAND_LISTENFD', have_listenfd) +xwayland_config_data.set10('HAVE_XWAYLAND_NO_TOUCH_POINTER_EMULATION', have_no_touch_pointer_emulation) configure_file( output: 'config.h', configuration: xwayland_config_data, diff --git a/xwayland/server.c b/xwayland/server.c index 7af01b6f99..d8006c44d4 100644 --- a/xwayland/server.c +++ b/xwayland/server.c @@ -73,6 +73,14 @@ noreturn static void exec_xwayland(struct wlr_xwayland_server *server) { argv[i++] = wmfd; } +#if HAVE_XWAYLAND_NO_TOUCH_POINTER_EMULATION + if (server->options.no_touch_pointer_emulation) { + argv[i++] = "-noTouchPointerEmulation"; + } +#else + server->options.no_touch_pointer_emulation = false; +#endif + argv[i++] = NULL; assert(i < sizeof(argv) / sizeof(argv[0])); From 2ff4e113e237d9f461e41b994db6c53b5a66a3d0 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 9 Jun 2021 11:48:25 +0200 Subject: [PATCH 005/188] backend/session: introduce wlr_device_change_event This struct contains additional information for session device change events, such as the DRM connector ID that has changed. --- backend/session/session.c | 25 ++++++++++++++++++++++++- include/wlr/backend/session.h | 18 +++++++++++++++++- 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/backend/session/session.c b/backend/session/session.c index b98f503deb..382f3d60bb 100644 --- a/backend/session/session.c +++ b/backend/session/session.c @@ -139,6 +139,27 @@ static bool is_drm_card(const char *sysname) { return true; } +static void read_udev_change_event(struct wlr_device_change_event *event, + struct udev_device *udev_dev) { + const char *hotplug = udev_device_get_property_value(udev_dev, "HOTPLUG"); + if (hotplug != NULL && strcmp(hotplug, "1") == 0) { + event->type = WLR_DEVICE_HOTPLUG; + struct wlr_device_hotplug_event *hotplug = &event->hotplug; + + const char *connector = + udev_device_get_property_value(udev_dev, "CONNECTOR"); + if (connector != NULL) { + hotplug->connector_id = strtoul(connector, NULL, 10); + } + + const char *prop = + udev_device_get_property_value(udev_dev, "PROPERTY"); + if (prop != NULL) { + hotplug->prop_id = strtoul(prop, NULL, 10); + } + } +} + static int handle_udev_event(int fd, uint32_t mask, void *data) { struct wlr_session *session = data; @@ -180,7 +201,9 @@ static int handle_udev_event(int fd, uint32_t mask, void *data) { if (strcmp(action, "change") == 0) { wlr_log(WLR_DEBUG, "DRM device %s changed", sysname); - wlr_signal_emit_safe(&dev->events.change, NULL); + struct wlr_device_change_event event = {0}; + read_udev_change_event(&event, udev_dev); + wlr_signal_emit_safe(&dev->events.change, &event); } else if (strcmp(action, "remove") == 0) { wlr_log(WLR_DEBUG, "DRM device %s removed", sysname); wlr_signal_emit_safe(&dev->events.remove, NULL); diff --git a/include/wlr/backend/session.h b/include/wlr/backend/session.h index 0107ea3628..7d34bfebea 100644 --- a/include/wlr/backend/session.h +++ b/include/wlr/backend/session.h @@ -15,7 +15,7 @@ struct wlr_device { struct wl_list link; struct { - struct wl_signal change; + struct wl_signal change; // struct wlr_device_change_event struct wl_signal remove; } events; }; @@ -57,6 +57,22 @@ struct wlr_session_add_event { const char *path; }; +enum wlr_device_change_type { + WLR_DEVICE_HOTPLUG = 1, +}; + +struct wlr_device_hotplug_event { + uint32_t connector_id; + uint32_t prop_id; +}; + +struct wlr_device_change_event { + enum wlr_device_change_type type; + union { + struct wlr_device_hotplug_event hotplug; + }; +}; + /* * Opens a session, taking control of the current virtual terminal. * This should not be called if another program is already in control From e13f3f860861af4495619e05be0b1554e32362bc Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 9 Jun 2021 12:03:20 +0200 Subject: [PATCH 006/188] backend/drm: handle per-connector hotplug events When a connector ID is specified in a hotplug event, don't scan all connectors. Only scan the connector that has changed. --- backend/drm/backend.c | 16 ++++++++++++---- backend/drm/drm.c | 39 ++++++++++++++++++++++++++++----------- include/backend/drm/drm.h | 3 ++- 3 files changed, 42 insertions(+), 16 deletions(-) diff --git a/backend/drm/backend.c b/backend/drm/backend.c index 5e01eddf73..cd5b8ee0e2 100644 --- a/backend/drm/backend.c +++ b/backend/drm/backend.c @@ -22,7 +22,7 @@ struct wlr_drm_backend *get_drm_backend_from_backend( static bool backend_start(struct wlr_backend *backend) { struct wlr_drm_backend *drm = get_drm_backend_from_backend(backend); - scan_drm_connectors(drm); + scan_drm_connectors(drm, NULL); return true; } @@ -102,7 +102,7 @@ static void handle_session_active(struct wl_listener *listener, void *data) { if (session->active) { wlr_log(WLR_INFO, "DRM fd resumed"); - scan_drm_connectors(drm); + scan_drm_connectors(drm, NULL); struct wlr_drm_connector *conn; wl_list_for_each(conn, &drm->outputs, link) { @@ -127,13 +127,21 @@ static void handle_session_active(struct wl_listener *listener, void *data) { static void handle_dev_change(struct wl_listener *listener, void *data) { struct wlr_drm_backend *drm = wl_container_of(listener, drm, dev_change); + struct wlr_device_change_event *change = data; if (!drm->session->active) { return; } - wlr_log(WLR_DEBUG, "%s invalidated", drm->name); - scan_drm_connectors(drm); + // TODO: add and handle lease uevents + switch (change->type) { + case WLR_DEVICE_HOTPLUG:; + wlr_log(WLR_DEBUG, "Received hotplug event for %s", drm->name); + scan_drm_connectors(drm, &change->hotplug); + break; + default: + wlr_log(WLR_DEBUG, "Received unknown change event for %s", drm->name); + } } static void handle_dev_remove(struct wl_listener *listener, void *data) { diff --git a/backend/drm/drm.c b/backend/drm/drm.c index 191ad064da..3031d09b2c 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -1177,7 +1177,8 @@ static uint32_t get_possible_crtcs(int fd, const drmModeConnector *conn) { static void disconnect_drm_connector(struct wlr_drm_connector *conn); -void scan_drm_connectors(struct wlr_drm_backend *drm) { +void scan_drm_connectors(struct wlr_drm_backend *drm, + struct wlr_device_hotplug_event *event) { /* * This GPU is not really a modesetting device. * It's just being used as a renderer. @@ -1186,7 +1187,12 @@ void scan_drm_connectors(struct wlr_drm_backend *drm) { return; } - wlr_log(WLR_INFO, "Scanning DRM connectors on %s", drm->name); + if (event != NULL && event->connector_id != 0) { + wlr_log(WLR_INFO, "Scanning DRM connector %"PRIu32" on %s", + event->connector_id, drm->name); + } else { + wlr_log(WLR_INFO, "Scanning DRM connectors on %s", drm->name); + } drmModeRes *res = drmModeGetResources(drm->fd); if (!res) { @@ -1203,25 +1209,36 @@ void scan_drm_connectors(struct wlr_drm_backend *drm) { 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); + uint32_t conn_id = res->connectors[i]; ssize_t index = -1; struct wlr_drm_connector *c, *wlr_conn = NULL; wl_list_for_each(c, &drm->outputs, link) { index++; - if (c->id == drm_conn->connector_id) { + if (c->id == conn_id) { wlr_conn = c; break; } } + // If the hotplug event contains a connector ID, ignore any other + // connector. + if (event != NULL && event->connector_id != 0 && + event->connector_id != conn_id) { + if (wlr_conn != NULL) { + seen[index] = true; + } + continue; + } + + drmModeConnector *drm_conn = drmModeGetConnector(drm->fd, conn_id); + if (!drm_conn) { + wlr_log_errno(WLR_ERROR, "Failed to get DRM connector"); + continue; + } + drmModeEncoder *curr_enc = drmModeGetEncoder(drm->fd, + drm_conn->encoder_id); + if (!wlr_conn) { wlr_conn = calloc(1, sizeof(*wlr_conn)); if (!wlr_conn) { diff --git a/include/backend/drm/drm.h b/include/backend/drm/drm.h index 4b2a1f7c8d..e045a72ee8 100644 --- a/include/backend/drm/drm.h +++ b/include/backend/drm/drm.h @@ -147,7 +147,8 @@ struct wlr_drm_backend *get_drm_backend_from_backend( bool check_drm_features(struct wlr_drm_backend *drm); bool init_drm_resources(struct wlr_drm_backend *drm); void finish_drm_resources(struct wlr_drm_backend *drm); -void scan_drm_connectors(struct wlr_drm_backend *state); +void scan_drm_connectors(struct wlr_drm_backend *state, + struct wlr_device_hotplug_event *event); int handle_drm_event(int fd, uint32_t mask, void *data); void destroy_drm_connector(struct wlr_drm_connector *conn); bool drm_connector_commit_state(struct wlr_drm_connector *conn, From 8634dd3e6ab9e629e18c973898c1a01835f4f925 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Sat, 6 Nov 2021 13:32:43 +0100 Subject: [PATCH 007/188] output: fix leak of wlr_drm_format --- types/output/cursor.c | 1 + 1 file changed, 1 insertion(+) diff --git a/types/output/cursor.c b/types/output/cursor.c index dcaccade56..0a6cbb757b 100644 --- a/types/output/cursor.c +++ b/types/output/cursor.c @@ -264,6 +264,7 @@ static struct wlr_buffer *render_cursor_buffer(struct wlr_output_cursor *cursor) wlr_swapchain_destroy(output->cursor_swapchain); output->cursor_swapchain = wlr_swapchain_create(allocator, width, height, format); + free(format); if (output->cursor_swapchain == NULL) { wlr_log(WLR_ERROR, "Failed to create cursor swapchain"); return NULL; From fc1ed72bdcb8fc3b541c46a54e28910d600cde43 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Sat, 6 Nov 2021 16:51:23 +0300 Subject: [PATCH 008/188] CONTRIBUTING.md: add CoC section --- CONTRIBUTING.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b63d8b4f17..a5b474e0c9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -113,6 +113,13 @@ process is: 4. **Merge** the merge request when all reviewers approve. 5. **File** follow-up tickets if appropriate. +## Code of Conduct + +Note that as a project hosted on freedesktop.org, wlroots follows its +[Code of Conduct], based on the Contributor Covenant. Please conduct yourself +in a respectful and civilized manner when communicating with community members +on IRC and bug tracker. + ## Style Reference wlroots is written in C with a style similar to the [kernel style], but with a @@ -397,5 +404,6 @@ static void subsurface_handle_surface_destroy(struct wl_listener *listener, [linear, "recipe" style]: https://www.bitsnbites.eu/git-history-work-log-vs-recipe/ [git-rebase.io]: https://git-rebase.io/ [reference issues]: https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically +[Code of Conduct]: https://www.freedesktop.org/wiki/CodeOfConduct/ [How to Write a Git Commit Message]: https://chris.beams.io/posts/git-commit/ [kernel style]: https://www.kernel.org/doc/Documentation/process/coding-style.rst From eb5f23d6d028281687d7fa63e82624dcf0e7a734 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 7 Nov 2021 13:28:23 +0100 Subject: [PATCH 009/188] scene: fix calloc size mismatch --- types/scene/wlr_scene.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 940886a641..5a2528d1b4 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -798,7 +798,7 @@ static const struct wlr_addon_interface output_addon_impl = { struct wlr_scene_output *wlr_scene_output_create(struct wlr_scene *scene, struct wlr_output *output) { - struct wlr_scene_output *scene_output = calloc(1, sizeof(*output)); + struct wlr_scene_output *scene_output = calloc(1, sizeof(*scene_output)); if (scene_output == NULL) { return NULL; } From e326b76959a4dfa1f41ba9548a81aac5df7730de Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Sun, 7 Nov 2021 20:34:24 +0100 Subject: [PATCH 010/188] text-input/input-method: handle strdup() failure --- types/wlr_input_method_v2.c | 8 ++++++++ types/wlr_text_input_v3.c | 4 ++++ 2 files changed, 12 insertions(+) diff --git a/types/wlr_input_method_v2.c b/types/wlr_input_method_v2.c index 6ad2615ffa..7ccd31ee02 100644 --- a/types/wlr_input_method_v2.c +++ b/types/wlr_input_method_v2.c @@ -88,6 +88,10 @@ static void im_commit_string(struct wl_client *client, } free(input_method->pending.commit_text); input_method->pending.commit_text = strdup(text); + if (input_method->pending.commit_text == NULL) { + wl_client_post_no_memory(client); + return; + } } static void im_set_preedit_string(struct wl_client *client, @@ -102,6 +106,10 @@ static void im_set_preedit_string(struct wl_client *client, input_method->pending.preedit.cursor_end = cursor_end; free(input_method->pending.preedit.text); input_method->pending.preedit.text = strdup(text); + if (input_method->pending.preedit.text == NULL) { + wl_client_post_no_memory(client); + return; + } } static void im_delete_surrounding_text(struct wl_client *client, diff --git a/types/wlr_text_input_v3.c b/types/wlr_text_input_v3.c index c6e43ec2e7..9a4c42c922 100644 --- a/types/wlr_text_input_v3.c +++ b/types/wlr_text_input_v3.c @@ -171,6 +171,10 @@ static void text_input_commit(struct wl_client *client, if (text_input->pending.surrounding.text) { text_input->current.surrounding.text = strdup(text_input->pending.surrounding.text); + if (text_input->current.surrounding.text == NULL) { + wl_client_post_no_memory(client); + return; + } } bool old_enabled = text_input->current_enabled; From f20c49d78a5406a0678e2f911f26965a479bf73f Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Fri, 5 Nov 2021 12:53:33 +0100 Subject: [PATCH 011/188] export-dmabuf-v1: stop using wlr_output_export_dmabuf --- types/wlr_export_dmabuf_v1.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/types/wlr_export_dmabuf_v1.c b/types/wlr_export_dmabuf_v1.c index c310806236..954a3270bc 100644 --- a/types/wlr_export_dmabuf_v1.c +++ b/types/wlr_export_dmabuf_v1.c @@ -66,7 +66,7 @@ static void frame_output_handle_commit(struct wl_listener *listener, wl_list_init(&frame->output_commit.link); struct wlr_dmabuf_attributes attribs = {0}; - if (!wlr_output_export_dmabuf(frame->output, &attribs)) { + if (!wlr_buffer_get_dmabuf(frame->output->front_buffer, &attribs)) { zwlr_export_dmabuf_frame_v1_send_cancel(frame->resource, ZWLR_EXPORT_DMABUF_FRAME_V1_CANCEL_REASON_TEMPORARY); frame_destroy(frame); @@ -86,8 +86,6 @@ static void frame_output_handle_commit(struct wl_listener *listener, attribs.fd[i], size, attribs.offset[i], attribs.stride[i], i); } - wlr_dmabuf_attributes_finish(&attribs); - time_t tv_sec = event->when->tv_sec; uint32_t tv_sec_hi = (sizeof(tv_sec) > 4) ? tv_sec >> 32 : 0; uint32_t tv_sec_lo = tv_sec & 0xFFFFFFFF; From fa77aeb80e70fba09bb0f0d42045dfc6ce1ce1a9 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Fri, 5 Nov 2021 12:54:06 +0100 Subject: [PATCH 012/188] screencopy-v1: stop using wlr_output_export_dmabuf --- types/wlr_screencopy_v1.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/types/wlr_screencopy_v1.c b/types/wlr_screencopy_v1.c index 04dd9e3d2e..7c7d7e95ed 100644 --- a/types/wlr_screencopy_v1.c +++ b/types/wlr_screencopy_v1.c @@ -489,13 +489,12 @@ static struct wlr_screencopy_v1_client *client_from_resource( } static uint32_t get_output_fourcc(struct wlr_output *output) { - struct wlr_dmabuf_attributes attr = { 0 }; - if (!wlr_output_export_dmabuf(output, &attr)) { + struct wlr_dmabuf_attributes attr = {0}; + if (!output->front_buffer || + !wlr_buffer_get_dmabuf(output->front_buffer, &attr)) { return DRM_FORMAT_INVALID; } - uint32_t format = attr.format; - wlr_dmabuf_attributes_finish(&attr); - return format; + return attr.format; } static void capture_output(struct wl_client *wl_client, From 76bab68e701bbdd1ed43e419b4c22a68e2a1db55 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Fri, 5 Nov 2021 12:49:32 +0100 Subject: [PATCH 013/188] output: drop wlr_output_export_dmabuf Callers can access output->front_buffer instead. --- include/wlr/types/wlr_output.h | 8 -------- types/output/output.c | 13 ------------- 2 files changed, 21 deletions(-) diff --git a/include/wlr/types/wlr_output.h b/include/wlr/types/wlr_output.h index 6c07372b95..17f48311e3 100644 --- a/include/wlr/types/wlr_output.h +++ b/include/wlr/types/wlr_output.h @@ -14,7 +14,6 @@ #include #include #include -#include #include #include @@ -401,13 +400,6 @@ size_t wlr_output_get_gamma_size(struct wlr_output *output); */ void wlr_output_set_gamma(struct wlr_output *output, size_t size, const uint16_t *r, const uint16_t *g, const uint16_t *b); -/** - * Exports the last committed buffer as a DMA-BUF. - * - * The caller is responsible for cleaning up the DMA-BUF attributes. - */ -bool wlr_output_export_dmabuf(struct wlr_output *output, - struct wlr_dmabuf_attributes *attribs); /** * Returns the wlr_output matching the provided wl_output resource. If the * resource isn't a wl_output, it aborts. If the resource is inert (because the diff --git a/types/output/output.c b/types/output/output.c index 6dc09d3a73..4e3f403ff1 100644 --- a/types/output/output.c +++ b/types/output/output.c @@ -790,19 +790,6 @@ size_t wlr_output_get_gamma_size(struct wlr_output *output) { return output->impl->get_gamma_size(output); } -bool wlr_output_export_dmabuf(struct wlr_output *output, - struct wlr_dmabuf_attributes *attribs) { - if (output->front_buffer == NULL) { - return false; - } - - struct wlr_dmabuf_attributes buf_attribs = {0}; - if (!wlr_buffer_get_dmabuf(output->front_buffer, &buf_attribs)) { - return false; - } - return wlr_dmabuf_attributes_copy(attribs, &buf_attribs); -} - void wlr_output_update_needs_frame(struct wlr_output *output) { if (output->needs_frame) { return; From ab16861e8670c9d80a6188e7d878bb095c9b56ad Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Sun, 7 Nov 2021 20:14:18 +0100 Subject: [PATCH 014/188] text-input: fix type of send_preedit_string() args The protocol uses a signed integer here, which is also what the wlr_input_method_v2_preedit_string struct provides to compositors from the input method protocol. Sway currently just passes those int32_t values directly to this function leading to an implicit conversion. --- include/wlr/types/wlr_text_input_v3.h | 2 +- types/wlr_text_input_v3.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/wlr/types/wlr_text_input_v3.h b/include/wlr/types/wlr_text_input_v3.h index 53c52a3751..c9ee0b2212 100644 --- a/include/wlr/types/wlr_text_input_v3.h +++ b/include/wlr/types/wlr_text_input_v3.h @@ -87,7 +87,7 @@ void wlr_text_input_v3_send_enter(struct wlr_text_input_v3 *text_input, // Sends leave to the currently focused surface and clears it void wlr_text_input_v3_send_leave(struct wlr_text_input_v3 *text_input); void wlr_text_input_v3_send_preedit_string(struct wlr_text_input_v3 *text_input, - const char *text, uint32_t cursor_begin, uint32_t cursor_end); + const char *text, int32_t cursor_begin, int32_t cursor_end); void wlr_text_input_v3_send_commit_string(struct wlr_text_input_v3 *text_input, const char *text); void wlr_text_input_v3_send_delete_surrounding_text( diff --git a/types/wlr_text_input_v3.c b/types/wlr_text_input_v3.c index 9a4c42c922..f5267b359d 100644 --- a/types/wlr_text_input_v3.c +++ b/types/wlr_text_input_v3.c @@ -42,7 +42,7 @@ void wlr_text_input_v3_send_leave(struct wlr_text_input_v3 *text_input) { } void wlr_text_input_v3_send_preedit_string(struct wlr_text_input_v3 *text_input, - const char *text, uint32_t cursor_begin, uint32_t cursor_end) { + const char *text, int32_t cursor_begin, int32_t cursor_end) { zwp_text_input_v3_send_preedit_string(text_input->resource, text, cursor_begin, cursor_end); } From 02a1ae169e66f53f2174add581c19d165d8ba882 Mon Sep 17 00:00:00 2001 From: Simon Zeni Date: Tue, 9 Nov 2021 09:42:22 -0500 Subject: [PATCH 015/188] render/allocator: make wlr_allocator part of the public API --- include/render/allocator/allocator.h | 47 +--------------------- include/wlr/render/allocator.h | 58 ++++++++++++++++++++++++++++ render/allocator/allocator.c | 1 + render/allocator/drm_dumb.c | 6 ++- render/allocator/gbm.c | 3 ++ render/allocator/shm.c | 3 ++ 6 files changed, 70 insertions(+), 48 deletions(-) create mode 100644 include/wlr/render/allocator.h diff --git a/include/render/allocator/allocator.h b/include/render/allocator/allocator.h index 5d8028cb9a..2abdd43d9c 100644 --- a/include/render/allocator/allocator.h +++ b/include/render/allocator/allocator.h @@ -1,52 +1,7 @@ #ifndef RENDER_ALLOCATOR_ALLOCATOR_H #define RENDER_ALLOCATOR_ALLOCATOR_H -#include -#include -#include - -struct wlr_allocator; -struct wlr_backend; -struct wlr_renderer; - -struct wlr_allocator_interface { - struct wlr_buffer *(*create_buffer)(struct wlr_allocator *alloc, - int width, int height, const struct wlr_drm_format *format); - void (*destroy)(struct wlr_allocator *alloc); -}; - -struct wlr_allocator { - const struct wlr_allocator_interface *impl; - - // Capabilities of the buffers created with this allocator - uint32_t buffer_caps; - - struct { - struct wl_signal destroy; - } events; -}; - -/** - * Creates the adequate wlr_allocator given a backend and a renderer - */ -struct wlr_allocator *wlr_allocator_autocreate(struct wlr_backend *backend, - struct wlr_renderer *renderer); -/** - * Destroy the allocator. - */ -void wlr_allocator_destroy(struct wlr_allocator *alloc); -/** - * Allocate a new buffer. - * - * When the caller is done with it, they must unreference it by calling - * wlr_buffer_drop. - */ -struct wlr_buffer *wlr_allocator_create_buffer(struct wlr_allocator *alloc, - int width, int height, const struct wlr_drm_format *format); - -// For wlr_allocator implementors -void wlr_allocator_init(struct wlr_allocator *alloc, - const struct wlr_allocator_interface *impl, uint32_t buffer_caps); +#include struct wlr_allocator *allocator_autocreate_with_drm_fd( struct wlr_backend *backend, struct wlr_renderer *renderer, int drm_fd); diff --git a/include/wlr/render/allocator.h b/include/wlr/render/allocator.h new file mode 100644 index 0000000000..3caeffa090 --- /dev/null +++ b/include/wlr/render/allocator.h @@ -0,0 +1,58 @@ +/* + * This an unstable interface of wlroots. No guarantees are made regarding the + * future consistency of this API. + */ +#ifndef WLR_USE_UNSTABLE +#error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" +#endif + +#ifndef WLR_ALLOCATOR_H +#define WLR_ALLOCATOR_H + +#include + +struct wlr_allocator; +struct wlr_backend; +struct wlr_drm_format; +struct wlr_renderer; + +struct wlr_allocator_interface { + struct wlr_buffer *(*create_buffer)(struct wlr_allocator *alloc, + int width, int height, const struct wlr_drm_format *format); + void (*destroy)(struct wlr_allocator *alloc); +}; + +void wlr_allocator_init(struct wlr_allocator *alloc, + const struct wlr_allocator_interface *impl, uint32_t buffer_caps); + +struct wlr_allocator { + const struct wlr_allocator_interface *impl; + + // Capabilities of the buffers created with this allocator + uint32_t buffer_caps; + + struct { + struct wl_signal destroy; + } events; +}; + +/** + * Creates the adequate wlr_allocator given a backend and a renderer + */ +struct wlr_allocator *wlr_allocator_autocreate(struct wlr_backend *backend, + struct wlr_renderer *renderer); +/** + * Destroy the allocator. + */ +void wlr_allocator_destroy(struct wlr_allocator *alloc); + +/** + * Allocate a new buffer. + * + * When the caller is done with it, they must unreference it by calling + * wlr_buffer_drop. + */ +struct wlr_buffer *wlr_allocator_create_buffer(struct wlr_allocator *alloc, + int width, int height, const struct wlr_drm_format *format); + +#endif diff --git a/render/allocator/allocator.c b/render/allocator/allocator.c index 0244e07a90..15d55a0dec 100644 --- a/render/allocator/allocator.c +++ b/render/allocator/allocator.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include diff --git a/render/allocator/drm_dumb.c b/render/allocator/drm_dumb.c index 170d9c331c..5b47462bc9 100644 --- a/render/allocator/drm_dumb.c +++ b/render/allocator/drm_dumb.c @@ -7,11 +7,13 @@ #include #include #include +#include +#include +#include +#include #include #include #include -#include -#include #include "render/allocator/drm_dumb.h" #include "render/pixel_format.h" diff --git a/render/allocator/gbm.c b/render/allocator/gbm.c index 86e4749bd3..b546e412ca 100644 --- a/render/allocator/gbm.c +++ b/render/allocator/gbm.c @@ -4,8 +4,11 @@ #include #include #include +#include +#include #include #include + #include "render/allocator/gbm.h" static const struct wlr_buffer_impl buffer_impl; diff --git a/render/allocator/shm.c b/render/allocator/shm.c index 77054dbe9c..044d5eb121 100644 --- a/render/allocator/shm.c +++ b/render/allocator/shm.c @@ -3,7 +3,10 @@ #include #include #include +#include +#include #include + #include "render/pixel_format.h" #include "render/allocator/shm.h" #include "util/shm.h" From 3a685b10b66b9da6e0baa3ad48409db14e76eced Mon Sep 17 00:00:00 2001 From: Cole Mickens Date: Tue, 9 Nov 2021 16:41:24 -0800 Subject: [PATCH 016/188] egl: use alts for EGL_EXT_device_enum, if missing --- render/egl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/render/egl.c b/render/egl.c index a1846c3e16..ec23ce8d8a 100644 --- a/render/egl.c +++ b/render/egl.c @@ -189,11 +189,11 @@ static struct wlr_egl *egl_create(void) { egl->exts.EXT_platform_device = check_egl_ext(client_exts_str, "EGL_EXT_platform_device"); - if (check_egl_ext(client_exts_str, "EGL_EXT_device_enumeration")) { + if (check_egl_ext(client_exts_str, "EGL_EXT_device_base") || check_egl_ext(client_exts_str, "EGL_EXT_device_enumeration")) { load_egl_proc(&egl->procs.eglQueryDevicesEXT, "eglQueryDevicesEXT"); } - if (check_egl_ext(client_exts_str, "EGL_EXT_device_query")) { + if (check_egl_ext(client_exts_str, "EGL_EXT_device_base") || check_egl_ext(client_exts_str, "EGL_EXT_device_query")) { egl->exts.EXT_device_query = true; load_egl_proc(&egl->procs.eglQueryDeviceStringEXT, "eglQueryDeviceStringEXT"); From 4a8e681a5fa82d59544fbdb8026f1606c41504e2 Mon Sep 17 00:00:00 2001 From: Raphael Robatsch Date: Thu, 11 Nov 2021 17:26:27 +0100 Subject: [PATCH 017/188] util/token: don't leak /dev/urandom fd to children Closes #3324. --- util/token.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/util/token.c b/util/token.c index cf6034a327..1b839aaa65 100644 --- a/util/token.c +++ b/util/token.c @@ -1,20 +1,31 @@ +#define _POSIX_C_SOURCE 200809L #include "util/token.h" #include "wlr/util/log.h" +#include #include #include #include #include +#include +#include +#include bool generate_token(char out[static TOKEN_STRLEN]) { static FILE *urandom = NULL; uint64_t data[2]; if (!urandom) { - if (!(urandom = fopen("/dev/urandom", "r"))) { + int fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC); + if (fd < 0) { wlr_log_errno(WLR_ERROR, "Failed to open random device"); return false; } + if (!(urandom = fdopen(fd, "r"))) { + wlr_log_errno(WLR_ERROR, "fdopen failed"); + close(fd); + return false; + } } if (fread(data, sizeof(data), 1, urandom) != 1) { wlr_log_errno(WLR_ERROR, "Failed to read from random device"); From 8274c85d21df4c790b46666f4383731c89352480 Mon Sep 17 00:00:00 2001 From: Roman Gilg Date: Sun, 14 Nov 2021 18:37:36 +0100 Subject: [PATCH 018/188] backend/headless: unlink input device on destroy Removing an input device requires unlinking it from the list of all headless input devices. For that implement a destroy function. --- backend/headless/input_device.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/backend/headless/input_device.c b/backend/headless/input_device.c index 76e69c5237..1d91f8d12b 100644 --- a/backend/headless/input_device.c +++ b/backend/headless/input_device.c @@ -11,7 +11,14 @@ #include "backend/headless.h" #include "util/signal.h" -static const struct wlr_input_device_impl input_device_impl = { 0 }; +static void input_device_destroy(struct wlr_input_device *wlr_dev) { + wl_list_remove(&wlr_dev->link); + free(wlr_dev); +} + +static const struct wlr_input_device_impl input_device_impl = { + .destroy = input_device_destroy, +}; bool wlr_input_device_is_headless(struct wlr_input_device *wlr_dev) { return wlr_dev->impl == &input_device_impl; From 9a4e1095cad154b7f8ce41cedbfb1e9a7e137d66 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 16 Nov 2021 22:55:54 +0100 Subject: [PATCH 019/188] linux-dmabuf-v1: properly validate flags We were send a protocol error if INTERLACED or BOTTOM_FIRST was set. This is incorrect for the zwp_linux_dmabuf_params.create code-path because this kills the client without allowing it to gracefully handle the error. We should only send a protocol error if the client provides a bit not listed in the protocol definition. --- types/wlr_linux_dmabuf_v1.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/types/wlr_linux_dmabuf_v1.c b/types/wlr_linux_dmabuf_v1.c index 7b08b350c8..b111a721c0 100644 --- a/types/wlr_linux_dmabuf_v1.c +++ b/types/wlr_linux_dmabuf_v1.c @@ -204,6 +204,17 @@ static void params_create_common(struct wl_resource *params_resource, goto err_out; } + /* reject unknown flags */ + uint32_t all_flags = ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_Y_INVERT | + ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_INTERLACED | + ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_BOTTOM_FIRST; + if (flags & ~all_flags) { + wl_resource_post_error(params_resource, + ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_FORMAT, + "Unknown dmabuf flags %"PRIu32, flags); + goto err_out; + } + attribs.width = width; attribs.height = height; attribs.format = format; @@ -265,14 +276,6 @@ static void params_create_common(struct wl_resource *params_resource, } } - /* reject unknown flags */ - if (attribs.flags & ~ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_Y_INVERT) { - wl_resource_post_error(params_resource, - ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_FORMAT, - "Unknown dmabuf flags %"PRIu32, attribs.flags); - goto err_out; - } - /* Check if dmabuf is usable */ if (!check_import_dmabuf(linux_dmabuf, &attribs)) { goto err_failed; From a04cfca4da42d1cc01047c1cd9e60ef504beae98 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 16 Nov 2021 22:51:06 +0100 Subject: [PATCH 020/188] Remove support for DMA-BUF flags They are never used in practice, which makes all of our flag handling effectively dead code. Also, APIs such as KMS don't provide a good way to deal with the flags. Let's just fail the DMA-BUF import when clients provide flags. --- backend/drm/renderer.c | 6 ------ backend/wayland/output.c | 12 +----------- backend/x11/output.c | 4 ---- include/render/gles2.h | 2 -- include/render/vulkan.h | 1 - include/wlr/render/dmabuf.h | 7 ------- include/wlr/render/gles2.h | 1 - render/gles2/renderer.c | 4 ---- render/gles2/shaders.c | 7 +------ render/gles2/texture.c | 3 --- render/vulkan/renderer.c | 14 -------------- render/vulkan/texture.c | 13 ------------- types/wlr_export_dmabuf_v1.c | 2 +- types/wlr_linux_dmabuf_v1.c | 6 +++++- types/wlr_screencopy_v1.c | 13 ++++--------- 15 files changed, 12 insertions(+), 83 deletions(-) diff --git a/backend/drm/renderer.c b/backend/drm/renderer.c index 40e068cd8f..6c26558f97 100644 --- a/backend/drm/renderer.c +++ b/backend/drm/renderer.c @@ -282,12 +282,6 @@ static struct wlr_drm_fb *drm_fb_create(struct wlr_drm_backend *drm, goto error_get_dmabuf; } - if (attribs.flags != 0) { - wlr_log(WLR_DEBUG, "Buffer with DMA-BUF flags 0x%"PRIX32" cannot be " - "scanned out", attribs.flags); - goto error_get_dmabuf; - } - if (formats && !wlr_drm_format_set_has(formats, attribs.format, attribs.modifier)) { // The format isn't supported by the plane. Try stripping the alpha diff --git a/backend/wayland/output.c b/backend/wayland/output.c index 8d8f036c84..3fa86a3cf2 100644 --- a/backend/wayland/output.c +++ b/backend/wayland/output.c @@ -166,18 +166,8 @@ static struct wl_buffer *import_dmabuf(struct wlr_wl_backend *wl, dmabuf->offset[i], dmabuf->stride[i], modifier_hi, modifier_lo); } - uint32_t flags = 0; - if (dmabuf->flags & WLR_DMABUF_ATTRIBUTES_FLAGS_Y_INVERT) { - flags |= ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_Y_INVERT; - } - if (dmabuf->flags & WLR_DMABUF_ATTRIBUTES_FLAGS_INTERLACED) { - flags |= ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_INTERLACED; - } - if (dmabuf->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, dmabuf->width, dmabuf->height, dmabuf->format, flags); + params, dmabuf->width, dmabuf->height, dmabuf->format, 0); // TODO: handle create() errors return wl_buffer; } diff --git a/backend/x11/output.c b/backend/x11/output.c index 3ad7b4a403..76172582f1 100644 --- a/backend/x11/output.c +++ b/backend/x11/output.c @@ -140,10 +140,6 @@ static xcb_pixmap_t import_dmabuf(struct wlr_x11_output *output, return XCB_PIXMAP_NONE; } - if (dmabuf->flags != 0) { - return XCB_PIXMAP_NONE; - } - // xcb closes the FDs after sending them, so we need to dup them here struct wlr_dmabuf_attributes dup_attrs = {0}; if (!wlr_dmabuf_attributes_copy(&dup_attrs, dmabuf)) { diff --git a/include/render/gles2.h b/include/render/gles2.h index fb2de1ab1e..245b2804e4 100644 --- a/include/render/gles2.h +++ b/include/render/gles2.h @@ -24,7 +24,6 @@ struct wlr_gles2_pixel_format { struct wlr_gles2_tex_shader { GLuint program; GLint proj; - GLint invert_y; GLint tex; GLint alpha; GLint pos_attrib; @@ -101,7 +100,6 @@ struct wlr_gles2_texture { EGLImageKHR image; - bool inverted_y; bool has_alpha; // Only affects target == GL_TEXTURE_2D diff --git a/include/render/vulkan.h b/include/render/vulkan.h index aa40198ce6..1cba5a1921 100644 --- a/include/render/vulkan.h +++ b/include/render/vulkan.h @@ -246,7 +246,6 @@ struct wlr_vk_texture { bool dmabuf_imported; bool owned; // if dmabuf_imported: whether we have ownership of the image bool transitioned; // if dma_imported: whether we transitioned it away from preinit - bool invert_y; // if dma_imported: whether we must flip y struct wl_list foreign_link; struct wl_list destroy_link; struct wl_list link; // wlr_gles2_renderer.textures diff --git a/include/wlr/render/dmabuf.h b/include/wlr/render/dmabuf.h index 75892d30f7..76aad62958 100644 --- a/include/wlr/render/dmabuf.h +++ b/include/wlr/render/dmabuf.h @@ -14,16 +14,9 @@ #define WLR_DMABUF_MAX_PLANES 4 -enum wlr_dmabuf_attributes_flags { - WLR_DMABUF_ATTRIBUTES_FLAGS_Y_INVERT = 1 << 0, - WLR_DMABUF_ATTRIBUTES_FLAGS_INTERLACED = 1 << 1, - WLR_DMABUF_ATTRIBUTES_FLAGS_BOTTOM_FIRST = 1 << 2, -}; - struct wlr_dmabuf_attributes { int32_t width, height; uint32_t format; - uint32_t flags; // enum wlr_dmabuf_attributes_flags uint64_t modifier; int n_planes; diff --git a/include/wlr/render/gles2.h b/include/wlr/render/gles2.h index dabe49ddee..e6844ce9d3 100644 --- a/include/wlr/render/gles2.h +++ b/include/wlr/render/gles2.h @@ -30,7 +30,6 @@ struct wlr_gles2_texture_attribs { GLenum target; /* either GL_TEXTURE_2D or GL_TEXTURE_EXTERNAL_OES */ GLuint tex; - bool inverted_y; bool has_alpha; }; diff --git a/render/gles2/renderer.c b/render/gles2/renderer.c index 01efaf1de9..527d85bfaa 100644 --- a/render/gles2/renderer.c +++ b/render/gles2/renderer.c @@ -309,7 +309,6 @@ static bool gles2_render_subtexture_with_matrix( glUseProgram(shader->program); glUniformMatrix3fv(shader->proj, 1, GL_FALSE, gl_matrix); - glUniform1i(shader->invert_y, texture->inverted_y); glUniform1i(shader->tex, 0); glUniform1f(shader->alpha, alpha); @@ -810,7 +809,6 @@ struct wlr_renderer *wlr_gles2_renderer_create(struct wlr_egl *egl) { goto error; } renderer->shaders.tex_rgba.proj = glGetUniformLocation(prog, "proj"); - renderer->shaders.tex_rgba.invert_y = glGetUniformLocation(prog, "invert_y"); renderer->shaders.tex_rgba.tex = glGetUniformLocation(prog, "tex"); renderer->shaders.tex_rgba.alpha = glGetUniformLocation(prog, "alpha"); renderer->shaders.tex_rgba.pos_attrib = glGetAttribLocation(prog, "pos"); @@ -822,7 +820,6 @@ struct wlr_renderer *wlr_gles2_renderer_create(struct wlr_egl *egl) { goto error; } renderer->shaders.tex_rgbx.proj = glGetUniformLocation(prog, "proj"); - renderer->shaders.tex_rgbx.invert_y = glGetUniformLocation(prog, "invert_y"); renderer->shaders.tex_rgbx.tex = glGetUniformLocation(prog, "tex"); renderer->shaders.tex_rgbx.alpha = glGetUniformLocation(prog, "alpha"); renderer->shaders.tex_rgbx.pos_attrib = glGetAttribLocation(prog, "pos"); @@ -835,7 +832,6 @@ struct wlr_renderer *wlr_gles2_renderer_create(struct wlr_egl *egl) { goto error; } renderer->shaders.tex_ext.proj = glGetUniformLocation(prog, "proj"); - renderer->shaders.tex_ext.invert_y = glGetUniformLocation(prog, "invert_y"); renderer->shaders.tex_ext.tex = glGetUniformLocation(prog, "tex"); renderer->shaders.tex_ext.alpha = glGetUniformLocation(prog, "alpha"); renderer->shaders.tex_ext.pos_attrib = glGetAttribLocation(prog, "pos"); diff --git a/render/gles2/shaders.c b/render/gles2/shaders.c index d854b270c4..7898059edc 100644 --- a/render/gles2/shaders.c +++ b/render/gles2/shaders.c @@ -28,18 +28,13 @@ const GLchar quad_fragment_src[] = // Textured quads const GLchar tex_vertex_src[] = "uniform mat3 proj;\n" -"uniform bool invert_y;\n" "attribute vec2 pos;\n" "attribute vec2 texcoord;\n" "varying vec2 v_texcoord;\n" "\n" "void main() {\n" " gl_Position = vec4(proj * vec3(pos, 1.0), 1.0);\n" -" if (invert_y) {\n" -" v_texcoord = vec2(texcoord.x, 1.0 - texcoord.y);\n" -" } else {\n" -" v_texcoord = texcoord;\n" -" }\n" +" v_texcoord = texcoord;\n" "}\n"; const GLchar tex_fragment_src_rgba[] = diff --git a/render/gles2/texture.c b/render/gles2/texture.c index 293b7a19b8..8d6f3fc248 100644 --- a/render/gles2/texture.c +++ b/render/gles2/texture.c @@ -250,8 +250,6 @@ static struct wlr_texture *gles2_texture_from_dmabuf( return NULL; } texture->drm_format = DRM_FORMAT_INVALID; // texture can't be written anyways - texture->inverted_y = - (attribs->flags & WLR_DMABUF_ATTRIBUTES_FLAGS_Y_INVERT) != 0; const struct wlr_pixel_format_info *drm_fmt = drm_get_pixel_format_info(attribs->format); @@ -363,6 +361,5 @@ void wlr_gles2_texture_get_attribs(struct wlr_texture *wlr_texture, memset(attribs, 0, sizeof(*attribs)); attribs->target = texture->target; attribs->tex = texture->tex; - attribs->inverted_y = texture->inverted_y; attribs->has_alpha = texture->has_alpha; } diff --git a/render/vulkan/renderer.c b/render/vulkan/renderer.c index 31ce0b76c1..a1d8d41efb 100644 --- a/render/vulkan/renderer.c +++ b/render/vulkan/renderer.c @@ -433,15 +433,6 @@ static struct wlr_vk_render_buffer *create_render_buffer( wlr_log(WLR_DEBUG, "vulkan create_render_buffer: %.4s, %dx%d", (const char*) &dmabuf.format, dmabuf.width, dmabuf.height); - // NOTE: we could at least support WLR_DMABUF_ATTRIBUTES_FLAGS_Y_INVERT - // if it is needed by anyone. Can be implemented using negative viewport - // height or flipping matrix. - if (dmabuf.flags != 0) { - wlr_log(WLR_ERROR, "dmabuf flags %x not supported/implemented on vulkan", - dmabuf.flags); - goto error_buffer; - } - buffer->image = vulkan_import_dmabuf(renderer, &dmabuf, buffer->memories, &buffer->mem_count, true); if (!buffer->image) { @@ -789,11 +780,6 @@ static bool vulkan_render_subtexture_with_matrix(struct wlr_renderer *wlr_render vert_pcr_data.uv_size[0] = box->width / wlr_texture->width; vert_pcr_data.uv_size[1] = box->height / wlr_texture->height; - if (texture->invert_y) { - vert_pcr_data.uv_off[1] += vert_pcr_data.uv_size[1]; - vert_pcr_data.uv_size[1] = -vert_pcr_data.uv_size[1]; - } - vkCmdPushConstants(cb, renderer->pipe_layout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(vert_pcr_data), &vert_pcr_data); vkCmdPushConstants(cb, renderer->pipe_layout, diff --git a/render/vulkan/texture.c b/render/vulkan/texture.c index f6fbec5a90..76c3701136 100644 --- a/render/vulkan/texture.c +++ b/render/vulkan/texture.c @@ -605,19 +605,6 @@ static struct wlr_texture *vulkan_texture_from_dmabuf(struct wlr_renderer *wlr_r goto error; } - uint32_t flags = attribs->flags; - if (flags & WLR_DMABUF_ATTRIBUTES_FLAGS_Y_INVERT) { - texture->invert_y = true; - flags &= ~WLR_DMABUF_ATTRIBUTES_FLAGS_Y_INVERT; - } - - if (flags != 0) { - wlr_log(WLR_ERROR, "dmabuf flags %x not supported/implemented on vulkan", - attribs->flags); - // NOTE: should probably make this a critical error in future - // return VK_NULL_HANDLE; - } - const struct wlr_pixel_format_info *format_info = drm_get_pixel_format_info(attribs->format); assert(format_info); diff --git a/types/wlr_export_dmabuf_v1.c b/types/wlr_export_dmabuf_v1.c index 954a3270bc..d26a9a450c 100644 --- a/types/wlr_export_dmabuf_v1.c +++ b/types/wlr_export_dmabuf_v1.c @@ -77,7 +77,7 @@ static void frame_output_handle_commit(struct wl_listener *listener, uint32_t mod_high = attribs.modifier >> 32; uint32_t mod_low = attribs.modifier & 0xFFFFFFFF; zwlr_export_dmabuf_frame_v1_send_frame(frame->resource, - attribs.width, attribs.height, 0, 0, attribs.flags, frame_flags, + attribs.width, attribs.height, 0, 0, 0, frame_flags, attribs.format, mod_high, mod_low, attribs.n_planes); for (int i = 0; i < attribs.n_planes; ++i) { diff --git a/types/wlr_linux_dmabuf_v1.c b/types/wlr_linux_dmabuf_v1.c index b111a721c0..9b4cdd4452 100644 --- a/types/wlr_linux_dmabuf_v1.c +++ b/types/wlr_linux_dmabuf_v1.c @@ -215,10 +215,14 @@ static void params_create_common(struct wl_resource *params_resource, goto err_out; } + if (flags != 0) { + wlr_log(WLR_ERROR, "dmabuf flags aren't supported"); + goto err_failed; + } + attribs.width = width; attribs.height = height; attribs.format = format; - attribs.flags = flags; if (width < 1 || height < 1) { wl_resource_post_error(params_resource, diff --git a/types/wlr_screencopy_v1.c b/types/wlr_screencopy_v1.c index 7c7d7e95ed..3f3d3bc5a2 100644 --- a/types/wlr_screencopy_v1.c +++ b/types/wlr_screencopy_v1.c @@ -254,8 +254,7 @@ static bool blit_dmabuf(struct wlr_renderer *renderer, return false; } -static bool frame_dma_copy(struct wlr_screencopy_frame_v1 *frame, - uint32_t *flags) { +static bool frame_dma_copy(struct wlr_screencopy_frame_v1 *frame) { struct wlr_dmabuf_v1_buffer *dma_buffer = frame->dma_buffer; struct wlr_output *output = frame->output; struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend); @@ -268,11 +267,7 @@ static bool frame_dma_copy(struct wlr_screencopy_frame_v1 *frame, return false; } - bool ok = blit_dmabuf(renderer, dma_buffer, output->front_buffer); - *flags = dma_buffer->attributes.flags & WLR_DMABUF_ATTRIBUTES_FLAGS_Y_INVERT ? - ZWLR_SCREENCOPY_FRAME_V1_FLAGS_Y_INVERT : 0; - - return ok; + return blit_dmabuf(renderer, dma_buffer, output->front_buffer); } static void frame_handle_output_commit(struct wl_listener *listener, @@ -282,7 +277,6 @@ static void frame_handle_output_commit(struct wl_listener *listener, struct wlr_output_event_commit *event = data; struct wlr_output *output = frame->output; struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend); - uint32_t flags; assert(renderer); if (!(event->committed & WLR_OUTPUT_STATE_BUFFER)) { @@ -304,8 +298,9 @@ static void frame_handle_output_commit(struct wl_listener *listener, wl_list_remove(&frame->output_commit.link); wl_list_init(&frame->output_commit.link); + uint32_t flags = 0; bool ok = frame->shm_buffer ? - frame_shm_copy(frame, &flags) : frame_dma_copy(frame, &flags); + frame_shm_copy(frame, &flags) : frame_dma_copy(frame); if (!ok) { zwlr_screencopy_frame_v1_send_failed(frame->resource); frame_destroy(frame); From b5d4bc3c62886f3e089165bd4d82bae781a6bda3 Mon Sep 17 00:00:00 2001 From: Demi Marie Obenour Date: Mon, 15 Nov 2021 09:44:12 -0500 Subject: [PATCH 021/188] Improve wlr_drm_format documentation A wlroots user can easily get confused and think that `cap` refers to wlroots buffer capabilities, not array capacity. --- include/wlr/render/drm_format_set.h | 23 ++++++++++++++++++++-- render/drm_format_set.c | 30 ++++++++++++++--------------- 2 files changed, 36 insertions(+), 17 deletions(-) diff --git a/include/wlr/render/drm_format_set.h b/include/wlr/render/drm_format_set.h index 15d4eea3f0..cb367a6322 100644 --- a/include/wlr/render/drm_format_set.h +++ b/include/wlr/render/drm_format_set.h @@ -5,19 +5,38 @@ #include #include +/** A single DRM format */ struct wlr_drm_format { + // The actual DRM format, from `drm_fourcc.h` uint32_t format; - size_t len, cap; + // The number of modifiers + size_t len; + // The capacity of the array; do not use. + size_t capacity; + // The actual modifiers uint64_t modifiers[]; }; +/** A set of DRM formats */ struct wlr_drm_format_set { - size_t len, cap; + // The number of formats + size_t len; + // The capacity of the array; private to wlroots + size_t capacity; + // A pointer to an array of `struct wlr_drm_format *` of length `len`. struct wlr_drm_format **formats; }; +/** + * Free all of the DRM formats in the set, making the set empty. Does not + * free the set itself. + */ void wlr_drm_format_set_finish(struct wlr_drm_format_set *set); +/** + * Return a pointer to a member of this `wlr_drm_format_set` of format + * `format`, or NULL if none exists. + */ const struct wlr_drm_format *wlr_drm_format_set_get( const struct wlr_drm_format_set *set, uint32_t format); diff --git a/render/drm_format_set.c b/render/drm_format_set.c index bd8f0cb756..3edc1925ba 100644 --- a/render/drm_format_set.c +++ b/render/drm_format_set.c @@ -15,7 +15,7 @@ void wlr_drm_format_set_finish(struct wlr_drm_format_set *set) { free(set->formats); set->len = 0; - set->cap = 0; + set->capacity = 0; set->formats = NULL; } @@ -74,8 +74,8 @@ bool wlr_drm_format_set_add(struct wlr_drm_format_set *set, uint32_t format, return false; } - if (set->len == set->cap) { - size_t new = set->cap ? set->cap * 2 : 4; + if (set->len == set->capacity) { + size_t new = set->capacity ? set->capacity * 2 : 4; struct wlr_drm_format **tmp = realloc(set->formats, sizeof(*fmt) + sizeof(fmt->modifiers[0]) * new); @@ -85,7 +85,7 @@ bool wlr_drm_format_set_add(struct wlr_drm_format_set *set, uint32_t format, return false; } - set->cap = new; + set->capacity = new; set->formats = tmp; } @@ -94,15 +94,15 @@ bool wlr_drm_format_set_add(struct wlr_drm_format_set *set, uint32_t format, } struct wlr_drm_format *wlr_drm_format_create(uint32_t format) { - size_t cap = 4; + size_t capacity = 4; struct wlr_drm_format *fmt = - calloc(1, sizeof(*fmt) + sizeof(fmt->modifiers[0]) * cap); + calloc(1, sizeof(*fmt) + sizeof(fmt->modifiers[0]) * capacity); if (!fmt) { wlr_log_errno(WLR_ERROR, "Allocation failed"); return NULL; } fmt->format = format; - fmt->cap = cap; + fmt->capacity = capacity; return fmt; } @@ -119,16 +119,16 @@ bool wlr_drm_format_add(struct wlr_drm_format **fmt_ptr, uint64_t modifier) { } } - if (fmt->len == fmt->cap) { - size_t cap = fmt->cap ? fmt->cap * 2 : 4; + if (fmt->len == fmt->capacity) { + size_t capacity = fmt->capacity ? fmt->capacity * 2 : 4; - fmt = realloc(fmt, sizeof(*fmt) + sizeof(fmt->modifiers[0]) * cap); + fmt = realloc(fmt, sizeof(*fmt) + sizeof(fmt->modifiers[0]) * capacity); if (!fmt) { wlr_log_errno(WLR_ERROR, "Allocation failed"); return false; } - fmt->cap = cap; + fmt->capacity = capacity; *fmt_ptr = fmt; } @@ -137,9 +137,9 @@ bool wlr_drm_format_add(struct wlr_drm_format **fmt_ptr, uint64_t modifier) { } struct wlr_drm_format *wlr_drm_format_dup(const struct wlr_drm_format *format) { - assert(format->len <= format->cap); + assert(format->len <= format->capacity); size_t format_size = sizeof(struct wlr_drm_format) + - format->cap * sizeof(format->modifiers[0]); + format->capacity * sizeof(format->modifiers[0]); struct wlr_drm_format *duped_format = malloc(format_size); if (duped_format == NULL) { return NULL; @@ -171,12 +171,12 @@ struct wlr_drm_format *wlr_drm_format_intersect( return NULL; } format->format = a->format; - format->cap = format_cap; + format->capacity = format_cap; for (size_t i = 0; i < a->len; i++) { for (size_t j = 0; j < b->len; j++) { if (a->modifiers[i] == b->modifiers[j]) { - assert(format->len < format->cap); + assert(format->len < format->capacity); format->modifiers[format->len] = a->modifiers[i]; format->len++; break; From 142d10e591c0f349843f718d87b44c8ba2b33476 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Fri, 5 Nov 2021 12:26:38 +0100 Subject: [PATCH 022/188] output: add wlr_output_init_render Co-authored-by: Simon Zeni --- include/wlr/types/wlr_output.h | 14 +++++++++++ types/output/cursor.c | 25 ++++++------------- types/output/render.c | 45 +++++++++++++++++++++++++--------- 3 files changed, 55 insertions(+), 29 deletions(-) diff --git a/include/wlr/types/wlr_output.h b/include/wlr/types/wlr_output.h index 17f48311e3..9d3dc3cbf1 100644 --- a/include/wlr/types/wlr_output.h +++ b/include/wlr/types/wlr_output.h @@ -182,6 +182,8 @@ struct wlr_output { struct wlr_buffer *cursor_front_buffer; int software_cursor_locks; // number of locks forcing software cursors + struct wlr_allocator *allocator; + struct wlr_renderer *renderer; struct wlr_swapchain *swapchain; struct wlr_buffer *back_buffer, *front_buffer; @@ -256,6 +258,18 @@ struct wlr_surface; void wlr_output_enable(struct wlr_output *output, bool enable); void wlr_output_create_global(struct wlr_output *output); void wlr_output_destroy_global(struct wlr_output *output); +/** + * Initialize the output's rendering subsystem with the provided allocator and + * renderer. Can only be called once. + * + * Call this function prior to any call to wlr_output_attach_render, + * wlr_output_commit or wlr_output_cursor_create. + * + * The buffer capabilities of the provided must match the capabilities of the + * output's backend. Returns false otherwise. + */ +bool wlr_output_init_render(struct wlr_output *output, + struct wlr_allocator *allocator, struct wlr_renderer *renderer); /** * Returns the preferred mode for this output. If the output doesn't support * modes, returns NULL. diff --git a/types/output/cursor.c b/types/output/cursor.c index 0a6cbb757b..f1e6aee5e6 100644 --- a/types/output/cursor.c +++ b/types/output/cursor.c @@ -6,7 +6,6 @@ #include #include #include -#include "backend/backend.h" #include "render/allocator/allocator.h" #include "render/swapchain.h" #include "types/wlr_output.h" @@ -38,7 +37,7 @@ void wlr_output_lock_software_cursors(struct wlr_output *output, bool lock) { } static void output_scissor(struct wlr_output *output, pixman_box32_t *rect) { - struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend); + struct wlr_renderer *renderer = output->renderer; assert(renderer); struct wlr_box box = { @@ -71,8 +70,7 @@ static void output_cursor_get_box(struct wlr_output_cursor *cursor, static void output_cursor_render(struct wlr_output_cursor *cursor, pixman_region32_t *damage) { - struct wlr_renderer *renderer = - wlr_backend_get_renderer(cursor->output->backend); + struct wlr_renderer *renderer = cursor->output->renderer; assert(renderer); struct wlr_texture *texture = cursor->texture; @@ -195,7 +193,7 @@ static void output_cursor_update_visible(struct wlr_output_cursor *cursor) { } static struct wlr_drm_format *output_pick_cursor_format(struct wlr_output *output) { - struct wlr_allocator *allocator = backend_get_allocator(output->backend); + struct wlr_allocator *allocator = output->allocator; assert(allocator != NULL); const struct wlr_drm_format_set *display_formats = NULL; @@ -226,17 +224,9 @@ static struct wlr_buffer *render_cursor_buffer(struct wlr_output_cursor *cursor) return NULL; } - struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend); - if (renderer == NULL) { - wlr_log(WLR_ERROR, "Failed to get backend renderer"); - return NULL; - } - - struct wlr_allocator *allocator = backend_get_allocator(output->backend); - if (allocator == NULL) { - wlr_log(WLR_ERROR, "Failed to get backend allocator"); - return NULL; - } + struct wlr_allocator *allocator = output->allocator; + struct wlr_renderer *renderer = output->renderer; + assert(allocator != NULL && renderer != NULL); int width = texture->width; int height = texture->height; @@ -370,8 +360,7 @@ static bool output_cursor_attempt_hardware(struct wlr_output_cursor *cursor) { bool wlr_output_cursor_set_image(struct wlr_output_cursor *cursor, const uint8_t *pixels, int32_t stride, uint32_t width, uint32_t height, int32_t hotspot_x, int32_t hotspot_y) { - struct wlr_renderer *renderer = - wlr_backend_get_renderer(cursor->output->backend); + struct wlr_renderer *renderer = cursor->output->renderer; if (!renderer) { // if the backend has no renderer, we can't draw a cursor, but this is // actually okay, for ex. with the noop backend diff --git a/types/output/render.c b/types/output/render.c index b37b8ea12b..2f6fe26008 100644 --- a/types/output/render.c +++ b/types/output/render.c @@ -11,6 +11,30 @@ #include "render/wlr_renderer.h" #include "types/wlr_output.h" +bool wlr_output_init_render(struct wlr_output *output, + struct wlr_allocator *allocator, struct wlr_renderer *renderer) { + assert(output->allocator == NULL && allocator != NULL); + assert(output->renderer == NULL && renderer != NULL); + + uint32_t backend_caps = backend_get_buffer_caps(output->backend); + uint32_t renderer_caps = renderer_get_render_buffer_caps(renderer); + + if (!(backend_caps & allocator->buffer_caps)) { + wlr_log(WLR_ERROR, "output backend and allocator buffer capabilities " + "don't match"); + return false; + } else if (!(backend_caps & renderer_caps)) { + wlr_log(WLR_ERROR, "output backend and renderer buffer capabilities " + "don't match"); + return false; + } + + output->allocator = allocator; + output->renderer = renderer; + + return true; +} + /** * Ensure the output has a suitable swapchain. The swapchain is re-created if * necessary. @@ -29,11 +53,8 @@ static bool output_create_swapchain(struct wlr_output *output, return true; } - struct wlr_allocator *allocator = backend_get_allocator(output->backend); - if (allocator == NULL) { - wlr_log(WLR_ERROR, "Failed to get backend allocator"); - return false; - } + struct wlr_allocator *allocator = output->allocator; + assert(allocator != NULL); const struct wlr_drm_format_set *display_formats = NULL; if (output->impl->get_primary_formats) { @@ -80,7 +101,7 @@ static bool output_attach_back_buffer(struct wlr_output *output, return false; } - struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend); + struct wlr_renderer *renderer = output->renderer; assert(renderer != NULL); struct wlr_buffer *buffer = @@ -103,7 +124,7 @@ void output_clear_back_buffer(struct wlr_output *output) { return; } - struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend); + struct wlr_renderer *renderer = output->renderer; assert(renderer != NULL); renderer_bind_buffer(renderer, NULL); @@ -130,7 +151,7 @@ static bool output_attach_empty_buffer(struct wlr_output *output) { int width, height; output_pending_resolution(output, &width, &height); - struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend); + struct wlr_renderer *renderer = output->renderer; wlr_renderer_begin(renderer, width, height); wlr_renderer_clear(renderer, (float[]){0, 0, 0, 0}); wlr_renderer_end(renderer); @@ -211,8 +232,8 @@ void wlr_output_lock_attach_render(struct wlr_output *output, bool lock) { struct wlr_drm_format *output_pick_format(struct wlr_output *output, const struct wlr_drm_format_set *display_formats) { - struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend); - struct wlr_allocator *allocator = backend_get_allocator(output->backend); + struct wlr_renderer *renderer = output->renderer; + struct wlr_allocator *allocator = output->allocator; assert(renderer != NULL && allocator != NULL); const struct wlr_drm_format_set *render_formats = @@ -264,7 +285,9 @@ struct wlr_drm_format *output_pick_format(struct wlr_output *output, } uint32_t wlr_output_preferred_read_format(struct wlr_output *output) { - struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend); + struct wlr_renderer *renderer = output->renderer; + assert(renderer != NULL); + if (!renderer->impl->preferred_read_format || !renderer->impl->read_pixels) { return DRM_FORMAT_INVALID; } From 6d6e70b9e0e40b6d38ba9276127b869bae893d43 Mon Sep 17 00:00:00 2001 From: Simon Zeni Date: Fri, 24 Sep 2021 09:34:51 -0400 Subject: [PATCH 023/188] examples: init wlr_output with allocator and renderer --- examples/fullscreen-shell.c | 8 +++++++- examples/multi-pointer.c | 12 +++++++++++- examples/output-layout.c | 9 ++++++++- examples/pointer.c | 12 +++++++++++- examples/quads.c | 9 ++++++++- examples/rotation.c | 9 ++++++++- examples/scene-graph.c | 14 +++++++++++--- examples/simple.c | 13 +++++++++++-- examples/tablet.c | 10 +++++++++- examples/touch.c | 12 +++++++++--- 10 files changed, 93 insertions(+), 15 deletions(-) diff --git a/examples/fullscreen-shell.c b/examples/fullscreen-shell.c index 07320045cd..5a9d4c926d 100644 --- a/examples/fullscreen-shell.c +++ b/examples/fullscreen-shell.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -25,6 +26,7 @@ struct fullscreen_server { struct wl_display *wl_display; struct wlr_backend *backend; struct wlr_renderer *renderer; + struct wlr_allocator *allocator; struct wlr_fullscreen_shell_v1 *fullscreen_shell; struct wl_listener present_surface; @@ -146,6 +148,8 @@ static void server_handle_new_output(struct wl_listener *listener, void *data) { wl_container_of(listener, server, new_output); struct wlr_output *wlr_output = data; + wlr_output_init_render(wlr_output, server->allocator, server->renderer); + struct fullscreen_output *output = calloc(1, sizeof(struct fullscreen_output)); output->wlr_output = wlr_output; @@ -203,8 +207,10 @@ int main(int argc, char *argv[]) { struct fullscreen_server server = {0}; server.wl_display = wl_display_create(); server.backend = wlr_backend_autocreate(server.wl_display); - server.renderer = wlr_backend_get_renderer(server.backend); + server.renderer = wlr_renderer_autocreate(server.backend); wlr_renderer_init_wl_display(server.renderer, server.wl_display); + server.allocator = wlr_allocator_autocreate(server.backend, + server.renderer); wlr_compositor_create(server.wl_display, server.renderer); diff --git a/examples/multi-pointer.c b/examples/multi-pointer.c index 4955628020..5095cb729d 100644 --- a/examples/multi-pointer.c +++ b/examples/multi-pointer.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -22,6 +23,8 @@ struct sample_state { struct wl_display *display; struct wlr_xcursor *xcursor; + struct wlr_renderer *renderer; + struct wlr_allocator *allocator; float default_color[4]; float clear_color[4]; struct wlr_output_layout *layout; @@ -90,7 +93,7 @@ static void output_frame_notify(struct wl_listener *listener, void *data) { struct sample_output *output = wl_container_of(listener, output, frame); struct sample_state *sample = output->sample; struct wlr_output *wlr_output = output->output; - struct wlr_renderer *renderer = wlr_backend_get_renderer(wlr_output->backend); + struct wlr_renderer *renderer = sample->renderer; wlr_output_attach_render(wlr_output, NULL); @@ -144,6 +147,9 @@ static void output_remove_notify(struct wl_listener *listener, void *data) { static void new_output_notify(struct wl_listener *listener, void *data) { struct wlr_output *output = data; struct sample_state *sample = wl_container_of(listener, sample, new_output); + + wlr_output_init_render(output, sample->allocator, sample->renderer); + struct sample_output *sample_output = calloc(1, sizeof(struct sample_output)); sample_output->output = output; sample_output->sample = sample; @@ -269,6 +275,10 @@ int main(int argc, char *argv[]) { if (!wlr) { exit(1); } + + state.renderer = wlr_renderer_autocreate(wlr); + state.allocator = wlr_allocator_autocreate(wlr, state.renderer); + wl_list_init(&state.cursors); wl_list_init(&state.pointers); wl_list_init(&state.outputs); diff --git a/examples/output-layout.c b/examples/output-layout.c index 356d5d56db..bc5cb7e900 100644 --- a/examples/output-layout.c +++ b/examples/output-layout.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -27,6 +28,7 @@ struct sample_state { struct wl_listener new_output; struct wl_listener new_input; struct wlr_renderer *renderer; + struct wlr_allocator *allocator; struct wlr_texture *cat_texture; struct wlr_output_layout *layout; float x_offs, y_offs; @@ -158,6 +160,9 @@ static void output_remove_notify(struct wl_listener *listener, void *data) { static void new_output_notify(struct wl_listener *listener, void *data) { struct wlr_output *output = data; struct sample_state *sample = wl_container_of(listener, sample, new_output); + + wlr_output_init_render(output, sample->allocator, sample->renderer); + struct sample_output *sample_output = calloc(1, sizeof(struct sample_output)); wlr_output_layout_add_auto(sample->layout, output); sample_output->output = output; @@ -273,11 +278,13 @@ int main(int argc, char *argv[]) { 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.renderer = wlr_renderer_autocreate(wlr); state.cat_texture = wlr_texture_from_pixels(state.renderer, DRM_FORMAT_ABGR8888, cat_tex.width * 4, cat_tex.width, cat_tex.height, cat_tex.pixel_data); + state.allocator = wlr_allocator_autocreate(wlr, state.renderer); + if (!wlr_backend_start(wlr)) { wlr_log(WLR_ERROR, "Failed to start backend"); wlr_backend_destroy(wlr); diff --git a/examples/pointer.c b/examples/pointer.c index 1abdf1cfe1..bf3701a455 100644 --- a/examples/pointer.c +++ b/examples/pointer.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -24,6 +25,8 @@ struct sample_state { struct wl_display *display; struct compositor_state *compositor; + struct wlr_renderer *renderer; + struct wlr_allocator *allocator; struct wlr_xcursor_manager *xcursor_manager; struct wlr_cursor *cursor; double cur_x, cur_y; @@ -95,7 +98,7 @@ 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); + struct wlr_renderer *renderer = state->renderer; assert(renderer); wlr_output_attach_render(wlr_output, NULL); @@ -250,6 +253,9 @@ static void output_remove_notify(struct wl_listener *listener, void *data) { static void new_output_notify(struct wl_listener *listener, void *data) { struct wlr_output *output = data; struct sample_state *sample = wl_container_of(listener, sample, new_output); + + wlr_output_init_render(output, sample->allocator, sample->renderer); + struct sample_output *sample_output = calloc(1, sizeof(struct sample_output)); sample_output->output = output; sample_output->state = sample; @@ -331,6 +337,10 @@ int main(int argc, char *argv[]) { if (!wlr) { exit(1); } + + state.renderer = wlr_renderer_autocreate(wlr); + state.allocator = wlr_allocator_autocreate(wlr, state.renderer); + state.cursor = wlr_cursor_create(); state.layout = wlr_output_layout_create(); wlr_cursor_attach_output_layout(state.cursor, state.layout); diff --git a/examples/quads.c b/examples/quads.c index 6696ef7ce9..d74ef25e68 100644 --- a/examples/quads.c +++ b/examples/quads.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -24,6 +25,7 @@ struct sample_state { struct wl_listener new_input; struct timespec last_frame; struct wlr_renderer *renderer; + struct wlr_allocator *allocator; struct wl_list outputs; }; @@ -103,6 +105,9 @@ static void output_remove_notify(struct wl_listener *listener, void *data) { static void new_output_notify(struct wl_listener *listener, void *data) { struct wlr_output *output = data; struct sample_state *sample = wl_container_of(listener, sample, new_output); + + wlr_output_init_render(output, sample->allocator, sample->renderer); + struct sample_output *sample_output = calloc(1, sizeof(struct sample_output)); struct wlr_output_mode *mode = wlr_output_preferred_mode(output); @@ -195,13 +200,15 @@ int main(int argc, char *argv[]) { state.new_input.notify = new_input_notify; clock_gettime(CLOCK_MONOTONIC, &state.last_frame); - state.renderer = wlr_backend_get_renderer(wlr); + state.renderer = wlr_renderer_autocreate(wlr); if (!state.renderer) { wlr_log(WLR_ERROR, "Could not start compositor, OOM"); wlr_backend_destroy(wlr); exit(EXIT_FAILURE); } + state.allocator = wlr_allocator_autocreate(wlr, state.renderer); + if (!wlr_backend_start(wlr)) { wlr_log(WLR_ERROR, "Failed to start backend"); wlr_backend_destroy(wlr); diff --git a/examples/rotation.c b/examples/rotation.c index ff34630ea0..cc1cfbb5b6 100644 --- a/examples/rotation.c +++ b/examples/rotation.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -25,6 +26,7 @@ struct sample_state { struct wl_listener new_input; struct timespec last_frame; struct wlr_renderer *renderer; + struct wlr_allocator *allocator; struct wlr_texture *cat_texture; struct wl_list outputs; enum wl_output_transform transform; @@ -105,6 +107,9 @@ static void output_remove_notify(struct wl_listener *listener, void *data) { static void new_output_notify(struct wl_listener *listener, void *data) { struct wlr_output *output = data; struct sample_state *sample = wl_container_of(listener, sample, new_output); + + wlr_output_init_render(output, sample->allocator, sample->renderer); + struct sample_output *sample_output = calloc(1, sizeof(struct sample_output)); sample_output->x_offs = sample_output->y_offs = 0; sample_output->x_vel = sample_output->y_vel = 128; @@ -245,7 +250,7 @@ int main(int argc, char *argv[]) { state.new_input.notify = new_input_notify; clock_gettime(CLOCK_MONOTONIC, &state.last_frame); - state.renderer = wlr_backend_get_renderer(wlr); + state.renderer = wlr_renderer_autocreate(wlr); if (!state.renderer) { wlr_log(WLR_ERROR, "Could not start compositor, OOM"); wlr_backend_destroy(wlr); @@ -259,6 +264,8 @@ int main(int argc, char *argv[]) { exit(EXIT_FAILURE); } + state.allocator = wlr_allocator_autocreate(wlr, state.renderer); + if (!wlr_backend_start(wlr)) { wlr_log(WLR_ERROR, "Failed to start backend"); wlr_backend_destroy(wlr); diff --git a/examples/scene-graph.c b/examples/scene-graph.c index d5c23a16b8..bd2003f53f 100644 --- a/examples/scene-graph.c +++ b/examples/scene-graph.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -25,6 +26,8 @@ static const int border_width = 3; struct server { struct wl_display *display; struct wlr_backend *backend; + struct wlr_renderer *renderer; + struct wlr_allocator *allocator; struct wlr_scene *scene; struct wl_list outputs; @@ -73,6 +76,8 @@ static void server_handle_new_output(struct wl_listener *listener, void *data) { struct server *server = wl_container_of(listener, server, new_output); struct wlr_output *wlr_output = data; + wlr_output_init_render(wlr_output, server->allocator, server->renderer); + struct output *output = calloc(1, sizeof(struct output)); output->wlr = wlr_output; @@ -161,11 +166,14 @@ int main(int argc, char *argv[]) { server.backend = wlr_backend_autocreate(server.display); server.scene = wlr_scene_create(); - struct wlr_renderer *renderer = wlr_backend_get_renderer(server.backend); - wlr_renderer_init_wl_display(renderer, server.display); + server.renderer = wlr_renderer_autocreate(server.backend); + wlr_renderer_init_wl_display(server.renderer, server.display); + + server.allocator = wlr_allocator_autocreate(server.backend, + server.renderer); struct wlr_compositor *compositor = - wlr_compositor_create(server.display, renderer); + wlr_compositor_create(server.display, server.renderer); wlr_xdg_shell_create(server.display); diff --git a/examples/simple.c b/examples/simple.c index 9135d163bb..94f3abe0e5 100644 --- a/examples/simple.c +++ b/examples/simple.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -18,6 +19,8 @@ struct sample_state { struct wl_display *display; struct wl_listener new_output; struct wl_listener new_input; + struct wlr_renderer *renderer; + struct wlr_allocator *allocator; struct timespec last_frame; float color[4]; int dec; @@ -61,8 +64,7 @@ static void output_frame_notify(struct wl_listener *listener, void *data) { wlr_output_attach_render(wlr_output, NULL); - struct wlr_renderer *renderer = - wlr_backend_get_renderer(wlr_output->backend); + struct wlr_renderer *renderer = sample->renderer; wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height); wlr_renderer_clear(renderer, sample->color); wlr_renderer_end(renderer); @@ -84,6 +86,9 @@ static void new_output_notify(struct wl_listener *listener, void *data) { struct wlr_output *output = data; struct sample_state *sample = wl_container_of(listener, sample, new_output); + + wlr_output_init_render(output, sample->allocator, sample->renderer); + struct sample_output *sample_output = calloc(1, sizeof(struct sample_output)); sample_output->output = output; @@ -171,6 +176,10 @@ int main(void) { if (!backend) { exit(1); } + + state.renderer = wlr_renderer_autocreate(backend); + state.allocator = wlr_allocator_autocreate(backend, state.renderer); + wl_signal_add(&backend->events.new_output, &state.new_output); state.new_output.notify = new_output_notify; wl_signal_add(&backend->events.new_input, &state.new_input); diff --git a/examples/tablet.c b/examples/tablet.c index 5d0e8dcc33..234bfb991e 100644 --- a/examples/tablet.c +++ b/examples/tablet.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -22,6 +23,7 @@ struct sample_state { struct wl_display *display; struct wlr_renderer *renderer; + struct wlr_allocator *allocator; bool proximity, tap, button; double distance; double pressure; @@ -237,6 +239,9 @@ static void output_remove_notify(struct wl_listener *listener, void *data) { static void new_output_notify(struct wl_listener *listener, void *data) { struct wlr_output *output = data; struct sample_state *sample = wl_container_of(listener, sample, new_output); + + wlr_output_init_render(output, sample->allocator, sample->renderer); + struct sample_output *sample_output = calloc(1, sizeof(struct sample_output)); sample_output->output = output; sample_output->sample = sample; @@ -361,11 +366,14 @@ int main(int argc, char *argv[]) { state.new_input.notify = new_input_notify; clock_gettime(CLOCK_MONOTONIC, &state.last_frame); - state.renderer = wlr_backend_get_renderer(wlr); + state.renderer = wlr_renderer_autocreate(wlr); if (!state.renderer) { wlr_log(WLR_ERROR, "Could not start compositor, OOM"); exit(EXIT_FAILURE); } + + state.allocator = wlr_allocator_autocreate(wlr, state.renderer); + if (!wlr_backend_start(wlr)) { wlr_log(WLR_ERROR, "Failed to start backend"); wlr_backend_destroy(wlr); diff --git a/examples/touch.c b/examples/touch.c index 0c9cd2b40a..ae2dcf8fc3 100644 --- a/examples/touch.c +++ b/examples/touch.c @@ -10,8 +10,9 @@ #include #include #include -#include +#include #include +#include #include #include #include @@ -23,6 +24,7 @@ struct sample_state { struct wl_display *display; struct wlr_renderer *renderer; + struct wlr_allocator *allocator; struct wlr_texture *cat_texture; struct wl_list touch_points; struct timespec last_frame; @@ -148,6 +150,9 @@ static void output_remove_notify(struct wl_listener *listener, void *data) { static void new_output_notify(struct wl_listener *listener, void *data) { struct wlr_output *output = data; struct sample_state *sample = wl_container_of(listener, sample, new_output); + + wlr_output_init_render(output, sample->allocator, sample->renderer); + struct sample_output *sample_output = calloc(1, sizeof(struct sample_output)); sample_output->output = output; sample_output->sample = sample; @@ -254,8 +259,7 @@ int main(int argc, char *argv[]) { state.new_input.notify = new_input_notify; clock_gettime(CLOCK_MONOTONIC, &state.last_frame); - - state.renderer = wlr_backend_get_renderer(wlr); + state.renderer = wlr_renderer_autocreate(wlr); if (!state.renderer) { wlr_log(WLR_ERROR, "Could not start compositor, OOM"); exit(EXIT_FAILURE); @@ -268,6 +272,8 @@ int main(int argc, char *argv[]) { exit(EXIT_FAILURE); } + state.allocator = wlr_allocator_autocreate(wlr, state.renderer); + if (!wlr_backend_start(wlr)) { wlr_log(WLR_ERROR, "Failed to start backend"); wlr_backend_destroy(wlr); From a6538ced35872a86d37d6ca32b1d9ddb5fe2c4b7 Mon Sep 17 00:00:00 2001 From: Simon Zeni Date: Mon, 15 Nov 2021 13:42:06 -0500 Subject: [PATCH 024/188] tinywl: autocreate allocator and init output --- tinywl/tinywl.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/tinywl/tinywl.c b/tinywl/tinywl.c index b11549beac..82f0977a27 100644 --- a/tinywl/tinywl.c +++ b/tinywl/tinywl.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -34,6 +35,7 @@ struct tinywl_server { struct wl_display *wl_display; struct wlr_backend *backend; struct wlr_renderer *renderer; + struct wlr_allocator *allocator; struct wlr_xdg_shell *xdg_shell; struct wl_listener new_xdg_surface; @@ -676,6 +678,10 @@ static void server_new_output(struct wl_listener *listener, void *data) { } } + /* Configures the output created by the backend to use our allocator + * and our renderer */ + wlr_output_init_render(wlr_output, server->allocator, server->renderer); + /* Allocates and configures our state for this output */ struct tinywl_output *output = calloc(1, sizeof(struct tinywl_output)); @@ -841,12 +847,20 @@ int main(int argc, char *argv[]) { * if an X11 server is running. */ server.backend = wlr_backend_autocreate(server.wl_display); - /* If we don't provide a renderer, autocreate makes a GLES2 renderer for us. + /* Autocreates a renderer, either Pixman, GLES2 or Vulkan for us. The user + * can also specify a renderer using the WLR_RENDERER env var. * The renderer is responsible for defining the various pixel formats it * supports for shared memory, this configures that for clients. */ - server.renderer = wlr_backend_get_renderer(server.backend); + server.renderer = wlr_renderer_autocreate(server.backend); wlr_renderer_init_wl_display(server.renderer, server.wl_display); + /* Autocreates an allocator for us. + * The allocator is the bridge between the renderer and the backend. It + * handles the buffer creation, allowing wlroots to render onto the + * screen */ + server.allocator = wlr_allocator_autocreate(server.backend, + server.renderer); + /* This creates some hands-off wlroots interfaces. The compositor is * necessary for clients to allocate surfaces and the data device manager * handles the clipboard. Each of these wlroots interfaces has room for you From 0c76aef2022525e9346b59596b5fa6e08b462998 Mon Sep 17 00:00:00 2001 From: Simon Zeni Date: Mon, 15 Nov 2021 12:10:57 -0500 Subject: [PATCH 025/188] backend: remove backend ensure renderer and allocator check --- backend/backend.c | 23 ++++------------------- 1 file changed, 4 insertions(+), 19 deletions(-) diff --git a/backend/backend.c b/backend/backend.c index f8fa45c4a5..16ad309f4b 100644 --- a/backend/backend.c +++ b/backend/backend.c @@ -208,21 +208,6 @@ static size_t parse_outputs_env(const char *name) { return outputs; } -static struct wlr_backend *ensure_backend_renderer_and_allocator( - struct wlr_backend *backend) { - struct wlr_renderer *renderer = wlr_backend_get_renderer(backend); - if (renderer == NULL) { - wlr_backend_destroy(backend); - return NULL; - } - struct wlr_allocator *allocator = backend_get_allocator(backend); - if (allocator == NULL) { - wlr_backend_destroy(backend); - return NULL; - } - return backend; -} - static struct wlr_backend *attempt_wl_backend(struct wl_display *display) { struct wlr_backend *backend = wlr_wl_backend_create(display, NULL); if (backend == NULL) { @@ -234,7 +219,7 @@ static struct wlr_backend *attempt_wl_backend(struct wl_display *display) { wlr_wl_output_create(backend); } - return ensure_backend_renderer_and_allocator(backend); + return backend; } #if WLR_HAS_X11_BACKEND @@ -250,7 +235,7 @@ static struct wlr_backend *attempt_x11_backend(struct wl_display *display, wlr_x11_output_create(backend); } - return ensure_backend_renderer_and_allocator(backend); + return backend; } #endif @@ -266,7 +251,7 @@ static struct wlr_backend *attempt_headless_backend( wlr_headless_add_output(backend, 1280, 720); } - return ensure_backend_renderer_and_allocator(backend); + return backend; } static struct wlr_backend *attempt_noop_backend(struct wl_display *display) { @@ -320,7 +305,7 @@ static struct wlr_backend *attempt_drm_backend(struct wl_display *display, return NULL; } - return ensure_backend_renderer_and_allocator(primary_drm); + return backend; } #endif From 6dc6af1534534394e62761bda7c7dbf01aaa3144 Mon Sep 17 00:00:00 2001 From: Simon Zeni Date: Tue, 16 Nov 2021 09:26:44 -0500 Subject: [PATCH 026/188] backend: remove backend_get_allocator --- backend/backend.c | 18 ------------------ include/backend/backend.h | 6 ------ include/wlr/backend.h | 1 - 3 files changed, 25 deletions(-) diff --git a/backend/backend.c b/backend/backend.c index 16ad309f4b..634e53b699 100644 --- a/backend/backend.c +++ b/backend/backend.c @@ -45,7 +45,6 @@ void wlr_backend_init(struct wlr_backend *backend, void wlr_backend_finish(struct wlr_backend *backend) { wlr_signal_emit_safe(&backend->events.destroy, backend); - wlr_allocator_destroy(backend->allocator); if (backend->has_own_renderer) { wlr_renderer_destroy(backend->renderer); } @@ -175,23 +174,6 @@ uint32_t backend_get_buffer_caps(struct wlr_backend *backend) { return backend->impl->get_buffer_caps(backend); } -struct wlr_allocator *backend_get_allocator(struct wlr_backend *backend) { - if (backend->allocator != NULL) { - return backend->allocator; - } - - struct wlr_renderer *renderer = wlr_backend_get_renderer(backend); - if (renderer == NULL) { - return NULL; - } - - backend->allocator = wlr_allocator_autocreate(backend, renderer); - if (backend->allocator == NULL) { - wlr_log(WLR_ERROR, "Failed to create backend allocator"); - } - return backend->allocator; -} - static size_t parse_outputs_env(const char *name) { const char *outputs_str = getenv(name); if (outputs_str == NULL) { diff --git a/include/backend/backend.h b/include/backend/backend.h index 7d88cd79da..8c7440c3d8 100644 --- a/include/backend/backend.h +++ b/include/backend/backend.h @@ -10,10 +10,4 @@ */ uint32_t backend_get_buffer_caps(struct wlr_backend *backend); -/** - * Get the backend's allocator. Automatically creates the allocator if - * necessary. - */ -struct wlr_allocator *backend_get_allocator(struct wlr_backend *backend); - #endif diff --git a/include/wlr/backend.h b/include/wlr/backend.h index d616fa1db9..9da8403948 100644 --- a/include/wlr/backend.h +++ b/include/wlr/backend.h @@ -30,7 +30,6 @@ struct wlr_backend { bool has_own_renderer; struct wlr_renderer *renderer; - struct wlr_allocator *allocator; }; /** From d07c87f668877c570a3de4ca5a34b05fd3ba3661 Mon Sep 17 00:00:00 2001 From: Simon Zeni Date: Mon, 15 Nov 2021 12:41:57 -0500 Subject: [PATCH 027/188] types/wlr_screencopy_v1: use renderer from output --- types/wlr_screencopy_v1.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/types/wlr_screencopy_v1.c b/types/wlr_screencopy_v1.c index 3f3d3bc5a2..e0e7916bad 100644 --- a/types/wlr_screencopy_v1.c +++ b/types/wlr_screencopy_v1.c @@ -192,7 +192,7 @@ static bool frame_shm_copy(struct wlr_screencopy_frame_v1 *frame, uint32_t *flags) { struct wl_shm_buffer *shm_buffer = frame->shm_buffer; struct wlr_output *output = frame->output; - struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend); + struct wlr_renderer *renderer = output->renderer; assert(renderer); int x = frame->box.x; @@ -257,7 +257,7 @@ static bool blit_dmabuf(struct wlr_renderer *renderer, static bool frame_dma_copy(struct wlr_screencopy_frame_v1 *frame) { struct wlr_dmabuf_v1_buffer *dma_buffer = frame->dma_buffer; struct wlr_output *output = frame->output; - struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend); + struct wlr_renderer *renderer = output->renderer; assert(renderer); // TODO: add support for copying regions with DMA-BUFs @@ -276,7 +276,7 @@ static void frame_handle_output_commit(struct wl_listener *listener, wl_container_of(listener, frame, output_commit); struct wlr_output_event_commit *event = data; struct wlr_output *output = frame->output; - struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend); + struct wlr_renderer *renderer = output->renderer; assert(renderer); if (!(event->committed & WLR_OUTPUT_STATE_BUFFER)) { @@ -538,7 +538,7 @@ static void capture_output(struct wl_client *wl_client, goto error; } - struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend); + struct wlr_renderer *renderer = output->renderer; assert(renderer); uint32_t drm_format = wlr_output_preferred_read_format(frame->output); From 5a98eae0dce0cf4d64406a48beefaff70b5074bd Mon Sep 17 00:00:00 2001 From: Simon Zeni Date: Mon, 15 Nov 2021 12:50:37 -0500 Subject: [PATCH 028/188] types/wlr_scene: use renderer from wlr_output --- types/scene/wlr_scene.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 5a2528d1b4..b6eed41776 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -605,7 +605,7 @@ struct wlr_scene_node *wlr_scene_node_at(struct wlr_scene_node *node, } static void scissor_output(struct wlr_output *output, pixman_box32_t *rect) { - struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend); + struct wlr_renderer *renderer = output->renderer; assert(renderer); struct wlr_box box = { @@ -628,7 +628,7 @@ static void scissor_output(struct wlr_output *output, pixman_box32_t *rect) { static void render_rect(struct wlr_output *output, pixman_region32_t *output_damage, const float color[static 4], const struct wlr_box *box, const float matrix[static 9]) { - struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend); + struct wlr_renderer *renderer = output->renderer; assert(renderer); pixman_region32_t damage; @@ -650,7 +650,7 @@ static void render_texture(struct wlr_output *output, pixman_region32_t *output_damage, struct wlr_texture *texture, const struct wlr_fbox *src_box, const struct wlr_box *dst_box, const float matrix[static 9]) { - struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend); + struct wlr_renderer *renderer = output->renderer; assert(renderer); struct wlr_fbox default_src_box = {0}; @@ -726,7 +726,7 @@ static void render_node_iterator(struct wlr_scene_node *node, case WLR_SCENE_NODE_BUFFER:; struct wlr_scene_buffer *scene_buffer = scene_buffer_from_node(node); - struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend); + struct wlr_renderer *renderer = output->renderer; texture = scene_buffer_get_texture(scene_buffer, renderer); if (texture == NULL) { return; @@ -768,8 +768,7 @@ void wlr_scene_render_output(struct wlr_scene *scene, struct wlr_output *output, damage = &full_region; } - struct wlr_renderer *renderer = - wlr_backend_get_renderer(output->backend); + struct wlr_renderer *renderer = output->renderer; assert(renderer); if (output->enabled && pixman_region32_not_empty(damage)) { @@ -919,7 +918,7 @@ static bool scene_output_scanout(struct wlr_scene_output *scene_output) { bool wlr_scene_output_commit(struct wlr_scene_output *scene_output) { struct wlr_output *output = scene_output->output; - struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend); + struct wlr_renderer *renderer = output->renderer; assert(renderer != NULL); bool scanout = scene_output_scanout(scene_output); From 5f111986056ff398e72d819481b6270c2b6dcbf6 Mon Sep 17 00:00:00 2001 From: Simon Zeni Date: Mon, 15 Nov 2021 12:53:03 -0500 Subject: [PATCH 029/188] backend/x11: get renderer from wlr_x11_output --- backend/x11/output.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/x11/output.c b/backend/x11/output.c index 76172582f1..9081769e3b 100644 --- a/backend/x11/output.c +++ b/backend/x11/output.c @@ -376,7 +376,7 @@ static void update_x11_output_cursor(struct wlr_x11_output *output, static bool output_cursor_to_picture(struct wlr_x11_output *output, struct wlr_buffer *buffer) { struct wlr_x11_backend *x11 = output->x11; - struct wlr_renderer *renderer = wlr_backend_get_renderer(&x11->backend); + struct wlr_renderer *renderer = output->wlr_output.renderer; if (output->cursor.pic != XCB_NONE) { xcb_render_free_picture(x11->xcb, output->cursor.pic); From a1430933392aabc25040cb22e6e4a0d6517b39b5 Mon Sep 17 00:00:00 2001 From: Simon Zeni Date: Mon, 15 Nov 2021 13:24:55 -0500 Subject: [PATCH 030/188] backend/headless: don't store the parent renderer --- backend/headless/backend.c | 39 +++----------------------------------- include/backend/headless.h | 2 -- 2 files changed, 3 insertions(+), 38 deletions(-) diff --git a/backend/headless/backend.c b/backend/headless/backend.c index 49c2c8bac9..886be27614 100644 --- a/backend/headless/backend.c +++ b/backend/headless/backend.c @@ -51,7 +51,6 @@ static void backend_destroy(struct wlr_backend *wlr_backend) { } wl_list_remove(&backend->display_destroy.link); - wl_list_remove(&backend->parent_renderer_destroy.link); struct wlr_headless_output *output, *output_tmp; wl_list_for_each_safe(output, output_tmp, &backend->outputs, link) { @@ -70,17 +69,6 @@ static void backend_destroy(struct wlr_backend *wlr_backend) { 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); - if (backend->parent_renderer != NULL) { - return backend->parent_renderer; - } else { - return wlr_backend->renderer; - } -} - static int backend_get_drm_fd(struct wlr_backend *wlr_backend) { struct wlr_headless_backend *backend = headless_backend_from_backend(wlr_backend); @@ -96,7 +84,6 @@ static uint32_t get_buffer_caps(struct wlr_backend *wlr_backend) { static const struct wlr_backend_impl backend_impl = { .start = backend_start, .destroy = backend_destroy, - .get_renderer = backend_get_renderer, .get_drm_fd = backend_get_drm_fd, .get_buffer_caps = get_buffer_caps, }; @@ -107,33 +94,13 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) { backend_destroy(&backend->backend); } -static void handle_renderer_destroy(struct wl_listener *listener, void *data) { - struct wlr_headless_backend *backend = - wl_container_of(listener, backend, parent_renderer_destroy); - backend_destroy(&backend->backend); -} - static bool backend_init(struct wlr_headless_backend *backend, - struct wl_display *display, struct wlr_renderer *renderer) { + struct wl_display *display) { wlr_backend_init(&backend->backend, &backend_impl); backend->display = display; wl_list_init(&backend->outputs); wl_list_init(&backend->input_devices); - wl_list_init(&backend->parent_renderer_destroy.link); - - if (renderer == NULL) { - renderer = wlr_renderer_autocreate(&backend->backend); - if (!renderer) { - wlr_log(WLR_ERROR, "Failed to create renderer"); - return false; - } - backend->backend.renderer = renderer; - } else { - backend->parent_renderer = renderer; - backend->parent_renderer_destroy.notify = handle_renderer_destroy; - wl_signal_add(&renderer->events.destroy, &backend->parent_renderer_destroy); - } backend->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &backend->display_destroy); @@ -202,7 +169,7 @@ struct wlr_backend *wlr_headless_backend_create(struct wl_display *display) { wlr_log(WLR_ERROR, "Failed to open DRM render node"); } - if (!backend_init(backend, display, NULL)) { + if (!backend_init(backend, display)) { goto error_init; } @@ -236,7 +203,7 @@ struct wlr_backend *wlr_headless_backend_create_with_renderer( } } - if (!backend_init(backend, display, renderer)) { + if (!backend_init(backend, display)) { goto error_init; } diff --git a/include/backend/headless.h b/include/backend/headless.h index 42a07db8d2..09d5856b24 100644 --- a/include/backend/headless.h +++ b/include/backend/headless.h @@ -14,8 +14,6 @@ struct wlr_headless_backend { size_t last_output_num; struct wl_list input_devices; struct wl_listener display_destroy; - struct wlr_renderer *parent_renderer; - struct wl_listener parent_renderer_destroy; bool started; }; From 42549a1c9a43ed4ed313d84a5252a8e3fc34c55c Mon Sep 17 00:00:00 2001 From: Simon Zeni Date: Mon, 15 Nov 2021 13:28:43 -0500 Subject: [PATCH 031/188] backend/drm: stop initializing backend renderer --- backend/drm/backend.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/backend/drm/backend.c b/backend/drm/backend.c index cd5b8ee0e2..ca91be15b8 100644 --- a/backend/drm/backend.c +++ b/backend/drm/backend.c @@ -232,10 +232,6 @@ struct wlr_backend *wlr_drm_backend_create(struct wl_display *display, } if (drm->parent) { - // Ensure we use the same renderer as the parent backend - drm->backend.renderer = wlr_backend_get_renderer(&drm->parent->backend); - assert(drm->backend.renderer != NULL); - if (!init_drm_renderer(drm, &drm->mgpu_renderer)) { wlr_log(WLR_ERROR, "Failed to initialize renderer"); goto error_resources; From d1ebd52ab28a4758d1760e2f50181474e7f49e2f Mon Sep 17 00:00:00 2001 From: Simon Zeni Date: Mon, 15 Nov 2021 13:29:59 -0500 Subject: [PATCH 032/188] backend/multi: remove backend_get_renderer --- backend/multi/backend.c | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/backend/multi/backend.c b/backend/multi/backend.c index ffe7947624..2309691d43 100644 --- a/backend/multi/backend.c +++ b/backend/multi/backend.c @@ -64,20 +64,6 @@ static void multi_backend_destroy(struct wlr_backend *wlr_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); @@ -136,7 +122,6 @@ static uint32_t multi_backend_get_buffer_caps(struct wlr_backend *backend) { static const 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, .get_drm_fd = multi_backend_get_drm_fd, @@ -211,15 +196,6 @@ bool wlr_multi_backend_add(struct wlr_backend *_multi, 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"); From fdf3169b4185dad34b01b62762a0218f1412897f Mon Sep 17 00:00:00 2001 From: Simon Zeni Date: Mon, 15 Nov 2021 13:30:38 -0500 Subject: [PATCH 033/188] backend: remove wlr_backend_get_renderer --- backend/backend.c | 33 --------------------------------- include/wlr/backend.h | 9 --------- include/wlr/backend/interface.h | 1 - 3 files changed, 43 deletions(-) diff --git a/backend/backend.c b/backend/backend.c index 634e53b699..cd96377b4d 100644 --- a/backend/backend.c +++ b/backend/backend.c @@ -45,9 +45,6 @@ void wlr_backend_init(struct wlr_backend *backend, void wlr_backend_finish(struct wlr_backend *backend) { wlr_signal_emit_safe(&backend->events.destroy, backend); - if (backend->has_own_renderer) { - wlr_renderer_destroy(backend->renderer); - } } bool wlr_backend_start(struct wlr_backend *backend) { @@ -69,36 +66,6 @@ void wlr_backend_destroy(struct wlr_backend *backend) { } } -static bool backend_create_renderer(struct wlr_backend *backend) { - if (backend->renderer != NULL) { - return true; - } - - backend->renderer = wlr_renderer_autocreate(backend); - if (backend->renderer == NULL) { - return false; - } - - backend->has_own_renderer = true; - return true; -} - -struct wlr_renderer *wlr_backend_get_renderer(struct wlr_backend *backend) { - if (backend->impl->get_renderer) { - return backend->impl->get_renderer(backend); - } - if (backend_get_buffer_caps(backend) != 0) { - // If the backend is capable of presenting buffers, automatically create - // the renderer if necessary. - if (!backend_create_renderer(backend)) { - wlr_log(WLR_ERROR, "Failed to create backend renderer"); - return NULL; - } - return backend->renderer; - } - return NULL; -} - struct wlr_session *wlr_backend_get_session(struct wlr_backend *backend) { if (backend->impl->get_session) { return backend->impl->get_session(backend); diff --git a/include/wlr/backend.h b/include/wlr/backend.h index 9da8403948..d781f74de7 100644 --- a/include/wlr/backend.h +++ b/include/wlr/backend.h @@ -25,11 +25,6 @@ struct wlr_backend { /** Raised when new outputs are added, passed the wlr_output */ struct wl_signal new_output; } events; - - // Private state - - bool has_own_renderer; - struct wlr_renderer *renderer; }; /** @@ -49,10 +44,6 @@ bool wlr_backend_start(struct wlr_backend *backend); * automatically when the wl_display is destroyed. */ void wlr_backend_destroy(struct wlr_backend *backend); -/** - * Obtains the wlr_renderer reference this backend is using. - */ -struct wlr_renderer *wlr_backend_get_renderer(struct wlr_backend *backend); /** * Obtains the wlr_session reference from this backend if there is any. * Might return NULL for backends that don't use a session. diff --git a/include/wlr/backend/interface.h b/include/wlr/backend/interface.h index 529f8f87a7..f0cafb0159 100644 --- a/include/wlr/backend/interface.h +++ b/include/wlr/backend/interface.h @@ -16,7 +16,6 @@ struct wlr_backend_impl { bool (*start)(struct wlr_backend *backend); void (*destroy)(struct wlr_backend *backend); - struct wlr_renderer *(*get_renderer)(struct wlr_backend *backend); struct wlr_session *(*get_session)(struct wlr_backend *backend); clockid_t (*get_presentation_clock)(struct wlr_backend *backend); int (*get_drm_fd)(struct wlr_backend *backend); From e736ebc63cfdf5fb1e8a0b082cf15086875d9a7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89rico=20Nogueira?= Date: Thu, 18 Nov 2021 23:06:13 -0300 Subject: [PATCH 034/188] docs: mention WLR_RENDERER=vulkan. This option was added with commit 8e346922508aa3eaccd6e12f2917f6574f349843. --- docs/env_vars.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/env_vars.md b/docs/env_vars.md index 1d39a6249b..2959ab581a 100644 --- a/docs/env_vars.md +++ b/docs/env_vars.md @@ -10,7 +10,7 @@ wlroots reads these environment variables * *WLR_XWAYLAND*: specifies the path to an Xwayland binary to be used (instead of following shell search semantics for "Xwayland") * *WLR_RENDERER*: forces the creation of a specified renderer (available - renderers: gles2, pixman) + renderers: gles2, pixman, vulkan) ## DRM backend From 33eba9080c5fb54484bd6f39a9f38d48b31a2dd4 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Fri, 19 Nov 2021 15:24:07 +0100 Subject: [PATCH 035/188] output: fix renderer buffer cap sanity check in wlr_output_init_render The backend and renderer don't directly interact together, so there's no point in checking that their buffer caps intersect. What we want to check is that: - The backend and allocator buffer caps are compatible, because the backend consumes buffers to display them. - The renderer and allocator buffer caps are compatible, because the renderer imports buffers to sample them or render to them. For instance, when running with the DRM backend and the Pixman renderer, the (backend & renderer) check will fail because backend = DMABUF and renderer = DATA_PTR. --- types/output/render.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/types/output/render.c b/types/output/render.c index 2f6fe26008..5bf5530e73 100644 --- a/types/output/render.c +++ b/types/output/render.c @@ -23,8 +23,8 @@ bool wlr_output_init_render(struct wlr_output *output, wlr_log(WLR_ERROR, "output backend and allocator buffer capabilities " "don't match"); return false; - } else if (!(backend_caps & renderer_caps)) { - wlr_log(WLR_ERROR, "output backend and renderer buffer capabilities " + } else if (!(renderer_caps & allocator->buffer_caps)) { + wlr_log(WLR_ERROR, "renderer and allocator buffer capabilities " "don't match"); return false; } From 25bb92faeec27fa67340eb417d427ce721dc3218 Mon Sep 17 00:00:00 2001 From: Simon Zeni Date: Fri, 19 Nov 2021 10:44:45 -0500 Subject: [PATCH 036/188] backend/multi: add asserts in wlr_multi_backend_add --- backend/multi/backend.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/backend/multi/backend.c b/backend/multi/backend.c index 2309691d43..b06bc48653 100644 --- a/backend/multi/backend.c +++ b/backend/multi/backend.c @@ -189,6 +189,9 @@ static struct subbackend_state *multi_backend_get_subbackend(struct wlr_multi_ba bool wlr_multi_backend_add(struct wlr_backend *_multi, struct wlr_backend *backend) { + assert(_multi && backend); + assert(_multi != backend); + struct wlr_multi_backend *multi = multi_backend_from_backend(_multi); if (multi_backend_get_subbackend(multi, backend)) { From c0fd60be633945fd7381a5b995572e4f2350a31e Mon Sep 17 00:00:00 2001 From: Simon Zeni Date: Fri, 19 Nov 2021 10:41:52 -0500 Subject: [PATCH 037/188] backend: fix attempt_backend_by_name multi backend self insertion --- backend/backend.c | 50 +++++++++++++++++++---------------------------- 1 file changed, 20 insertions(+), 30 deletions(-) diff --git a/backend/backend.c b/backend/backend.c index cd96377b4d..7f0de3c2ca 100644 --- a/backend/backend.c +++ b/backend/backend.c @@ -258,46 +258,46 @@ static struct wlr_backend *attempt_drm_backend(struct wl_display *display, } #endif -static struct wlr_backend *attempt_backend_by_name(struct wl_display *display, - struct wlr_backend *backend, struct wlr_session **session, - const char *name) { +static bool attempt_backend_by_name(struct wl_display *display, + struct wlr_multi_backend *multi, char *name) { + struct wlr_backend *backend = NULL; if (strcmp(name, "wayland") == 0) { - return attempt_wl_backend(display); + backend = attempt_wl_backend(display); #if WLR_HAS_X11_BACKEND } else if (strcmp(name, "x11") == 0) { - return attempt_x11_backend(display, NULL); + backend = attempt_x11_backend(display, NULL); #endif } else if (strcmp(name, "headless") == 0) { - return attempt_headless_backend(display); + backend = attempt_headless_backend(display); } else if (strcmp(name, "noop") == 0) { - return attempt_noop_backend(display); + backend = attempt_noop_backend(display); } else if (strcmp(name, "drm") == 0 || strcmp(name, "libinput") == 0) { // DRM and libinput need a session - if (!*session) { - *session = session_create_and_wait(display); - if (!*session) { + if (multi->session == NULL) { + multi->session = session_create_and_wait(display); + if (multi->session == NULL) { wlr_log(WLR_ERROR, "failed to start a session"); - return NULL; + return false; } } if (strcmp(name, "libinput") == 0) { #if WLR_HAS_LIBINPUT_BACKEND - return wlr_libinput_backend_create(display, *session); -#else - return NULL; + backend = wlr_libinput_backend_create(display, multi->session); #endif } else { #if WLR_HAS_DRM_BACKEND - return attempt_drm_backend(display, backend, *session); -#else - return NULL; + // attempt_drm_backend adds the multi drm backends itself + return attempt_drm_backend(display, &multi->backend, + multi->session) != NULL; #endif } + } else { + wlr_log(WLR_ERROR, "unrecognized backend '%s'", name); + return false; } - wlr_log(WLR_ERROR, "unrecognized backend '%s'", name); - return NULL; + return wlr_multi_backend_add(&multi->backend, backend); } struct wlr_backend *wlr_backend_autocreate(struct wl_display *display) { @@ -323,17 +323,7 @@ struct wlr_backend *wlr_backend_autocreate(struct wl_display *display) { char *saveptr; char *name = strtok_r(names, ",", &saveptr); while (name != NULL) { - struct wlr_backend *subbackend = attempt_backend_by_name(display, - backend, &multi->session, name); - 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)) { + if (!attempt_backend_by_name(display, multi, name)) { wlr_log(WLR_ERROR, "failed to add backend '%s'", name); wlr_session_destroy(multi->session); wlr_backend_destroy(backend); From ee210758fca4182e1ba3a37051fb3d7eec099da4 Mon Sep 17 00:00:00 2001 From: Simon Zeni Date: Fri, 19 Nov 2021 10:17:04 -0500 Subject: [PATCH 038/188] tinywl: init output render before commit --- tinywl/tinywl.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tinywl/tinywl.c b/tinywl/tinywl.c index 82f0977a27..ab5ed7ce4e 100644 --- a/tinywl/tinywl.c +++ b/tinywl/tinywl.c @@ -664,6 +664,10 @@ static void server_new_output(struct wl_listener *listener, void *data) { wl_container_of(listener, server, new_output); struct wlr_output *wlr_output = data; + /* Configures the output created by the backend to use our allocator + * and our renderer. Must be done once, before commiting the output */ + wlr_output_init_render(wlr_output, server->allocator, server->renderer); + /* Some backends don't have modes. DRM+KMS does, and we need to set a mode * before we can use the output. The mode is a tuple of (width, height, * refresh rate), and each monitor supports only a specific set of modes. We @@ -678,10 +682,6 @@ static void server_new_output(struct wl_listener *listener, void *data) { } } - /* Configures the output created by the backend to use our allocator - * and our renderer */ - wlr_output_init_render(wlr_output, server->allocator, server->renderer); - /* Allocates and configures our state for this output */ struct tinywl_output *output = calloc(1, sizeof(struct tinywl_output)); From 7508f87fcbfa8c9feb72fcd38f323113059c1b83 Mon Sep 17 00:00:00 2001 From: Manuel Stoeckl Date: Wed, 10 Nov 2021 22:54:04 -0500 Subject: [PATCH 039/188] output: lift up output format fallback logic This makes it possible for the two functions using output_pick_format (output_pick_cursor_format and output_create_swapchain) to select different buffer formats. --- include/types/wlr_output.h | 2 +- types/output/cursor.c | 8 ++++- types/output/render.c | 62 ++++++++++++++++++-------------------- 3 files changed, 37 insertions(+), 35 deletions(-) diff --git a/include/types/wlr_output.h b/include/types/wlr_output.h index cde64e3acd..1159d00a14 100644 --- a/include/types/wlr_output.h +++ b/include/types/wlr_output.h @@ -8,7 +8,7 @@ void output_pending_resolution(struct wlr_output *output, int *width, int *height); struct wlr_drm_format *output_pick_format(struct wlr_output *output, - const struct wlr_drm_format_set *display_formats); + const struct wlr_drm_format_set *display_formats, uint32_t format); void output_clear_back_buffer(struct wlr_output *output); bool output_ensure_buffer(struct wlr_output *output); diff --git a/types/output/cursor.c b/types/output/cursor.c index f1e6aee5e6..e141420176 100644 --- a/types/output/cursor.c +++ b/types/output/cursor.c @@ -206,7 +206,13 @@ static struct wlr_drm_format *output_pick_cursor_format(struct wlr_output *outpu } } - return output_pick_format(output, display_formats); + struct wlr_drm_format *format = output_pick_format(output, display_formats, + DRM_FORMAT_ARGB8888); + if (format == NULL) { + format = output_pick_format(output, display_formats, + DRM_FORMAT_XRGB8888); + } + return format; } static struct wlr_buffer *render_cursor_buffer(struct wlr_output_cursor *cursor) { diff --git a/types/output/render.c b/types/output/render.c index 5bf5530e73..8b35355d97 100644 --- a/types/output/render.c +++ b/types/output/render.c @@ -66,7 +66,12 @@ static bool output_create_swapchain(struct wlr_output *output, } } - struct wlr_drm_format *format = output_pick_format(output, display_formats); + struct wlr_drm_format *format = output_pick_format(output, display_formats, + DRM_FORMAT_ARGB8888); + if (format == NULL) { + format = output_pick_format(output, display_formats, + DRM_FORMAT_XRGB8888); + } if (format == NULL) { wlr_log(WLR_ERROR, "Failed to pick primary buffer format for output '%s'", output->name); @@ -231,7 +236,8 @@ void wlr_output_lock_attach_render(struct wlr_output *output, bool lock) { } struct wlr_drm_format *output_pick_format(struct wlr_output *output, - const struct wlr_drm_format_set *display_formats) { + const struct wlr_drm_format_set *display_formats, + uint32_t fmt) { struct wlr_renderer *renderer = output->renderer; struct wlr_allocator *allocator = output->allocator; assert(renderer != NULL && allocator != NULL); @@ -243,41 +249,31 @@ struct wlr_drm_format *output_pick_format(struct wlr_output *output, return NULL; } - struct wlr_drm_format *format = NULL; - const uint32_t candidates[] = { DRM_FORMAT_ARGB8888, DRM_FORMAT_XRGB8888 }; - for (size_t i = 0; i < sizeof(candidates) / sizeof(candidates[0]); i++) { - uint32_t fmt = candidates[i]; - - const struct wlr_drm_format *render_format = - wlr_drm_format_set_get(render_formats, fmt); - if (render_format == NULL) { - wlr_log(WLR_DEBUG, "Renderer doesn't support format 0x%"PRIX32, fmt); - continue; - } - - if (display_formats != NULL) { - const struct wlr_drm_format *display_format = - wlr_drm_format_set_get(display_formats, fmt); - if (display_format == NULL) { - wlr_log(WLR_DEBUG, "Output doesn't support format 0x%"PRIX32, fmt); - continue; - } - format = wlr_drm_format_intersect(display_format, render_format); - } else { - // The output can display any format - format = wlr_drm_format_dup(render_format); - } + const struct wlr_drm_format *render_format = + wlr_drm_format_set_get(render_formats, fmt); + if (render_format == NULL) { + wlr_log(WLR_DEBUG, "Renderer doesn't support format 0x%"PRIX32, fmt); + return NULL; + } - if (format == NULL) { - wlr_log(WLR_DEBUG, "Failed to intersect display and render " - "modifiers for format 0x%"PRIX32, fmt); - } else { - break; + struct wlr_drm_format *format = NULL; + if (display_formats != NULL) { + const struct wlr_drm_format *display_format = + wlr_drm_format_set_get(display_formats, fmt); + if (display_format == NULL) { + wlr_log(WLR_DEBUG, "Output doesn't support format 0x%"PRIX32, fmt); + return NULL; } + format = wlr_drm_format_intersect(display_format, render_format); + } else { + // The output can display any format + format = wlr_drm_format_dup(render_format); } + if (format == NULL) { - wlr_log(WLR_ERROR, "Failed to choose a format for output '%s'", - output->name); + wlr_log(WLR_DEBUG, "Failed to intersect display and render " + "modifiers for format 0x%"PRIX32 " on output '%s", + fmt, output->name); return NULL; } From 3d7d6ec06ff519e4b28198fd514d511c6d670b0b Mon Sep 17 00:00:00 2001 From: Manuel Stoeckl Date: Wed, 10 Nov 2021 22:59:31 -0500 Subject: [PATCH 040/188] output: use XRGB8888 format instead of ARGB8888 Most (and possibly all) compositors using wlroots only ever render fully opaque content. To provide better performance, this change switches the default format used by wlr_output buffers from ARGB8888 to the opaque XRGB8888. Compositors like mutter, kwin, and weston already default to XRGB8888, so this change is unlikely to expose any new bugs in underlying drivers and hardware. This does not affect the hardware cursor's buffer format, which is still ARGB8888 by default. As part of this change, the X11 backend (which does not support changing format at runtime) now picks a true color, 24 bit depth visual (i.e. XRGB8888) instead of a 32 bit depth (ARGB8888) one. --- backend/x11/backend.c | 4 ++-- types/output/render.c | 6 +----- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/backend/x11/backend.c b/backend/x11/backend.c index 9eaf56644a..6db9aa6c94 100644 --- a/backend/x11/backend.c +++ b/backend/x11/backend.c @@ -572,9 +572,9 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_display *display, goto error_event; } - x11->depth = get_depth(x11->screen, 32); + x11->depth = get_depth(x11->screen, 24); if (!x11->depth) { - wlr_log(WLR_ERROR, "Failed to get 32-bit depth for X11 screen"); + wlr_log(WLR_ERROR, "Failed to get 24-bit depth for X11 screen"); goto error_event; } diff --git a/types/output/render.c b/types/output/render.c index 8b35355d97..adce1ee339 100644 --- a/types/output/render.c +++ b/types/output/render.c @@ -67,11 +67,7 @@ static bool output_create_swapchain(struct wlr_output *output, } struct wlr_drm_format *format = output_pick_format(output, display_formats, - DRM_FORMAT_ARGB8888); - if (format == NULL) { - format = output_pick_format(output, display_formats, - DRM_FORMAT_XRGB8888); - } + DRM_FORMAT_XRGB8888); if (format == NULL) { wlr_log(WLR_ERROR, "Failed to pick primary buffer format for output '%s'", output->name); From e879d566bb5e0140036170b73757a613de51e4ca Mon Sep 17 00:00:00 2001 From: Manuel Stoeckl Date: Wed, 10 Nov 2021 23:20:10 -0500 Subject: [PATCH 041/188] output: Add function to set preferred render format This change introduces new double buffered state to the wlr_output, corresponding to the buffer format to render to. The format being rendered to does not control the bit depth of colors being sent to the display; it does generally determine the format with which screenshot data is provided. The DRM backend _may_ sent higher bit depths if the render format depth is increased, but hardware and other limitations may apply. --- include/wlr/interfaces/wlr_output.h | 1 + include/wlr/types/wlr_output.h | 19 ++++++++++++ types/output/output.c | 46 +++++++++++++++++++++++++++++ types/output/render.c | 22 +++++++++----- 4 files changed, 81 insertions(+), 7 deletions(-) diff --git a/include/wlr/interfaces/wlr_output.h b/include/wlr/interfaces/wlr_output.h index 100754f685..5ae1ab56f4 100644 --- a/include/wlr/interfaces/wlr_output.h +++ b/include/wlr/interfaces/wlr_output.h @@ -21,6 +21,7 @@ (WLR_OUTPUT_STATE_DAMAGE | \ WLR_OUTPUT_STATE_SCALE | \ WLR_OUTPUT_STATE_TRANSFORM | \ + WLR_OUTPUT_STATE_RENDER_FORMAT | \ WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED) /** diff --git a/include/wlr/types/wlr_output.h b/include/wlr/types/wlr_output.h index 9d3dc3cbf1..7d7fbaad8f 100644 --- a/include/wlr/types/wlr_output.h +++ b/include/wlr/types/wlr_output.h @@ -61,6 +61,7 @@ enum wlr_output_state_field { WLR_OUTPUT_STATE_TRANSFORM = 1 << 5, WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED = 1 << 6, WLR_OUTPUT_STATE_GAMMA_LUT = 1 << 7, + WLR_OUTPUT_STATE_RENDER_FORMAT = 1 << 8, }; enum wlr_output_state_mode_type { @@ -78,6 +79,7 @@ struct wlr_output_state { float scale; enum wl_output_transform transform; bool adaptive_sync_enabled; + uint32_t render_format; // only valid if WLR_OUTPUT_STATE_BUFFER struct wlr_buffer *buffer; @@ -134,6 +136,7 @@ struct wlr_output { enum wl_output_subpixel subpixel; enum wl_output_transform transform; enum wlr_output_adaptive_sync_status adaptive_sync_status; + uint32_t render_format; bool needs_frame; // damage for cursors and fullscreen surface, in output-local coordinates @@ -308,6 +311,22 @@ void wlr_output_set_transform(struct wlr_output *output, * Adaptive sync is double-buffered state, see `wlr_output_commit`. */ void wlr_output_enable_adaptive_sync(struct wlr_output *output, bool enabled); +/** + * Set the output buffer render format. Default value: DRM_FORMAT_XRGB8888 + * + * While high bit depth render formats are necessary for a monitor to receive + * useful high bit data, they do not guarantee it; a DRM_FORMAT_XBGR2101010 + * buffer will only lead to sending 10-bpc image data to the monitor if + * hardware and software permit this. + * + * This only affects the format of the output buffer used when rendering, + * as with `wlr_output_attach_render`. It has no impact on the cursor buffer + * format, or on the formats supported for direct scan-out (see also + * `wlr_output_attach_buffer`). + * + * This format is double-buffered state, see `wlr_output_commit`. + */ +void wlr_output_set_render_format(struct wlr_output *output, uint32_t format); /** * Sets a scale for the output. * diff --git a/types/output/output.c b/types/output/output.c index 4e3f403ff1..76c9ed9e9c 100644 --- a/types/output/output.c +++ b/types/output/output.c @@ -1,10 +1,13 @@ #define _POSIX_C_SOURCE 200809L #include +#include +#include #include #include #include #include #include +#include "render/allocator/allocator.h" #include "render/swapchain.h" #include "types/wlr_output.h" #include "util/global.h" @@ -296,6 +299,16 @@ void wlr_output_enable_adaptive_sync(struct wlr_output *output, bool enabled) { output->pending.adaptive_sync_enabled = enabled; } +void wlr_output_set_render_format(struct wlr_output *output, uint32_t format) { + if (output->render_format == format) { + output->pending.committed &= ~WLR_OUTPUT_STATE_RENDER_FORMAT; + return; + } + + output->pending.committed |= WLR_OUTPUT_STATE_RENDER_FORMAT; + output->pending.render_format = format; +} + void wlr_output_set_subpixel(struct wlr_output *output, enum wl_output_subpixel subpixel) { if (output->subpixel == subpixel) { @@ -343,6 +356,7 @@ void wlr_output_init(struct wlr_output *output, struct wlr_backend *backend, output->impl = impl; output->display = display; wl_list_init(&output->modes); + output->render_format = DRM_FORMAT_XRGB8888; output->transform = WL_OUTPUT_TRANSFORM_NORMAL; output->scale = 1; output->commit_seq = 0; @@ -542,6 +556,30 @@ static bool output_basic_test(struct wlr_output *output) { } } + if (output->pending.committed & WLR_OUTPUT_STATE_RENDER_FORMAT) { + struct wlr_allocator *allocator = output->allocator; + assert(allocator != NULL); + + const struct wlr_drm_format_set *display_formats = NULL; + if (output->impl->get_primary_formats) { + display_formats = + output->impl->get_primary_formats(output, allocator->buffer_caps); + if (display_formats == NULL) { + wlr_log(WLR_ERROR, "Failed to get primary display formats"); + return false; + } + } + + struct wlr_drm_format *format = output_pick_format(output, display_formats, + output->pending.render_format); + if (format == NULL) { + wlr_log(WLR_ERROR, "Failed to pick primary buffer format for output"); + return false; + } + + free(format); + } + bool enabled = output->enabled; if (output->pending.committed & WLR_OUTPUT_STATE_ENABLED) { enabled = output->pending.enabled; @@ -569,6 +607,10 @@ static bool output_basic_test(struct wlr_output *output) { wlr_log(WLR_DEBUG, "Tried to enable adaptive sync on a disabled output"); return false; } + if (!enabled && output->pending.committed & WLR_OUTPUT_STATE_RENDER_FORMAT) { + wlr_log(WLR_DEBUG, "Tried to set format for a disabled output"); + return false; + } if (!enabled && output->pending.committed & WLR_OUTPUT_STATE_GAMMA_LUT) { wlr_log(WLR_DEBUG, "Tried to set the gamma lut on a disabled output"); return false; @@ -642,6 +684,10 @@ bool wlr_output_commit(struct wlr_output *output) { } } + if (output->pending.committed & WLR_OUTPUT_STATE_RENDER_FORMAT) { + output->render_format = output->pending.render_format; + } + output->commit_seq++; bool scale_updated = output->pending.committed & WLR_OUTPUT_STATE_SCALE; diff --git a/types/output/render.c b/types/output/render.c index adce1ee339..f23d1a9a8b 100644 --- a/types/output/render.c +++ b/types/output/render.c @@ -9,6 +9,7 @@ #include "render/drm_format_set.h" #include "render/swapchain.h" #include "render/wlr_renderer.h" +#include "render/pixel_format.h" #include "types/wlr_output.h" bool wlr_output_init_render(struct wlr_output *output, @@ -47,12 +48,6 @@ static bool output_create_swapchain(struct wlr_output *output, int width, height; output_pending_resolution(output, &width, &height); - if (output->swapchain != NULL && output->swapchain->width == width && - output->swapchain->height == height && - (allow_modifiers || output->swapchain->format->len == 0)) { - return true; - } - struct wlr_allocator *allocator = output->allocator; assert(allocator != NULL); @@ -67,12 +62,22 @@ static bool output_create_swapchain(struct wlr_output *output, } struct wlr_drm_format *format = output_pick_format(output, display_formats, - DRM_FORMAT_XRGB8888); + output->render_format); if (format == NULL) { wlr_log(WLR_ERROR, "Failed to pick primary buffer format for output '%s'", output->name); return false; } + + if (output->swapchain != NULL && output->swapchain->width == width && + output->swapchain->height == height && + output->swapchain->format->format == format->format && + (allow_modifiers || output->swapchain->format->len == 0)) { + // no change, keep existing swapchain + free(format); + return true; + } + wlr_log(WLR_DEBUG, "Choosing primary buffer format 0x%"PRIX32" for output '%s'", format->format, output->name); @@ -171,6 +176,9 @@ bool output_ensure_buffer(struct wlr_output *output) { if (output->pending.committed & WLR_OUTPUT_STATE_MODE) { needs_new_buffer = true; } + if (output->pending.committed & WLR_OUTPUT_STATE_RENDER_FORMAT) { + needs_new_buffer = true; + } if (!needs_new_buffer || (output->pending.committed & WLR_OUTPUT_STATE_BUFFER)) { return true; From d0bb7df6303cbe919f5d6d456cd4a8d46904b115 Mon Sep 17 00:00:00 2001 From: Manuel Stoeckl Date: Thu, 18 Nov 2021 17:55:56 -0500 Subject: [PATCH 042/188] output: remove XRGB8888 cursor fallback format All graphics drivers supporting cursor planes support ARGB8888, the default cursor format, so this fallback is almost certainly unused. Essentially all cursor themes use alpha transparency to make it clearer where relative to the screen content the cursor hotspot is. It is better to fall back to a slightly slower software cursor than it is to fall back to the opaque square that is a hardware cursor without an alpha channel. --- types/output/cursor.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/types/output/cursor.c b/types/output/cursor.c index e141420176..8fa2cf0103 100644 --- a/types/output/cursor.c +++ b/types/output/cursor.c @@ -206,13 +206,7 @@ static struct wlr_drm_format *output_pick_cursor_format(struct wlr_output *outpu } } - struct wlr_drm_format *format = output_pick_format(output, display_formats, - DRM_FORMAT_ARGB8888); - if (format == NULL) { - format = output_pick_format(output, display_formats, - DRM_FORMAT_XRGB8888); - } - return format; + return output_pick_format(output, display_formats, DRM_FORMAT_ARGB8888); } static struct wlr_buffer *render_cursor_buffer(struct wlr_output_cursor *cursor) { From a37f538ca0cc6d504358eb797150751b60c4511b Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 3 Nov 2021 13:37:24 +0100 Subject: [PATCH 043/188] Introduce WLR_DEVICE_LEASE events This will allow the DRM backend to reload its lessee list. --- backend/session/session.c | 8 ++++++++ include/wlr/backend/session.h | 1 + 2 files changed, 9 insertions(+) diff --git a/backend/session/session.c b/backend/session/session.c index 382f3d60bb..e83a8b4c1a 100644 --- a/backend/session/session.c +++ b/backend/session/session.c @@ -157,6 +157,14 @@ static void read_udev_change_event(struct wlr_device_change_event *event, if (prop != NULL) { hotplug->prop_id = strtoul(prop, NULL, 10); } + + return; + } + + const char *lease = udev_device_get_property_value(udev_dev, "LEASE"); + if (lease != NULL && strcmp(lease, "1") == 0) { + event->type = WLR_DEVICE_LEASE; + return; } } diff --git a/include/wlr/backend/session.h b/include/wlr/backend/session.h index 7d34bfebea..c8461ff7c7 100644 --- a/include/wlr/backend/session.h +++ b/include/wlr/backend/session.h @@ -59,6 +59,7 @@ struct wlr_session_add_event { enum wlr_device_change_type { WLR_DEVICE_HOTPLUG = 1, + WLR_DEVICE_LEASE, }; struct wlr_device_hotplug_event { From 86f5ecf46867236d4b96a3ce1ad664a8963d6ae4 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 3 Nov 2021 14:03:59 +0100 Subject: [PATCH 044/188] backend/drm: introduce wlr_drm_lease Closes: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3183 --- backend/drm/drm.c | 75 ++++++++++++++++------------ include/backend/drm/drm.h | 5 +- include/wlr/backend/drm.h | 32 +++++++++--- include/wlr/types/wlr_drm_lease_v1.h | 3 +- types/wlr_drm_lease_v1.c | 17 +++---- 5 files changed, 78 insertions(+), 54 deletions(-) diff --git a/backend/drm/drm.c b/backend/drm/drm.c index 3031d09b2c..64db81f5cd 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -1554,17 +1554,13 @@ int wlr_drm_backend_get_non_master_fd(struct wlr_backend *backend) { return fd; } -/* TODO: make the function return a `wlr_drm_lease` to provide a destroy event - * that can be fired when the kernel notifies us through uevent that the lease - * has been destroyed - */ -int wlr_drm_create_lease(struct wlr_output **outputs, size_t n_outputs, - uint32_t *lessee_id) { +struct wlr_drm_lease *wlr_drm_create_lease(struct wlr_output **outputs, + size_t n_outputs, int *lease_fd_ptr) { assert(outputs); if (n_outputs == 0) { wlr_log(WLR_ERROR, "Can't lease 0 outputs"); - return -1; + return NULL; } struct wlr_drm_backend *drm = @@ -1575,11 +1571,11 @@ int wlr_drm_create_lease(struct wlr_output **outputs, size_t n_outputs, for (size_t i = 0; i < n_outputs; ++i) { struct wlr_drm_connector *conn = get_drm_connector_from_output(outputs[i]); - assert(conn->lessee_id == 0); + assert(conn->lease == NULL); if (conn->backend != drm) { wlr_log(WLR_ERROR, "Can't lease output from different backends"); - return -1; + return NULL; } objects[n_objects++] = conn->id; @@ -1587,7 +1583,7 @@ int wlr_drm_create_lease(struct wlr_output **outputs, size_t n_outputs, if (!conn->crtc) { wlr_log(WLR_ERROR, "Connector has no CRTC"); - return -1; + return NULL; } objects[n_objects++] = conn->crtc->id; @@ -1604,50 +1600,63 @@ int wlr_drm_create_lease(struct wlr_output **outputs, size_t n_outputs, assert(n_objects != 0); - wlr_log(WLR_DEBUG, "Issuing DRM lease with the %d objects", n_objects); + struct wlr_drm_lease *lease = calloc(1, sizeof(*lease)); + if (lease == NULL) { + return NULL; + } + + lease->backend = drm; + wl_signal_init(&lease->events.destroy); + + wlr_log(WLR_DEBUG, "Issuing DRM lease with %d objects", n_objects); int lease_fd = drmModeCreateLease(drm->fd, objects, n_objects, 0, - lessee_id); + &lease->lessee_id); if (lease_fd < 0) { - return lease_fd; + free(lease); + return NULL; } + *lease_fd_ptr = lease_fd; - wlr_log(WLR_DEBUG, "Issued DRM lease %"PRIu32, *lessee_id); + wlr_log(WLR_DEBUG, "Issued DRM lease %"PRIu32, lease->lessee_id); for (size_t i = 0; i < n_outputs; ++i) { struct wlr_drm_connector *conn = get_drm_connector_from_output(outputs[i]); - conn->lessee_id = *lessee_id; - conn->crtc->lessee_id = *lessee_id; + conn->lease = lease; + conn->crtc->lease = lease; } - return lease_fd; + return lease; } -bool wlr_drm_backend_terminate_lease(struct wlr_backend *backend, - uint32_t lessee_id) { - wlr_log(WLR_DEBUG, "Terminating DRM lease %d", lessee_id); +void wlr_drm_lease_terminate(struct wlr_drm_lease *lease) { + struct wlr_drm_backend *drm = lease->backend; - assert(backend); - struct wlr_drm_backend *drm = get_drm_backend_from_backend(backend); - - int r = drmModeRevokeLease(drm->fd, lessee_id); - if (r < 0) { - wlr_log_errno(WLR_DEBUG, "Failed to terminate lease"); + wlr_log(WLR_DEBUG, "Terminating DRM lease %d", lease->lessee_id); + int ret = drmModeRevokeLease(drm->fd, lease->lessee_id); + if (ret < 0) { + wlr_log_errno(WLR_ERROR, "Failed to terminate lease"); } + drm_lease_destroy(lease); +} + +void drm_lease_destroy(struct wlr_drm_lease *lease) { + struct wlr_drm_backend *drm = lease->backend; + + wlr_signal_emit_safe(&lease->events.destroy, NULL); + struct wlr_drm_connector *conn; wl_list_for_each(conn, &drm->outputs, link) { - if (conn->lessee_id == lessee_id) { - conn->lessee_id = 0; - /* Will be re-initialized in scan_drm_connectors */ + if (conn->lease == lease) { + conn->lease = NULL; } } for (size_t i = 0; i < drm->num_crtcs; ++i) { - if (drm->crtcs[i].lessee_id == lessee_id) { - drm->crtcs[i].lessee_id = 0; + if (drm->crtcs[i].lease == lease) { + drm->crtcs[i].lease = NULL; } } - return r >= 0; + free(lease); } - diff --git a/include/backend/drm/drm.h b/include/backend/drm/drm.h index e045a72ee8..d3640afb81 100644 --- a/include/backend/drm/drm.h +++ b/include/backend/drm/drm.h @@ -36,7 +36,7 @@ struct wlr_drm_plane { struct wlr_drm_crtc { uint32_t id; - uint32_t lessee_id; + struct wlr_drm_lease *lease; // Atomic modesetting only uint32_t mode_id; @@ -118,7 +118,7 @@ struct wlr_drm_connector { enum wlr_drm_connector_status status; bool desired_enabled; uint32_t id; - uint32_t lessee_id; + struct wlr_drm_lease *lease; struct wlr_drm_crtc *crtc; uint32_t possible_crtcs; @@ -157,6 +157,7 @@ bool drm_connector_is_cursor_visible(struct wlr_drm_connector *conn); bool drm_connector_supports_vrr(struct wlr_drm_connector *conn); size_t drm_crtc_get_gamma_lut_size(struct wlr_drm_backend *drm, struct wlr_drm_crtc *crtc); +void drm_lease_destroy(struct wlr_drm_lease *lease); struct wlr_drm_fb *plane_get_next_fb(struct wlr_drm_plane *plane); diff --git a/include/wlr/backend/drm.h b/include/wlr/backend/drm.h index 0e9f610817..800bc585fb 100644 --- a/include/wlr/backend/drm.h +++ b/include/wlr/backend/drm.h @@ -14,6 +14,20 @@ #include #include +struct wlr_drm_backend; + +struct wlr_drm_lease { + int fd; + uint32_t lessee_id; + struct wlr_drm_backend *backend; + + struct { + struct wl_signal destroy; + } events; + + void *data; +}; + /** * Creates a DRM backend using the specified GPU file descriptor (typically from * a device node in /dev/dri). @@ -41,18 +55,20 @@ uint32_t wlr_drm_connector_get_id(struct wlr_output *output); int wlr_drm_backend_get_non_master_fd(struct wlr_backend *backend); /** - * Leases a given output to the caller. The output must be from the associated - * DRM backend. - * Returns a valid opened DRM FD or -1 on error. + * Leases the given outputs to the caller. The outputs must be from the + * associated DRM backend. + * + * Returns NULL on error. */ -int wlr_drm_create_lease(struct wlr_output **outputs, size_t n_outputs, - uint32_t *lessee_id); +struct wlr_drm_lease *wlr_drm_create_lease(struct wlr_output **outputs, + size_t n_outputs, int *lease_fd); /** - * Terminates a given lease. The output will be owned again by the backend + * Terminates and destroys a given lease. + * + * The outputs will be owned again by the backend. */ -bool wlr_drm_backend_terminate_lease(struct wlr_backend *backend, - uint32_t lessee_id); +void wlr_drm_lease_terminate(struct wlr_drm_lease *lease); /** * Add mode to the list of available modes diff --git a/include/wlr/types/wlr_drm_lease_v1.h b/include/wlr/types/wlr_drm_lease_v1.h index bf625235df..5b4137c5b4 100644 --- a/include/wlr/types/wlr_drm_lease_v1.h +++ b/include/wlr/types/wlr_drm_lease_v1.h @@ -81,14 +81,13 @@ struct wlr_drm_lease_request_v1 { struct wlr_drm_lease_v1 { struct wl_resource *resource; + struct wlr_drm_lease *drm_lease; struct wlr_drm_lease_device_v1 *device; struct wlr_drm_lease_connector_v1 **connectors; size_t n_connectors; - uint32_t lessee_id; - struct wl_list link; // wlr_drm_lease_device_v1::leases void *data; diff --git a/types/wlr_drm_lease_v1.c b/types/wlr_drm_lease_v1.c index a917930eff..eaca8f1702 100644 --- a/types/wlr_drm_lease_v1.c +++ b/types/wlr_drm_lease_v1.c @@ -52,13 +52,11 @@ static void drm_lease_v1_destroy(struct wlr_drm_lease_v1 *lease) { return; } - wlr_log(WLR_DEBUG, "Destroying lease %"PRIu32, lease->lessee_id); + wlr_log(WLR_DEBUG, "Destroying lease %"PRIu32, lease->drm_lease->lessee_id); wp_drm_lease_v1_send_finished(lease->resource); - struct wlr_drm_lease_device_v1 *device = lease->device; - wlr_drm_backend_terminate_lease(device->backend, lease->lessee_id); - lease->lessee_id = 0; + wlr_drm_lease_terminate(lease->drm_lease); for (size_t i = 0; i < lease->n_connectors; ++i) { lease->connectors[i]->active_lease = NULL; @@ -171,10 +169,10 @@ struct wlr_drm_lease_v1 *wlr_drm_lease_request_v1_grant( outputs[i] = request->connectors[i]->output; } - int fd = wlr_drm_create_lease(outputs, request->n_connectors, - &lease->lessee_id); - if (fd < 0) { - wlr_log_errno(WLR_ERROR, "drm_create_lease failed"); + int fd; + lease->drm_lease = wlr_drm_create_lease(outputs, request->n_connectors, &fd); + if (!lease->drm_lease) { + wlr_log(WLR_ERROR, "wlr_drm_create_lease failed"); wp_drm_lease_v1_send_finished(lease->resource); return NULL; } @@ -183,6 +181,7 @@ struct wlr_drm_lease_v1 *wlr_drm_lease_request_v1_grant( sizeof(struct wlr_drm_lease_connector_v1 *)); if (!lease->connectors) { wlr_log(WLR_ERROR, "Failed to allocate lease connectors list"); + close(fd); wp_drm_lease_v1_send_finished(lease->resource); return NULL; } @@ -212,7 +211,7 @@ void wlr_drm_lease_request_v1_reject( void wlr_drm_lease_v1_revoke(struct wlr_drm_lease_v1 *lease) { assert(lease); - wlr_log(WLR_DEBUG, "Revoking lease %"PRIu32, lease->lessee_id); + wlr_log(WLR_DEBUG, "Revoking lease %"PRIu32, lease->drm_lease->lessee_id); drm_lease_v1_destroy(lease); } From 6bb897330980f65194b75547c3f909e614bdcd49 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 3 Nov 2021 14:22:35 +0100 Subject: [PATCH 045/188] drm-lease-v1: listen to lease destroy event --- include/wlr/types/wlr_drm_lease_v1.h | 2 ++ types/wlr_drm_lease_v1.c | 54 ++++++++++++++-------------- 2 files changed, 29 insertions(+), 27 deletions(-) diff --git a/include/wlr/types/wlr_drm_lease_v1.h b/include/wlr/types/wlr_drm_lease_v1.h index 5b4137c5b4..5a7827ea34 100644 --- a/include/wlr/types/wlr_drm_lease_v1.h +++ b/include/wlr/types/wlr_drm_lease_v1.h @@ -90,6 +90,8 @@ struct wlr_drm_lease_v1 { struct wl_list link; // wlr_drm_lease_device_v1::leases + struct wl_listener destroy; + void *data; }; diff --git a/types/wlr_drm_lease_v1.c b/types/wlr_drm_lease_v1.c index eaca8f1702..7da40686e8 100644 --- a/types/wlr_drm_lease_v1.c +++ b/types/wlr_drm_lease_v1.c @@ -47,28 +47,6 @@ static struct wlr_drm_lease_v1 *drm_lease_v1_from_resource( return wl_resource_get_user_data(resource); } -static void drm_lease_v1_destroy(struct wlr_drm_lease_v1 *lease) { - if (!lease) { - return; - } - - wlr_log(WLR_DEBUG, "Destroying lease %"PRIu32, lease->drm_lease->lessee_id); - - wp_drm_lease_v1_send_finished(lease->resource); - - wlr_drm_lease_terminate(lease->drm_lease); - - for (size_t i = 0; i < lease->n_connectors; ++i) { - lease->connectors[i]->active_lease = NULL; - } - - wl_list_remove(&lease->link); - wl_resource_set_user_data(lease->resource, NULL); - - free(lease->connectors); - free(lease); -} - static void drm_lease_request_v1_destroy( struct wlr_drm_lease_request_v1 *request) { if (!request) { @@ -93,7 +71,7 @@ static void drm_lease_connector_v1_destroy( wlr_log(WLR_DEBUG, "Destroying connector %s", connector->output->name); if (connector->active_lease) { - drm_lease_v1_destroy(connector->active_lease); + wlr_drm_lease_terminate(connector->active_lease->drm_lease); } struct wl_resource *resource, *tmp; @@ -140,7 +118,7 @@ static void drm_lease_device_v1_destroy( struct wlr_drm_lease_v1 *lease, *tmp_lease; wl_list_for_each_safe(lease, tmp_lease, &device->leases, link) { - drm_lease_v1_destroy(lease); + wlr_drm_lease_terminate(lease->drm_lease); } struct wlr_drm_lease_connector_v1 *connector, *tmp_connector; @@ -154,6 +132,26 @@ static void drm_lease_device_v1_destroy( free(device); } +static void lease_handle_destroy(struct wl_listener *listener, void *data) { + struct wlr_drm_lease_v1 *lease = wl_container_of(listener, lease, destroy); + + wlr_log(WLR_DEBUG, "Destroying lease %"PRIu32, lease->drm_lease->lessee_id); + + wp_drm_lease_v1_send_finished(lease->resource); + + wl_list_remove(&lease->destroy.link); + + for (size_t i = 0; i < lease->n_connectors; ++i) { + lease->connectors[i]->active_lease = NULL; + } + + wl_list_remove(&lease->link); + wl_resource_set_user_data(lease->resource, NULL); + + free(lease->connectors); + free(lease); +} + struct wlr_drm_lease_v1 *wlr_drm_lease_request_v1_grant( struct wlr_drm_lease_request_v1 *request) { assert(request->lease); @@ -191,6 +189,9 @@ struct wlr_drm_lease_v1 *wlr_drm_lease_request_v1_grant( lease->connectors[i]->active_lease = lease; } + lease->destroy.notify = lease_handle_destroy; + wl_signal_add(&lease->drm_lease->events.destroy, &lease->destroy); + wlr_log(WLR_DEBUG, "Granting request %p", request); wp_drm_lease_v1_send_lease_fd(lease->resource, fd); @@ -212,13 +213,12 @@ void wlr_drm_lease_request_v1_reject( void wlr_drm_lease_v1_revoke(struct wlr_drm_lease_v1 *lease) { assert(lease); wlr_log(WLR_DEBUG, "Revoking lease %"PRIu32, lease->drm_lease->lessee_id); - - drm_lease_v1_destroy(lease); + wlr_drm_lease_terminate(lease->drm_lease); } static void drm_lease_v1_handle_resource_destroy(struct wl_resource *resource) { struct wlr_drm_lease_v1 *lease = drm_lease_v1_from_resource(resource); - drm_lease_v1_destroy(lease); + wlr_drm_lease_terminate(lease->drm_lease); } static void drm_lease_v1_handle_destroy( From e656697a7d6ad851cf299276287665f2aaf80369 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 3 Nov 2021 14:23:00 +0100 Subject: [PATCH 046/188] backend/drm: scan leases on uevent Closes: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3181 --- backend/drm/backend.c | 7 +++++-- backend/drm/drm.c | 30 ++++++++++++++++++++++++++++++ include/backend/drm/drm.h | 1 + 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/backend/drm/backend.c b/backend/drm/backend.c index ca91be15b8..10f324a02f 100644 --- a/backend/drm/backend.c +++ b/backend/drm/backend.c @@ -133,12 +133,15 @@ static void handle_dev_change(struct wl_listener *listener, void *data) { return; } - // TODO: add and handle lease uevents switch (change->type) { - case WLR_DEVICE_HOTPLUG:; + case WLR_DEVICE_HOTPLUG: wlr_log(WLR_DEBUG, "Received hotplug event for %s", drm->name); scan_drm_connectors(drm, &change->hotplug); break; + case WLR_DEVICE_LEASE: + wlr_log(WLR_DEBUG, "Received lease event for %s", drm->name); + scan_drm_leases(drm); + break; default: wlr_log(WLR_DEBUG, "Received unknown change event for %s", drm->name); } diff --git a/backend/drm/drm.c b/backend/drm/drm.c index 64db81f5cd..40506cd46d 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -1425,6 +1425,36 @@ void scan_drm_connectors(struct wlr_drm_backend *drm, } } +void scan_drm_leases(struct wlr_drm_backend *drm) { + drmModeLesseeListRes *list = drmModeListLessees(drm->fd); + if (list == NULL) { + wlr_log_errno(WLR_ERROR, "drmModeListLessees failed"); + return; + } + + struct wlr_drm_connector *conn; + wl_list_for_each(conn, &drm->outputs, link) { + if (conn->lease == NULL) { + continue; + } + + bool found = false; + for (size_t i = 0; i < list->count; i++) { + if (list->lessees[i] == conn->lease->lessee_id) { + found = true; + break; + } + } + if (!found) { + wlr_log(WLR_DEBUG, "DRM lease %"PRIu32" has been terminated", + conn->lease->lessee_id); + drm_lease_destroy(conn->lease); + } + } + + drmFree(list); +} + static int mhz_to_nsec(int mhz) { return 1000000000000LL / mhz; } diff --git a/include/backend/drm/drm.h b/include/backend/drm/drm.h index d3640afb81..c5d3358f91 100644 --- a/include/backend/drm/drm.h +++ b/include/backend/drm/drm.h @@ -149,6 +149,7 @@ bool init_drm_resources(struct wlr_drm_backend *drm); void finish_drm_resources(struct wlr_drm_backend *drm); void scan_drm_connectors(struct wlr_drm_backend *state, struct wlr_device_hotplug_event *event); +void scan_drm_leases(struct wlr_drm_backend *drm); int handle_drm_event(int fd, uint32_t mask, void *data); void destroy_drm_connector(struct wlr_drm_connector *conn); bool drm_connector_commit_state(struct wlr_drm_connector *conn, From 52c34e82539e1f09711046a3f1e1fdad100eb5c0 Mon Sep 17 00:00:00 2001 From: Simon Zeni Date: Thu, 18 Nov 2021 16:29:05 -0500 Subject: [PATCH 047/188] tinywl: build with meson if examples option is enabled --- meson.build | 1 + tinywl/meson.build | 5 +++++ 2 files changed, 6 insertions(+) create mode 100644 tinywl/meson.build diff --git a/meson.build b/meson.build index c546b74d0e..2f29694967 100644 --- a/meson.build +++ b/meson.build @@ -180,6 +180,7 @@ summary(features + internal_features, bool_yn: true) if get_option('examples') subdir('examples') + subdir('tinywl') endif pkgconfig = import('pkgconfig') diff --git a/tinywl/meson.build b/tinywl/meson.build new file mode 100644 index 0000000000..82d31d22f6 --- /dev/null +++ b/tinywl/meson.build @@ -0,0 +1,5 @@ +executable( + 'tinywl', + ['tinywl.c', protocols_client_header['xdg-shell']], + dependencies: wlroots, +) From d70d74ad4f3069687ed7f98f9cbbe9f6551e05c6 Mon Sep 17 00:00:00 2001 From: Simon Zeni Date: Fri, 19 Nov 2021 10:52:57 -0500 Subject: [PATCH 048/188] ci/archlinux: enable address and undefined sanitizers --- .builds/archlinux.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.builds/archlinux.yml b/.builds/archlinux.yml index 4d8e3767b0..ec98563c93 100644 --- a/.builds/archlinux.yml +++ b/.builds/archlinux.yml @@ -23,14 +23,14 @@ sources: tasks: - setup: | cd wlroots - CC=gcc meson build-gcc --fatal-meson-warnings --default-library=both -Dauto_features=enabled --prefix /usr + CC=gcc meson build-gcc --fatal-meson-warnings --default-library=both -Dauto_features=enabled --prefix /usr -Db_sanitize=address,undefined CC=clang meson build-clang --fatal-meson-warnings -Dauto_features=enabled - gcc: | cd wlroots/build-gcc ninja sudo ninja install cd ../tinywl - make + CFLAGS="-fsanitize=address,undefined -fno-omit-frame-pointer" make - clang: | cd wlroots/build-clang ninja From 3d73b899ffdb6cdb45f69acfa5d7a914943dc592 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 22 Nov 2021 10:32:55 +0100 Subject: [PATCH 049/188] linux-dmabuf-v1: hide wlr_linux_buffer_params_v1 The parameters are used when the client is in the process of building a buffer. There's no reason why this internal implementation detail should be exposed in our public header. --- include/wlr/types/wlr_linux_dmabuf_v1.h | 11 ++++------- types/wlr_linux_dmabuf_v1.c | 7 +++++++ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/include/wlr/types/wlr_linux_dmabuf_v1.h b/include/wlr/types/wlr_linux_dmabuf_v1.h index a2db7fea34..b4eb8675d0 100644 --- a/include/wlr/types/wlr_linux_dmabuf_v1.h +++ b/include/wlr/types/wlr_linux_dmabuf_v1.h @@ -20,6 +20,8 @@ struct wlr_dmabuf_v1_buffer { struct wl_resource *resource; // can be NULL if the client destroyed it struct wlr_dmabuf_attributes attributes; + // private state + struct wl_listener release; }; @@ -36,13 +38,6 @@ bool wlr_dmabuf_v1_resource_is_buffer(struct wl_resource *buffer_resource); struct wlr_dmabuf_v1_buffer *wlr_dmabuf_v1_buffer_from_buffer_resource( struct wl_resource *buffer_resource); -struct wlr_linux_buffer_params_v1 { - struct wl_resource *resource; - struct wlr_linux_dmabuf_v1 *linux_dmabuf; - struct wlr_dmabuf_attributes attributes; - bool has_modifier; -}; - /* the protocol interface */ struct wlr_linux_dmabuf_v1 { struct wl_global *global; @@ -52,6 +47,8 @@ struct wlr_linux_dmabuf_v1 { struct wl_signal destroy; } events; + // private state + struct wl_listener display_destroy; struct wl_listener renderer_destroy; }; diff --git a/types/wlr_linux_dmabuf_v1.c b/types/wlr_linux_dmabuf_v1.c index 9b4cdd4452..63b52023d5 100644 --- a/types/wlr_linux_dmabuf_v1.c +++ b/types/wlr_linux_dmabuf_v1.c @@ -13,6 +13,13 @@ #define LINUX_DMABUF_VERSION 3 +struct wlr_linux_buffer_params_v1 { + struct wl_resource *resource; + struct wlr_linux_dmabuf_v1 *linux_dmabuf; + struct wlr_dmabuf_attributes attributes; + bool has_modifier; +}; + static void buffer_handle_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); From 3b93da70a0b9419c56958c0095ce441dbc1282b1 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 22 Nov 2021 19:29:30 +0100 Subject: [PATCH 050/188] backend/wayland: report parent presentation clock There's no guarantee that the parent Wayland compositor uses CLOCK_MONOTONIC for reporting presentation timestamps, they could be using e.g. CLOCK_MONOTONIC_RAW or another system-specific clock. Forward the value via wlr_backend_impl.get_presentation_clock. References: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/3254#note_1143061 --- backend/wayland/backend.c | 19 +++++++++++++++++++ include/backend/wayland.h | 1 + 2 files changed, 20 insertions(+) diff --git a/backend/wayland/backend.c b/backend/wayland/backend.c index ea216511c4..3d962d4fe3 100644 --- a/backend/wayland/backend.c +++ b/backend/wayland/backend.c @@ -75,6 +75,16 @@ static const struct xdg_wm_base_listener xdg_wm_base_listener = { xdg_wm_base_handle_ping, }; +static void presentation_handle_clock_id(void *data, + struct wp_presentation *presentation, uint32_t clock) { + struct wlr_wl_backend *wl = data; + wl->presentation_clock = clock; +} + +static const struct wp_presentation_listener presentation_listener = { + .clock_id = presentation_handle_clock_id, +}; + static void linux_dmabuf_v1_handle_format(void *data, struct zwp_linux_dmabuf_v1 *linux_dmabuf_v1, uint32_t format) { // Note, this event is deprecated @@ -227,6 +237,8 @@ static void registry_global(void *data, struct wl_registry *registry, } else if (strcmp(iface, wp_presentation_interface.name) == 0) { wl->presentation = wl_registry_bind(registry, name, &wp_presentation_interface, 1); + wp_presentation_add_listener(wl->presentation, + &presentation_listener, wl); } else if (strcmp(iface, zwp_tablet_manager_v2_interface.name) == 0) { wl->tablet_manager = wl_registry_bind(registry, name, &zwp_tablet_manager_v2_interface, 1); @@ -352,6 +364,11 @@ static void backend_destroy(struct wlr_backend *backend) { free(wl); } +static clockid_t backend_get_presentation_clock(struct wlr_backend *backend) { + struct wlr_wl_backend *wl = get_wl_backend_from_backend(backend); + return wl->presentation_clock; +} + static int backend_get_drm_fd(struct wlr_backend *backend) { struct wlr_wl_backend *wl = get_wl_backend_from_backend(backend); return wl->drm_fd; @@ -366,6 +383,7 @@ static uint32_t get_buffer_caps(struct wlr_backend *backend) { static const struct wlr_backend_impl backend_impl = { .start = backend_start, .destroy = backend_destroy, + .get_presentation_clock = backend_get_presentation_clock, .get_drm_fd = backend_get_drm_fd, .get_buffer_caps = get_buffer_caps, }; @@ -397,6 +415,7 @@ struct wlr_backend *wlr_wl_backend_create(struct wl_display *display, wl_list_init(&wl->outputs); wl_list_init(&wl->seats); wl_list_init(&wl->buffers); + wl->presentation_clock = CLOCK_MONOTONIC; wl->remote_display = wl_display_connect(remote); if (!wl->remote_display) { diff --git a/include/backend/wayland.h b/include/backend/wayland.h index 3235494dfa..32783597ff 100644 --- a/include/backend/wayland.h +++ b/include/backend/wayland.h @@ -40,6 +40,7 @@ struct wlr_wl_backend { struct zwp_relative_pointer_manager_v1 *zwp_relative_pointer_manager_v1; struct wl_list seats; // wlr_wl_seat.link struct zwp_tablet_manager_v2 *tablet_manager; + clockid_t presentation_clock; struct wlr_drm_format_set shm_formats; struct wlr_drm_format_set linux_dmabuf_v1_formats; struct wl_drm *legacy_drm; From c9ba9e82b6a829d6e6d5acc65753b3ade46cefb9 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Mon, 22 Nov 2021 22:43:39 +0100 Subject: [PATCH 051/188] wlr_drag: emit destroy after wl_data_device.leave --- types/data_device/wlr_drag.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/types/data_device/wlr_drag.c b/types/data_device/wlr_drag.c index 53bf4066a1..1952dcb884 100644 --- a/types/data_device/wlr_drag.c +++ b/types/data_device/wlr_drag.c @@ -138,9 +138,6 @@ static void drag_destroy(struct wlr_drag *drag) { } } - // We issue destroy after ending the grab to allow focus changes. - wlr_signal_emit_safe(&drag->events.destroy, drag); - if (drag->started) { drag_set_focus(drag, NULL, 0, 0); @@ -148,6 +145,13 @@ static void drag_destroy(struct wlr_drag *drag) { drag->seat->drag = NULL; } + // We issue destroy after ending the grab to allow focus changes. + // Furthermore, we wait until after clearing the drag focus in order + // to ensure that the wl_data_device.leave is sent before emitting the + // signal. This allows e.g. wl_pointer.enter to be sent in the destroy + // signal handler. + wlr_signal_emit_safe(&drag->events.destroy, drag); + if (drag->source) { wl_list_remove(&drag->source_destroy.link); } From 1d9c1bcea6a223379af63b4d779d53663eaffcf8 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 22 Nov 2021 22:30:40 +0100 Subject: [PATCH 052/188] input-device: remove wlr_input_device.link This field's ownership is unclear: it's in wlr_input_device, but it's not managed by the common code, it's up to each individual backend to use it and clean it up. Since this is a backend implementation detail, move it to the backend-specific structs. --- backend/headless/backend.c | 5 ++-- backend/headless/input_device.c | 8 +++--- backend/libinput/backend.c | 6 ++--- backend/libinput/events.c | 16 ++++++------ backend/wayland/backend.c | 4 +-- backend/wayland/seat.c | 38 ++++++++++++++-------------- backend/wayland/tablet_v2.c | 4 +-- include/backend/headless.h | 2 +- include/backend/libinput.h | 2 +- include/backend/wayland.h | 1 + include/wlr/types/wlr_input_device.h | 2 -- 11 files changed, 44 insertions(+), 44 deletions(-) diff --git a/backend/headless/backend.c b/backend/headless/backend.c index 886be27614..ad0c4ebfe8 100644 --- a/backend/headless/backend.c +++ b/backend/headless/backend.c @@ -33,8 +33,7 @@ static bool backend_start(struct wlr_backend *wlr_backend) { } struct wlr_headless_input_device *input_device; - wl_list_for_each(input_device, &backend->input_devices, - wlr_input_device.link) { + wl_list_for_each(input_device, &backend->input_devices, link) { wlr_signal_emit_safe(&backend->backend.events.new_input, &input_device->wlr_input_device); } @@ -59,7 +58,7 @@ static void backend_destroy(struct wlr_backend *wlr_backend) { 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) { + &backend->input_devices, link) { wlr_input_device_destroy(&input_device->wlr_input_device); } diff --git a/backend/headless/input_device.c b/backend/headless/input_device.c index 1d91f8d12b..4df01aff80 100644 --- a/backend/headless/input_device.c +++ b/backend/headless/input_device.c @@ -12,8 +12,10 @@ #include "util/signal.h" static void input_device_destroy(struct wlr_input_device *wlr_dev) { - wl_list_remove(&wlr_dev->link); - free(wlr_dev); + struct wlr_headless_input_device *dev = + wl_container_of(wlr_dev, dev, wlr_input_device); + wl_list_remove(&dev->link); + free(dev); } static const struct wlr_input_device_impl input_device_impl = { @@ -93,7 +95,7 @@ struct wlr_input_device *wlr_headless_add_input_device( wlr_switch_init(wlr_device->switch_device, NULL); } - wl_list_insert(&backend->input_devices, &wlr_device->link); + wl_list_insert(&backend->input_devices, &device->link); if (backend->started) { wlr_signal_emit_safe(&backend->backend.events.new_input, wlr_device); diff --git a/backend/libinput/backend.c b/backend/libinput/backend.c index 00145e419c..4191f35071 100644 --- a/backend/libinput/backend.c +++ b/backend/libinput/backend.c @@ -143,9 +143,9 @@ static void backend_destroy(struct wlr_backend *wlr_backend) { struct wl_list **wlr_devices_ptr; wl_array_for_each(wlr_devices_ptr, &backend->wlr_device_lists) { - struct wlr_input_device *wlr_dev, *next; - wl_list_for_each_safe(wlr_dev, next, *wlr_devices_ptr, link) { - wlr_input_device_destroy(wlr_dev); + struct wlr_libinput_input_device *dev, *tmp; + wl_list_for_each_safe(dev, tmp, *wlr_devices_ptr, link) { + wlr_input_device_destroy(&dev->wlr_input_device); } free(*wlr_devices_ptr); } diff --git a/backend/libinput/events.c b/backend/libinput/events.c index 8288377942..67598998b6 100644 --- a/backend/libinput/events.c +++ b/backend/libinput/events.c @@ -23,10 +23,10 @@ struct wlr_input_device *get_appropriate_device( if (!wlr_devices) { return NULL; } - struct wlr_input_device *dev; + struct wlr_libinput_input_device *dev; wl_list_for_each(dev, wlr_devices, link) { - if (dev->type == desired_type) { - return dev; + if (dev->wlr_input_device.type == desired_type) { + return &dev->wlr_input_device; } } return NULL; @@ -36,7 +36,7 @@ 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); + wl_list_remove(&dev->link); free(dev); } @@ -63,7 +63,7 @@ static struct wlr_input_device *allocate_device( if (output_name != NULL) { wlr_dev->output_name = strdup(output_name); } - wl_list_insert(wlr_devices, &wlr_dev->link); + wl_list_insert(wlr_devices, &dev->link); dev->handle = libinput_dev; libinput_device_ref(libinput_dev); wlr_input_device_init(wlr_dev, type, &input_device_impl, @@ -198,7 +198,7 @@ static void handle_device_added(struct wlr_libinput_backend *backend, fail: wlr_log(WLR_ERROR, "Could not allocate new device"); - struct wlr_input_device *dev, *tmp_dev; + struct wlr_libinput_input_device *dev, *tmp_dev; wl_list_for_each_safe(dev, tmp_dev, wlr_devices, link) { free(dev); } @@ -215,9 +215,9 @@ static void handle_device_removed(struct wlr_libinput_backend *backend, if (!wlr_devices) { return; } - struct wlr_input_device *dev, *tmp_dev; + struct wlr_libinput_input_device *dev, *tmp_dev; wl_list_for_each_safe(dev, tmp_dev, wlr_devices, link) { - wlr_input_device_destroy(dev); + wlr_input_device_destroy(&dev->wlr_input_device); } size_t i = 0; struct wl_list **ptr; diff --git a/backend/wayland/backend.c b/backend/wayland/backend.c index 3d962d4fe3..63d6a7df6a 100644 --- a/backend/wayland/backend.c +++ b/backend/wayland/backend.c @@ -314,9 +314,9 @@ static void backend_destroy(struct wlr_backend *backend) { wlr_output_destroy(&output->wlr_output); } - struct wlr_input_device *input_device, *tmp_input_device; + struct wlr_wl_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_input_device_destroy(&input_device->wlr_input_device); } struct wlr_wl_buffer *buffer, *tmp_buffer; diff --git a/backend/wayland/seat.c b/backend/wayland/seat.c index 508551a0f4..7792d136eb 100644 --- a/backend/wayland/seat.c +++ b/backend/wayland/seat.c @@ -25,12 +25,13 @@ static struct wlr_wl_pointer *output_get_pointer( struct wlr_wl_output *output, const struct wl_pointer *wl_pointer) { - struct wlr_input_device *wlr_dev; - wl_list_for_each(wlr_dev, &output->backend->devices, link) { - if (wlr_dev->type != WLR_INPUT_DEVICE_POINTER) { + struct wlr_wl_input_device *dev; + wl_list_for_each(dev, &output->backend->devices, link) { + if (dev->wlr_input_device.type != WLR_INPUT_DEVICE_POINTER) { continue; } - struct wlr_wl_pointer *pointer = pointer_get_wl(wlr_dev->pointer); + struct wlr_wl_pointer *pointer = + pointer_get_wl(dev->wlr_input_device.pointer); if (pointer->output == output && pointer->wl_pointer == wl_pointer) { return pointer; } @@ -440,7 +441,7 @@ static void input_device_destroy(struct wlr_input_device *wlr_dev) { } // We can't destroy pointer here because we might have multiple devices // exposing it to compositor. - wl_list_remove(&dev->wlr_input_device.link); + wl_list_remove(&dev->link); free(dev); } @@ -473,7 +474,7 @@ struct wlr_wl_input_device *create_wl_input_device( wlr_input_device_init(wlr_dev, type, &input_device_impl, name, vendor, product); - wl_list_insert(&seat->backend->devices, &wlr_dev->link); + wl_list_insert(&seat->backend->devices, &dev->link); return dev; } @@ -822,19 +823,20 @@ static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat, struct wl_pointer *wl_pointer = seat->pointer; - struct wlr_input_device *device, *tmp; + struct wlr_wl_input_device *device, *tmp; wl_list_for_each_safe(device, tmp, &backend->devices, link) { - if (device->type != WLR_INPUT_DEVICE_POINTER) { + if (device->wlr_input_device.type != WLR_INPUT_DEVICE_POINTER) { continue; } - struct wlr_wl_pointer *pointer = pointer_get_wl(device->pointer); + struct wlr_wl_pointer *pointer = + pointer_get_wl(device->wlr_input_device.pointer); if (pointer->wl_pointer != wl_pointer) { continue; } wlr_log(WLR_DEBUG, "dropping pointer %s", pointer->input_device->wlr_input_device.name); struct wlr_wl_output *output = pointer->output; - wlr_input_device_destroy(device); + wlr_input_device_destroy(&device->wlr_input_device); assert(seat->active_pointer != pointer); assert(output->cursor.pointer != pointer); } @@ -856,18 +858,16 @@ static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat, if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && seat->keyboard != NULL) { wlr_log(WLR_DEBUG, "seat %p dropped keyboard", (void *)wl_seat); - struct wlr_input_device *device, *tmp; + struct wlr_wl_input_device *device, *tmp; wl_list_for_each_safe(device, tmp, &backend->devices, link) { - if (device->type != WLR_INPUT_DEVICE_KEYBOARD) { + if (device->wlr_input_device.type != WLR_INPUT_DEVICE_KEYBOARD) { continue; } - struct wlr_wl_input_device *input_device = - get_wl_input_device_from_input_device(device); - if (input_device->seat != seat) { + if (device->seat != seat) { continue; } - wlr_input_device_destroy(device); + wlr_input_device_destroy(&device->wlr_input_device); } assert(seat->keyboard == NULL); // free'ed by input_device_destroy } @@ -883,10 +883,10 @@ static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat, if (!(caps & WL_SEAT_CAPABILITY_TOUCH) && seat->touch != NULL) { wlr_log(WLR_DEBUG, "seat %p dropped touch", (void *)wl_seat); - struct wlr_input_device *device, *tmp; + struct wlr_wl_input_device *device, *tmp; wl_list_for_each_safe(device, tmp, &backend->devices, link) { - if (device->type == WLR_INPUT_DEVICE_TOUCH) { - wlr_input_device_destroy(device); + if (device->wlr_input_device.type == WLR_INPUT_DEVICE_TOUCH) { + wlr_input_device_destroy(&device->wlr_input_device); } } diff --git a/backend/wayland/tablet_v2.c b/backend/wayland/tablet_v2.c index 9f90a36968..cda6510115 100644 --- a/backend/wayland/tablet_v2.c +++ b/backend/wayland/tablet_v2.c @@ -402,7 +402,7 @@ static void handle_tablet_pad_removed(void *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); + wl_list_remove(&dev->link); struct wlr_wl_tablet_pad_group *group, *it; wl_list_for_each_safe(group, it, &tablet_pad->groups, group.link) { @@ -873,7 +873,7 @@ static void handle_tablet_removed(void *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); + wl_list_remove(&dev->link); zwp_tablet_v2_destroy(dev->resource); free(dev); diff --git a/include/backend/headless.h b/include/backend/headless.h index 09d5856b24..f60d07e66d 100644 --- a/include/backend/headless.h +++ b/include/backend/headless.h @@ -29,7 +29,7 @@ struct wlr_headless_output { struct wlr_headless_input_device { struct wlr_input_device wlr_input_device; - + struct wl_list link; struct wlr_headless_backend *backend; }; diff --git a/include/backend/libinput.h b/include/backend/libinput.h index e85ad29132..a6841d7e9e 100644 --- a/include/backend/libinput.h +++ b/include/backend/libinput.h @@ -26,7 +26,7 @@ struct wlr_libinput_backend { struct wlr_libinput_input_device { struct wlr_input_device wlr_input_device; - + struct wl_list link; struct libinput_device *handle; }; diff --git a/include/backend/wayland.h b/include/backend/wayland.h index 32783597ff..4e5d363614 100644 --- a/include/backend/wayland.h +++ b/include/backend/wayland.h @@ -87,6 +87,7 @@ struct wlr_wl_output { struct wlr_wl_input_device { struct wlr_input_device wlr_input_device; + struct wl_list link; uint32_t fingers; struct wlr_wl_backend *backend; diff --git a/include/wlr/types/wlr_input_device.h b/include/wlr/types/wlr_input_device.h index 8ed9b4659f..cbc877df67 100644 --- a/include/wlr/types/wlr_input_device.h +++ b/include/wlr/types/wlr_input_device.h @@ -53,8 +53,6 @@ struct wlr_input_device { } events; void *data; - - struct wl_list link; }; #endif From 5332935afcdd519dd2dd9dbfeb50c6aa0f91e9c3 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 7 Nov 2021 13:22:14 +0100 Subject: [PATCH 053/188] render/vulkan: quiet glslangValidator This suppresses the output filename printed to stdout. Errors and warnings should still be printed to stderr as usual. --- render/vulkan/meson.build | 3 +++ render/vulkan/shaders/meson.build | 3 +++ 2 files changed, 6 insertions(+) diff --git a/render/vulkan/meson.build b/render/vulkan/meson.build index 6ae8df0c3d..0c45a7136a 100644 --- a/render/vulkan/meson.build +++ b/render/vulkan/meson.build @@ -33,6 +33,9 @@ if not glslang.found() endif endif +glslang_version_info = run_command(glslang, '--version', check: true).stdout() +glslang_version = glslang_version_info.split('\n')[0].split(':')[-1] + wlr_files += files( 'renderer.c', 'texture.c', diff --git a/render/vulkan/shaders/meson.build b/render/vulkan/shaders/meson.build index b183c46ca6..906618c290 100644 --- a/render/vulkan/shaders/meson.build +++ b/render/vulkan/shaders/meson.build @@ -8,6 +8,9 @@ vulkan_shaders = [] foreach shader : vulkan_shaders_src name = shader.underscorify() + '_data' args = [glslang, '-V', '@INPUT@', '-o', '@OUTPUT@', '--vn', name] + if glslang_version.version_compare('>=11.0.0') + args += '--quiet' + endif header = custom_target( shader + '_spv', output: shader + '.h', From f132d66816263a509b46092e14755b5e3f0e258f Mon Sep 17 00:00:00 2001 From: Joshua Ashton Date: Wed, 10 Nov 2021 11:41:39 +0000 Subject: [PATCH 054/188] render/vulkan: Optimize vertex shader This ends up being a horrible global load: s_getpc_b64 s[4:5] // 000000000000: BE841C80 v_add_u32 v0, s2, v0 // 000000000004: 68000002 v_sub_co_u32 v1, vcc, 0, v0 // 000000000008: 34020080 v_max_i32 v1, v0, v1 // 00000000000C: 1A020300 v_and_b32 v1, 3, v1 // 000000000010: 26020283 v_cmp_lt_i32 s[0:1], v0, 0 // 000000000014: D0C10000 00010100 v_sub_co_u32 v0, vcc, 0, v1 // 00000000001C: 34000280 v_cndmask_b32 v0, v1, v0, s[0:1] // 000000000020: D1000000 00020101 v_lshlrev_b32 v1, 3, v0 // 000000000028: 24020083 v_mad_u32_u24 v0, v0, 8, 4 // 00000000002C: D1C30000 02111100 v_min_u32 v1, 32, v1 // 000000000034: 1C0202A0 v_min_u32 v0, 32, v0 // 000000000038: 1C0000A0 s_getpc_b64 s[0:1] // 00000000003C: BE801C00 s_add_u32 s0, s0, 0x0000003c // 000000000040: 8000FF00 0000003C s_addc_u32 s1, s1, 0 // 000000000048: 82018001 global_load_dword v1, v[1:2], s[0:1] // 00000000004C: DC508000 01000001 global_load_dword v0, v[0:1], s[0:1] // 000000000054: DC508000 00000000 v_mov_b32 v2, 0 // 00000000005C: 7E040280 v_mov_b32 v3, 1.0 // 000000000060: 7E0602F2 s_waitcnt vmcnt(0) // 000000000064: BF8C0F70 exp pos0, v1, v0, v2, v3 done // 000000000068: C40008CF 03020001 exp param0, off, off, off, off // 000000000070: C4000200 00000000 s_endpgm // 000000000078: BF810000 v_cndmask_b32 v0, s0, v0, vcc // 00000000007C: 00000000 v_cndmask_b32 v0, s0, v0, vcc // 000000000080: 00000000 v_add_f16 v192, s0, v0 // 000000000084: 3F800000 v_cndmask_b32 v0, s0, v0, vcc // 000000000088: 00000000 v_add_f16 v192, s0, v0 // 00000000008C: 3F800000 v_add_f16 v192, s0, v0 // 000000000090: 3F800000 v_cndmask_b32 v0, s0, v0, vcc // 000000000094: 00000000 v_add_f16 v192, s0, v0 // 000000000098: 3F800000 v_cndmask_b32 v0, s0, v0, vcc // 00000000009C: 00000000 With some bit magic, we can get something much nicer: v_add_u32 v0, s2, v0 // 000000000000: 68000002 v_add_u32 v1, 1, v0 // 000000000004: 68020081 v_and_b32 v1, 2, v1 // 000000000008: 26020282 v_cvt_f32_i32 v1, v1 // 00000000000C: 7E020B01 v_mul_f32 v1, 0.5, v1 // 000000000010: 0A0202F0 v_and_b32 v0, 2, v0 // 000000000014: 26000082 v_cvt_f32_i32 v0, v0 // 000000000018: 7E000B00 v_mul_f32 v0, 0.5, v0 // 00000000001C: 0A0000F0 v_mov_b32 v2, 0 // 000000000020: 7E040280 v_mov_b32 v3, 1.0 // 000000000024: 7E0602F2 exp pos0, v1, v0, v2, v3 done // 000000000028: C40008CF 03020001 exp param0, off, off, off, off // 000000000030: C4000200 00000000 s_endpgm // 000000000038: BF810000 The above output was based on just shoving it in ShaderPlayground -- I was not able to use pipeline feedback as I was unable to get RenderDoc working due to the EXT_physical_device_drm requirement. I additionally considered using >> 1 instead of * 0.5, but AMD has dedicated modifiers to merge a * 0.5, * 2.0, etc in a single instruction. (Albeit, not taken advantage of in the code above, but might with ACO) Signed-off-by: Joshua Ashton --- render/vulkan/shaders/common.vert | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/render/vulkan/shaders/common.vert b/render/vulkan/shaders/common.vert index fa31d26c25..c6175d248f 100644 --- a/render/vulkan/shaders/common.vert +++ b/render/vulkan/shaders/common.vert @@ -10,16 +10,9 @@ layout(push_constant, row_major) uniform UBO { layout(location = 0) out vec2 uv; -// 4 outlining points and uv coords -const vec2[] values = { - {0, 0}, - {1, 0}, - {1, 1}, - {0, 1}, -}; - void main() { - vec2 pos = values[gl_VertexIndex % 4]; + vec2 pos = vec2(float((gl_VertexIndex + 1) & 2) * 0.5f, + float(gl_VertexIndex & 2) * 0.5f); uv = data.uv_offset + pos * data.uv_size; gl_Position = data.proj * vec4(pos, 0.0, 1.0); } From bcefb71cf61b4d12465c8ba41c79ec6b651e045e Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 24 Nov 2021 10:57:53 +0100 Subject: [PATCH 055/188] docs/env_vars: drop WLR_DIRECT_TTY The direct session is gone, so this env var isn't looked up anymore. --- docs/env_vars.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/env_vars.md b/docs/env_vars.md index 2959ab581a..66c6dcc6c1 100644 --- a/docs/env_vars.md +++ b/docs/env_vars.md @@ -6,7 +6,6 @@ wlroots reads these environment variables libinput, drm, wayland, x11, headless, noop) * *WLR_NO_HARDWARE_CURSORS*: set to 1 to use software cursors instead of hardware cursors -* *WLR_DIRECT_TTY*: specifies the tty to be used (instead of using /dev/tty) * *WLR_XWAYLAND*: specifies the path to an Xwayland binary to be used (instead of following shell search semantics for "Xwayland") * *WLR_RENDERER*: forces the creation of a specified renderer (available From bf578255606d2bcc9d283c3a5042deddf29c52b5 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 24 Nov 2021 10:35:30 +0100 Subject: [PATCH 056/188] render: pick DRM FD in autocreate If the backend hasn't picked a DRM FD but supports DMA-BUF, pick an arbitrary render node. This will allow removing the DRM device selection logic from the headless backend. --- render/wlr_renderer.c | 69 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/render/wlr_renderer.c b/render/wlr_renderer.c index 8a1f1e9418..6667a81990 100644 --- a/render/wlr_renderer.c +++ b/render/wlr_renderer.c @@ -1,6 +1,9 @@ +#define _POSIX_C_SOURCE 200809L #include +#include #include #include +#include #include #include #include @@ -9,6 +12,7 @@ #include #include #include +#include #include @@ -21,6 +25,7 @@ #include #endif // WLR_HAS_VULKAN_RENDERER +#include "backend/backend.h" #include "util/signal.h" #include "render/pixel_format.h" #include "render/wlr_renderer.h" @@ -296,10 +301,72 @@ struct wlr_renderer *renderer_autocreate_with_drm_fd(int drm_fd) { return NULL; } +static int open_drm_render_node(void) { + uint32_t flags = 0; + int devices_len = drmGetDevices2(flags, NULL, 0); + if (devices_len < 0) { + wlr_log(WLR_ERROR, "drmGetDevices2 failed: %s", strerror(-devices_len)); + return -1; + } + drmDevice **devices = calloc(devices_len, sizeof(drmDevice *)); + if (devices == NULL) { + wlr_log_errno(WLR_ERROR, "Allocation failed"); + return -1; + } + devices_len = drmGetDevices2(flags, devices, devices_len); + if (devices_len < 0) { + free(devices); + wlr_log(WLR_ERROR, "drmGetDevices2 failed: %s", strerror(-devices_len)); + return -1; + } + + int fd = -1; + for (int i = 0; i < devices_len; i++) { + drmDevice *dev = devices[i]; + if (dev->available_nodes & (1 << DRM_NODE_RENDER)) { + const char *name = dev->nodes[DRM_NODE_RENDER]; + wlr_log(WLR_DEBUG, "Opening DRM render node '%s'", name); + fd = open(name, O_RDWR | O_CLOEXEC); + if (fd < 0) { + wlr_log_errno(WLR_ERROR, "Failed to open '%s'", name); + goto out; + } + break; + } + } + if (fd < 0) { + wlr_log(WLR_ERROR, "Failed to find any DRM render node"); + } + +out: + for (int i = 0; i < devices_len; i++) { + drmFreeDevice(&devices[i]); + } + free(devices); + + return fd; +} + struct wlr_renderer *wlr_renderer_autocreate(struct wlr_backend *backend) { // Note, drm_fd may be negative if unavailable int drm_fd = wlr_backend_get_drm_fd(backend); - return renderer_autocreate_with_drm_fd(drm_fd); + + // If the backend hasn't picked a DRM FD, but accepts DMA-BUFs, pick an + // arbitrary render node + int render_drm_fd = -1; + uint32_t backend_caps = backend_get_buffer_caps(backend); + if (drm_fd < 0 && (backend_caps & WLR_BUFFER_CAP_DMABUF) != 0) { + render_drm_fd = open_drm_render_node(); + drm_fd = render_drm_fd; + } + + struct wlr_renderer *renderer = renderer_autocreate_with_drm_fd(drm_fd); + + if (render_drm_fd >= 0) { + close(render_drm_fd); + } + + return renderer; } int wlr_renderer_get_drm_fd(struct wlr_renderer *r) { From e4f748c6e9d618eb2ee8f15aa22aae8dcde4cc4a Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 24 Nov 2021 10:33:13 +0100 Subject: [PATCH 057/188] render/allocator: fallback to renderer DRM FD in autocreate If the backend doesn't have a DRM FD, fallback to the renderer's. This accomodates for the situation where the headless backend hasn't picked a DRM FD in particular, but the renderer has picked one. --- render/allocator/allocator.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/render/allocator/allocator.c b/render/allocator/allocator.c index 15d55a0dec..5108ad0437 100644 --- a/render/allocator/allocator.c +++ b/render/allocator/allocator.c @@ -139,6 +139,9 @@ struct wlr_allocator *wlr_allocator_autocreate(struct wlr_backend *backend, struct wlr_renderer *renderer) { // Note, drm_fd may be negative if unavailable int drm_fd = wlr_backend_get_drm_fd(backend); + if (drm_fd < 0) { + drm_fd = wlr_renderer_get_drm_fd(renderer); + } return allocator_autocreate_with_drm_fd(backend, renderer, drm_fd); } From f29abe4c777ba8293ab76334ef2ac83eaa0043f6 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 24 Nov 2021 10:36:39 +0100 Subject: [PATCH 058/188] backend/headless: stop picking a DRM FD Sometimes the headless backend is used standalone with the Pixman renderer, sometimes it's used together with another backend which has already picked a DRM FD. In both of these cases it doesn't make sense to pick a DRM FD. Broadly speaking the headless backend doesn't really care which DRM device is used for the buffers it receives. So it doesn't really make sense to tie it to a particular DRM device. Let the backend users (e.g. wlr_renderer_autocreate) open an arbitrary DRM FD as needed instead. --- backend/headless/backend.c | 93 ++------------------------------------ backend/headless/output.c | 1 - include/backend/headless.h | 1 - 3 files changed, 4 insertions(+), 91 deletions(-) diff --git a/backend/headless/backend.c b/backend/headless/backend.c index ad0c4ebfe8..8d74287d38 100644 --- a/backend/headless/backend.c +++ b/backend/headless/backend.c @@ -1,16 +1,9 @@ -#define _POSIX_C_SOURCE 200809L #include -#include -#include #include -#include #include #include -#include #include -#include #include "backend/headless.h" -#include "render/drm_format_set.h" #include "util/signal.h" struct wlr_headless_backend *headless_backend_from_backend( @@ -64,16 +57,9 @@ static void backend_destroy(struct wlr_backend *wlr_backend) { wlr_backend_finish(wlr_backend); - close(backend->drm_fd); free(backend); } -static int backend_get_drm_fd(struct wlr_backend *wlr_backend) { - struct wlr_headless_backend *backend = - headless_backend_from_backend(wlr_backend); - return backend->drm_fd; -} - static uint32_t get_buffer_caps(struct wlr_backend *wlr_backend) { return WLR_BUFFER_CAP_DATA_PTR | WLR_BUFFER_CAP_DMABUF @@ -83,7 +69,6 @@ static uint32_t get_buffer_caps(struct wlr_backend *wlr_backend) { static const struct wlr_backend_impl backend_impl = { .start = backend_start, .destroy = backend_destroy, - .get_drm_fd = backend_get_drm_fd, .get_buffer_caps = get_buffer_caps, }; @@ -107,52 +92,6 @@ static bool backend_init(struct wlr_headless_backend *backend, return true; } -static int open_drm_render_node(void) { - uint32_t flags = 0; - int devices_len = drmGetDevices2(flags, NULL, 0); - if (devices_len < 0) { - wlr_log(WLR_ERROR, "drmGetDevices2 failed: %s", strerror(-devices_len)); - return -1; - } - drmDevice **devices = calloc(devices_len, sizeof(drmDevice *)); - if (devices == NULL) { - wlr_log_errno(WLR_ERROR, "Allocation failed"); - return -1; - } - devices_len = drmGetDevices2(flags, devices, devices_len); - if (devices_len < 0) { - free(devices); - wlr_log(WLR_ERROR, "drmGetDevices2 failed: %s", strerror(-devices_len)); - return -1; - } - - int fd = -1; - for (int i = 0; i < devices_len; i++) { - drmDevice *dev = devices[i]; - if (dev->available_nodes & (1 << DRM_NODE_RENDER)) { - const char *name = dev->nodes[DRM_NODE_RENDER]; - wlr_log(WLR_DEBUG, "Opening DRM render node '%s'", name); - fd = open(name, O_RDWR | O_CLOEXEC); - if (fd < 0) { - wlr_log_errno(WLR_ERROR, "Failed to open '%s'", name); - goto out; - } - break; - } - } - if (fd < 0) { - wlr_log(WLR_ERROR, "Failed to find any DRM render node"); - } - -out: - for (int i = 0; i < devices_len; i++) { - drmFreeDevice(&devices[i]); - } - free(devices); - - return fd; -} - struct wlr_backend *wlr_headless_backend_create(struct wl_display *display) { wlr_log(WLR_INFO, "Creating headless backend"); @@ -163,21 +102,12 @@ struct wlr_backend *wlr_headless_backend_create(struct wl_display *display) { return NULL; } - backend->drm_fd = open_drm_render_node(); - if (backend->drm_fd < 0) { - wlr_log(WLR_ERROR, "Failed to open DRM render node"); - } - if (!backend_init(backend, display)) { - goto error_init; + free(backend); + return NULL; } return &backend->backend; - -error_init: - close(backend->drm_fd); - free(backend); - return NULL; } struct wlr_backend *wlr_headless_backend_create_with_renderer( @@ -191,27 +121,12 @@ struct wlr_backend *wlr_headless_backend_create_with_renderer( return NULL; } - int drm_fd = wlr_renderer_get_drm_fd(renderer); - if (drm_fd < 0) { - wlr_log(WLR_ERROR, "Failed to get DRM device FD from parent renderer"); - backend->drm_fd = -1; - } else { - backend->drm_fd = fcntl(drm_fd, F_DUPFD_CLOEXEC, 0); - if (backend->drm_fd < 0) { - wlr_log_errno(WLR_ERROR, "fcntl(F_DUPFD_CLOEXEC) failed"); - } - } - if (!backend_init(backend, display)) { - goto error_init; + free(backend); + return NULL; } return &backend->backend; - -error_init: - close(backend->drm_fd); - free(backend); - return NULL; } bool wlr_backend_is_headless(struct wlr_backend *backend) { diff --git a/backend/headless/output.c b/backend/headless/output.c index 43762b2392..50d914e447 100644 --- a/backend/headless/output.c +++ b/backend/headless/output.c @@ -2,7 +2,6 @@ #include #include #include -#include #include #include "backend/headless.h" #include "util/signal.h" diff --git a/include/backend/headless.h b/include/backend/headless.h index f60d07e66d..e126ac4efc 100644 --- a/include/backend/headless.h +++ b/include/backend/headless.h @@ -8,7 +8,6 @@ struct wlr_headless_backend { struct wlr_backend backend; - int drm_fd; struct wl_display *display; struct wl_list outputs; size_t last_output_num; From 2e33139ef732040c86e913ecb984c8eb0bf66cb2 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 24 Nov 2021 10:52:39 +0100 Subject: [PATCH 059/188] render: introduce WLR_RENDER_DRM_DEVICE This env var allows to override the DRM node used by the GLES2 and Vulkan renderers. It's especially useful to select a DRM node when running with the headless backend. References: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/2656 --- docs/env_vars.md | 2 ++ render/wlr_renderer.c | 29 ++++++++++++++++++++++++++--- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/docs/env_vars.md b/docs/env_vars.md index 66c6dcc6c1..a07b2f1974 100644 --- a/docs/env_vars.md +++ b/docs/env_vars.md @@ -10,6 +10,8 @@ wlroots reads these environment variables of following shell search semantics for "Xwayland") * *WLR_RENDERER*: forces the creation of a specified renderer (available renderers: gles2, pixman, vulkan) +* *WLR_RENDER_DRM_DEVICE*: specifies the DRM node to use for + hardware-accelerated renderers. ## DRM backend diff --git a/render/wlr_renderer.c b/render/wlr_renderer.c index 6667a81990..ff31591357 100644 --- a/render/wlr_renderer.c +++ b/render/wlr_renderer.c @@ -348,18 +348,41 @@ static int open_drm_render_node(void) { } struct wlr_renderer *wlr_renderer_autocreate(struct wlr_backend *backend) { - // Note, drm_fd may be negative if unavailable - int drm_fd = wlr_backend_get_drm_fd(backend); + int drm_fd = -1; + int render_drm_fd = -1; + + // Allow the user to override the render node + const char *render_name = getenv("WLR_RENDER_DRM_DEVICE"); + if (render_name != NULL) { + wlr_log(WLR_INFO, + "Opening DRM render node '%s' from WLR_RENDER_DRM_DEVICE", + render_name); + render_drm_fd = open(render_name, O_RDWR | O_CLOEXEC); + if (render_drm_fd < 0) { + wlr_log_errno(WLR_ERROR, "Failed to open '%s'", render_name); + return NULL; + } + if (drmGetNodeTypeFromFd(render_drm_fd) != DRM_NODE_RENDER) { + wlr_log(WLR_ERROR, "'%s' is not a DRM render node", render_name); + close(render_drm_fd); + return NULL; + } + drm_fd = render_drm_fd; + } + + if (drm_fd < 0) { + drm_fd = wlr_backend_get_drm_fd(backend); + } // If the backend hasn't picked a DRM FD, but accepts DMA-BUFs, pick an // arbitrary render node - int render_drm_fd = -1; uint32_t backend_caps = backend_get_buffer_caps(backend); if (drm_fd < 0 && (backend_caps & WLR_BUFFER_CAP_DMABUF) != 0) { render_drm_fd = open_drm_render_node(); drm_fd = render_drm_fd; } + // Note, drm_fd may be negative if unavailable struct wlr_renderer *renderer = renderer_autocreate_with_drm_fd(drm_fd); if (render_drm_fd >= 0) { From b234edcf58bbd73d64e8d5986aa5cc380d2f48d6 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 25 Nov 2021 10:50:28 +0100 Subject: [PATCH 060/188] backend/headless: drop wlr_headless_backend_create_with_renderer The headless backend no longer needs a parent renderer: it no longer needs to return it in wlr_backend_impl.get_renderer, nor does it need to return its DRM FD in wlr_backend_impl.get_drm_fd. Drop this function altogether since it now behaves exactly like wlr_headless_backend_create. --- backend/headless/backend.c | 41 +++++----------------------------- include/wlr/backend/headless.h | 5 ----- 2 files changed, 6 insertions(+), 40 deletions(-) diff --git a/backend/headless/backend.c b/backend/headless/backend.c index 8d74287d38..b778810511 100644 --- a/backend/headless/backend.c +++ b/backend/headless/backend.c @@ -78,20 +78,6 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) { backend_destroy(&backend->backend); } -static bool backend_init(struct wlr_headless_backend *backend, - struct wl_display *display) { - wlr_backend_init(&backend->backend, &backend_impl); - - backend->display = display; - wl_list_init(&backend->outputs); - wl_list_init(&backend->input_devices); - - backend->display_destroy.notify = handle_display_destroy; - wl_display_add_destroy_listener(display, &backend->display_destroy); - - return true; -} - struct wlr_backend *wlr_headless_backend_create(struct wl_display *display) { wlr_log(WLR_INFO, "Creating headless backend"); @@ -102,29 +88,14 @@ struct wlr_backend *wlr_headless_backend_create(struct wl_display *display) { return NULL; } - if (!backend_init(backend, display)) { - free(backend); - return NULL; - } - - return &backend->backend; -} - -struct wlr_backend *wlr_headless_backend_create_with_renderer( - struct wl_display *display, struct wlr_renderer *renderer) { - wlr_log(WLR_INFO, "Creating headless backend with parent renderer"); + wlr_backend_init(&backend->backend, &backend_impl); - 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; - } + backend->display = display; + wl_list_init(&backend->outputs); + wl_list_init(&backend->input_devices); - if (!backend_init(backend, display)) { - free(backend); - return NULL; - } + backend->display_destroy.notify = handle_display_destroy; + wl_display_add_destroy_listener(display, &backend->display_destroy); return &backend->backend; } diff --git a/include/wlr/backend/headless.h b/include/wlr/backend/headless.h index 3eb1677d2e..07dac9b84e 100644 --- a/include/wlr/backend/headless.h +++ b/include/wlr/backend/headless.h @@ -18,11 +18,6 @@ * default. */ struct wlr_backend *wlr_headless_backend_create(struct wl_display *display); -/** - * Creates a headless backend with an existing renderer. - */ -struct wlr_backend *wlr_headless_backend_create_with_renderer( - struct wl_display *display, struct wlr_renderer *renderer); /** * Create a new headless output backed by an in-memory EGL framebuffer. You can * read pixels from this framebuffer via wlr_renderer_read_pixels but it is From 1d3dd7fc08f22f2e82cb812ced822369e984d845 Mon Sep 17 00:00:00 2001 From: Simon Zeni Date: Thu, 30 Sep 2021 10:53:18 -0400 Subject: [PATCH 061/188] backend: remove noop backend --- backend/backend.c | 17 -------- backend/meson.build | 1 - backend/noop/backend.c | 79 ---------------------------------- backend/noop/meson.build | 4 -- backend/noop/output.c | 87 -------------------------------------- docs/env_vars.md | 2 +- include/backend/noop.h | 27 ------------ include/wlr/backend/noop.h | 31 -------------- 8 files changed, 1 insertion(+), 247 deletions(-) delete mode 100644 backend/noop/backend.c delete mode 100644 backend/noop/meson.build delete mode 100644 backend/noop/output.c delete mode 100644 include/backend/noop.h delete mode 100644 include/wlr/backend/noop.h diff --git a/backend/backend.c b/backend/backend.c index 7f0de3c2ca..33a2185188 100644 --- a/backend/backend.c +++ b/backend/backend.c @@ -9,7 +9,6 @@ #include #include #include -#include #include #include #include @@ -203,20 +202,6 @@ static struct wlr_backend *attempt_headless_backend( 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; -} - #if WLR_HAS_DRM_BACKEND static struct wlr_backend *attempt_drm_backend(struct wl_display *display, struct wlr_backend *backend, struct wlr_session *session) { @@ -269,8 +254,6 @@ static bool attempt_backend_by_name(struct wl_display *display, #endif } else if (strcmp(name, "headless") == 0) { backend = attempt_headless_backend(display); - } else if (strcmp(name, "noop") == 0) { - backend = attempt_noop_backend(display); } else if (strcmp(name, "drm") == 0 || strcmp(name, "libinput") == 0) { // DRM and libinput need a session if (multi->session == NULL) { diff --git a/backend/meson.build b/backend/meson.build index 7fffc8395d..71a00dc9b1 100644 --- a/backend/meson.build +++ b/backend/meson.build @@ -16,7 +16,6 @@ endforeach subdir('multi') subdir('wayland') -subdir('noop') subdir('headless') subdir('session') diff --git a/backend/noop/backend.c b/backend/noop/backend.c deleted file mode 100644 index 7bdda051ec..0000000000 --- a/backend/noop/backend.c +++ /dev/null @@ -1,79 +0,0 @@ -#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_backend_finish(wlr_backend); - - wl_list_remove(&backend->display_destroy.link); - - free(backend); -} - -static const struct wlr_backend_impl backend_impl = { - .start = backend_start, - .destroy = backend_destroy, -}; - -static void handle_display_destroy(struct wl_listener *listener, void *data) { - struct wlr_noop_backend *noop = - wl_container_of(listener, noop, display_destroy); - backend_destroy(&noop->backend); -} - -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); - - backend->display_destroy.notify = handle_display_destroy; - wl_display_add_destroy_listener(display, &backend->display_destroy); - - return &backend->backend; -} - -bool wlr_backend_is_noop(struct wlr_backend *backend) { - return backend->impl == &backend_impl; -} diff --git a/backend/noop/meson.build b/backend/noop/meson.build deleted file mode 100644 index 950c071609..0000000000 --- a/backend/noop/meson.build +++ /dev/null @@ -1,4 +0,0 @@ -wlr_files += files( - 'backend.c', - 'output.c', -) diff --git a/backend/noop/output.c b/backend/noop/output.c deleted file mode 100644 index bde8082088..0000000000 --- a/backend/noop/output.c +++ /dev/null @@ -1,87 +0,0 @@ -#include -#include -#include -#include -#include -#include "backend/noop.h" -#include "util/signal.h" - -static const uint32_t SUPPORTED_OUTPUT_STATE = - WLR_OUTPUT_STATE_BACKEND_OPTIONAL | - WLR_OUTPUT_STATE_MODE; - -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_commit(struct wlr_output *wlr_output) { - uint32_t unsupported = - wlr_output->pending.committed & ~SUPPORTED_OUTPUT_STATE; - if (unsupported != 0) { - wlr_log(WLR_DEBUG, "Unsupported output state fields: 0x%"PRIx32, - unsupported); - 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, - .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; -} diff --git a/docs/env_vars.md b/docs/env_vars.md index a07b2f1974..609ca97bd2 100644 --- a/docs/env_vars.md +++ b/docs/env_vars.md @@ -3,7 +3,7 @@ wlroots reads these environment variables # wlroots specific * *WLR_BACKENDS*: comma-separated list of backends to use (available backends: - libinput, drm, wayland, x11, headless, noop) + libinput, drm, wayland, x11, headless) * *WLR_NO_HARDWARE_CURSORS*: set to 1 to use software cursors instead of hardware cursors * *WLR_XWAYLAND*: specifies the path to an Xwayland binary to be used (instead diff --git a/include/backend/noop.h b/include/backend/noop.h deleted file mode 100644 index 5a8ee67797..0000000000 --- a/include/backend/noop.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef BACKEND_NOOP_H -#define BACKEND_NOOP_H - -#include -#include - -struct wlr_noop_backend { - struct wlr_backend backend; - struct wl_display *display; - struct wl_list outputs; - size_t last_output_num; - bool started; - - struct wl_listener display_destroy; -}; - -struct wlr_noop_output { - struct wlr_output wlr_output; - - struct wlr_noop_backend *backend; - struct wl_list link; -}; - -struct wlr_noop_backend *noop_backend_from_backend( - struct wlr_backend *wlr_backend); - -#endif diff --git a/include/wlr/backend/noop.h b/include/wlr/backend/noop.h deleted file mode 100644 index 592b8f35b7..0000000000 --- a/include/wlr/backend/noop.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * This an unstable interface of wlroots. No guarantees are made regarding the - * future consistency of this API. - */ -#ifndef WLR_USE_UNSTABLE -#error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" -#endif - -#ifndef WLR_BACKEND_NOOP_H -#define WLR_BACKEND_NOOP_H - -#include -#include - -/** - * Creates a noop backend. Noop backends do not have a framebuffer and are not - * capable of rendering anything. They are useful for when there's no real - * outputs connected; you can stash your views on a noop output until an output - * is connected. - */ -struct wlr_backend *wlr_noop_backend_create(struct wl_display *display); - -/** - * Create a new noop output. - */ -struct wlr_output *wlr_noop_add_output(struct wlr_backend *backend); - -bool wlr_backend_is_noop(struct wlr_backend *backend); -bool wlr_output_is_noop(struct wlr_output *output); - -#endif From 585a908a014a49a718771375264ed284d4599d59 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 25 Oct 2021 18:29:24 +0200 Subject: [PATCH 062/188] scene: add wlr_scene_xdg_surface_create This allows compositors to easily add an xdg_surface to the scene-graph while retaining the ability to unconstraint popups and decide their final position. Compositors can handle new popups with the wlr_xdg_shell.new_surface event, get the parent scene-graph node via wlr_xdg_popup.parent.data, create a new scene-graph node via wlr_scene_xdg_surface_tree_create, and unconstraint the popup if they want to. --- include/wlr/types/wlr_scene.h | 11 +++ types/meson.build | 1 + types/scene/xdg_shell.c | 124 ++++++++++++++++++++++++++++++++++ 3 files changed, 136 insertions(+) create mode 100644 types/scene/xdg_shell.c diff --git a/include/wlr/types/wlr_scene.h b/include/wlr/types/wlr_scene.h index 538b894125..622dbb2808 100644 --- a/include/wlr/types/wlr_scene.h +++ b/include/wlr/types/wlr_scene.h @@ -25,6 +25,7 @@ struct wlr_output; struct wlr_output_layout; +struct wlr_xdg_surface; enum wlr_scene_node_type { WLR_SCENE_NODE_ROOT, @@ -298,4 +299,14 @@ bool wlr_scene_attach_output_layout(struct wlr_scene *scene, struct wlr_scene_node *wlr_scene_subsurface_tree_create( struct wlr_scene_node *parent, struct wlr_surface *surface); +/** + * Add a node displaying an xdg_surface and all of its sub-surfaces to the + * scene-graph. + * + * The origin of the returned scene-graph node will match the top-left corner + * of the xdg_surface window geometry. + */ +struct wlr_scene_node *wlr_scene_xdg_surface_create( + struct wlr_scene_node *parent, struct wlr_xdg_surface *xdg_surface); + #endif diff --git a/types/meson.build b/types/meson.build index 89e834cec1..1694ac94a5 100644 --- a/types/meson.build +++ b/types/meson.build @@ -10,6 +10,7 @@ wlr_files += files( 'scene/subsurface_tree.c', 'scene/wlr_scene.c', 'scene/output_layout.c', + 'scene/xdg_shell.c', 'seat/wlr_seat_keyboard.c', 'seat/wlr_seat_pointer.c', 'seat/wlr_seat_touch.c', diff --git a/types/scene/xdg_shell.c b/types/scene/xdg_shell.c new file mode 100644 index 0000000000..9b3ad71c37 --- /dev/null +++ b/types/scene/xdg_shell.c @@ -0,0 +1,124 @@ +#include +#include +#include + +struct wlr_scene_xdg_surface { + struct wlr_scene_tree *tree; + struct wlr_xdg_surface *xdg_surface; + struct wlr_scene_node *surface_node; + + struct wl_listener tree_destroy; + struct wl_listener xdg_surface_destroy; + struct wl_listener xdg_surface_map; + struct wl_listener xdg_surface_unmap; + struct wl_listener xdg_surface_commit; +}; + +static void scene_xdg_surface_handle_tree_destroy(struct wl_listener *listener, + void *data) { + struct wlr_scene_xdg_surface *scene_xdg_surface = + wl_container_of(listener, scene_xdg_surface, tree_destroy); + // tree and surface_node will be cleaned up by scene_node_finish + wl_list_remove(&scene_xdg_surface->tree_destroy.link); + wl_list_remove(&scene_xdg_surface->xdg_surface_destroy.link); + wl_list_remove(&scene_xdg_surface->xdg_surface_map.link); + wl_list_remove(&scene_xdg_surface->xdg_surface_unmap.link); + wl_list_remove(&scene_xdg_surface->xdg_surface_commit.link); + free(scene_xdg_surface); +} + +static void scene_xdg_surface_handle_xdg_surface_destroy(struct wl_listener *listener, + void *data) { + struct wlr_scene_xdg_surface *scene_xdg_surface = + wl_container_of(listener, scene_xdg_surface, xdg_surface_destroy); + wlr_scene_node_destroy(&scene_xdg_surface->tree->node); +} + +static void scene_xdg_surface_handle_xdg_surface_map(struct wl_listener *listener, + void *data) { + struct wlr_scene_xdg_surface *scene_xdg_surface = + wl_container_of(listener, scene_xdg_surface, xdg_surface_map); + wlr_scene_node_set_enabled(&scene_xdg_surface->tree->node, true); +} + +static void scene_xdg_surface_handle_xdg_surface_unmap(struct wl_listener *listener, + void *data) { + struct wlr_scene_xdg_surface *scene_xdg_surface = + wl_container_of(listener, scene_xdg_surface, xdg_surface_unmap); + wlr_scene_node_set_enabled(&scene_xdg_surface->tree->node, false); +} + +static void scene_xdg_surface_update_position( + struct wlr_scene_xdg_surface *scene_xdg_surface) { + struct wlr_xdg_surface *xdg_surface = scene_xdg_surface->xdg_surface; + + struct wlr_box geo = {0}; + wlr_xdg_surface_get_geometry(xdg_surface, &geo); + wlr_scene_node_set_position(scene_xdg_surface->surface_node, + -geo.x, -geo.y); + + if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { + struct wlr_xdg_popup *popup = xdg_surface->popup; + wlr_scene_node_set_position(&scene_xdg_surface->tree->node, + popup->geometry.x, popup->geometry.y); + } +} + +static void scene_xdg_surface_handle_xdg_surface_commit(struct wl_listener *listener, + void *data) { + struct wlr_scene_xdg_surface *scene_xdg_surface = + wl_container_of(listener, scene_xdg_surface, xdg_surface_commit); + scene_xdg_surface_update_position(scene_xdg_surface); +} + +struct wlr_scene_node *wlr_scene_xdg_surface_create( + struct wlr_scene_node *parent, struct wlr_xdg_surface *xdg_surface) { + struct wlr_scene_xdg_surface *scene_xdg_surface = + calloc(1, sizeof(*scene_xdg_surface)); + if (scene_xdg_surface == NULL) { + return NULL; + } + + scene_xdg_surface->xdg_surface = xdg_surface; + + scene_xdg_surface->tree = wlr_scene_tree_create(parent); + if (scene_xdg_surface->tree == NULL) { + free(scene_xdg_surface); + return NULL; + } + + scene_xdg_surface->surface_node = wlr_scene_subsurface_tree_create( + &scene_xdg_surface->tree->node, xdg_surface->surface); + if (scene_xdg_surface->surface_node == NULL) { + wlr_scene_node_destroy(&scene_xdg_surface->tree->node); + free(scene_xdg_surface); + return NULL; + } + + scene_xdg_surface->tree_destroy.notify = + scene_xdg_surface_handle_tree_destroy; + wl_signal_add(&scene_xdg_surface->tree->node.events.destroy, + &scene_xdg_surface->tree_destroy); + + scene_xdg_surface->xdg_surface_destroy.notify = + scene_xdg_surface_handle_xdg_surface_destroy; + wl_signal_add(&xdg_surface->events.destroy, &scene_xdg_surface->xdg_surface_destroy); + + scene_xdg_surface->xdg_surface_map.notify = + scene_xdg_surface_handle_xdg_surface_map; + wl_signal_add(&xdg_surface->events.map, &scene_xdg_surface->xdg_surface_map); + + scene_xdg_surface->xdg_surface_unmap.notify = + scene_xdg_surface_handle_xdg_surface_unmap; + wl_signal_add(&xdg_surface->events.unmap, &scene_xdg_surface->xdg_surface_unmap); + + scene_xdg_surface->xdg_surface_commit.notify = + scene_xdg_surface_handle_xdg_surface_commit; + wl_signal_add(&xdg_surface->surface->events.commit, + &scene_xdg_surface->xdg_surface_commit); + + wlr_scene_node_set_enabled(&scene_xdg_surface->tree->node, xdg_surface->mapped); + scene_xdg_surface_update_position(scene_xdg_surface); + + return &scene_xdg_surface->tree->node; +} From d78cb808b1b051eae443e368c0b88bc90258fd02 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 31 Mar 2021 16:50:17 +0200 Subject: [PATCH 063/188] render/drm_format_set: add wlr_drm_format_has --- include/render/drm_format_set.h | 1 + render/drm_format_set.c | 23 ++++++++++++----------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/include/render/drm_format_set.h b/include/render/drm_format_set.h index c438722bc3..c94c2af29b 100644 --- a/include/render/drm_format_set.h +++ b/include/render/drm_format_set.h @@ -4,6 +4,7 @@ #include struct wlr_drm_format *wlr_drm_format_create(uint32_t format); +bool wlr_drm_format_has(const struct wlr_drm_format *fmt, uint64_t modifier); bool wlr_drm_format_add(struct wlr_drm_format **fmt_ptr, uint64_t modifier); struct wlr_drm_format *wlr_drm_format_dup(const struct wlr_drm_format *format); /** diff --git a/render/drm_format_set.c b/render/drm_format_set.c index 3edc1925ba..ef2929b75d 100644 --- a/render/drm_format_set.c +++ b/render/drm_format_set.c @@ -48,13 +48,7 @@ bool wlr_drm_format_set_has(const struct wlr_drm_format_set *set, return true; } - for (size_t i = 0; i < fmt->len; ++i) { - if (fmt->modifiers[i] == modifier) { - return true; - } - } - - return false; + return wlr_drm_format_has(fmt, modifier); } bool wlr_drm_format_set_add(struct wlr_drm_format_set *set, uint32_t format, @@ -106,6 +100,15 @@ struct wlr_drm_format *wlr_drm_format_create(uint32_t format) { return fmt; } +bool wlr_drm_format_has(const struct wlr_drm_format *fmt, uint64_t modifier) { + for (size_t i = 0; i < fmt->len; ++i) { + if (fmt->modifiers[i] == modifier) { + return true; + } + } + return false; +} + bool wlr_drm_format_add(struct wlr_drm_format **fmt_ptr, uint64_t modifier) { struct wlr_drm_format *fmt = *fmt_ptr; @@ -113,10 +116,8 @@ bool wlr_drm_format_add(struct wlr_drm_format **fmt_ptr, uint64_t modifier) { return true; } - for (size_t i = 0; i < fmt->len; ++i) { - if (fmt->modifiers[i] == modifier) { - return true; - } + if (wlr_drm_format_has(fmt, modifier)) { + return true; } if (fmt->len == fmt->capacity) { From affe9eda5708a8368dd96870ddf2442c5b637202 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 31 Mar 2021 17:07:55 +0200 Subject: [PATCH 064/188] Require INVALID for implicit format modifiers See [1] for the motivation. [1]: https://gitlab.freedesktop.org/wayland/wayland-protocols/-/merge_requests/75 --- backend/drm/drm.c | 18 ++++++++---------- backend/drm/renderer.c | 2 +- backend/x11/backend.c | 1 + render/allocator/gbm.c | 18 ++++++++++++------ render/drm_format_set.c | 31 ++++++++++++------------------- render/egl.c | 11 +++++------ types/wlr_linux_dmabuf_v1.c | 9 ++++----- 7 files changed, 43 insertions(+), 47 deletions(-) diff --git a/backend/drm/drm.c b/backend/drm/drm.c index 40506cd46d..e22e069057 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -118,9 +118,14 @@ static bool add_plane(struct wlr_drm_backend *drm, 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); + for (size_t i = 0; i < drm_plane->count_formats; ++i) { + // Force a LINEAR layout for the cursor if the driver doesn't support + // modifiers + uint64_t mod = DRM_FORMAT_MOD_INVALID; + if (type == DRM_PLANE_TYPE_CURSOR) { + mod = DRM_FORMAT_MOD_LINEAR; + } + wlr_drm_format_set_add(&p->formats, drm_plane->formats[i], mod); } if (p->props.in_formats && drm->addfb2_modifiers) { @@ -150,13 +155,6 @@ static bool add_plane(struct wlr_drm_backend *drm, } drmModeFreePropertyBlob(blob); - } else if (type == DRM_PLANE_TYPE_CURSOR) { - // Force a LINEAR layout for the cursor if the driver doesn't support - // modifiers - for (size_t i = 0; i < p->formats.len; ++i) { - wlr_drm_format_set_add(&p->formats, p->formats.formats[i]->format, - DRM_FORMAT_MOD_LINEAR); - } } switch (type) { diff --git a/backend/drm/renderer.c b/backend/drm/renderer.c index 6c26558f97..3d1a5fb213 100644 --- a/backend/drm/renderer.c +++ b/backend/drm/renderer.c @@ -149,7 +149,7 @@ struct wlr_drm_format *drm_plane_pick_render_format( const struct wlr_drm_format_set *plane_formats = &plane->formats; uint32_t fmt = DRM_FORMAT_ARGB8888; - if (!wlr_drm_format_set_has(&plane->formats, fmt, DRM_FORMAT_MOD_INVALID)) { + if (!wlr_drm_format_set_get(&plane->formats, fmt)) { const struct wlr_pixel_format_info *format_info = drm_get_pixel_format_info(fmt); assert(format_info != NULL && diff --git a/backend/x11/backend.c b/backend/x11/backend.c index 6db9aa6c94..ae20559cb6 100644 --- a/backend/x11/backend.c +++ b/backend/x11/backend.c @@ -354,6 +354,7 @@ static bool query_formats(struct wlr_x11_backend *x11) { } if (x11->have_dri3) { + // X11 always supports implicit modifiers wlr_drm_format_set_add(&x11->dri3_formats, format->drm, DRM_FORMAT_MOD_INVALID); if (!query_dri3_modifiers(x11, format)) { diff --git a/render/allocator/gbm.c b/render/allocator/gbm.c index b546e412ca..8f73862a24 100644 --- a/render/allocator/gbm.c +++ b/render/allocator/gbm.c @@ -10,6 +10,7 @@ #include #include "render/allocator/gbm.h" +#include "render/drm_format_set.h" static const struct wlr_buffer_impl buffer_impl; @@ -89,17 +90,22 @@ static struct wlr_gbm_buffer *create_buffer(struct wlr_gbm_allocator *alloc, int width, int height, const struct wlr_drm_format *format) { struct gbm_device *gbm_device = alloc->gbm_device; - struct gbm_bo *bo = NULL; + assert(format->len > 0); + bool has_modifier = true; - if (format->len > 0) { - bo = gbm_bo_create_with_modifiers(gbm_device, width, height, - format->format, format->modifiers, format->len); - } + uint64_t fallback_modifier = DRM_FORMAT_MOD_INVALID; + struct gbm_bo *bo = gbm_bo_create_with_modifiers(gbm_device, width, height, + format->format, format->modifiers, format->len); if (bo == NULL) { uint32_t usage = GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING; if (format->len == 1 && format->modifiers[0] == DRM_FORMAT_MOD_LINEAR) { usage |= GBM_BO_USE_LINEAR; + fallback_modifier = DRM_FORMAT_MOD_LINEAR; + } else if (!wlr_drm_format_has(format, DRM_FORMAT_MOD_INVALID)) { + // If the format doesn't accept an implicit modifier, bail out. + wlr_log(WLR_ERROR, "gbm_bo_create_with_modifiers failed"); + return NULL; } bo = gbm_bo_create(gbm_device, width, height, format->format, usage); has_modifier = false; @@ -128,7 +134,7 @@ static struct wlr_gbm_buffer *create_buffer(struct wlr_gbm_allocator *alloc, // don't populate the modifier field: other parts of the stack may not // understand modifiers, and they can't strip the modifier. if (!has_modifier) { - buffer->dmabuf.modifier = DRM_FORMAT_MOD_INVALID; + buffer->dmabuf.modifier = fallback_modifier; } wlr_log(WLR_DEBUG, "Allocated %dx%d GBM buffer (format 0x%"PRIX32", " diff --git a/render/drm_format_set.c b/render/drm_format_set.c index ef2929b75d..90256098d7 100644 --- a/render/drm_format_set.c +++ b/render/drm_format_set.c @@ -43,11 +43,6 @@ bool wlr_drm_format_set_has(const struct wlr_drm_format_set *set, if (!fmt) { return false; } - - if (modifier == DRM_FORMAT_MOD_INVALID) { - return true; - } - return wlr_drm_format_has(fmt, modifier); } @@ -112,10 +107,6 @@ bool wlr_drm_format_has(const struct wlr_drm_format *fmt, uint64_t modifier) { bool wlr_drm_format_add(struct wlr_drm_format **fmt_ptr, uint64_t modifier) { struct wlr_drm_format *fmt = *fmt_ptr; - if (modifier == DRM_FORMAT_MOD_INVALID) { - return true; - } - if (wlr_drm_format_has(fmt, modifier)) { return true; } @@ -153,15 +144,17 @@ struct wlr_drm_format *wlr_drm_format_intersect( const struct wlr_drm_format *a, const struct wlr_drm_format *b) { assert(a->format == b->format); - // Special case: if a format only supports LINEAR and the other doesn't - // support any modifier, force LINEAR. This will force the allocator to - // create a buffer with a LINEAR layout instead of an implicit modifier. - if (a->len == 0 && b->len == 1 && b->modifiers[0] == DRM_FORMAT_MOD_LINEAR) { - return wlr_drm_format_dup(b); - } - if (b->len == 0 && a->len == 1 && a->modifiers[0] == DRM_FORMAT_MOD_LINEAR) { + // Special case: if a format only supports LINEAR and the other supports + // implicit modifiers, force LINEAR. This will force the allocator to + // create a buffer with a linear layout instead of an implicit modifier. + if (a->len == 1 && a->modifiers[0] == DRM_FORMAT_MOD_LINEAR && + wlr_drm_format_has(b, DRM_FORMAT_MOD_INVALID)) { return wlr_drm_format_dup(a); } + if (b->len == 1 && b->modifiers[0] == DRM_FORMAT_MOD_LINEAR && + wlr_drm_format_has(a, DRM_FORMAT_MOD_INVALID)) { + return wlr_drm_format_dup(b); + } size_t format_cap = a->len < b->len ? a->len : b->len; size_t format_size = sizeof(struct wlr_drm_format) + @@ -185,9 +178,9 @@ struct wlr_drm_format *wlr_drm_format_intersect( } } - // If both formats support modifiers, but the intersection is empty, then - // the formats aren't compatible with each other - if (format->len == 0 && a->len > 0 && b->len > 0) { + // If the intersection is empty, then the formats aren't compatible with + // each other. + if (format->len == 0) { free(format); return NULL; } diff --git a/render/egl.c b/render/egl.c index ec23ce8d8a..a398d586b2 100644 --- a/render/egl.c +++ b/render/egl.c @@ -119,12 +119,11 @@ static void init_dmabuf_formats(struct wlr_egl *egl) { has_modifiers = has_modifiers || modifiers_len > 0; - if (modifiers_len == 0) { - wlr_drm_format_set_add(&egl->dmabuf_texture_formats, fmt, - DRM_FORMAT_MOD_INVALID); - wlr_drm_format_set_add(&egl->dmabuf_render_formats, fmt, - DRM_FORMAT_MOD_INVALID); - } + // EGL always supports implicit modifiers + wlr_drm_format_set_add(&egl->dmabuf_texture_formats, fmt, + DRM_FORMAT_MOD_INVALID); + wlr_drm_format_set_add(&egl->dmabuf_render_formats, fmt, + DRM_FORMAT_MOD_INVALID); for (int j = 0; j < modifiers_len; j++) { wlr_drm_format_set_add(&egl->dmabuf_texture_formats, fmt, diff --git a/types/wlr_linux_dmabuf_v1.c b/types/wlr_linux_dmabuf_v1.c index 63b52023d5..dc5ca8af27 100644 --- a/types/wlr_linux_dmabuf_v1.c +++ b/types/wlr_linux_dmabuf_v1.c @@ -9,6 +9,7 @@ #include #include #include "linux-dmabuf-unstable-v1-protocol.h" +#include "render/drm_format_set.h" #include "util/signal.h" #define LINUX_DMABUF_VERSION 3 @@ -427,7 +428,9 @@ static const struct zwp_linux_dmabuf_v1_interface linux_dmabuf_impl = { static void linux_dmabuf_send_modifiers(struct wl_resource *resource, const struct wlr_drm_format *fmt) { if (wl_resource_get_version(resource) < ZWP_LINUX_DMABUF_V1_MODIFIER_SINCE_VERSION) { - zwp_linux_dmabuf_v1_send_format(resource, fmt->format); + if (wlr_drm_format_has(fmt, DRM_FORMAT_MOD_INVALID)) { + zwp_linux_dmabuf_v1_send_format(resource, fmt->format); + } return; } @@ -436,10 +439,6 @@ static void linux_dmabuf_send_modifiers(struct wl_resource *resource, zwp_linux_dmabuf_v1_send_modifier(resource, fmt->format, mod >> 32, mod & 0xFFFFFFFF); } - - // We always support buffers with an implicit modifier - zwp_linux_dmabuf_v1_send_modifier(resource, fmt->format, - DRM_FORMAT_MOD_INVALID >> 32, DRM_FORMAT_MOD_INVALID & 0xFFFFFFFF); } static void linux_dmabuf_send_formats(struct wlr_linux_dmabuf_v1 *linux_dmabuf, From e163a7cf486bf85e982e0fbe9c7a6588b0afb657 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 31 Mar 2021 22:05:18 +0200 Subject: [PATCH 065/188] backend/drm: fail on explicit modifier in drmModeAddFB2 drmModeAddFB2 doesn't support explicit modifiers. Only accept INVALID which indicates an implicit modifier and LINEAR which may indicate that GBM_BO_USE_LINEAR has been used. --- backend/drm/renderer.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/backend/drm/renderer.c b/backend/drm/renderer.c index 3d1a5fb213..bfc94a24d0 100644 --- a/backend/drm/renderer.c +++ b/backend/drm/renderer.c @@ -220,6 +220,13 @@ static uint32_t get_fb_for_bo(struct wlr_drm_backend *drm, wlr_log_errno(WLR_DEBUG, "drmModeAddFB2WithModifiers failed"); } } else { + if (dmabuf->modifier != DRM_FORMAT_MOD_INVALID && + dmabuf->modifier != DRM_FORMAT_MOD_LINEAR) { + wlr_log(WLR_ERROR, "Cannot import DRM framebuffer with explicit " + "modifier 0x%"PRIX64, dmabuf->modifier); + return 0; + } + int ret = drmModeAddFB2(drm->fd, dmabuf->width, dmabuf->height, dmabuf->format, handles, dmabuf->stride, dmabuf->offset, &id, 0); if (ret != 0 && dmabuf->format == DRM_FORMAT_ARGB8888 && From d5df8d5cbf5055a4712f3871f4ba227464ebebb5 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 31 Mar 2021 22:20:24 +0200 Subject: [PATCH 066/188] render/egl: always add LINEAR to supported modifiers --- render/egl.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/render/egl.c b/render/egl.c index a398d586b2..9dae0724ba 100644 --- a/render/egl.c +++ b/render/egl.c @@ -125,6 +125,15 @@ static void init_dmabuf_formats(struct wlr_egl *egl) { wlr_drm_format_set_add(&egl->dmabuf_render_formats, fmt, DRM_FORMAT_MOD_INVALID); + if (modifiers_len == 0) { + // Asume the linear layout is supported if the driver doesn't + // explicitly say otherwise + wlr_drm_format_set_add(&egl->dmabuf_texture_formats, fmt, + DRM_FORMAT_MOD_LINEAR); + wlr_drm_format_set_add(&egl->dmabuf_render_formats, fmt, + DRM_FORMAT_MOD_LINEAR); + } + for (int j = 0; j < modifiers_len; j++) { wlr_drm_format_set_add(&egl->dmabuf_texture_formats, fmt, modifiers[j]); From 98f2efde9837dec3e39fbe1f56832d18940eb6cd Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 31 Mar 2021 22:07:54 +0200 Subject: [PATCH 067/188] render/drm_format_set: remove special LINEAR case This was used to make the intersection of INVALID and LINEAR result in LINEAR. We can now just require LINEAR to be in both lists. --- render/drm_format_set.c | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/render/drm_format_set.c b/render/drm_format_set.c index 90256098d7..82997eda9e 100644 --- a/render/drm_format_set.c +++ b/render/drm_format_set.c @@ -144,18 +144,6 @@ struct wlr_drm_format *wlr_drm_format_intersect( const struct wlr_drm_format *a, const struct wlr_drm_format *b) { assert(a->format == b->format); - // Special case: if a format only supports LINEAR and the other supports - // implicit modifiers, force LINEAR. This will force the allocator to - // create a buffer with a linear layout instead of an implicit modifier. - if (a->len == 1 && a->modifiers[0] == DRM_FORMAT_MOD_LINEAR && - wlr_drm_format_has(b, DRM_FORMAT_MOD_INVALID)) { - return wlr_drm_format_dup(a); - } - if (b->len == 1 && b->modifiers[0] == DRM_FORMAT_MOD_LINEAR && - wlr_drm_format_has(a, DRM_FORMAT_MOD_INVALID)) { - return wlr_drm_format_dup(b); - } - size_t format_cap = a->len < b->len ? a->len : b->len; size_t format_size = sizeof(struct wlr_drm_format) + format_cap * sizeof(a->modifiers[0]); From ef1669d33e15134e09d30c6989d53773629fd9ba Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 24 Nov 2021 12:17:39 +0100 Subject: [PATCH 068/188] backend/drm: always add LINEAR to supported modifiers --- backend/drm/drm.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/backend/drm/drm.c b/backend/drm/drm.c index e22e069057..d7c532316b 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -121,11 +121,12 @@ static bool add_plane(struct wlr_drm_backend *drm, for (size_t i = 0; i < drm_plane->count_formats; ++i) { // Force a LINEAR layout for the cursor if the driver doesn't support // modifiers - uint64_t mod = DRM_FORMAT_MOD_INVALID; - if (type == DRM_PLANE_TYPE_CURSOR) { - mod = DRM_FORMAT_MOD_LINEAR; + wlr_drm_format_set_add(&p->formats, drm_plane->formats[i], + DRM_FORMAT_MOD_LINEAR); + if (type != DRM_PLANE_TYPE_CURSOR) { + wlr_drm_format_set_add(&p->formats, drm_plane->formats[i], + DRM_FORMAT_MOD_INVALID); } - wlr_drm_format_set_add(&p->formats, drm_plane->formats[i], mod); } if (p->props.in_formats && drm->addfb2_modifiers) { From 83d78f9fd415a1110e888b3c5f9cc9fb79627217 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 24 Nov 2021 21:42:08 +0100 Subject: [PATCH 069/188] render: add DMA-BUF docs Document some of the assumptions for DMA-BUF buffer sharing and modifiers. --- include/wlr/render/allocator.h | 12 ++++++++++++ include/wlr/render/dmabuf.h | 21 +++++++++++++++++++-- include/wlr/render/drm_format_set.h | 21 +++++++++++++++++++-- 3 files changed, 50 insertions(+), 4 deletions(-) diff --git a/include/wlr/render/allocator.h b/include/wlr/render/allocator.h index 3caeffa090..3150e36c33 100644 --- a/include/wlr/render/allocator.h +++ b/include/wlr/render/allocator.h @@ -51,6 +51,18 @@ void wlr_allocator_destroy(struct wlr_allocator *alloc); * * When the caller is done with it, they must unreference it by calling * wlr_buffer_drop. + * + * The `format` passed in indicates the format to use and the list of + * acceptable modifiers. The order in which modifiers are listed is not + * significant. + * + * When running with legacy drivers which don't support explicit modifiers, the + * allocator must recognize two modifiers: INVALID (for implicit tiling and/or + * compression) and LINEAR. + * + * The allocator must return a buffer using one of the modifiers listed. In + * particular, allocators must not return a buffer with an implicit modifier + * unless the user has allowed it by passing INVALID in the modifier list. */ struct wlr_buffer *wlr_allocator_create_buffer(struct wlr_allocator *alloc, int width, int height, const struct wlr_drm_format *format); diff --git a/include/wlr/render/dmabuf.h b/include/wlr/render/dmabuf.h index 76aad62958..62f8e02175 100644 --- a/include/wlr/render/dmabuf.h +++ b/include/wlr/render/dmabuf.h @@ -14,10 +14,27 @@ #define WLR_DMABUF_MAX_PLANES 4 +/** + * A Linux DMA-BUF pixel buffer. + * + * If the buffer was allocated with explicit modifiers enabled, the `modifier` + * field must not be INVALID. + * + * If the buffer was allocated with explicit modifiers disabled (either because + * the driver doesn't support it, or because the user didn't specify a valid + * modifier list), the `modifier` field can have two values: INVALID means that + * an implicit vendor-defined modifier is in use, LINEAR means that the buffer + * is linear. The `modifier` field must not have any other value. + * + * When importing a DMA-BUF, users must not ignore the modifier unless it's + * INVALID or LINEAR. In particular, users must not import a DMA-BUF to a + * legacy API which doesn't support specifying an explicit modifier unless the + * modifier is set to INVALID or LINEAR. + */ struct wlr_dmabuf_attributes { int32_t width, height; - uint32_t format; - uint64_t modifier; + uint32_t format; // FourCC code, see DRM_FORMAT_* in + uint64_t modifier; // see DRM_FORMAT_MOD_* in int n_planes; uint32_t offset[WLR_DMABUF_MAX_PLANES]; diff --git a/include/wlr/render/drm_format_set.h b/include/wlr/render/drm_format_set.h index cb367a6322..9427df3bd0 100644 --- a/include/wlr/render/drm_format_set.h +++ b/include/wlr/render/drm_format_set.h @@ -5,7 +5,7 @@ #include #include -/** A single DRM format */ +/** A single DRM format, with a set of modifiers attached. */ struct wlr_drm_format { // The actual DRM format, from `drm_fourcc.h` uint32_t format; @@ -17,7 +17,24 @@ struct wlr_drm_format { uint64_t modifiers[]; }; -/** A set of DRM formats */ +/** + * A set of DRM formats and modifiers. + * + * This is used to describe the supported format + modifier combinations. For + * instance, backends will report the set they can display, and renderers will + * report the set they can render to. For a more general overview of formats + * and modifiers, see: + * https://lore.kernel.org/dri-devel/20210905122742.86029-1-daniels@collabora.com/ + * + * For compatibility with legacy drivers which don't support explicit + * modifiers, the special modifier DRM_FORMAT_MOD_INVALID is used to indicate + * that implicit modifiers are supported. Legacy drivers can also support the + * DRM_FORMAT_MOD_LINEAR modifier, which forces the buffer to have a linear + * layout. + * + * Users must not assume that implicit modifiers are supported unless INVALID + * is listed in the modifier list. + */ struct wlr_drm_format_set { // The number of formats size_t len; From 254ab890e787ccee1e33fb420781c9696761ce3c Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Fri, 26 Nov 2021 10:26:01 +0100 Subject: [PATCH 070/188] scene: add support for viewporter If the surface has a source box set, use that. Closes: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3172 --- types/scene/wlr_scene.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index b6eed41776..03439223b3 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -715,7 +715,11 @@ static void render_node_iterator(struct wlr_scene_node *node, wlr_matrix_project_box(matrix, &dst_box, transform, 0.0, output->transform_matrix); - render_texture(output, output_damage, texture, NULL, &dst_box, matrix); + struct wlr_fbox src_box = {0}; + wlr_surface_get_buffer_source_box(surface, &src_box); + + render_texture(output, output_damage, texture, + &src_box, &dst_box, matrix); break; case WLR_SCENE_NODE_RECT:; struct wlr_scene_rect *scene_rect = scene_rect_from_node(node); From d37eb5c2eaedcada7dd39da1a31c6e9a39c8ed46 Mon Sep 17 00:00:00 2001 From: Rouven Czerwinski Date: Sat, 27 Nov 2021 19:08:18 +0100 Subject: [PATCH 071/188] linux-dmabuf-v1: filter out LINEAR if implicit If only INVALID and LINEAR are valid modifiers, we need to filter out LINEAR since Xwayland won't be able to allocate a BO with the explicit linear modifier on hardware that does not support explicit modifiers. The addition of LINEAR is an internal implementation detail which simplifies the wlroots architecture for now. Evntually Xwayland should be fixed to filter out modifiers that are not supported by the GBM implementation, see [1]. This could be done by querying EGL for the supported modifiers. [1]: https://gitlab.freedesktop.org/xorg/xserver/-/issues/1166 --- types/wlr_linux_dmabuf_v1.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/types/wlr_linux_dmabuf_v1.c b/types/wlr_linux_dmabuf_v1.c index dc5ca8af27..4b2b965ccd 100644 --- a/types/wlr_linux_dmabuf_v1.c +++ b/types/wlr_linux_dmabuf_v1.c @@ -434,6 +434,16 @@ static void linux_dmabuf_send_modifiers(struct wl_resource *resource, return; } + // In case only INVALID and LINEAR are advertised, send INVALID only due to XWayland: + // https://gitlab.freedesktop.org/xorg/xserver/-/issues/1166 + if (fmt->len == 2 && wlr_drm_format_has(fmt, DRM_FORMAT_MOD_INVALID) + && wlr_drm_format_has(fmt, DRM_FORMAT_MOD_INVALID)) { + uint64_t mod = DRM_FORMAT_MOD_INVALID; + zwp_linux_dmabuf_v1_send_modifier(resource, fmt->format, + mod >> 32, mod & 0xFFFFFFFF); + return; + } + for (size_t i = 0; i < fmt->len; i++) { uint64_t mod = fmt->modifiers[i]; zwp_linux_dmabuf_v1_send_modifier(resource, fmt->format, From 0fb479ca611b31be20e7adc44ff14491ffcadf11 Mon Sep 17 00:00:00 2001 From: Jonathan Wong Date: Sun, 28 Nov 2021 16:56:09 +0000 Subject: [PATCH 072/188] Added whitespace between "output" and "(not" --- types/wlr_cursor.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/types/wlr_cursor.c b/types/wlr_cursor.c index 7ae9c46cd7..a6b0716c76 100644 --- a/types/wlr_cursor.c +++ b/types/wlr_cursor.c @@ -854,7 +854,7 @@ void wlr_cursor_map_input_to_output(struct wlr_cursor *cur, struct wlr_cursor_device *c_device = get_cursor_device(cur, dev); if (!c_device) { wlr_log(WLR_ERROR, "Cannot map device \"%s\" to output" - "(not found in this cursor)", dev->name); + " (not found in this cursor)", dev->name); return; } From bff5b2c5597b30b90b201825b6c1c6537bb06d84 Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Mon, 29 Nov 2021 08:12:45 -0500 Subject: [PATCH 073/188] Insert new outputs at the end of the list This prevents auto-configuring a new output from changing the position of existing outputs. (v2: simplify insert-at-end logic) --- types/wlr_output_layout.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/types/wlr_output_layout.c b/types/wlr_output_layout.c index 782f5b71cd..eb672f06e3 100644 --- a/types/wlr_output_layout.c +++ b/types/wlr_output_layout.c @@ -185,7 +185,12 @@ static struct wlr_output_layout_output *output_layout_output_create( l_output->state->layout = layout; l_output->output = output; wl_signal_init(&l_output->events.destroy); - wl_list_insert(&layout->outputs, &l_output->link); + + /* + * Insert at the end of the list so that auto-configuring the + * new output doesn't change the layout of other outputs + */ + wl_list_insert(layout->outputs.prev, &l_output->link); wl_signal_add(&output->events.mode, &l_output->state->mode); l_output->state->mode.notify = handle_output_mode; From fbaefd90fc24f1c06be0d24cc3c4a01c71671157 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Fri, 26 Nov 2021 22:46:10 +0100 Subject: [PATCH 074/188] backend/drm: poison buffers which cannot be scanned out Rather than repeatedly trying to import DMA-BUFs which cannot be scanned out, mark the failed ones with a special "poison" marker. Inspired from [1]. [1]: https://gitlab.freedesktop.org/wayland/weston/-/merge_requests/731 --- backend/drm/renderer.c | 53 +++++++++++++++++++++++++++++++++++------- 1 file changed, 45 insertions(+), 8 deletions(-) diff --git a/backend/drm/renderer.c b/backend/drm/renderer.c index bfc94a24d0..815ba98187 100644 --- a/backend/drm/renderer.c +++ b/backend/drm/renderer.c @@ -275,18 +275,54 @@ static void close_all_bo_handles(struct wlr_drm_backend *drm, } } -static struct wlr_drm_fb *drm_fb_create(struct wlr_drm_backend *drm, - struct wlr_buffer *buf, const struct wlr_drm_format_set *formats) { - struct wlr_drm_fb *fb = calloc(1, sizeof(*fb)); - if (!fb) { +static void drm_poisoned_fb_handle_destroy(struct wlr_addon *addon) { + wlr_addon_finish(addon); + free(addon); +} + +static const struct wlr_addon_interface poisoned_fb_addon_impl = { + .name = "wlr_drm_poisoned_fb", + .destroy = drm_poisoned_fb_handle_destroy, +}; + +static bool is_buffer_poisoned(struct wlr_drm_backend *drm, + struct wlr_buffer *buf) { + return wlr_addon_find(&buf->addons, drm, &poisoned_fb_addon_impl) != NULL; +} + +/** + * Mark the buffer as "poisoned", ie. it cannot be imported into KMS. This + * allows us to avoid repeatedly trying to import it when it's not + * scanout-capable. + */ +static void poison_buffer(struct wlr_drm_backend *drm, + struct wlr_buffer *buf) { + struct wlr_addon *addon = calloc(1, sizeof(*addon)); + if (addon == NULL) { wlr_log_errno(WLR_ERROR, "Allocation failed"); - return NULL; + return; } + wlr_addon_init(addon, &buf->addons, drm, &poisoned_fb_addon_impl); + wlr_log(WLR_DEBUG, "Poisoning buffer"); +} +static struct wlr_drm_fb *drm_fb_create(struct wlr_drm_backend *drm, + struct wlr_buffer *buf, const struct wlr_drm_format_set *formats) { struct wlr_dmabuf_attributes attribs; if (!wlr_buffer_get_dmabuf(buf, &attribs)) { wlr_log(WLR_DEBUG, "Failed to get DMA-BUF from buffer"); - goto error_get_dmabuf; + return NULL; + } + + if (is_buffer_poisoned(drm, buf)) { + wlr_log(WLR_DEBUG, "Buffer is poisoned"); + return NULL; + } + + struct wlr_drm_fb *fb = calloc(1, sizeof(*fb)); + if (!fb) { + wlr_log_errno(WLR_ERROR, "Allocation failed"); + return NULL; } if (formats && !wlr_drm_format_set_has(formats, attribs.format, @@ -302,7 +338,7 @@ static struct wlr_drm_fb *drm_fb_create(struct wlr_drm_backend *drm, wlr_log(WLR_DEBUG, "Buffer format 0x%"PRIX32" with modifier " "0x%"PRIX64" cannot be scanned out", attribs.format, attribs.modifier); - goto error_get_dmabuf; + goto error_fb; } } @@ -318,6 +354,7 @@ static struct wlr_drm_fb *drm_fb_create(struct wlr_drm_backend *drm, fb->id = get_fb_for_bo(drm, &attribs, handles); if (!fb->id) { wlr_log(WLR_DEBUG, "Failed to import BO in KMS"); + poison_buffer(drm, buf); goto error_bo_handle; } @@ -333,7 +370,7 @@ static struct wlr_drm_fb *drm_fb_create(struct wlr_drm_backend *drm, error_bo_handle: close_all_bo_handles(drm, handles); -error_get_dmabuf: +error_fb: free(fb); return NULL; } From 6bfb930aa70ceb41455b9da2719a10538493a65d Mon Sep 17 00:00:00 2001 From: Rouven Czerwinski Date: Mon, 29 Nov 2021 09:40:02 +0100 Subject: [PATCH 075/188] linux-dmabuf-v1: fix implicit check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The implicit check to filter out LINEAR for dmabuf checked for INVALID twice instead of checking for INVALID & LINEAR. Fix this. Fixes: d37eb5c2eaed ("linux-dmabuf-v1: filter out LINEAR if implicit") Reported-by: Dawid Czeluśniak --- types/wlr_linux_dmabuf_v1.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/types/wlr_linux_dmabuf_v1.c b/types/wlr_linux_dmabuf_v1.c index 4b2b965ccd..b91fb59d92 100644 --- a/types/wlr_linux_dmabuf_v1.c +++ b/types/wlr_linux_dmabuf_v1.c @@ -437,7 +437,7 @@ static void linux_dmabuf_send_modifiers(struct wl_resource *resource, // In case only INVALID and LINEAR are advertised, send INVALID only due to XWayland: // https://gitlab.freedesktop.org/xorg/xserver/-/issues/1166 if (fmt->len == 2 && wlr_drm_format_has(fmt, DRM_FORMAT_MOD_INVALID) - && wlr_drm_format_has(fmt, DRM_FORMAT_MOD_INVALID)) { + && wlr_drm_format_has(fmt, DRM_FORMAT_MOD_LINEAR)) { uint64_t mod = DRM_FORMAT_MOD_INVALID; zwp_linux_dmabuf_v1_send_modifier(resource, fmt->format, mod >> 32, mod & 0xFFFFFFFF); From 456b971099b135512ce01383b486965290bf29db Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 30 Nov 2021 18:07:39 +0100 Subject: [PATCH 076/188] output: destroy swapchain when disabled This avoids consuming GPU memory when an output is disabled. --- types/output/output.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/types/output/output.c b/types/output/output.c index 76c9ed9e9c..d6f8f97ac8 100644 --- a/types/output/output.c +++ b/types/output/output.c @@ -715,6 +715,15 @@ bool wlr_output_commit(struct wlr_output *output) { wlr_output_schedule_done(output); } + // Destroy the swapchains when an output is disabled + if ((output->pending.committed & WLR_OUTPUT_STATE_ENABLED) && + !output->pending.enabled) { + wlr_swapchain_destroy(output->swapchain); + output->swapchain = NULL; + wlr_swapchain_destroy(output->cursor_swapchain); + output->cursor_swapchain = NULL; + } + // Unset the front-buffer when a new buffer will replace it or when the // output is getting disabled if ((output->pending.committed & WLR_OUTPUT_STATE_BUFFER) || From 2540de494e66c6fa056e9608755bfa7474d4ee47 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 30 Nov 2021 18:33:08 +0100 Subject: [PATCH 077/188] output: don't leave dangling cursor_front_buffer Sometimes we were calling wlr_output_impl.set_cursor with a NULL buffer, but we weren't clearing wlr_output.cursor_front_buffer. Avoid leaving a dangling buffer behind. Introduce a helper function output_set_hardware_cursor which calls wlr_output_impl.set_cursor and keeps cursor_front_buffer in sync. --- types/output/cursor.c | 43 +++++++++++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/types/output/cursor.c b/types/output/cursor.c index 8fa2cf0103..efbf650326 100644 --- a/types/output/cursor.c +++ b/types/output/cursor.c @@ -11,6 +11,26 @@ #include "types/wlr_output.h" #include "util/signal.h" +static bool output_set_hardware_cursor(struct wlr_output *output, + struct wlr_buffer *buffer, int hotspot_x, int hotspot_y) { + if (!output->impl->set_cursor) { + return false; + } + + if (!output->impl->set_cursor(output, buffer, hotspot_x, hotspot_y)) { + return false; + } + + wlr_buffer_unlock(output->cursor_front_buffer); + output->cursor_front_buffer = NULL; + + if (buffer != NULL) { + output->cursor_front_buffer = wlr_buffer_lock(buffer); + } + + return true; +} + static void output_cursor_damage_whole(struct wlr_output_cursor *cursor); void wlr_output_lock_software_cursors(struct wlr_output *output, bool lock) { @@ -25,8 +45,7 @@ void wlr_output_lock_software_cursors(struct wlr_output *output, bool lock) { output->software_cursor_locks); if (output->software_cursor_locks > 0 && output->hardware_cursor != NULL) { - assert(output->impl->set_cursor); - output->impl->set_cursor(output, NULL, 0, 0); + output_set_hardware_cursor(output, NULL, 0, 0); output_cursor_damage_whole(output->hardware_cursor); output->hardware_cursor = NULL; } @@ -345,14 +364,10 @@ static bool output_cursor_attempt_hardware(struct wlr_output_cursor *cursor) { wlr_output_transform_invert(output->transform), buffer ? buffer->width : 0, buffer ? buffer->height : 0); - bool ok = output->impl->set_cursor(cursor->output, buffer, - hotspot.x, hotspot.y); + bool ok = output_set_hardware_cursor(output, buffer, hotspot.x, hotspot.y); + wlr_buffer_unlock(buffer); if (ok) { - wlr_buffer_unlock(output->cursor_front_buffer); - output->cursor_front_buffer = buffer; output->hardware_cursor = cursor; - } else { - wlr_buffer_unlock(buffer); } return ok; } @@ -465,9 +480,8 @@ void wlr_output_cursor_set_surface(struct wlr_output_cursor *cursor, wlr_output_transform_invert(cursor->output->transform), buffer ? buffer->width : 0, buffer ? buffer->height : 0); - assert(cursor->output->impl->set_cursor); - cursor->output->impl->set_cursor(cursor->output, - buffer, hotspot.x, hotspot.y); + output_set_hardware_cursor(cursor->output, buffer, + hotspot.x, hotspot.y); } return; } @@ -490,8 +504,7 @@ void wlr_output_cursor_set_surface(struct wlr_output_cursor *cursor, cursor->height = 0; if (cursor->output->hardware_cursor == cursor) { - assert(cursor->output->impl->set_cursor); - cursor->output->impl->set_cursor(cursor->output, NULL, 0, 0); + output_set_hardware_cursor(cursor->output, NULL, 0, 0); } } } @@ -552,9 +565,7 @@ void wlr_output_cursor_destroy(struct wlr_output_cursor *cursor) { wlr_signal_emit_safe(&cursor->events.destroy, cursor); if (cursor->output->hardware_cursor == cursor) { // If this cursor was the hardware cursor, disable it - if (cursor->output->impl->set_cursor) { - cursor->output->impl->set_cursor(cursor->output, NULL, 0, 0); - } + output_set_hardware_cursor(cursor->output, NULL, 0, 0); cursor->output->hardware_cursor = NULL; } wlr_texture_destroy(cursor->texture); From e93435016e0dfad0fefed3b68a9b60fe453c723e Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 30 Nov 2021 20:33:36 +0100 Subject: [PATCH 078/188] output: fix typo in wlr_output_impl.get_primary_formats docs The buffer capabilities indicate whether the formats returned are for DMA-BUFs or shared memory buffers. --- include/wlr/interfaces/wlr_output.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/wlr/interfaces/wlr_output.h b/include/wlr/interfaces/wlr_output.h index 5ae1ab56f4..79cc8ee0ad 100644 --- a/include/wlr/interfaces/wlr_output.h +++ b/include/wlr/interfaces/wlr_output.h @@ -86,7 +86,7 @@ struct wlr_output_impl { */ void (*get_cursor_size)(struct wlr_output *output, int *width, int *height); /** - * Get the list of DMA-BUF formats suitable for the primary buffer, + * Get the list of DRM formats suitable for the primary buffer, * assuming a buffer with the specified capabilities. * * If unimplemented, the primary buffer has no format constraint. If NULL From 697a1cd0f59f9a64f02e47b0f62a7184d04ad794 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 30 Nov 2021 20:26:11 +0100 Subject: [PATCH 079/188] output: add wlr_output_get_primary_formats This allows compositors to get primary formats without manually calling wlr_output_impl.get_primary_formats. For example, the Sway patch for linux-dmabuf feedback [1] needs this. [1]: https://github.com/swaywm/sway/pull/6313 --- include/wlr/types/wlr_output.h | 10 ++++++++++ types/output/output.c | 30 ++++++++++++++++++++---------- types/output/render.c | 12 ++---------- 3 files changed, 32 insertions(+), 20 deletions(-) diff --git a/include/wlr/types/wlr_output.h b/include/wlr/types/wlr_output.h index 7d7fbaad8f..9e18a21668 100644 --- a/include/wlr/types/wlr_output.h +++ b/include/wlr/types/wlr_output.h @@ -460,6 +460,16 @@ void wlr_output_lock_software_cursors(struct wlr_output *output, bool lock); */ void wlr_output_render_software_cursors(struct wlr_output *output, pixman_region32_t *damage); +/** + * Get the set of DRM formats suitable for the primary buffer, assuming a + * buffer with the specified capabilities. + * + * NULL is returned if the backend doesn't have any format constraint, ie. all + * formats are supported. An empty set is returned if the backend doesn't + * support any format. + */ +const struct wlr_drm_format_set *wlr_output_get_primary_formats( + struct wlr_output *output, uint32_t buffer_caps); struct wlr_output_cursor *wlr_output_cursor_create(struct wlr_output *output); diff --git a/types/output/output.c b/types/output/output.c index d6f8f97ac8..e82ec1dc81 100644 --- a/types/output/output.c +++ b/types/output/output.c @@ -560,16 +560,8 @@ static bool output_basic_test(struct wlr_output *output) { struct wlr_allocator *allocator = output->allocator; assert(allocator != NULL); - const struct wlr_drm_format_set *display_formats = NULL; - if (output->impl->get_primary_formats) { - display_formats = - output->impl->get_primary_formats(output, allocator->buffer_caps); - if (display_formats == NULL) { - wlr_log(WLR_ERROR, "Failed to get primary display formats"); - return false; - } - } - + const struct wlr_drm_format_set *display_formats = + wlr_output_get_primary_formats(output, allocator->buffer_caps); struct wlr_drm_format *format = output_pick_format(output, display_formats, output->pending.render_format); if (format == NULL) { @@ -868,3 +860,21 @@ void wlr_output_damage_whole(struct wlr_output *output) { pixman_region32_fini(&damage); } + +const struct wlr_drm_format_set *wlr_output_get_primary_formats( + struct wlr_output *output, uint32_t buffer_caps) { + if (!output->impl->get_primary_formats) { + return NULL; + } + + const struct wlr_drm_format_set *formats = + output->impl->get_primary_formats(output, buffer_caps); + if (formats == NULL) { + wlr_log(WLR_ERROR, "Failed to get primary display formats"); + + static const struct wlr_drm_format_set empty_format_set = {0}; + return &empty_format_set; + } + + return formats; +} diff --git a/types/output/render.c b/types/output/render.c index f23d1a9a8b..97c62a58d9 100644 --- a/types/output/render.c +++ b/types/output/render.c @@ -51,16 +51,8 @@ static bool output_create_swapchain(struct wlr_output *output, struct wlr_allocator *allocator = output->allocator; assert(allocator != NULL); - const struct wlr_drm_format_set *display_formats = NULL; - if (output->impl->get_primary_formats) { - display_formats = - output->impl->get_primary_formats(output, allocator->buffer_caps); - if (display_formats == NULL) { - wlr_log(WLR_ERROR, "Failed to get primary display formats"); - return false; - } - } - + const struct wlr_drm_format_set *display_formats = + wlr_output_get_primary_formats(output, allocator->buffer_caps); struct wlr_drm_format *format = output_pick_format(output, display_formats, output->render_format); if (format == NULL) { From dd84c5a1ccc322c4775282bbfc781f17e4ddac3a Mon Sep 17 00:00:00 2001 From: Simon Zeni Date: Tue, 30 Nov 2021 14:51:07 -0500 Subject: [PATCH 080/188] types/wlr_drm_lease_v1: add NULL check to drm lease resource destroy --- types/wlr_drm_lease_v1.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/types/wlr_drm_lease_v1.c b/types/wlr_drm_lease_v1.c index 7da40686e8..7063e0d312 100644 --- a/types/wlr_drm_lease_v1.c +++ b/types/wlr_drm_lease_v1.c @@ -218,7 +218,9 @@ void wlr_drm_lease_v1_revoke(struct wlr_drm_lease_v1 *lease) { static void drm_lease_v1_handle_resource_destroy(struct wl_resource *resource) { struct wlr_drm_lease_v1 *lease = drm_lease_v1_from_resource(resource); - wlr_drm_lease_terminate(lease->drm_lease); + if (lease != NULL) { + wlr_drm_lease_terminate(lease->drm_lease); + } } static void drm_lease_v1_handle_destroy( From ba974a4e9f17e5f75efebe312e8bd1b24ad7b06d Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 30 Nov 2021 20:55:04 +0100 Subject: [PATCH 081/188] scene: add wlr_scene_get_scene_output This allows getting a wlr_scene_output from a wlr_output. Since an output can only be added once to a scene-graph there's no ambiguity. This is useful for compositors using wlr_scene_attach_output_layout: the output layout integration automatically creates a scene-graph output for each wlr_output added to the layout. --- include/wlr/types/wlr_scene.h | 7 +++++++ types/scene/wlr_scene.c | 12 ++++++++++++ 2 files changed, 19 insertions(+) diff --git a/include/wlr/types/wlr_scene.h b/include/wlr/types/wlr_scene.h index 622dbb2808..bca0b269e3 100644 --- a/include/wlr/types/wlr_scene.h +++ b/include/wlr/types/wlr_scene.h @@ -282,6 +282,13 @@ bool wlr_scene_output_commit(struct wlr_scene_output *scene_output); */ void wlr_scene_output_for_each_surface(struct wlr_scene_output *scene_output, wlr_surface_iterator_func_t iterator, void *user_data); +/** + * Get a scene-graph output from a wlr_output. + * + * If the output hasn't been added to the scene-graph, returns NULL. + */ +struct wlr_scene_output *wlr_scene_get_scene_output(struct wlr_scene *scene, + struct wlr_output *output); /** * Attach an output layout to a scene. diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 03439223b3..d1236fe0e8 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -828,6 +828,18 @@ void wlr_scene_output_destroy(struct wlr_scene_output *scene_output) { free(scene_output); } +struct wlr_scene_output *wlr_scene_get_scene_output(struct wlr_scene *scene, + struct wlr_output *output) { + struct wlr_addon *addon = + wlr_addon_find(&output->addons, scene, &output_addon_impl); + if (addon == NULL) { + return NULL; + } + struct wlr_scene_output *scene_output = + wl_container_of(addon, scene_output, addon); + return scene_output; +} + void wlr_scene_output_set_position(struct wlr_scene_output *scene_output, int lx, int ly) { if (scene_output->x == lx && scene_output->y == ly) { From a44b2af6722397d5750c81cb29d09c4e6a52e546 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Thu, 25 Nov 2021 20:28:54 +0100 Subject: [PATCH 082/188] tinywl: use wlr_scene --- tinywl/tinywl.c | 268 ++++++++++++++++-------------------------------- 1 file changed, 89 insertions(+), 179 deletions(-) diff --git a/tinywl/tinywl.c b/tinywl/tinywl.c index ab5ed7ce4e..5ce7c4e818 100644 --- a/tinywl/tinywl.c +++ b/tinywl/tinywl.c @@ -1,4 +1,5 @@ #define _POSIX_C_SOURCE 200112L +#include #include #include #include @@ -14,10 +15,10 @@ #include #include #include -#include #include #include #include +#include #include #include #include @@ -36,6 +37,7 @@ struct tinywl_server { struct wlr_backend *backend; struct wlr_renderer *renderer; struct wlr_allocator *allocator; + struct wlr_scene *scene; struct wlr_xdg_shell *xdg_shell; struct wl_listener new_xdg_surface; @@ -76,12 +78,12 @@ struct tinywl_view { struct wl_list link; struct tinywl_server *server; struct wlr_xdg_surface *xdg_surface; + struct wlr_scene_node *scene_node; struct wl_listener map; struct wl_listener unmap; struct wl_listener destroy; struct wl_listener request_move; struct wl_listener request_resize; - bool mapped; int x, y; }; @@ -118,6 +120,7 @@ static void focus_view(struct tinywl_view *view, struct wlr_surface *surface) { } struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat); /* Move the view to the front */ + wlr_scene_node_raise_to_top(view->scene_node); wl_list_remove(&view->link); wl_list_insert(&server->views, &view->link); /* Activate the new surface */ @@ -166,14 +169,9 @@ static bool handle_keybinding(struct tinywl_server *server, xkb_keysym_t sym) { if (wl_list_length(&server->views) < 2) { break; } - struct tinywl_view *current_view = wl_container_of( - server->views.next, current_view, link); struct tinywl_view *next_view = wl_container_of( - current_view->link.next, next_view, link); + server->views.prev, next_view, link); focus_view(next_view, next_view->xdg_surface->surface); - /* Move the previous view to the end of the list */ - wl_list_remove(¤t_view->link); - wl_list_insert(server->views.prev, ¤t_view->link); break; default: return false; @@ -199,7 +197,8 @@ static void keyboard_handle_key( bool handled = false; uint32_t modifiers = wlr_keyboard_get_modifiers(keyboard->device->keyboard); - if ((modifiers & WLR_MODIFIER_ALT) && event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { + if ((modifiers & WLR_MODIFIER_ALT) && + event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { /* If alt is held down and this button was _pressed_, we attempt to * process it as a compositor keybinding. */ for (int i = 0; i < nsyms; i++) { @@ -310,52 +309,32 @@ static void seat_request_set_selection(struct wl_listener *listener, void *data) wlr_seat_set_selection(server->seat, event->source, event->serial); } -static bool view_at(struct tinywl_view *view, - double lx, double ly, struct wlr_surface **surface, - double *sx, double *sy) { - /* - * XDG toplevels may have nested surfaces, such as popup windows for context - * menus or tooltips. This function tests if any of those are underneath the - * coordinates lx and ly (in output Layout Coordinates). If so, it sets the - * surface pointer to that wlr_surface and the sx and sy coordinates to the - * coordinates relative to that surface's top-left corner. - */ - double view_sx = lx - view->x; - double view_sy = ly - view->y; - - double _sx, _sy; - struct wlr_surface *_surface = NULL; - _surface = wlr_xdg_surface_surface_at( - view->xdg_surface, view_sx, view_sy, &_sx, &_sy); - - if (_surface != NULL) { - *sx = _sx; - *sy = _sy; - *surface = _surface; - return true; - } - - return false; -} - static struct tinywl_view *desktop_view_at( struct tinywl_server *server, double lx, double ly, struct wlr_surface **surface, double *sx, double *sy) { - /* This iterates over all of our surfaces and attempts to find one under the - * cursor. This relies on server->views being ordered from top-to-bottom. */ - struct tinywl_view *view; - wl_list_for_each(view, &server->views, link) { - if (view_at(view, lx, ly, surface, sx, sy)) { - return view; - } + /* This returns the topmost node in the scene at the given layout coords. + * we only care about surface nodes as we are specifically looking for a + * surface in the surface tree of a tinywl_view. */ + struct wlr_scene_node *node = wlr_scene_node_at( + &server->scene->node, lx, ly, sx, sy); + if (node == NULL || node->type != WLR_SCENE_NODE_SURFACE) { + return NULL; + } + *surface = wlr_scene_surface_from_node(node)->surface; + /* Find the node corresponding to the tinywl_view at the root of this + * surface tree, it is the only one for which we set the data field. */ + while (node != NULL && node->data == NULL) { + node = node->parent; } - return NULL; + return node->data; } static void process_cursor_move(struct tinywl_server *server, uint32_t time) { /* Move the grabbed view to the new position. */ - server->grabbed_view->x = server->cursor->x - server->grab_x; - server->grabbed_view->y = server->cursor->y - server->grab_y; + struct tinywl_view *view = server->grabbed_view; + view->x = server->cursor->x - server->grab_x; + view->y = server->cursor->y - server->grab_y; + wlr_scene_node_set_position(view->scene_node, view->x, view->y); } static void process_cursor_resize(struct tinywl_server *server, uint32_t time) { @@ -404,6 +383,7 @@ static void process_cursor_resize(struct tinywl_server *server, uint32_t time) { wlr_xdg_surface_get_geometry(view->xdg_surface, &geo_box); view->x = new_left - geo_box.x; view->y = new_top - geo_box.y; + wlr_scene_node_set_position(view->scene_node, view->x, view->y); int new_width = new_right - new_left; int new_height = new_bottom - new_top; @@ -530,74 +510,12 @@ static void server_cursor_frame(struct wl_listener *listener, void *data) { wlr_seat_pointer_notify_frame(server->seat); } -/* Used to move all of the data necessary to render a surface from the top-level - * frame handler to the per-surface render function. */ -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, +// TODO: We should avoid sending the frame done event twice if a surface +// appears on multiple outputs. +// https://github.com/swaywm/wlroots/issues/3210 +static void send_frame_done(struct wlr_surface *surface, int sx, int sy, void *data) { - /* This function is called for every surface that needs to be rendered. */ - struct render_data *rdata = data; - struct tinywl_view *view = rdata->view; - struct wlr_output *output = rdata->output; - - /* We first obtain a wlr_texture, which is a GPU resource. wlroots - * automatically handles negotiating these with the client. The underlying - * resource could be an opaque handle passed from the client, or the client - * could have sent a pixel buffer which we copied to the GPU, or a few other - * means. You don't have to worry about this, wlroots takes care of it. */ - struct wlr_texture *texture = wlr_surface_get_texture(surface); - if (texture == NULL) { - return; - } - - /* The view has a position in layout coordinates. If you have two displays, - * one next to the other, both 1080p, a view on the rightmost display might - * have layout coordinates of 2000,100. We need to translate that to - * output-local coordinates, or (2000 - 1920). */ - double ox = 0, oy = 0; - wlr_output_layout_output_coords( - view->server->output_layout, output, &ox, &oy); - ox += view->x + sx, oy += view->y + sy; - - /* We also have to apply the scale factor for HiDPI outputs. This is only - * part of the puzzle, TinyWL does not fully support HiDPI. */ - struct wlr_box box = { - .x = ox * output->scale, - .y = oy * output->scale, - .width = surface->current.width * output->scale, - .height = surface->current.height * output->scale, - }; - - /* - * Those familiar with OpenGL are also familiar with the role of matricies - * in graphics programming. We need to prepare a matrix to render the view - * with. wlr_matrix_project_box is a helper which takes a box with a desired - * x, y coordinates, width and height, and an output geometry, then - * prepares an orthographic projection and multiplies the necessary - * transforms to produce a model-view-projection matrix. - * - * Naturally you can do this any way you like, for example to make a 3D - * compositor. - */ - 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); - - /* This takes our matrix, the texture, and an alpha, and performs the actual - * rendering on the GPU. */ - wlr_render_texture_with_matrix(rdata->renderer, texture, matrix, 1); - - /* This lets the client know that we've displayed that frame and it can - * prepare another one now if it likes. */ - wlr_surface_send_frame_done(surface, rdata->when); + wlr_surface_send_frame_done(surface, data); } static void output_frame(struct wl_listener *listener, void *data) { @@ -605,56 +523,16 @@ static void output_frame(struct wl_listener *listener, void *data) { * generally at the output's refresh rate (e.g. 60Hz). */ struct tinywl_output *output = wl_container_of(listener, output, frame); - struct wlr_renderer *renderer = output->server->renderer; - struct timespec now; - clock_gettime(CLOCK_MONOTONIC, &now); + struct wlr_scene_output *scene_output = wlr_scene_get_scene_output( + output->server->scene, output->wlr_output); - /* wlr_output_attach_render makes the OpenGL context current. */ - if (!wlr_output_attach_render(output->wlr_output, NULL)) { - return; - } - /* The "effective" resolution can change if you rotate your outputs. */ - int width, height; - wlr_output_effective_resolution(output->wlr_output, &width, &height); - /* Begin the renderer (calls glViewport and some other GL sanity checks) */ - wlr_renderer_begin(renderer, width, height); - - float color[4] = {0.3, 0.3, 0.3, 1.0}; - wlr_renderer_clear(renderer, color); - - /* Each subsequent window we render is rendered on top of the last. Because - * our view list is ordered front-to-back, we iterate over it backwards. */ - struct tinywl_view *view; - wl_list_for_each_reverse(view, &output->server->views, link) { - if (!view->mapped) { - /* An unmapped view should not be rendered. */ - continue; - } - struct render_data rdata = { - .output = output->wlr_output, - .view = view, - .renderer = renderer, - .when = &now, - }; - /* This calls our render_surface function for each surface among the - * xdg_surface's toplevel and popups. */ - wlr_xdg_surface_for_each_surface(view->xdg_surface, - render_surface, &rdata); - } + /* Render the scene if needed and commit the output */ + wlr_scene_output_commit(scene_output); - /* Hardware cursors are rendered by the GPU on a separate plane, and can be - * moved around without re-rendering what's beneath them - which is more - * efficient. However, not all hardware supports hardware cursors. For this - * reason, wlroots provides a software fallback, which we ask it to render - * here. wlr_cursor handles configuring hardware vs software cursors for you, - * and this function is a no-op when hardware cursors are in use. */ - wlr_output_render_software_cursors(output->wlr_output, NULL); - - /* Conclude rendering and swap the buffers, showing the final frame - * on-screen. */ - wlr_renderer_end(renderer); - wlr_output_commit(output->wlr_output); + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + wlr_scene_output_for_each_surface(scene_output, send_frame_done, &now); } static void server_new_output(struct wl_listener *listener, void *data) { @@ -704,23 +582,32 @@ static void server_new_output(struct wl_listener *listener, void *data) { wlr_output_layout_add_auto(server->output_layout, wlr_output); } -static void xdg_surface_map(struct wl_listener *listener, void *data) { +static void xdg_toplevel_map(struct wl_listener *listener, void *data) { /* Called when the surface is mapped, or ready to display on-screen. */ struct tinywl_view *view = wl_container_of(listener, view, map); - view->mapped = true; + + wl_list_insert(&view->server->views, &view->link); + focus_view(view, view->xdg_surface->surface); } -static void xdg_surface_unmap(struct wl_listener *listener, void *data) { +static void xdg_toplevel_unmap(struct wl_listener *listener, void *data) { /* Called when the surface is unmapped, and should no longer be shown. */ struct tinywl_view *view = wl_container_of(listener, view, unmap); - view->mapped = false; + + wl_list_remove(&view->link); } -static void xdg_surface_destroy(struct wl_listener *listener, void *data) { +static void xdg_toplevel_destroy(struct wl_listener *listener, void *data) { /* Called when the surface is destroyed and should never be shown again. */ struct tinywl_view *view = wl_container_of(listener, view, destroy); - wl_list_remove(&view->link); + + wl_list_remove(&view->map.link); + wl_list_remove(&view->unmap.link); + wl_list_remove(&view->destroy.link); + wl_list_remove(&view->request_move.link); + wl_list_remove(&view->request_resize.link); + free(view); } @@ -746,8 +633,10 @@ static void begin_interactive(struct tinywl_view *view, struct wlr_box geo_box; wlr_xdg_surface_get_geometry(view->xdg_surface, &geo_box); - double border_x = (view->x + geo_box.x) + ((edges & WLR_EDGE_RIGHT) ? geo_box.width : 0); - double border_y = (view->y + geo_box.y) + ((edges & WLR_EDGE_BOTTOM) ? geo_box.height : 0); + double border_x = (view->x + geo_box.x) + + ((edges & WLR_EDGE_RIGHT) ? geo_box.width : 0); + double border_y = (view->y + geo_box.y) + + ((edges & WLR_EDGE_BOTTOM) ? geo_box.height : 0); server->grab_x = server->cursor->x - border_x; server->grab_y = server->cursor->y - border_y; @@ -788,22 +677,38 @@ static void server_new_xdg_surface(struct wl_listener *listener, void *data) { struct tinywl_server *server = wl_container_of(listener, server, new_xdg_surface); struct wlr_xdg_surface *xdg_surface = data; - if (xdg_surface->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL) { + + /* We must add xdg popups to the scene graph so they get rendered. The + * wlroots scene graph provides a helper for this, but to use it we must + * provide the proper parent scene node of the xdg popup. To enable this, + * we always set the user data field of xdg_surfaces to the corresponding + * scene node. */ + if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { + struct wlr_xdg_surface *parent = wlr_xdg_surface_from_wlr_surface( + xdg_surface->popup->parent); + struct wlr_scene_node *parent_node = parent->data; + xdg_surface->data = wlr_scene_xdg_surface_create( + parent_node, xdg_surface); return; } + assert(xdg_surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL); /* Allocate a tinywl_view for this surface */ struct tinywl_view *view = calloc(1, sizeof(struct tinywl_view)); view->server = server; view->xdg_surface = xdg_surface; + view->scene_node = wlr_scene_xdg_surface_create( + &view->server->scene->node, view->xdg_surface); + view->scene_node->data = view; + xdg_surface->data = view->scene_node; /* Listen to the various events it can emit */ - view->map.notify = xdg_surface_map; + view->map.notify = xdg_toplevel_map; wl_signal_add(&xdg_surface->events.map, &view->map); - view->unmap.notify = xdg_surface_unmap; + view->unmap.notify = xdg_toplevel_unmap; wl_signal_add(&xdg_surface->events.unmap, &view->unmap); - view->destroy.notify = xdg_surface_destroy; + view->destroy.notify = xdg_toplevel_destroy; wl_signal_add(&xdg_surface->events.destroy, &view->destroy); /* cotd */ @@ -812,9 +717,6 @@ static void server_new_xdg_surface(struct wl_listener *listener, void *data) { wl_signal_add(&toplevel->events.request_move, &view->request_move); view->request_resize.notify = xdg_toplevel_request_resize; wl_signal_add(&toplevel->events.request_resize, &view->request_resize); - - /* Add it to the list of views. */ - wl_list_insert(&server->views, &view->link); } int main(int argc, char *argv[]) { @@ -880,9 +782,17 @@ int main(int argc, char *argv[]) { server.new_output.notify = server_new_output; wl_signal_add(&server.backend->events.new_output, &server.new_output); - /* Set up our list of views and the xdg-shell. The xdg-shell is a Wayland - * protocol which is used for application windows. For more detail on - * shells, refer to my article: + /* Create a scene graph. This is a wlroots abstraction that handles all + * rendering and damage tracking. All the compositor author needs to do + * is add things that should be rendered to the scene graph at the proper + * positions and then call wlr_scene_output_commit() to render a frame if + * necessary. + */ + server.scene = wlr_scene_create(); + wlr_scene_attach_output_layout(server.scene, server.output_layout); + + /* Set up the xdg-shell. The xdg-shell is a Wayland protocol which is used + * for application windows. For more detail on shells, refer to my article: * * https://drewdevault.com/2018/07/29/Wayland-shells.html */ From 611b9ca84331f3278cfc31e188a86ed117aadb82 Mon Sep 17 00:00:00 2001 From: Moon Sungjoon Date: Wed, 1 Dec 2021 05:40:23 +0900 Subject: [PATCH 083/188] backend/wayland: improve wayland input device name Closes: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3332 This makes input device names include it's type name --- backend/wayland/seat.c | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/backend/wayland/seat.c b/backend/wayland/seat.c index 7792d136eb..c2bac46a91 100644 --- a/backend/wayland/seat.c +++ b/backend/wayland/seat.c @@ -468,9 +468,32 @@ struct wlr_wl_input_device *create_wl_input_device( unsigned int vendor = 0, product = 0; - size_t name_size = 8 + strlen(seat->name) + 1; + const char *type_name; + + switch (type) { + case WLR_INPUT_DEVICE_KEYBOARD: + type_name = "keyboard"; + break; + case WLR_INPUT_DEVICE_POINTER: + type_name = "pointer"; + break; + case WLR_INPUT_DEVICE_TOUCH: + type_name = "touch"; + break; + case WLR_INPUT_DEVICE_TABLET_TOOL: + type_name = "tablet-tool"; + break; + case WLR_INPUT_DEVICE_TABLET_PAD: + type_name = "tablet-pad"; + break; + case WLR_INPUT_DEVICE_SWITCH: + type_name = "switch"; + break; + } + + size_t name_size = 8 + strlen(type_name) + strlen(seat->name) + 1; char name[name_size]; - (void) snprintf(name, name_size, "wayland-%s", seat->name); + (void) snprintf(name, name_size, "wayland-%s-%s", type_name, seat->name); wlr_input_device_init(wlr_dev, type, &input_device_impl, name, vendor, product); From 812ab2e716dc7c1ac906a206709ce009ae30c45c Mon Sep 17 00:00:00 2001 From: Quantum Date: Wed, 1 Dec 2021 02:31:45 -0500 Subject: [PATCH 084/188] Fix uninitialized variable errors in release mode When using `meson --buildtype=release`, `-Wextra -Werror` is passed. This includes `-Werror=maybe-uninitialized`, which complains about the instances fixed in this commit. --- backend/wayland/seat.c | 2 +- tinywl/tinywl.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/wayland/seat.c b/backend/wayland/seat.c index c2bac46a91..76c25fe5a1 100644 --- a/backend/wayland/seat.c +++ b/backend/wayland/seat.c @@ -468,7 +468,7 @@ struct wlr_wl_input_device *create_wl_input_device( unsigned int vendor = 0, product = 0; - const char *type_name; + const char *type_name = "unknown"; switch (type) { case WLR_INPUT_DEVICE_KEYBOARD: diff --git a/tinywl/tinywl.c b/tinywl/tinywl.c index 5ce7c4e818..487757409e 100644 --- a/tinywl/tinywl.c +++ b/tinywl/tinywl.c @@ -475,7 +475,7 @@ static void server_cursor_button(struct wl_listener *listener, void *data) { wlr_seat_pointer_notify_button(server->seat, event->time_msec, event->button, event->state); double sx, sy; - struct wlr_surface *surface; + struct wlr_surface *surface = NULL; struct tinywl_view *view = desktop_view_at(server, server->cursor->x, server->cursor->y, &surface, &sx, &sy); if (event->state == WLR_BUTTON_RELEASED) { From ffd4a27714833826542e1b083b3f3ad431f49107 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 1 Dec 2021 14:03:37 +0100 Subject: [PATCH 085/188] render/egl: store IMG_context_priority in wlr_egl The next commit will split extension lookup and context initialization. --- include/wlr/render/egl.h | 1 + render/egl.c | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/include/wlr/render/egl.h b/include/wlr/render/egl.h index 903b70a3fc..8db0718ab7 100644 --- a/include/wlr/render/egl.h +++ b/include/wlr/render/egl.h @@ -40,6 +40,7 @@ struct wlr_egl { bool KHR_image_base; bool EXT_image_dma_buf_import; bool EXT_image_dma_buf_import_modifiers; + bool IMG_context_priority; // Device extensions bool EXT_device_drm; diff --git a/render/egl.c b/render/egl.c index 9dae0724ba..a7b39cc276 100644 --- a/render/egl.c +++ b/render/egl.c @@ -324,6 +324,9 @@ static bool egl_init(struct wlr_egl *egl, EGLenum platform, return false; } + egl->exts.IMG_context_priority = + check_egl_ext(display_exts_str, "EGL_IMG_context_priority"); + wlr_log(WLR_INFO, "Supported EGL display extensions: %s", display_exts_str); if (device_exts_str != NULL) { wlr_log(WLR_INFO, "Supported EGL device extensions: %s", device_exts_str); @@ -336,9 +339,6 @@ static bool egl_init(struct wlr_egl *egl, EGLenum platform, init_dmabuf_formats(egl); - bool ext_context_priority = - check_egl_ext(display_exts_str, "EGL_IMG_context_priority"); - size_t atti = 0; EGLint attribs[5]; attribs[atti++] = EGL_CONTEXT_CLIENT_VERSION; @@ -346,7 +346,7 @@ static bool egl_init(struct wlr_egl *egl, EGLenum platform, // Request a high priority context if possible // TODO: only do this if we're running as the DRM master - bool request_high_priority = ext_context_priority; + bool request_high_priority = egl->exts.IMG_context_priority; // Try to reschedule all of our rendering to be completed first. If it // fails, it will fallback to the default priority (MEDIUM). From 051d1ce90ef9ab3ce931b3b2df43012960df514b Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 27 Nov 2021 20:01:48 +0100 Subject: [PATCH 086/188] render/egl: add wlr_egl_create_with_context This allows creating a wlr_egl from an already-existing EGL display and context. This is useful to allow compositors to choose the exact EGL initialization parameters. --- include/wlr/render/egl.h | 3 ++ render/egl.c | 59 ++++++++++++++++++++++++++++++++++------ 2 files changed, 53 insertions(+), 9 deletions(-) diff --git a/include/wlr/render/egl.h b/include/wlr/render/egl.h index 8db0718ab7..2af073cae8 100644 --- a/include/wlr/render/egl.h +++ b/include/wlr/render/egl.h @@ -69,6 +69,9 @@ struct wlr_egl { struct wlr_drm_format_set dmabuf_render_formats; }; +struct wlr_egl *wlr_egl_create_with_context(EGLDisplay display, + EGLContext context); + /** * Make the EGL context current. * diff --git a/render/egl.c b/render/egl.c index a7b39cc276..a5801a1d14 100644 --- a/render/egl.c +++ b/render/egl.c @@ -232,14 +232,8 @@ static struct wlr_egl *egl_create(void) { return egl; } -static bool egl_init(struct wlr_egl *egl, EGLenum platform, - void *remote_display) { - egl->display = egl->procs.eglGetPlatformDisplayEXT(platform, - remote_display, NULL); - if (egl->display == EGL_NO_DISPLAY) { - wlr_log(WLR_ERROR, "Failed to create EGL display"); - return false; - } +static bool egl_init_display(struct wlr_egl *egl, EGLDisplay *display) { + egl->display = display; EGLint major, minor; if (eglInitialize(egl->display, &major, &minor) == EGL_FALSE) { @@ -327,11 +321,11 @@ static bool egl_init(struct wlr_egl *egl, EGLenum platform, egl->exts.IMG_context_priority = check_egl_ext(display_exts_str, "EGL_IMG_context_priority"); + wlr_log(WLR_INFO, "Using EGL %d.%d", (int)major, (int)minor); wlr_log(WLR_INFO, "Supported EGL display extensions: %s", display_exts_str); if (device_exts_str != NULL) { wlr_log(WLR_INFO, "Supported EGL device extensions: %s", device_exts_str); } - wlr_log(WLR_INFO, "Using EGL %d.%d", (int)major, (int)minor); wlr_log(WLR_INFO, "EGL vendor: %s", eglQueryString(egl->display, EGL_VENDOR)); if (driver_name != NULL) { wlr_log(WLR_INFO, "EGL driver name: %s", driver_name); @@ -339,6 +333,23 @@ static bool egl_init(struct wlr_egl *egl, EGLenum platform, init_dmabuf_formats(egl); + return true; +} + +static bool egl_init(struct wlr_egl *egl, EGLenum platform, + void *remote_display) { + EGLDisplay display = egl->procs.eglGetPlatformDisplayEXT(platform, + remote_display, NULL); + if (display == EGL_NO_DISPLAY) { + wlr_log(WLR_ERROR, "Failed to create EGL display"); + return false; + } + + if (!egl_init_display(egl, display)) { + eglTerminate(display); + return false; + } + size_t atti = 0; EGLint attribs[5]; attribs[atti++] = EGL_CONTEXT_CLIENT_VERSION; @@ -516,6 +527,36 @@ struct wlr_egl *wlr_egl_create_with_drm_fd(int drm_fd) { return NULL; } +struct wlr_egl *wlr_egl_create_with_context(EGLDisplay display, + EGLContext context) { + EGLint client_type; + if (!eglQueryContext(display, context, EGL_CONTEXT_CLIENT_TYPE, &client_type) || + client_type != EGL_OPENGL_ES_API) { + wlr_log(WLR_ERROR, "Unsupported EGL context client type (need OpenGL ES)"); + return NULL; + } + + EGLint client_version; + if (!eglQueryContext(display, context, EGL_CONTEXT_CLIENT_VERSION, &client_version) || + client_version < 2) { + wlr_log(WLR_ERROR, "Unsupported EGL context client version (need OpenGL ES >= 2)"); + return NULL; + } + + struct wlr_egl *egl = egl_create(); + if (egl == NULL) { + return NULL; + } + + if (!egl_init_display(egl, display)) { + return NULL; + } + + egl->context = context; + + return egl; +} + void wlr_egl_destroy(struct wlr_egl *egl) { if (egl == NULL) { return; From de0bc7831954a5e4baa367b01bb02b755b53e5d0 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 1 Dec 2021 14:12:09 +0100 Subject: [PATCH 087/188] render/pixman: advertise MOD_INVALID instead of MOD_LINEAR The backends and allocators use INVALID, but the renderer uses LINEAR. Running a compositor with WLR_RENDERER=pixman results in: 00:00:00.744 [types/output/render.c:59] Failed to pick primary buffer format for output 'WL-1' --- render/pixman/renderer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/render/pixman/renderer.c b/render/pixman/renderer.c index c65523b0fc..13db08800f 100644 --- a/render/pixman/renderer.c +++ b/render/pixman/renderer.c @@ -528,7 +528,7 @@ struct wlr_renderer *wlr_pixman_renderer_create(void) { for (size_t i = 0; i < len; ++i) { wlr_drm_format_set_add(&renderer->drm_formats, formats[i], - DRM_FORMAT_MOD_LINEAR); + DRM_FORMAT_MOD_INVALID); } return &renderer->wlr_renderer; From 1bf9676e8781c281265267d71ad7c911516be023 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 27 Nov 2021 18:33:04 +0100 Subject: [PATCH 088/188] render/egl: improve modifier support detection Support for EXT_image_dma_buf_import_modifiers doesn't necessarily indicate support for modifiers. For instance, Mesa will advertise EXT_image_dma_buf_import_modifiers for all drivers. This is a trick to allow EGL clients to enumerate supported formats (something EXT_image_dma_buf_import is missing). For more information, see [1]. Add a new wlr_egl.has_modifiers flag which indicates whether modifiers are supported. It's set to true if any eglQueryDmaBufModifiersEXT query returned a non-empty list. Use that flag to figure out whether the buffer modifier should be passed to the EGL implementation on import. [1]: https://github.com/KhronosGroup/EGL-Registry/issues/142 --- include/wlr/render/egl.h | 1 + render/egl.c | 23 +++++++++-------------- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/include/wlr/render/egl.h b/include/wlr/render/egl.h index 2af073cae8..0d84958be3 100644 --- a/include/wlr/render/egl.h +++ b/include/wlr/render/egl.h @@ -65,6 +65,7 @@ struct wlr_egl { PFNEGLQUERYDEVICESEXTPROC eglQueryDevicesEXT; } procs; + bool has_modifiers; struct wlr_drm_format_set dmabuf_texture_formats; struct wlr_drm_format_set dmabuf_render_formats; }; diff --git a/render/egl.c b/render/egl.c index a5801a1d14..d5d6cfb7dd 100644 --- a/render/egl.c +++ b/render/egl.c @@ -160,6 +160,8 @@ static void init_dmabuf_formats(struct wlr_egl *egl) { has_modifiers ? "supported" : "unsupported"); free(str_formats); + egl->has_modifiers = has_modifiers; + out: free(formats); } @@ -644,19 +646,11 @@ EGLImageKHR wlr_egl_create_image_from_dmabuf(struct wlr_egl *egl, return NULL; } - bool has_modifier = false; - - // we assume the same way we assumed formats without the import_modifiers - // extension that mod_linear is supported. The special mod mod_invalid - // is sometimes used to signal modifier unawareness which is what we - // have here if (attributes->modifier != DRM_FORMAT_MOD_INVALID && - attributes->modifier != DRM_FORMAT_MOD_LINEAR) { - if (!egl->exts.EXT_image_dma_buf_import_modifiers) { - wlr_log(WLR_ERROR, "dmabuf modifiers extension not present"); - return NULL; - } - has_modifier = true; + attributes->modifier != DRM_FORMAT_MOD_LINEAR && + !egl->has_modifiers) { + wlr_log(WLR_ERROR, "EGL implementation doesn't support modifiers"); + return NULL; } unsigned int atti = 0; @@ -702,14 +696,15 @@ EGLImageKHR wlr_egl_create_image_from_dmabuf(struct wlr_egl *egl, } }; - for (int i=0; i < attributes->n_planes; i++) { + for (int i = 0; i < attributes->n_planes; i++) { attribs[atti++] = attr_names[i].fd; attribs[atti++] = attributes->fd[i]; attribs[atti++] = attr_names[i].offset; attribs[atti++] = attributes->offset[i]; attribs[atti++] = attr_names[i].pitch; attribs[atti++] = attributes->stride[i]; - if (has_modifier) { + if (egl->has_modifiers && + attributes->modifier != DRM_FORMAT_MOD_INVALID) { attribs[atti++] = attr_names[i].mod_lo; attribs[atti++] = attributes->modifier & 0xFFFFFFFF; attribs[atti++] = attr_names[i].mod_hi; From 0d32118a8087061c17eac30e9b00ee527d6d3a4e Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 1 Dec 2021 08:49:48 +0100 Subject: [PATCH 089/188] output: fix modifier stripping DRM formats with an empty modifier list are invalid. Instead of emptying the list, reduce it to { INVALID }. Add a check to make sure the renderer and backend support implicit modifiers, so that we don't fallback on e.g. Vulkan. Closes: https://github.com/swaywm/sway/issues/6692 --- types/output/render.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/types/output/render.c b/types/output/render.c index 97c62a58d9..985b93a987 100644 --- a/types/output/render.c +++ b/types/output/render.c @@ -74,7 +74,14 @@ static bool output_create_swapchain(struct wlr_output *output, format->format, output->name); if (!allow_modifiers && (format->len != 1 || format->modifiers[0] != DRM_FORMAT_MOD_LINEAR)) { + if (!wlr_drm_format_has(format, DRM_FORMAT_MOD_INVALID)) { + wlr_log(WLR_DEBUG, "Implicit modifiers not supported"); + free(format); + return false; + } + format->len = 0; + wlr_drm_format_add(&format, DRM_FORMAT_MOD_INVALID); } struct wlr_swapchain *swapchain = From 92080b3a014d92af628de964e66aca705da3041d Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Fri, 3 Dec 2021 11:45:57 +0100 Subject: [PATCH 090/188] readme: update wrapper libraries link Closes: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3336 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 33c59104b8..2f9adf71e6 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,6 @@ See [CONTRIBUTING.md]. [wiki]: https://gitlab.freedesktop.org/wlroots/wlroots/-/wikis/Getting-started [#sway-devel on Libera Chat]: https://web.libera.chat/gamja/?channels=#sway-devel [Sway]: https://github.com/swaywm/sway -[wrapper libraries]: https://github.com/search?q=topic%3Abindings+org%3Aswaywm&type=Repositories +[wrapper libraries]: https://gitlab.freedesktop.org/wlroots/wlroots/-/wikis/Projects-which-use-wlroots#wrapper-libraries [libseat]: https://git.sr.ht/~kennylevinsen/seatd [CONTRIBUTING.md]: https://gitlab.freedesktop.org/wlroots/wlroots/-/blob/master/CONTRIBUTING.md From 7201aae3d607ad121ea0dfb3a50eb2c3f65b7d85 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 1 Dec 2021 13:35:50 +0100 Subject: [PATCH 091/188] render/drm-format-set: add wlr_drm_format_set_intersect This intersects two DRM format sets. This is useful for implementing DMA-BUF feedback in compositors, see e.g. the Sway PR [1]. [1]: https://github.com/swaywm/sway/pull/6313 --- include/wlr/render/drm_format_set.h | 9 +++++++ render/drm_format_set.c | 38 +++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/include/wlr/render/drm_format_set.h b/include/wlr/render/drm_format_set.h index 9427df3bd0..9f4a86ea4c 100644 --- a/include/wlr/render/drm_format_set.h +++ b/include/wlr/render/drm_format_set.h @@ -63,4 +63,13 @@ bool wlr_drm_format_set_has(const struct wlr_drm_format_set *set, bool wlr_drm_format_set_add(struct wlr_drm_format_set *set, uint32_t format, uint64_t modifier); +/** + * Intersect two DRM format sets `a` and `b`, storing in the destination set + * `dst` the format + modifier pairs which are in both source sets. + * + * Returns false on failure or when the intersection is empty. + */ +bool wlr_drm_format_set_intersect(struct wlr_drm_format_set *dst, + const struct wlr_drm_format_set *a, const struct wlr_drm_format_set *b); + #endif diff --git a/render/drm_format_set.c b/render/drm_format_set.c index 82997eda9e..134717462e 100644 --- a/render/drm_format_set.c +++ b/render/drm_format_set.c @@ -175,3 +175,41 @@ struct wlr_drm_format *wlr_drm_format_intersect( return format; } + +bool wlr_drm_format_set_intersect(struct wlr_drm_format_set *dst, + const struct wlr_drm_format_set *a, const struct wlr_drm_format_set *b) { + assert(dst != a && dst != b); + + struct wlr_drm_format_set out = {0}; + out.capacity = a->len < b->len ? a->len : b->len; + out.formats = calloc(out.capacity, sizeof(struct wlr_drm_format *)); + if (out.formats == NULL) { + wlr_log_errno(WLR_ERROR, "Allocation failed"); + return false; + } + + for (size_t i = 0; i < a->len; i++) { + for (size_t j = 0; j < b->len; j++) { + if (a->formats[i]->format == b->formats[j]->format) { + // When the two formats have no common modifier, keep + // intersecting the rest of the formats: they may be compatible + // with each other + struct wlr_drm_format *format = + wlr_drm_format_intersect(a->formats[i], b->formats[j]); + if (format != NULL) { + out.formats[out.len] = format; + out.len++; + } + break; + } + } + } + + if (out.len == 0) { + wlr_drm_format_set_finish(&out); + return false; + } + + *dst = out; + return true; +} From f016eca97cf046c5273c5112201588d4fdca9818 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Fri, 3 Dec 2021 11:06:49 +0100 Subject: [PATCH 092/188] output: add wlr_output_event_commit.buffer This allows output commit listeners to access the newly committed buffer. Currently wlr_output.front_buffer is used but it'll get removed in the next commit. --- include/wlr/types/wlr_output.h | 1 + types/output/output.c | 1 + 2 files changed, 2 insertions(+) diff --git a/include/wlr/types/wlr_output.h b/include/wlr/types/wlr_output.h index 9e18a21668..689b8158c0 100644 --- a/include/wlr/types/wlr_output.h +++ b/include/wlr/types/wlr_output.h @@ -211,6 +211,7 @@ struct wlr_output_event_commit { struct wlr_output *output; uint32_t committed; // bitmask of enum wlr_output_state_field struct timespec *when; + struct wlr_buffer *buffer; // NULL if no buffer is committed }; enum wlr_output_present_flag { diff --git a/types/output/output.c b/types/output/output.c index e82ec1dc81..8655ef981c 100644 --- a/types/output/output.c +++ b/types/output/output.c @@ -743,6 +743,7 @@ bool wlr_output_commit(struct wlr_output *output) { .output = output, .committed = committed, .when = &now, + .buffer = back_buffer, }; wlr_signal_emit_safe(&output->events.commit, &event); From 60b7267e1846b1bce39d30e73abe817faaa8fcff Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Fri, 3 Dec 2021 11:10:05 +0100 Subject: [PATCH 093/188] export-dmabuf-v1: use wlr_output_event_commit.buffer --- types/wlr_export_dmabuf_v1.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/types/wlr_export_dmabuf_v1.c b/types/wlr_export_dmabuf_v1.c index d26a9a450c..b47758c631 100644 --- a/types/wlr_export_dmabuf_v1.c +++ b/types/wlr_export_dmabuf_v1.c @@ -66,7 +66,7 @@ static void frame_output_handle_commit(struct wl_listener *listener, wl_list_init(&frame->output_commit.link); struct wlr_dmabuf_attributes attribs = {0}; - if (!wlr_buffer_get_dmabuf(frame->output->front_buffer, &attribs)) { + if (!wlr_buffer_get_dmabuf(event->buffer, &attribs)) { zwlr_export_dmabuf_frame_v1_send_cancel(frame->resource, ZWLR_EXPORT_DMABUF_FRAME_V1_CANCEL_REASON_TEMPORARY); frame_destroy(frame); From 45069fb6234010e59933baad813c63def0254536 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Fri, 3 Dec 2021 11:19:20 +0100 Subject: [PATCH 094/188] screencopy-v1: use wlr_output_event_commit.buffer --- types/wlr_screencopy_v1.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/types/wlr_screencopy_v1.c b/types/wlr_screencopy_v1.c index e0e7916bad..e2c020db5b 100644 --- a/types/wlr_screencopy_v1.c +++ b/types/wlr_screencopy_v1.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -189,7 +190,7 @@ static void frame_send_ready(struct wlr_screencopy_frame_v1 *frame, } static bool frame_shm_copy(struct wlr_screencopy_frame_v1 *frame, - uint32_t *flags) { + struct wlr_buffer *src_buffer, uint32_t *flags) { struct wl_shm_buffer *shm_buffer = frame->shm_buffer; struct wlr_output *output = frame->output; struct wlr_renderer *renderer = output->renderer; @@ -208,7 +209,7 @@ static bool frame_shm_copy(struct wlr_screencopy_frame_v1 *frame, void *data = wl_shm_buffer_get_data(shm_buffer); uint32_t renderer_flags = 0; bool ok; - ok = wlr_renderer_begin_with_buffer(renderer, output->front_buffer); + ok = wlr_renderer_begin_with_buffer(renderer, src_buffer); ok = ok && wlr_renderer_read_pixels(renderer, drm_format, &renderer_flags, stride, width, height, x, y, 0, 0, data); wlr_renderer_end(renderer); @@ -254,20 +255,21 @@ static bool blit_dmabuf(struct wlr_renderer *renderer, return false; } -static bool frame_dma_copy(struct wlr_screencopy_frame_v1 *frame) { - struct wlr_dmabuf_v1_buffer *dma_buffer = frame->dma_buffer; +static bool frame_dma_copy(struct wlr_screencopy_frame_v1 *frame, + struct wlr_buffer *src_buffer) { + struct wlr_dmabuf_v1_buffer *dst_buffer = frame->dma_buffer; struct wlr_output *output = frame->output; struct wlr_renderer *renderer = output->renderer; assert(renderer); // TODO: add support for copying regions with DMA-BUFs if (frame->box.x != 0 || frame->box.y != 0 || - output->width != frame->box.width || - output->height != frame->box.height) { + src_buffer->width != frame->box.width || + src_buffer->height != frame->box.height) { return false; } - return blit_dmabuf(renderer, dma_buffer, output->front_buffer); + return blit_dmabuf(renderer, dst_buffer, src_buffer); } static void frame_handle_output_commit(struct wl_listener *listener, @@ -277,6 +279,7 @@ static void frame_handle_output_commit(struct wl_listener *listener, struct wlr_output_event_commit *event = data; struct wlr_output *output = frame->output; struct wlr_renderer *renderer = output->renderer; + struct wlr_buffer *buffer = event->buffer; assert(renderer); if (!(event->committed & WLR_OUTPUT_STATE_BUFFER)) { @@ -298,9 +301,10 @@ static void frame_handle_output_commit(struct wl_listener *listener, wl_list_remove(&frame->output_commit.link); wl_list_init(&frame->output_commit.link); + uint32_t flags = 0; bool ok = frame->shm_buffer ? - frame_shm_copy(frame, &flags) : frame_dma_copy(frame); + frame_shm_copy(frame, buffer, &flags) : frame_dma_copy(frame, buffer); if (!ok) { zwlr_screencopy_frame_v1_send_failed(frame->resource); frame_destroy(frame); @@ -483,15 +487,6 @@ static struct wlr_screencopy_v1_client *client_from_resource( return wl_resource_get_user_data(resource); } -static uint32_t get_output_fourcc(struct wlr_output *output) { - struct wlr_dmabuf_attributes attr = {0}; - if (!output->front_buffer || - !wlr_buffer_get_dmabuf(output->front_buffer, &attr)) { - return DRM_FORMAT_INVALID; - } - return attr.format; -} - static void capture_output(struct wl_client *wl_client, struct wlr_screencopy_v1_client *client, uint32_t version, uint32_t id, int32_t overlay_cursor, struct wlr_output *output, @@ -549,7 +544,12 @@ static void capture_output(struct wl_client *wl_client, } frame->format = convert_drm_format_to_wl_shm(drm_format); - frame->fourcc = get_output_fourcc(output); + if (output->allocator && + (output->allocator->buffer_caps & WLR_BUFFER_CAP_DMABUF)) { + frame->fourcc = output->render_format; + } else { + frame->fourcc = DRM_FORMAT_INVALID; + } struct wlr_box buffer_box = {0}; if (box == NULL) { From efeb8346cfc6a18d2d9341460033883e4f89c28a Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 1 Dec 2021 08:49:48 +0100 Subject: [PATCH 095/188] output: drop front_buffer This lets backends immediately release committed buffers if they want to. --- include/wlr/types/wlr_output.h | 2 +- types/output/output.c | 18 ++++-------------- 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/include/wlr/types/wlr_output.h b/include/wlr/types/wlr_output.h index 689b8158c0..fd8e655637 100644 --- a/include/wlr/types/wlr_output.h +++ b/include/wlr/types/wlr_output.h @@ -188,7 +188,7 @@ struct wlr_output { struct wlr_allocator *allocator; struct wlr_renderer *renderer; struct wlr_swapchain *swapchain; - struct wlr_buffer *back_buffer, *front_buffer; + struct wlr_buffer *back_buffer; struct wl_listener display_destroy; diff --git a/types/output/output.c b/types/output/output.c index 8655ef981c..9a715d120c 100644 --- a/types/output/output.c +++ b/types/output/output.c @@ -393,9 +393,6 @@ void wlr_output_destroy(struct wlr_output *output) { return; } - wlr_buffer_unlock(output->front_buffer); - output->front_buffer = NULL; - wl_list_remove(&output->display_destroy.link); wlr_output_destroy_global(output); output_clear_back_buffer(output); @@ -716,15 +713,6 @@ bool wlr_output_commit(struct wlr_output *output) { output->cursor_swapchain = NULL; } - // Unset the front-buffer when a new buffer will replace it or when the - // output is getting disabled - if ((output->pending.committed & WLR_OUTPUT_STATE_BUFFER) || - ((output->pending.committed & WLR_OUTPUT_STATE_ENABLED) && - !output->pending.enabled)) { - wlr_buffer_unlock(output->front_buffer); - output->front_buffer = NULL; - } - if (output->pending.committed & WLR_OUTPUT_STATE_BUFFER) { output->frame_pending = true; output->needs_frame = false; @@ -732,8 +720,6 @@ bool wlr_output_commit(struct wlr_output *output) { if (back_buffer != NULL) { wlr_swapchain_set_buffer_submitted(output->swapchain, back_buffer); - wlr_buffer_unlock(output->front_buffer); - output->front_buffer = back_buffer; } uint32_t committed = output->pending.committed; @@ -747,6 +733,10 @@ bool wlr_output_commit(struct wlr_output *output) { }; wlr_signal_emit_safe(&output->events.commit, &event); + if (back_buffer != NULL) { + wlr_buffer_unlock(back_buffer); + } + return true; } From ca1af8119c8f441b6ab95eb2210b65c39d21cf18 Mon Sep 17 00:00:00 2001 From: tiosgz Date: Sat, 4 Dec 2021 22:12:58 +0000 Subject: [PATCH 096/188] Fix wlr_scene_node_lower_to_bottom Before this commit, it would keep the node at the top or make it second- topmost. --- types/scene/wlr_scene.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index d1236fe0e8..b1db7b8f20 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -483,7 +483,7 @@ void wlr_scene_node_raise_to_top(struct wlr_scene_node *node) { void wlr_scene_node_lower_to_bottom(struct wlr_scene_node *node) { struct wlr_scene_node *current_bottom = wl_container_of( - node->parent->state.children.prev, current_bottom, state.link); + node->parent->state.children.next, current_bottom, state.link); if (node == current_bottom) { return; } From c9f3c2b4f7052e4d4901661a953072ac030bf153 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Sat, 20 Nov 2021 13:30:18 +0300 Subject: [PATCH 097/188] surface: fix damage transformation --- types/wlr_surface.c | 41 ++++++++++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/types/wlr_surface.c b/types/wlr_surface.c index ffc77547a0..6237a552a9 100644 --- a/types/wlr_surface.c +++ b/types/wlr_surface.c @@ -123,6 +123,19 @@ static void surface_handle_set_input_region(struct wl_client *client, } } +static void surface_state_transformed_buffer_size(struct wlr_surface_state *state, + int *out_width, int *out_height) { + int width = state->buffer_width; + int height = state->buffer_height; + if ((state->transform & WL_OUTPUT_TRANSFORM_90) != 0) { + int tmp = width; + width = height; + height = tmp; + } + *out_width = width; + *out_height = height; +} + /** * Computes the surface viewport source size, ie. the size after applying the * surface's scale, transform and cropping (via the viewport's source @@ -140,15 +153,10 @@ static void surface_state_viewport_src_size(struct wlr_surface_state *state, *out_width = state->viewport.src.width; *out_height = state->viewport.src.height; } else { - int width = state->buffer_width / state->scale; - int height = state->buffer_height / state->scale; - if ((state->transform & WL_OUTPUT_TRANSFORM_90) != 0) { - int tmp = width; - width = height; - height = tmp; - } - *out_width = width; - *out_height = height; + surface_state_transformed_buffer_size(state, + out_width, out_height); + *out_width /= state->scale; + *out_height /= state->scale; } } @@ -225,10 +233,13 @@ static void surface_update_damage(pixman_region32_t *buffer_damage, floor(pending->viewport.src.y)); } + wlr_region_scale(&surface_damage, &surface_damage, pending->scale); + + int width, height; + surface_state_transformed_buffer_size(pending, &width, &height); wlr_region_transform(&surface_damage, &surface_damage, wlr_output_transform_invert(pending->transform), - pending->width, pending->height); - wlr_region_scale(&surface_damage, &surface_damage, pending->scale); + width, height); pixman_region32_union(buffer_damage, &pending->buffer_damage, &surface_damage); @@ -1472,7 +1483,11 @@ void wlr_surface_get_buffer_source_box(struct wlr_surface *surface, box->y = surface->current.viewport.src.y * surface->current.scale; box->width = surface->current.viewport.src.width * surface->current.scale; box->height = surface->current.viewport.src.height * surface->current.scale; - wlr_fbox_transform(box, box, surface->current.transform, - surface->current.buffer_width, surface->current.buffer_height); + + int width, height; + surface_state_transformed_buffer_size(&surface->current, &width, &height); + wlr_fbox_transform(box, box, + wlr_output_transform_invert(surface->current.transform), + width, height); } } From 77d811a21b3d1dfb4ef6634e870c6c37228f3c15 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 7 Jun 2021 16:31:53 +0200 Subject: [PATCH 098/188] render: add wlr_renderer_init_wl_shm This allows compositors to initialize wl_shm without initializing other globals like linux-dmabuf. --- include/wlr/render/wlr_renderer.h | 8 +++++++- render/wlr_renderer.c | 24 +++++++++++++++++++----- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/include/wlr/render/wlr_renderer.h b/include/wlr/render/wlr_renderer.h index bae2fa9763..d8b04dc72d 100644 --- a/include/wlr/render/wlr_renderer.h +++ b/include/wlr/render/wlr_renderer.h @@ -99,13 +99,19 @@ bool wlr_renderer_read_pixels(struct wlr_renderer *r, uint32_t fmt, uint32_t src_x, uint32_t src_y, uint32_t dst_x, uint32_t dst_y, void *data); /** - * Creates necessary shm and invokes the initialization of the implementation. + * Initializes wl_shm, linux-dmabuf and other buffer factory protocols. * * Returns false on failure. */ bool wlr_renderer_init_wl_display(struct wlr_renderer *r, struct wl_display *wl_display); +/** + * Initializes wl_shm on the provided wl_display. + */ +bool wlr_renderer_init_wl_shm(struct wlr_renderer *r, + struct wl_display *wl_display); + /** * Obtains the FD of the DRM device used for rendering, or -1 if unavailable. * diff --git a/render/wlr_renderer.c b/render/wlr_renderer.c index ff31591357..2b5cf99454 100644 --- a/render/wlr_renderer.c +++ b/render/wlr_renderer.c @@ -202,17 +202,18 @@ bool wlr_renderer_read_pixels(struct wlr_renderer *r, uint32_t fmt, src_x, src_y, dst_x, dst_y, data); } -bool wlr_renderer_init_wl_display(struct wlr_renderer *r, +bool wlr_renderer_init_wl_shm(struct wlr_renderer *r, struct wl_display *wl_display) { - if (wl_display_init_shm(wl_display)) { - wlr_log(WLR_ERROR, "Failed to initialize shm"); + if (wl_display_init_shm(wl_display) != 0) { + wlr_log(WLR_ERROR, "Failed to initialize wl_shm"); return false; } size_t len; const uint32_t *formats = wlr_renderer_get_shm_texture_formats(r, &len); if (formats == NULL) { - wlr_log(WLR_ERROR, "Failed to initialize shm: cannot get formats"); + wlr_log(WLR_ERROR, "Failed to initialize wl_shm: " + "cannot get renderer formats"); return false; } @@ -229,11 +230,24 @@ bool wlr_renderer_init_wl_display(struct wlr_renderer *r, xrgb8888 = true; break; default: - wl_display_add_shm_format(wl_display, fmt); + if (wl_display_add_shm_format(wl_display, fmt) == NULL) { + wlr_log(WLR_ERROR, "Failed to initialize wl_shm: " + "failed to add format"); + return false; + } } } assert(argb8888 && xrgb8888); + return true; +} + +bool wlr_renderer_init_wl_display(struct wlr_renderer *r, + struct wl_display *wl_display) { + if (!wlr_renderer_init_wl_shm(r, wl_display)) { + return false; + } + if (wlr_renderer_get_dmabuf_texture_formats(r) != NULL) { if (wlr_renderer_get_drm_fd(r) >= 0) { if (wlr_drm_create(wl_display, r) == NULL) { From 1d8340754b85eaa60669875c32f9476411941892 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 13 Nov 2018 12:35:08 +0100 Subject: [PATCH 099/188] linux-dmabuf-v1: implement v4 Implement a basic version of linux-dmabuf-unstable-v1 version 4. Only default hints are implemented. The new wlr_linux_dmabuf_feedback_v1 data structure will allow compositors to define their own custom hints in the future. This data structure makes it easy to describe feedback metadata. It's converted to a "compiled" form suitable for marshalling over the Wayland socket via feedback_compile. --- include/wlr/types/wlr_linux_dmabuf_v1.h | 15 ++ protocol/meson.build | 2 +- types/wlr_linux_dmabuf_v1.c | 271 +++++++++++++++++++++++- 3 files changed, 283 insertions(+), 5 deletions(-) diff --git a/include/wlr/types/wlr_linux_dmabuf_v1.h b/include/wlr/types/wlr_linux_dmabuf_v1.h index b4eb8675d0..bbbb277232 100644 --- a/include/wlr/types/wlr_linux_dmabuf_v1.h +++ b/include/wlr/types/wlr_linux_dmabuf_v1.h @@ -10,6 +10,7 @@ #define WLR_TYPES_WLR_LINUX_DMABUF_H #include +#include #include #include #include @@ -38,6 +39,18 @@ bool wlr_dmabuf_v1_resource_is_buffer(struct wl_resource *buffer_resource); struct wlr_dmabuf_v1_buffer *wlr_dmabuf_v1_buffer_from_buffer_resource( struct wl_resource *buffer_resource); +struct wlr_linux_dmabuf_feedback_v1 { + dev_t main_device; + size_t tranches_len; + const struct wlr_linux_dmabuf_feedback_v1_tranche *tranches; +}; + +struct wlr_linux_dmabuf_feedback_v1_tranche { + dev_t target_device; + uint32_t flags; // bitfield of enum zwp_linux_dmabuf_feedback_v1_tranche_flags + const struct wlr_drm_format_set *formats; +}; + /* the protocol interface */ struct wlr_linux_dmabuf_v1 { struct wl_global *global; @@ -49,6 +62,8 @@ struct wlr_linux_dmabuf_v1 { // private state + struct wlr_linux_dmabuf_feedback_v1_compiled *default_feedback; + struct wl_listener display_destroy; struct wl_listener renderer_destroy; }; diff --git a/protocol/meson.build b/protocol/meson.build index 8d8b2502e5..3e34f78818 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -1,5 +1,5 @@ wayland_protos = dependency('wayland-protocols', - version: '>=1.23', + version: '>=1.24', fallback: ['wayland-protocols', 'wayland_protocols'], default_options: ['tests=false'], ) diff --git a/types/wlr_linux_dmabuf_v1.c b/types/wlr_linux_dmabuf_v1.c index b91fb59d92..325b536254 100644 --- a/types/wlr_linux_dmabuf_v1.c +++ b/types/wlr_linux_dmabuf_v1.c @@ -2,17 +2,17 @@ #include #include #include +#include #include -#include -#include #include #include #include #include "linux-dmabuf-unstable-v1-protocol.h" #include "render/drm_format_set.h" #include "util/signal.h" +#include "util/shm.h" -#define LINUX_DMABUF_VERSION 3 +#define LINUX_DMABUF_VERSION 4 struct wlr_linux_buffer_params_v1 { struct wl_resource *resource; @@ -21,6 +21,32 @@ struct wlr_linux_buffer_params_v1 { bool has_modifier; }; +struct wlr_linux_dmabuf_feedback_v1_compiled_tranche { + dev_t target_device; + uint32_t flags; // bitfield of enum zwp_linux_dmabuf_feedback_v1_tranche_flags + struct wl_array indices; // uint16_t +}; + +struct wlr_linux_dmabuf_feedback_v1_compiled { + dev_t main_device; + int table_fd; + size_t table_size; + + size_t tranches_len; + struct wlr_linux_dmabuf_feedback_v1_compiled_tranche tranches[]; +}; + +struct wlr_linux_dmabuf_feedback_v1_table_entry { + uint32_t format; + uint32_t pad; // unused + uint64_t modifier; +}; + +// TODO: switch back to static_assert once this fix propagates in stable trees: +// https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=255290 +_Static_assert(sizeof(struct wlr_linux_dmabuf_feedback_v1_table_entry) == 16, + "Expected wlr_linux_dmabuf_feedback_v1_table_entry to be tightly packed"); + static void buffer_handle_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); @@ -415,6 +441,228 @@ static void linux_dmabuf_create_params(struct wl_client *client, &buffer_params_impl, params, params_handle_resource_destroy); } +static void linux_dmabuf_feedback_destroy(struct wl_client *client, + struct wl_resource *resource) { + wl_resource_destroy(resource); +} + +static const struct zwp_linux_dmabuf_feedback_v1_interface + linux_dmabuf_feedback_impl = { + .destroy = linux_dmabuf_feedback_destroy, +}; + +static struct wlr_linux_dmabuf_feedback_v1_compiled *feedback_compile( + const struct wlr_linux_dmabuf_feedback_v1 *feedback) { + assert(feedback->tranches_len > 0); + + // Require the last tranche to be the fallback tranche and contain all + // formats/modifiers + const struct wlr_linux_dmabuf_feedback_v1_tranche *fallback_tranche = + &feedback->tranches[feedback->tranches_len - 1]; + + size_t table_len = 0; + for (size_t i = 0; i < fallback_tranche->formats->len; i++) { + const struct wlr_drm_format *fmt = fallback_tranche->formats->formats[i]; + table_len += 1 + fmt->len; + } + assert(table_len > 0); + + size_t table_size = + table_len * sizeof(struct wlr_linux_dmabuf_feedback_v1_table_entry); + int rw_fd, ro_fd; + if (!allocate_shm_file_pair(table_size, &rw_fd, &ro_fd)) { + wlr_log(WLR_ERROR, "Failed to allocate shm file for format table"); + return NULL; + } + + struct wlr_linux_dmabuf_feedback_v1_table_entry *table = + mmap(NULL, table_size, PROT_READ | PROT_WRITE, MAP_SHARED, rw_fd, 0); + if (table == MAP_FAILED) { + wlr_log_errno(WLR_ERROR, "mmap failed"); + close(rw_fd); + close(ro_fd); + return NULL; + } + + close(rw_fd); + + size_t n = 0; + for (size_t i = 0; i < fallback_tranche->formats->len; i++) { + const struct wlr_drm_format *fmt = fallback_tranche->formats->formats[i]; + + table[n] = (struct wlr_linux_dmabuf_feedback_v1_table_entry){ + .format = fmt->format, + .modifier = DRM_FORMAT_MOD_INVALID, + }; + n++; + + for (size_t k = 0; k < fmt->len; k++) { + table[n] = (struct wlr_linux_dmabuf_feedback_v1_table_entry){ + .format = fmt->format, + .modifier = fmt->modifiers[k], + }; + n++; + } + } + assert(n == table_len); + + munmap(table, table_size); + + struct wlr_linux_dmabuf_feedback_v1_compiled *compiled = calloc(1, + sizeof(struct wlr_linux_dmabuf_feedback_v1_compiled) + + feedback->tranches_len * sizeof(struct wlr_linux_dmabuf_feedback_v1_compiled_tranche)); + if (compiled == NULL) { + close(ro_fd); + return NULL; + } + + compiled->main_device = feedback->main_device; + compiled->tranches_len = feedback->tranches_len; + compiled->table_fd = ro_fd; + compiled->table_size = table_size; + + // Build the indices lists for all but the last (fallback) tranches + for (size_t i = 0; i < feedback->tranches_len - 1; i++) { + assert(false); // TODO: unimplemented + } + + struct wlr_linux_dmabuf_feedback_v1_compiled_tranche *fallback_compiled_tranche = + &compiled->tranches[compiled->tranches_len - 1]; + fallback_compiled_tranche->target_device = fallback_tranche->target_device; + fallback_compiled_tranche->flags = fallback_tranche->flags; + + // Build the indices list for the last (fallback) tranche + wl_array_init(&fallback_compiled_tranche->indices); + if (!wl_array_add(&fallback_compiled_tranche->indices, + table_len * sizeof(uint16_t))) { + wlr_log(WLR_ERROR, "Failed to allocate fallback tranche indices array"); + goto error_compiled; + } + + n = 0; + uint16_t *index_ptr; + wl_array_for_each(index_ptr, &fallback_compiled_tranche->indices) { + *index_ptr = n; + n++; + } + + return compiled; + +error_compiled: + close(compiled->table_fd); + free(compiled); + return NULL; +} + +static void compiled_feedback_destroy( + struct wlr_linux_dmabuf_feedback_v1_compiled *feedback) { + for (size_t i = 0; i < feedback->tranches_len; i++) { + wl_array_release(&feedback->tranches[i].indices); + } + close(feedback->table_fd); + free(feedback); +} + +static bool feedback_tranche_init_with_renderer( + struct wlr_linux_dmabuf_feedback_v1_tranche *tranche, + struct wlr_renderer *renderer) { + memset(tranche, 0, sizeof(*tranche)); + + int drm_fd = wlr_renderer_get_drm_fd(renderer); + if (drm_fd < 0) { + wlr_log(WLR_ERROR, "Failed to get DRM FD from renderer"); + return false; + } + + struct stat stat; + if (fstat(drm_fd, &stat) != 0) { + wlr_log_errno(WLR_ERROR, "fstat failed"); + return false; + } + tranche->target_device = stat.st_rdev; + + tranche->formats = wlr_renderer_get_dmabuf_texture_formats(renderer); + if (tranche->formats == NULL) { + wlr_log(WLR_ERROR, "Failed to get renderer DMA-BUF texture formats"); + return false; + } + + return true; +} + +static struct wlr_linux_dmabuf_feedback_v1_compiled *compile_default_feedback( + struct wlr_renderer *renderer) { + struct wlr_linux_dmabuf_feedback_v1_tranche tranche = {0}; + if (!feedback_tranche_init_with_renderer(&tranche, renderer)) { + return NULL; + } + + const struct wlr_linux_dmabuf_feedback_v1 feedback = { + .main_device = tranche.target_device, + .tranches = &tranche, + .tranches_len = 1, + }; + + return feedback_compile(&feedback); +} + +static void feedback_tranche_send( + const struct wlr_linux_dmabuf_feedback_v1_compiled_tranche *tranche, + struct wl_resource *resource) { + struct wl_array dev_array = { + .size = sizeof(tranche->target_device), + .data = (void *)&tranche->target_device, + }; + zwp_linux_dmabuf_feedback_v1_send_tranche_target_device(resource, &dev_array); + zwp_linux_dmabuf_feedback_v1_send_tranche_flags(resource, tranche->flags); + zwp_linux_dmabuf_feedback_v1_send_tranche_formats(resource, + (struct wl_array *)&tranche->indices); + zwp_linux_dmabuf_feedback_v1_send_tranche_done(resource); +} + +static void feedback_send(const struct wlr_linux_dmabuf_feedback_v1_compiled *feedback, + struct wl_resource *resource) { + struct wl_array dev_array = { + .size = sizeof(feedback->main_device), + .data = (void *)&feedback->main_device, + }; + zwp_linux_dmabuf_feedback_v1_send_main_device(resource, &dev_array); + + zwp_linux_dmabuf_feedback_v1_send_format_table(resource, + feedback->table_fd, feedback->table_size); + + for (size_t i = 0; i < feedback->tranches_len; i++) { + feedback_tranche_send(&feedback->tranches[i], resource); + } + + zwp_linux_dmabuf_feedback_v1_send_done(resource); +} + +static void linux_dmabuf_get_default_feedback(struct wl_client *client, + struct wl_resource *resource, uint32_t id) { + struct wlr_linux_dmabuf_v1 *linux_dmabuf = + linux_dmabuf_from_resource(resource); + + uint32_t version = wl_resource_get_version(resource); + struct wl_resource *feedback_resource = wl_resource_create(client, + &zwp_linux_dmabuf_feedback_v1_interface, version, id); + if (feedback_resource == NULL) { + wl_client_post_no_memory(client); + return; + } + wl_resource_set_implementation(feedback_resource, &linux_dmabuf_feedback_impl, + NULL, NULL); + + feedback_send(linux_dmabuf->default_feedback, feedback_resource); +} + +static void linux_dmabuf_get_surface_feedback(struct wl_client *client, + struct wl_resource *resource, uint32_t id, + struct wl_resource *surface_resource) { + // TODO: implement per-surface feedback + linux_dmabuf_get_default_feedback(client, resource, id); +} + static void linux_dmabuf_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); @@ -423,6 +671,8 @@ static void linux_dmabuf_destroy(struct wl_client *client, static const struct zwp_linux_dmabuf_v1_interface linux_dmabuf_impl = { .destroy = linux_dmabuf_destroy, .create_params = linux_dmabuf_create_params, + .get_default_feedback = linux_dmabuf_get_default_feedback, + .get_surface_feedback = linux_dmabuf_get_surface_feedback, }; static void linux_dmabuf_send_modifiers(struct wl_resource *resource, @@ -477,12 +727,17 @@ static void linux_dmabuf_bind(struct wl_client *client, void *data, } wl_resource_set_implementation(resource, &linux_dmabuf_impl, linux_dmabuf, NULL); - linux_dmabuf_send_formats(linux_dmabuf, resource); + + if (version < ZWP_LINUX_DMABUF_V1_GET_DEFAULT_FEEDBACK_SINCE_VERSION) { + linux_dmabuf_send_formats(linux_dmabuf, resource); + } } static void linux_dmabuf_v1_destroy(struct wlr_linux_dmabuf_v1 *linux_dmabuf) { wlr_signal_emit_safe(&linux_dmabuf->events.destroy, linux_dmabuf); + compiled_feedback_destroy(linux_dmabuf->default_feedback); + wl_list_remove(&linux_dmabuf->display_destroy.link); wl_list_remove(&linux_dmabuf->renderer_destroy.link); @@ -523,6 +778,14 @@ struct wlr_linux_dmabuf_v1 *wlr_linux_dmabuf_v1_create(struct wl_display *displa return NULL; } + linux_dmabuf->default_feedback = compile_default_feedback(renderer); + if (linux_dmabuf->default_feedback == NULL) { + wlr_log(WLR_ERROR, "Failed to init default linux-dmabuf feedback"); + wl_global_destroy(linux_dmabuf->global); + free(linux_dmabuf); + return NULL; + } + linux_dmabuf->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &linux_dmabuf->display_destroy); From c50c4fc5cc7fc4c1e8e7ecd5d21bb821d0f7cfb6 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 7 Jun 2021 18:20:45 +0200 Subject: [PATCH 100/188] linux-dmabuf-v1: add per-surface feedback --- include/wlr/types/wlr_linux_dmabuf_v1.h | 12 ++ types/wlr_linux_dmabuf_v1.c | 217 +++++++++++++++++++++++- 2 files changed, 226 insertions(+), 3 deletions(-) diff --git a/include/wlr/types/wlr_linux_dmabuf_v1.h b/include/wlr/types/wlr_linux_dmabuf_v1.h index bbbb277232..df89a12192 100644 --- a/include/wlr/types/wlr_linux_dmabuf_v1.h +++ b/include/wlr/types/wlr_linux_dmabuf_v1.h @@ -15,6 +15,8 @@ #include #include +struct wlr_surface; + struct wlr_dmabuf_v1_buffer { struct wlr_buffer base; @@ -63,6 +65,7 @@ struct wlr_linux_dmabuf_v1 { // private state struct wlr_linux_dmabuf_feedback_v1_compiled *default_feedback; + struct wl_list surfaces; // wlr_linux_dmabuf_v1_surface.link struct wl_listener display_destroy; struct wl_listener renderer_destroy; @@ -74,4 +77,13 @@ struct wlr_linux_dmabuf_v1 { struct wlr_linux_dmabuf_v1 *wlr_linux_dmabuf_v1_create(struct wl_display *display, struct wlr_renderer *renderer); +/** + * Set a surface's DMA-BUF feedback. + * + * Passing a NULL feedback resets it to the default feedback. + */ +bool wlr_linux_dmabuf_v1_set_surface_feedback( + struct wlr_linux_dmabuf_v1 *linux_dmabuf, struct wlr_surface *surface, + const struct wlr_linux_dmabuf_feedback_v1 *feedback); + #endif diff --git a/types/wlr_linux_dmabuf_v1.c b/types/wlr_linux_dmabuf_v1.c index 325b536254..ae9bfeb907 100644 --- a/types/wlr_linux_dmabuf_v1.c +++ b/types/wlr_linux_dmabuf_v1.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include "linux-dmabuf-unstable-v1-protocol.h" #include "render/drm_format_set.h" @@ -47,6 +48,17 @@ struct wlr_linux_dmabuf_feedback_v1_table_entry { _Static_assert(sizeof(struct wlr_linux_dmabuf_feedback_v1_table_entry) == 16, "Expected wlr_linux_dmabuf_feedback_v1_table_entry to be tightly packed"); +struct wlr_linux_dmabuf_v1_surface { + struct wlr_surface *surface; + struct wlr_linux_dmabuf_v1 *linux_dmabuf; + struct wl_list link; // wlr_linux_dmabuf_v1.surfaces + + struct wlr_addon addon; + struct wlr_linux_dmabuf_feedback_v1_compiled *feedback; + + struct wl_list feedback_resources; // wl_resource_get_link +}; + static void buffer_handle_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); @@ -451,6 +463,36 @@ static const struct zwp_linux_dmabuf_feedback_v1_interface .destroy = linux_dmabuf_feedback_destroy, }; +static ssize_t get_drm_format_set_index(const struct wlr_drm_format_set *set, + uint32_t format, uint64_t modifier) { + bool format_found = false; + const struct wlr_drm_format *fmt; + size_t idx = 0; + for (size_t i = 0; i < set->len; i++) { + fmt = set->formats[i]; + if (fmt->format == format) { + format_found = true; + break; + } + idx += 1 + fmt->len; + } + if (!format_found) { + return -1; + } + + if (modifier == DRM_FORMAT_MOD_INVALID) { + return idx; + } + + for (size_t i = 0; i < fmt->len; i++) { + if (fmt->modifiers[i] == modifier) { + return idx; + } + idx++; + } + return -1; +} + static struct wlr_linux_dmabuf_feedback_v1_compiled *feedback_compile( const struct wlr_linux_dmabuf_feedback_v1 *feedback) { assert(feedback->tranches_len > 0); @@ -523,7 +565,53 @@ static struct wlr_linux_dmabuf_feedback_v1_compiled *feedback_compile( // Build the indices lists for all but the last (fallback) tranches for (size_t i = 0; i < feedback->tranches_len - 1; i++) { - assert(false); // TODO: unimplemented + const struct wlr_linux_dmabuf_feedback_v1_tranche *tranche = + &feedback->tranches[i]; + struct wlr_linux_dmabuf_feedback_v1_compiled_tranche *compiled_tranche = + &compiled->tranches[i]; + + compiled_tranche->target_device = tranche->target_device; + compiled_tranche->flags = tranche->flags; + + wl_array_init(&compiled_tranche->indices); + if (!wl_array_add(&compiled_tranche->indices, table_len * sizeof(uint16_t))) { + wlr_log(WLR_ERROR, "Failed to allocate tranche indices array"); + goto error_compiled; + } + + n = 0; + uint16_t *indices = compiled_tranche->indices.data; + for (size_t j = 0; j < tranche->formats->len; j++) { + const struct wlr_drm_format *fmt = tranche->formats->formats[j]; + + ssize_t index = get_drm_format_set_index( + fallback_tranche->formats, fmt->format, + DRM_FORMAT_MOD_INVALID); + if (index < 0) { + wlr_log(WLR_ERROR, "Format 0x%" PRIX32 " and modifier " + "INVALID are in tranche #%zu but are missing " + "from the fallback tranche", + fmt->format, i); + goto error_compiled; + } + indices[n] = index; + n++; + + for (size_t k = 0; k < fmt->len; k++) { + ssize_t index = get_drm_format_set_index( + fallback_tranche->formats, fmt->format, fmt->modifiers[k]); + if (index < 0) { + wlr_log(WLR_ERROR, "Format 0x%" PRIX32 " and modifier " + "0x%" PRIX64 " are in tranche #%zu but are missing " + "from the fallback tranche", + fmt->format, fmt->modifiers[k], i); + goto error_compiled; + } + indices[n] = index; + n++; + } + } + compiled_tranche->indices.size = n * sizeof(uint16_t); } struct wlr_linux_dmabuf_feedback_v1_compiled_tranche *fallback_compiled_tranche = @@ -556,6 +644,9 @@ static struct wlr_linux_dmabuf_feedback_v1_compiled *feedback_compile( static void compiled_feedback_destroy( struct wlr_linux_dmabuf_feedback_v1_compiled *feedback) { + if (feedback == NULL) { + return; + } for (size_t i = 0; i < feedback->tranches_len; i++) { wl_array_release(&feedback->tranches[i].indices); } @@ -656,11 +747,96 @@ static void linux_dmabuf_get_default_feedback(struct wl_client *client, feedback_send(linux_dmabuf->default_feedback, feedback_resource); } +static void surface_destroy(struct wlr_linux_dmabuf_v1_surface *surface) { + struct wl_resource *resource, *resource_tmp; + wl_resource_for_each_safe(resource, resource_tmp, &surface->feedback_resources) { + struct wl_list *link = wl_resource_get_link(resource); + wl_list_remove(link); + wl_list_init(link); + } + + compiled_feedback_destroy(surface->feedback); + + wlr_addon_finish(&surface->addon); + wl_list_remove(&surface->link); + free(surface); +} + +static void surface_addon_destroy(struct wlr_addon *addon) { + struct wlr_linux_dmabuf_v1_surface *surface = + wl_container_of(addon, surface, addon); + surface_destroy(surface); +} + +static const struct wlr_addon_interface surface_addon_impl = { + .name = "wlr_linux_dmabuf_v1_surface", + .destroy = surface_addon_destroy, +}; + +static struct wlr_linux_dmabuf_v1_surface *surface_get_or_create( + struct wlr_linux_dmabuf_v1 *linux_dmabuf, + struct wlr_surface *wlr_surface) { + struct wlr_addon *addon = + wlr_addon_find(&wlr_surface->addons, linux_dmabuf, &surface_addon_impl); + if (addon != NULL) { + struct wlr_linux_dmabuf_v1_surface *surface = + wl_container_of(addon, surface, addon); + return surface; + } + + struct wlr_linux_dmabuf_v1_surface *surface = calloc(1, sizeof(*surface)); + if (surface == NULL) { + return NULL; + } + + surface->surface = wlr_surface; + surface->linux_dmabuf = linux_dmabuf; + wl_list_init(&surface->feedback_resources); + wlr_addon_init(&surface->addon, &wlr_surface->addons, linux_dmabuf, + &surface_addon_impl); + wl_list_insert(&linux_dmabuf->surfaces, &surface->link); + + return surface; +} + +static const struct wlr_linux_dmabuf_feedback_v1_compiled *surface_get_feedback( + struct wlr_linux_dmabuf_v1_surface *surface) { + if (surface->feedback != NULL) { + return surface->feedback; + } + return surface->linux_dmabuf->default_feedback; +} + +static void surface_feedback_handle_resource_destroy(struct wl_resource *resource) { + wl_list_remove(wl_resource_get_link(resource)); +} + static void linux_dmabuf_get_surface_feedback(struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *surface_resource) { - // TODO: implement per-surface feedback - linux_dmabuf_get_default_feedback(client, resource, id); + struct wlr_linux_dmabuf_v1 *linux_dmabuf = + linux_dmabuf_from_resource(resource); + struct wlr_surface *wlr_surface = wlr_surface_from_resource(surface_resource); + + struct wlr_linux_dmabuf_v1_surface *surface = + surface_get_or_create(linux_dmabuf, wlr_surface); + if (surface == NULL) { + wl_client_post_no_memory(client); + return; + } + + uint32_t version = wl_resource_get_version(resource); + struct wl_resource *feedback_resource = wl_resource_create(client, + &zwp_linux_dmabuf_feedback_v1_interface, version, id); + if (feedback_resource == NULL) { + wl_client_post_no_memory(client); + return; + } + wl_resource_set_implementation(feedback_resource, &linux_dmabuf_feedback_impl, + NULL, surface_feedback_handle_resource_destroy); + wl_list_insert(&surface->feedback_resources, wl_resource_get_link(feedback_resource)); + + feedback_send(surface_get_feedback(surface), feedback_resource); } static void linux_dmabuf_destroy(struct wl_client *client, @@ -736,6 +912,11 @@ static void linux_dmabuf_bind(struct wl_client *client, void *data, static void linux_dmabuf_v1_destroy(struct wlr_linux_dmabuf_v1 *linux_dmabuf) { wlr_signal_emit_safe(&linux_dmabuf->events.destroy, linux_dmabuf); + struct wlr_linux_dmabuf_v1_surface *surface, *surface_tmp; + wl_list_for_each_safe(surface, surface_tmp, &linux_dmabuf->surfaces, link) { + surface_destroy(surface); + } + compiled_feedback_destroy(linux_dmabuf->default_feedback); wl_list_remove(&linux_dmabuf->display_destroy.link); @@ -767,6 +948,7 @@ struct wlr_linux_dmabuf_v1 *wlr_linux_dmabuf_v1_create(struct wl_display *displa } linux_dmabuf->renderer = renderer; + wl_list_init(&linux_dmabuf->surfaces); wl_signal_init(&linux_dmabuf->events.destroy); linux_dmabuf->global = @@ -794,3 +976,32 @@ struct wlr_linux_dmabuf_v1 *wlr_linux_dmabuf_v1_create(struct wl_display *displa return linux_dmabuf; } + +bool wlr_linux_dmabuf_v1_set_surface_feedback( + struct wlr_linux_dmabuf_v1 *linux_dmabuf, + struct wlr_surface *wlr_surface, + const struct wlr_linux_dmabuf_feedback_v1 *feedback) { + struct wlr_linux_dmabuf_v1_surface *surface = + surface_get_or_create(linux_dmabuf, wlr_surface); + if (surface == NULL) { + return false; + } + + struct wlr_linux_dmabuf_feedback_v1_compiled *compiled = NULL; + if (feedback != NULL) { + compiled = feedback_compile(feedback); + if (compiled == NULL) { + return false; + } + } + + compiled_feedback_destroy(surface->feedback); + surface->feedback = compiled; + + struct wl_resource *resource; + wl_resource_for_each(resource, &surface->feedback_resources) { + feedback_send(surface_get_feedback(surface), resource); + } + + return true; +} From ad28490cf496e0ddae991fc553f24e63efe72a34 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 7 Dec 2021 16:11:29 +0100 Subject: [PATCH 101/188] build: move wayland-client dep to backend/wayland/ wayland-client isn't really used by wlroots core, so let's move the dep to where it's needed in the Wayland backend. --- backend/wayland/meson.build | 6 ++++++ examples/meson.build | 1 + meson.build | 5 ----- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/backend/wayland/meson.build b/backend/wayland/meson.build index ebe315296b..103d3f9fa6 100644 --- a/backend/wayland/meson.build +++ b/backend/wayland/meson.build @@ -1,3 +1,9 @@ +wayland_client = dependency('wayland-client', + fallback: ['wayland', 'wayland_client_dep'], + default_options: wayland_project_options, +) +wlr_deps += wayland_client + wlr_files += files( 'backend.c', 'output.c', diff --git a/examples/meson.build b/examples/meson.build index 1e428e81b2..3ec4dac4e9 100644 --- a/examples/meson.build +++ b/examples/meson.build @@ -1,6 +1,7 @@ threads = dependency('threads') wayland_egl = dependency('wayland-egl') wayland_cursor = dependency('wayland-cursor') +wayland_client = dependency('wayland-client') libpng = dependency('libpng', required: false, disabler: true) egl = dependency('egl', required: false, disabler: true) glesv2 = dependency('glesv2', required: false, disabler: true) diff --git a/meson.build b/meson.build index 2f29694967..2e72e4451d 100644 --- a/meson.build +++ b/meson.build @@ -103,10 +103,6 @@ wayland_server = dependency('wayland-server', fallback: ['wayland', 'wayland_server_dep'], default_options: wayland_project_options, ) -wayland_client = dependency('wayland-client', - fallback: ['wayland', 'wayland_client_dep'], - default_options: wayland_project_options, -) drm = dependency('libdrm', version: '>=2.4.105') gbm = dependency('gbm', version: '>=17.1.0') @@ -119,7 +115,6 @@ rt = cc.find_library('rt') wlr_files = [] wlr_deps = [ wayland_server, - wayland_client, drm, gbm, xkbcommon, From 83bdb3ad0759a8043f0010e4bfd18f03b88f1e1e Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 7 Dec 2021 16:19:26 +0100 Subject: [PATCH 102/188] examples/layer-shell: remove wlroots dependency This is a client example, it shouldn't use a compositor library like wlroots. --- examples/layer-shell.c | 13 +++++-------- examples/meson.build | 2 +- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/examples/layer-shell.c b/examples/layer-shell.c index 274858d4f7..56a8d4aa57 100644 --- a/examples/layer-shell.c +++ b/examples/layer-shell.c @@ -11,7 +11,6 @@ #include #include #include -#include #include "egl_common.h" #include "wlr-layer-shell-unstable-v1-client-protocol.h" #include "xdg-shell-client-protocol.h" @@ -177,8 +176,7 @@ static const struct xdg_surface_listener xdg_surface_listener = { 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); + fprintf(stderr, "Popup configured %dx%d@%d,%d\n", width, height, x, y); popup_width = width; popup_height = height; if (popup_egl_window) { @@ -197,7 +195,7 @@ static void popup_destroy(void) { } static void xdg_popup_done(void *data, struct xdg_popup *xdg_popup) { - wlr_log(WLR_DEBUG, "Popup done"); + fprintf(stderr, "Popup done\n"); popup_destroy(); } @@ -377,17 +375,17 @@ static void wl_keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard, 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"); + fprintf(stderr, "Keyboard enter\n"); } 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"); + fprintf(stderr, "Keyboard leave\n"); } 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); + fprintf(stderr, "Key event: %d %d\n", key, state); } static void wl_keyboard_modifiers(void *data, struct wl_keyboard *wl_keyboard, @@ -473,7 +471,6 @@ static const struct wl_registry_listener registry_listener = { }; 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; diff --git a/examples/meson.build b/examples/meson.build index 3ec4dac4e9..3189f0b438 100644 --- a/examples/meson.build +++ b/examples/meson.build @@ -82,7 +82,7 @@ clients = { }, 'layer-shell': { 'src': ['layer-shell.c', 'egl_common.c'], - 'dep': [wayland_egl, wayland_cursor, wlroots, egl, glesv2], + 'dep': [wayland_egl, wayland_cursor, egl, glesv2], 'proto': [ 'wlr-layer-shell-unstable-v1', 'xdg-shell', From 90e9d327ddc64b1b80c0e3b286cfbb9675b619d0 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 7 Dec 2021 16:20:20 +0100 Subject: [PATCH 103/188] examples: remove unnecessary wlroots deps for clients These clients don't need wlroots. --- examples/meson.build | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/examples/meson.build b/examples/meson.build index 3189f0b438..bb798f259c 100644 --- a/examples/meson.build +++ b/examples/meson.build @@ -103,7 +103,7 @@ clients = { }, 'output-power-management': { 'src': 'output-power-management.c', - 'dep': [wayland_client, wlroots], + 'dep': [wayland_client], 'proto': ['wlr-output-power-management-unstable-v1'], }, 'pointer-constraints': { @@ -174,12 +174,10 @@ clients = { }, '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'], }, 'input-method-keyboard-grab': { @@ -216,7 +214,7 @@ foreach name, info : clients executable( name, [info.get('src'), extra_src], - dependencies: [wayland_client, info.get('dep')], + dependencies: [wayland_client, info.get('dep', [])], build_by_default: get_option('examples'), ) endforeach From 1fbd13ec799c472558aef37436367f0e947f7d89 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 7 Dec 2021 16:20:54 +0100 Subject: [PATCH 104/188] examples: remove unnecessary partial_dependency() call The definition of the "drm" dep already calls it. --- examples/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/meson.build b/examples/meson.build index bb798f259c..26d103bbb2 100644 --- a/examples/meson.build +++ b/examples/meson.build @@ -129,7 +129,7 @@ clients = { libavcodec, libavformat, libavutil, - drm.partial_dependency(compile_args: true), # + drm, threads, ], 'proto': ['wlr-export-dmabuf-unstable-v1'], From 36a2b19485ad299ee0039eb97c0f688d68205539 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 9 Dec 2021 15:43:19 +0100 Subject: [PATCH 105/188] output: introduce wlr_output_set_name wlroots picks names for all outputs, but it might be desirable for compositor to override it. For instance, Sway will use a headless output as a fallback in case no outputs are connected. Sway wants to clearly label the fallback output as such and label "real" headless outputs starting from HEADLESS-1. --- backend/drm/drm.c | 3 +-- backend/headless/output.c | 6 ++++-- backend/wayland/output.c | 6 ++++-- backend/x11/output.c | 6 ++++-- include/wlr/types/wlr_output.h | 13 ++++++++++++- types/output/output.c | 8 ++++++++ 6 files changed, 33 insertions(+), 9 deletions(-) diff --git a/backend/drm/drm.c b/backend/drm/drm.c index d7c532316b..1a46b49598 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -1299,8 +1299,7 @@ void scan_drm_connectors(struct wlr_drm_backend *drm, wlr_output_init(&wlr_conn->output, &drm->backend, &output_impl, drm->display); - memcpy(wlr_conn->output.name, wlr_conn->name, - sizeof(wlr_conn->output.name)); + wlr_output_set_name(&wlr_conn->output, wlr_conn->name); wlr_conn->output.phys_width = drm_conn->mmWidth; wlr_conn->output.phys_height = drm_conn->mmHeight; diff --git a/backend/headless/output.c b/backend/headless/output.c index 50d914e447..fbec8110a4 100644 --- a/backend/headless/output.c +++ b/backend/headless/output.c @@ -116,8 +116,10 @@ struct wlr_output *wlr_headless_add_output(struct wlr_backend *wlr_backend, output_set_custom_mode(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 name[64]; + snprintf(name, sizeof(name), "HEADLESS-%zd", ++backend->last_output_num); + wlr_output_set_name(wlr_output, name); char description[128]; snprintf(description, sizeof(description), diff --git a/backend/wayland/output.c b/backend/wayland/output.c index 3fa86a3cf2..27195592d0 100644 --- a/backend/wayland/output.c +++ b/backend/wayland/output.c @@ -520,8 +520,10 @@ struct wlr_output *wlr_wl_output_create(struct wlr_backend *wlr_backend) { 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 name[64]; + snprintf(name, sizeof(name), "WL-%zd", ++backend->last_output_num); + wlr_output_set_name(wlr_output, name); char description[128]; snprintf(description, sizeof(description), diff --git a/backend/x11/output.c b/backend/x11/output.c index 9081769e3b..25194e7089 100644 --- a/backend/x11/output.c +++ b/backend/x11/output.c @@ -512,8 +512,10 @@ struct wlr_output *wlr_x11_output_create(struct wlr_backend *backend) { wlr_output_update_custom_mode(wlr_output, 1024, 768, 0); - snprintf(wlr_output->name, sizeof(wlr_output->name), "X11-%zd", - ++x11->last_output_num); + char name[64]; + snprintf(name, sizeof(name), "X11-%zd", ++x11->last_output_num); + wlr_output_set_name(wlr_output, name); + parse_xcb_setup(wlr_output, x11->xcb); char description[128]; diff --git a/include/wlr/types/wlr_output.h b/include/wlr/types/wlr_output.h index fd8e655637..791023d2e0 100644 --- a/include/wlr/types/wlr_output.h +++ b/include/wlr/types/wlr_output.h @@ -118,7 +118,7 @@ struct wlr_output { struct wl_global *global; struct wl_list resources; - char name[24]; + char *name; char *description; // may be NULL char make[56]; char model[16]; @@ -336,6 +336,17 @@ void wlr_output_set_render_format(struct wlr_output *output, uint32_t format); void wlr_output_set_scale(struct wlr_output *output, float scale); void wlr_output_set_subpixel(struct wlr_output *output, enum wl_output_subpixel subpixel); +/** + * Set the output name. + * + * Output names are subject to the following rules: + * + * - Each output name must be unique. + * - The name cannot change after the output has been advertised to clients. + * + * For more details, see the protocol documentation for wl_output.name. + */ +void wlr_output_set_name(struct wlr_output *output, const char *name); void wlr_output_set_description(struct wlr_output *output, const char *desc); /** * Schedule a done event. diff --git a/types/output/output.c b/types/output/output.c index 9a715d120c..91a45f1315 100644 --- a/types/output/output.c +++ b/types/output/output.c @@ -324,6 +324,13 @@ void wlr_output_set_subpixel(struct wlr_output *output, wlr_output_schedule_done(output); } +void wlr_output_set_name(struct wlr_output *output, const char *name) { + assert(output->global == NULL); + + free(output->name); + output->name = strdup(name); +} + void wlr_output_set_description(struct wlr_output *output, const char *desc) { if (output->description != NULL && desc != NULL && strcmp(output->description, desc) == 0) { @@ -420,6 +427,7 @@ void wlr_output_destroy(struct wlr_output *output) { wl_event_source_remove(output->idle_done); } + free(output->name); free(output->description); pixman_region32_fini(&output->pending.damage); From 818fc4a87b9ea6b7174443e19c1a7c2756b3c002 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 9 Dec 2021 16:39:09 +0100 Subject: [PATCH 106/188] Fix incorrect %zd formatting directives %zd is for ssize_t. For size_t we should use %zu. --- backend/headless/output.c | 4 ++-- backend/wayland/output.c | 4 ++-- backend/x11/output.c | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/backend/headless/output.c b/backend/headless/output.c index fbec8110a4..f03d6ee0d5 100644 --- a/backend/headless/output.c +++ b/backend/headless/output.c @@ -118,12 +118,12 @@ struct wlr_output *wlr_headless_add_output(struct wlr_backend *wlr_backend, strncpy(wlr_output->model, "headless", sizeof(wlr_output->model)); char name[64]; - snprintf(name, sizeof(name), "HEADLESS-%zd", ++backend->last_output_num); + snprintf(name, sizeof(name), "HEADLESS-%zu", ++backend->last_output_num); wlr_output_set_name(wlr_output, name); char description[128]; snprintf(description, sizeof(description), - "Headless output %zd", backend->last_output_num); + "Headless output %zu", backend->last_output_num); wlr_output_set_description(wlr_output, description); struct wl_event_loop *ev = wl_display_get_event_loop(backend->display); diff --git a/backend/wayland/output.c b/backend/wayland/output.c index 27195592d0..de5d42edd2 100644 --- a/backend/wayland/output.c +++ b/backend/wayland/output.c @@ -522,12 +522,12 @@ struct wlr_output *wlr_wl_output_create(struct wlr_backend *wlr_backend) { strncpy(wlr_output->model, "wayland", sizeof(wlr_output->model)); char name[64]; - snprintf(name, sizeof(name), "WL-%zd", ++backend->last_output_num); + snprintf(name, sizeof(name), "WL-%zu", ++backend->last_output_num); wlr_output_set_name(wlr_output, name); char description[128]; snprintf(description, sizeof(description), - "Wayland output %zd", backend->last_output_num); + "Wayland output %zu", backend->last_output_num); wlr_output_set_description(wlr_output, description); output->backend = backend; diff --git a/backend/x11/output.c b/backend/x11/output.c index 25194e7089..b87b08dc57 100644 --- a/backend/x11/output.c +++ b/backend/x11/output.c @@ -513,14 +513,14 @@ struct wlr_output *wlr_x11_output_create(struct wlr_backend *backend) { wlr_output_update_custom_mode(wlr_output, 1024, 768, 0); char name[64]; - snprintf(name, sizeof(name), "X11-%zd", ++x11->last_output_num); + snprintf(name, sizeof(name), "X11-%zu", ++x11->last_output_num); wlr_output_set_name(wlr_output, name); parse_xcb_setup(wlr_output, x11->xcb); char description[128]; snprintf(description, sizeof(description), - "X11 output %zd", x11->last_output_num); + "X11 output %zu", x11->last_output_num); wlr_output_set_description(wlr_output, description); // The X11 protocol requires us to set a colormap and border pixel if the From f463ca669ac7ddc24a19b32aac33e457c93aaf61 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Fri, 3 Dec 2021 23:14:13 +0300 Subject: [PATCH 107/188] subsurface: simplify and fix parent commit handling --- types/wlr_surface.c | 49 ++++++++++++--------------------------------- 1 file changed, 13 insertions(+), 36 deletions(-) diff --git a/types/wlr_surface.c b/types/wlr_surface.c index 6237a552a9..bef4300f12 100644 --- a/types/wlr_surface.c +++ b/types/wlr_surface.c @@ -400,6 +400,8 @@ static void surface_update_input_region(struct wlr_surface *surface) { static void surface_state_init(struct wlr_surface_state *state); +static void subsurface_parent_commit(struct wlr_subsurface *subsurface); + static void surface_cache_pending(struct wlr_surface *surface) { struct wlr_surface_state *cached = calloc(1, sizeof(*cached)); if (!cached) { @@ -452,6 +454,7 @@ static void surface_commit_state(struct wlr_surface *surface, // TODO: damage all the subsurfaces surface_damage_subsurfaces(subsurface); } + subsurface_parent_commit(subsurface); } wl_list_for_each_reverse(subsurface, &surface->pending.subsurfaces_below, pending.link) { @@ -463,6 +466,7 @@ static void surface_commit_state(struct wlr_surface *surface, // TODO: damage all the subsurfaces surface_damage_subsurfaces(subsurface); } + subsurface_parent_commit(subsurface); } // If we're committing the pending state, bump the pending sequence number @@ -497,28 +501,12 @@ static bool subsurface_is_synchronized(struct wlr_subsurface *subsurface) { return false; } -/** - * Recursive function to commit the effectively synchronized children. - */ -static void subsurface_parent_commit(struct wlr_subsurface *subsurface, - bool synchronized) { +static void subsurface_parent_commit(struct wlr_subsurface *subsurface) { struct wlr_surface *surface = subsurface->surface; - if (synchronized || subsurface->synchronized) { - if (subsurface->has_cache) { - wlr_surface_unlock_cached(surface, subsurface->cached_seq); - subsurface->has_cache = false; - subsurface->cached_seq = 0; - } - struct wlr_subsurface *subsurface; - wl_list_for_each(subsurface, &surface->current.subsurfaces_below, - current.link) { - subsurface_parent_commit(subsurface, true); - } - wl_list_for_each(subsurface, &surface->current.subsurfaces_above, - current.link) { - subsurface_parent_commit(subsurface, true); - } + if (subsurface->synchronized && subsurface->has_cache) { + wlr_surface_unlock_cached(surface, subsurface->cached_seq); + subsurface->has_cache = false; } } @@ -557,13 +545,6 @@ static void surface_handle_commit(struct wl_client *client, } else { surface_commit_state(surface, &surface->pending); } - - wl_list_for_each(subsurface, &surface->current.subsurfaces_below, current.link) { - subsurface_parent_commit(subsurface, false); - } - wl_list_for_each(subsurface, &surface->current.subsurfaces_above, current.link) { - subsurface_parent_commit(subsurface, false); - } } static void surface_handle_set_buffer_transform(struct wl_client *client, @@ -989,15 +970,11 @@ static void subsurface_handle_set_desync(struct wl_client *client, if (subsurface->synchronized) { subsurface->synchronized = false; - if (!subsurface_is_synchronized(subsurface)) { - if (subsurface->has_cache) { - wlr_surface_unlock_cached(subsurface->surface, - subsurface->cached_seq); - subsurface->has_cache = false; - subsurface->cached_seq = 0; - } - - subsurface_parent_commit(subsurface, true); + if (!subsurface_is_synchronized(subsurface) && + subsurface->has_cache) { + wlr_surface_unlock_cached(subsurface->surface, + subsurface->cached_seq); + subsurface->has_cache = false; } } } From df7d28034325ae80fd4b1660e2c6d5f67dc30d67 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Fri, 3 Dec 2021 23:15:55 +0300 Subject: [PATCH 108/188] subsurface: apply position change at the right moment Subsurface position is considered to be a part of the parent surface's state, therefore it should be modified when the parent is committed. --- types/wlr_surface.c | 48 ++++++++++++++++++++++----------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/types/wlr_surface.c b/types/wlr_surface.c index bef4300f12..360e4dffee 100644 --- a/types/wlr_surface.c +++ b/types/wlr_surface.c @@ -508,6 +508,30 @@ static void subsurface_parent_commit(struct wlr_subsurface *subsurface) { wlr_surface_unlock_cached(surface, subsurface->cached_seq); subsurface->has_cache = false; } + + if (subsurface->current.x != subsurface->pending.x || + subsurface->current.y != subsurface->pending.y) { + // Subsurface has moved + int dx = subsurface->current.x - subsurface->pending.x; + int dy = subsurface->current.y - subsurface->pending.y; + + subsurface->current.x = subsurface->pending.x; + subsurface->current.y = subsurface->pending.y; + + if ((surface->current.transform & WL_OUTPUT_TRANSFORM_90) != 0) { + int tmp = dx; + dx = dy; + dy = tmp; + } + + pixman_region32_union_rect(&surface->buffer_damage, + &surface->buffer_damage, + dx * surface->previous.scale, dy * surface->previous.scale, + surface->previous.buffer_width, surface->previous.buffer_height); + pixman_region32_union_rect(&surface->buffer_damage, + &surface->buffer_damage, 0, 0, + surface->current.buffer_width, surface->current.buffer_height); + } } static void subsurface_commit(struct wlr_subsurface *subsurface) { @@ -1055,30 +1079,6 @@ static void subsurface_role_commit(struct wlr_surface *surface) { return; } - if (subsurface->current.x != subsurface->pending.x || - subsurface->current.y != subsurface->pending.y) { - // Subsurface has moved - int dx = subsurface->current.x - subsurface->pending.x; - int dy = subsurface->current.y - subsurface->pending.y; - - subsurface->current.x = subsurface->pending.x; - subsurface->current.y = subsurface->pending.y; - - if ((surface->current.transform & WL_OUTPUT_TRANSFORM_90) != 0) { - int tmp = dx; - dx = dy; - dy = tmp; - } - - pixman_region32_union_rect(&surface->buffer_damage, - &surface->buffer_damage, - dx * surface->previous.scale, dy * surface->previous.scale, - surface->previous.buffer_width, surface->previous.buffer_height); - pixman_region32_union_rect(&surface->buffer_damage, - &surface->buffer_damage, 0, 0, - surface->current.buffer_width, surface->current.buffer_height); - } - subsurface_consider_map(subsurface, true); } From 7964bdae760a5417fe18cd893f91bd85c7123173 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Sat, 4 Dec 2021 20:05:32 +0300 Subject: [PATCH 109/188] surface: fix non-buffer damage handling This commit fixes the way the damage that doesn't come directly from the client is handled. --- include/wlr/types/wlr_surface.h | 12 +++- types/wlr_surface.c | 108 +++++++++++--------------------- 2 files changed, 44 insertions(+), 76 deletions(-) diff --git a/include/wlr/types/wlr_surface.h b/include/wlr/types/wlr_surface.h index ae3ae80f6b..4255a1f2c3 100644 --- a/include/wlr/types/wlr_surface.h +++ b/include/wlr/types/wlr_surface.h @@ -111,6 +111,11 @@ struct wlr_surface { * positions need to be damaged. */ pixman_region32_t buffer_damage; + /** + * The last commit's damage caused by surface and its subsurfaces' + * movement, in surface-local coordinates. + */ + pixman_region32_t external_damage; /** * The current opaque region, in surface-local coordinates. It is clipped to * the surface bounds. If the surface's buffer is using a fully opaque @@ -273,9 +278,10 @@ void wlr_surface_for_each_surface(struct wlr_surface *surface, wlr_surface_iterator_func_t iterator, void *user_data); /** - * Get the effective damage to the surface in terms of surface local - * coordinates. This includes damage induced by resizing and moving the - * surface. The damage is not expected to be bounded by the surface itself. + * Get the effective surface damage in surface-local coordinate space. Besides + * buffer damage, this includes damage induced by resizing and moving the + * surface and its subsurfaces. The resulting damage is not expected to be + * bounded by the surface itself. */ void wlr_surface_get_effective_damage(struct wlr_surface *surface, pixman_region32_t *damage); diff --git a/types/wlr_surface.c b/types/wlr_surface.c index 360e4dffee..42b75f96fb 100644 --- a/types/wlr_surface.c +++ b/types/wlr_surface.c @@ -315,29 +315,6 @@ static void surface_state_move(struct wlr_surface_state *state, next->cached_state_locks = 0; } -static void surface_damage_subsurfaces(struct wlr_subsurface *subsurface) { - // XXX: This is probably the wrong way to do it, because this damage should - // come from the client, but weston doesn't do it correctly either and it - // seems to work ok. See the comment on weston_surface_damage for more info - // about a better approach. - struct wlr_surface *surface = subsurface->surface; - pixman_region32_union_rect(&surface->buffer_damage, - &surface->buffer_damage, 0, 0, - surface->current.buffer_width, surface->current.buffer_height); - - subsurface->reordered = false; - - struct wlr_subsurface *child; - wl_list_for_each(child, &subsurface->surface->current.subsurfaces_below, - current.link) { - surface_damage_subsurfaces(child); - } - wl_list_for_each(child, &subsurface->surface->current.subsurfaces_above, - current.link) { - surface_damage_subsurfaces(child); - } -} - static void surface_apply_damage(struct wlr_surface *surface) { if (surface->current.buffer == NULL) { // NULL commit @@ -427,6 +404,15 @@ static void surface_commit_state(struct wlr_surface *surface, surface->sy += next->dy; surface_update_damage(&surface->buffer_damage, &surface->current, next); + pixman_region32_clear(&surface->external_damage); + if (surface->current.width > next->width || + surface->current.height > next->height || + next->dx != 0 || next->dy != 0) { + pixman_region32_union_rect(&surface->external_damage, + &surface->external_damage, -next->dx, -next->dy, + surface->current.width, surface->current.height); + } + surface->previous.scale = surface->current.scale; surface->previous.transform = surface->current.transform; surface->previous.width = surface->current.width; @@ -450,10 +436,6 @@ static void surface_commit_state(struct wlr_surface *surface, wl_list_insert(&surface->current.subsurfaces_above, &subsurface->current.link); - if (subsurface->reordered) { - // TODO: damage all the subsurfaces - surface_damage_subsurfaces(subsurface); - } subsurface_parent_commit(subsurface); } wl_list_for_each_reverse(subsurface, &surface->pending.subsurfaces_below, @@ -462,10 +444,6 @@ static void surface_commit_state(struct wlr_surface *surface, wl_list_insert(&surface->current.subsurfaces_below, &subsurface->current.link); - if (subsurface->reordered) { - // TODO: damage all the subsurfaces - surface_damage_subsurfaces(subsurface); - } subsurface_parent_commit(subsurface); } @@ -501,36 +479,37 @@ static bool subsurface_is_synchronized(struct wlr_subsurface *subsurface) { return false; } +static void collect_subsurface_damage_iter(struct wlr_surface *surface, + int sx, int sy, void *data) { + struct wlr_subsurface *subsurface = data; + pixman_region32_t *damage = &subsurface->parent->external_damage; + pixman_region32_union_rect(damage, damage, + subsurface->current.x + sx, + subsurface->current.y + sy, + surface->current.width, surface->current.height); +} + static void subsurface_parent_commit(struct wlr_subsurface *subsurface) { struct wlr_surface *surface = subsurface->surface; + + bool moved = subsurface->current.x != subsurface->pending.x || + subsurface->current.y != subsurface->pending.y; + if (subsurface->mapped && moved) { + wlr_surface_for_each_surface(surface, + collect_subsurface_damage_iter, subsurface); + } if (subsurface->synchronized && subsurface->has_cache) { wlr_surface_unlock_cached(surface, subsurface->cached_seq); subsurface->has_cache = false; } - if (subsurface->current.x != subsurface->pending.x || - subsurface->current.y != subsurface->pending.y) { - // Subsurface has moved - int dx = subsurface->current.x - subsurface->pending.x; - int dy = subsurface->current.y - subsurface->pending.y; - - subsurface->current.x = subsurface->pending.x; - subsurface->current.y = subsurface->pending.y; - - if ((surface->current.transform & WL_OUTPUT_TRANSFORM_90) != 0) { - int tmp = dx; - dx = dy; - dy = tmp; - } - - pixman_region32_union_rect(&surface->buffer_damage, - &surface->buffer_damage, - dx * surface->previous.scale, dy * surface->previous.scale, - surface->previous.buffer_width, surface->previous.buffer_height); - pixman_region32_union_rect(&surface->buffer_damage, - &surface->buffer_damage, 0, 0, - surface->current.buffer_width, surface->current.buffer_height); + subsurface->current.x = subsurface->pending.x; + subsurface->current.y = subsurface->pending.y; + if (subsurface->mapped && (moved || subsurface->reordered)) { + subsurface->reordered = false; + wlr_surface_for_each_surface(surface, + collect_subsurface_damage_iter, subsurface); } } @@ -720,6 +699,7 @@ static void surface_handle_resource_destroy(struct wl_resource *resource) { surface_state_finish(&surface->pending); surface_state_finish(&surface->current); pixman_region32_fini(&surface->buffer_damage); + pixman_region32_fini(&surface->external_damage); pixman_region32_fini(&surface->opaque_region); pixman_region32_fini(&surface->input_region); if (surface->buffer != NULL) { @@ -766,6 +746,7 @@ struct wlr_surface *surface_create(struct wl_client *client, wl_list_init(&surface->current_outputs); wl_list_init(&surface->cached); pixman_region32_init(&surface->buffer_damage); + pixman_region32_init(&surface->external_damage); pixman_region32_init(&surface->opaque_region); pixman_region32_init(&surface->input_region); wlr_addon_set_init(&surface->addons); @@ -1427,26 +1408,7 @@ void wlr_surface_get_effective_damage(struct wlr_surface *surface, wlr_region_scale_xy(damage, damage, scale_x, scale_y); } - // On resize, damage the previous bounds of the surface. The current bounds - // have already been damaged in surface_update_damage. - if (surface->previous.width > surface->current.width || - surface->previous.height > surface->current.height) { - pixman_region32_union_rect(damage, damage, 0, 0, - surface->previous.width, surface->previous.height); - } - - // On move, damage where the surface was with its old dimensions. - if (surface->current.dx != 0 || surface->current.dy != 0) { - int prev_x = -surface->current.dx; - int prev_y = -surface->current.dy; - if ((surface->previous.transform & WL_OUTPUT_TRANSFORM_90) != 0) { - int temp = prev_x; - prev_x = prev_y; - prev_y = temp; - } - pixman_region32_union_rect(damage, damage, prev_x, prev_y, - surface->previous.width, surface->previous.height); - } + pixman_region32_union(damage, damage, &surface->external_damage); } void wlr_surface_get_buffer_source_box(struct wlr_surface *surface, From 0fcc842291d9d714e9c210839ae72429c5c3eae4 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Mon, 6 Dec 2021 14:59:00 +0300 Subject: [PATCH 110/188] subsurface: don't add to parent list immediately --- include/wlr/types/wlr_surface.h | 1 + types/wlr_surface.c | 15 ++++++++++----- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/include/wlr/types/wlr_surface.h b/include/wlr/types/wlr_surface.h index 4255a1f2c3..59168eaa36 100644 --- a/include/wlr/types/wlr_surface.h +++ b/include/wlr/types/wlr_surface.h @@ -185,6 +185,7 @@ struct wlr_subsurface { bool synchronized; bool reordered; bool mapped; + bool added; struct wl_listener surface_destroy; struct wl_listener parent_destroy; diff --git a/types/wlr_surface.c b/types/wlr_surface.c index 42b75f96fb..d5d4957149 100644 --- a/types/wlr_surface.c +++ b/types/wlr_surface.c @@ -511,6 +511,12 @@ static void subsurface_parent_commit(struct wlr_subsurface *subsurface) { wlr_surface_for_each_surface(surface, collect_subsurface_damage_iter, subsurface); } + + if (!subsurface->added) { + subsurface->added = true; + wlr_signal_emit_safe(&subsurface->parent->events.new_subsurface, + subsurface); + } } static void subsurface_commit(struct wlr_subsurface *subsurface) { @@ -877,12 +883,12 @@ static struct wlr_subsurface *subsurface_find_sibling( struct wlr_surface *parent = subsurface->parent; struct wlr_subsurface *sibling; - wl_list_for_each(sibling, &parent->current.subsurfaces_below, current.link) { + wl_list_for_each(sibling, &parent->pending.subsurfaces_below, pending.link) { if (sibling->surface == surface && sibling != subsurface) { return sibling; } } - wl_list_for_each(sibling, &parent->current.subsurfaces_above, current.link) { + wl_list_for_each(sibling, &parent->pending.subsurfaces_above, pending.link) { if (sibling->surface == surface && sibling != subsurface) { return sibling; } @@ -1134,14 +1140,13 @@ struct wlr_subsurface *subsurface_create(struct wlr_surface *surface, subsurface->parent = parent; wl_signal_add(&parent->events.destroy, &subsurface->parent_destroy); subsurface->parent_destroy.notify = subsurface_handle_parent_destroy; - wl_list_insert(parent->current.subsurfaces_above.prev, &subsurface->current.link); + + wl_list_init(&subsurface->current.link); wl_list_insert(parent->pending.subsurfaces_above.prev, &subsurface->pending.link); surface->role_data = subsurface; - wlr_signal_emit_safe(&parent->events.new_subsurface, subsurface); - return subsurface; } From e3fefda0235e548e4d23639adfe375edf380825c Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 7 Dec 2021 16:37:51 +0100 Subject: [PATCH 111/188] output: add support for protocol interface version 4 Two new events are added: name and description. The name is immutable. The description can be updated on-the-fly. --- meson.build | 2 +- types/output/output.c | 32 +++++++++++++++++++++++++++----- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/meson.build b/meson.build index 2e72e4451d..a84f3df79c 100644 --- a/meson.build +++ b/meson.build @@ -99,7 +99,7 @@ internal_features = { wayland_project_options = ['tests=false', 'documentation=false'] wayland_server = dependency('wayland-server', - version: '>=1.19', + version: '>=1.20', fallback: ['wayland', 'wayland_server_dep'], default_options: wayland_project_options, ) diff --git a/types/output/output.c b/types/output/output.c index 91a45f1315..900cfda676 100644 --- a/types/output/output.c +++ b/types/output/output.c @@ -13,7 +13,7 @@ #include "util/global.h" #include "util/signal.h" -#define OUTPUT_VERSION 3 +#define OUTPUT_VERSION 4 static void send_geometry(struct wl_resource *resource) { struct wlr_output *output = wlr_output_from_resource(resource); @@ -43,6 +43,23 @@ static void send_scale(struct wl_resource *resource) { } } +static void send_name(struct wl_resource *resource) { + struct wlr_output *output = wlr_output_from_resource(resource); + uint32_t version = wl_resource_get_version(resource); + if (version >= WL_OUTPUT_NAME_SINCE_VERSION) { + wl_output_send_name(resource, output->name); + } +} + +static void send_description(struct wl_resource *resource) { + struct wlr_output *output = wlr_output_from_resource(resource); + uint32_t version = wl_resource_get_version(resource); + if (output->description != NULL && + version >= WL_OUTPUT_DESCRIPTION_SINCE_VERSION) { + wl_output_send_description(resource, output->description); + } +} + static void send_done(struct wl_resource *resource) { uint32_t version = wl_resource_get_version(resource); if (version >= WL_OUTPUT_DONE_SINCE_VERSION) { @@ -87,6 +104,8 @@ static void output_bind(struct wl_client *wl_client, void *data, send_geometry(resource); send_current_mode(resource); send_scale(resource); + send_name(resource); + send_description(resource); send_done(resource); struct wlr_output_event_bind evt = { @@ -131,10 +150,7 @@ static void schedule_done_handle_idle_timer(void *data) { struct wl_resource *resource; wl_resource_for_each(resource, &output->resources) { - uint32_t version = wl_resource_get_version(resource); - if (version >= WL_OUTPUT_DONE_SINCE_VERSION) { - wl_output_send_done(resource); - } + send_done(resource); } } @@ -344,6 +360,12 @@ void wlr_output_set_description(struct wlr_output *output, const char *desc) { output->description = NULL; } + struct wl_resource *resource; + wl_resource_for_each(resource, &output->resources) { + send_description(resource); + } + wlr_output_schedule_done(output); + wlr_signal_emit_safe(&output->events.description, output); } From f6d3efbf4b76a2100949fb0b88662a1324d4af54 Mon Sep 17 00:00:00 2001 From: Chris Chamberlain Date: Mon, 13 Dec 2021 14:53:41 +0100 Subject: [PATCH 112/188] backend: fix return value of attempt_drm_backend The multi backend was returned instead of the primary DRM backend. --- backend/backend.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/backend.c b/backend/backend.c index 33a2185188..34c1e3667e 100644 --- a/backend/backend.c +++ b/backend/backend.c @@ -239,7 +239,7 @@ static struct wlr_backend *attempt_drm_backend(struct wl_display *display, return NULL; } - return backend; + return primary_drm; } #endif From d8ca4945581577f570c02ad46878571c48a08c79 Mon Sep 17 00:00:00 2001 From: Chris Chamberlain Date: Fri, 10 Dec 2021 21:14:57 +0100 Subject: [PATCH 113/188] backend/drm: add wlr_drm_backend_monitor This helper is responsible for listening for new DRM devices and create new child DRM backends as necessary. --- backend/backend.c | 3 + backend/drm/meson.build | 1 + backend/drm/monitor.c | 94 +++++++++++++++++++++++++++++++ backend/session/session.c | 6 +- include/backend/drm/monitor.h | 24 ++++++++ include/backend/session/session.h | 3 + 6 files changed, 128 insertions(+), 3 deletions(-) create mode 100644 backend/drm/monitor.c create mode 100644 include/backend/drm/monitor.h diff --git a/backend/backend.c b/backend/backend.c index 34c1e3667e..24150b13dd 100644 --- a/backend/backend.c +++ b/backend/backend.c @@ -21,6 +21,7 @@ #if WLR_HAS_DRM_BACKEND #include +#include "backend/drm/monitor.h" #endif #if WLR_HAS_LIBINPUT_BACKEND @@ -375,6 +376,8 @@ struct wlr_backend *wlr_backend_autocreate(struct wl_display *display) { return NULL; } + drm_backend_monitor_create(backend, primary_drm, multi->session); + return backend; #endif diff --git a/backend/drm/meson.build b/backend/drm/meson.build index b076b4728e..cc791f362b 100644 --- a/backend/drm/meson.build +++ b/backend/drm/meson.build @@ -4,6 +4,7 @@ wlr_files += files( 'cvt.c', 'drm.c', 'legacy.c', + 'monitor.c', 'properties.c', 'renderer.c', 'util.c', diff --git a/backend/drm/monitor.c b/backend/drm/monitor.c new file mode 100644 index 0000000000..539e7925f2 --- /dev/null +++ b/backend/drm/monitor.c @@ -0,0 +1,94 @@ +#include +#include +#include "backend/drm/monitor.h" +#include "backend/multi.h" +#include "backend/session/session.h" + +static void drm_backend_monitor_destroy(struct wlr_drm_backend_monitor* monitor) { + wl_list_remove(&monitor->session_add_drm_card.link); + wl_list_remove(&monitor->session_destroy.link); + wl_list_remove(&monitor->primary_drm_destroy.link); + wl_list_remove(&monitor->multi_destroy.link); + free(monitor); +} + +static void handle_add_drm_card(struct wl_listener *listener, void *data) { + struct wlr_session_add_event *event = data; + struct wlr_drm_backend_monitor *backend_monitor = + wl_container_of(listener, backend_monitor, session_add_drm_card); + + struct wlr_device *dev = + session_open_if_kms(backend_monitor->session, event->path); + if (!dev) { + wlr_log(WLR_ERROR, "Unable to open %s as DRM device", event->path); + return; + } + + wlr_log(WLR_DEBUG, "Creating DRM backend for %s after hotplug", event->path); + struct wlr_backend *child_drm = wlr_drm_backend_create( + backend_monitor->session->display, backend_monitor->session, + dev, backend_monitor->primary_drm); + if (!child_drm) { + wlr_log(WLR_ERROR, "Failed to create DRM backend after hotplug"); + return; + } + + if (!wlr_multi_backend_add(backend_monitor->multi, child_drm)) { + wlr_log(WLR_ERROR, "Failed to add new drm backend to multi backend"); + wlr_backend_destroy(child_drm); + return; + } + + if (!wlr_backend_start(child_drm)) { + wlr_log(WLR_ERROR, "Failed to start new child DRM backend"); + wlr_backend_destroy(child_drm); + } +} + +static void handle_session_destroy(struct wl_listener *listener, void *data) { + struct wlr_drm_backend_monitor *backend_monitor = + wl_container_of(listener, backend_monitor, session_destroy); + drm_backend_monitor_destroy(backend_monitor); +} + +static void handle_primary_drm_destroy(struct wl_listener *listener, void *data) { + struct wlr_drm_backend_monitor *backend_monitor = + wl_container_of(listener, backend_monitor, primary_drm_destroy); + drm_backend_monitor_destroy(backend_monitor); +} + +static void handle_multi_destroy(struct wl_listener *listener, void *data) { + struct wlr_drm_backend_monitor *backend_monitor = + wl_container_of(listener, backend_monitor, multi_destroy); + drm_backend_monitor_destroy(backend_monitor); +} + +struct wlr_drm_backend_monitor *drm_backend_monitor_create( + struct wlr_backend *multi, + struct wlr_backend *primary_drm, + struct wlr_session *session) { + struct wlr_drm_backend_monitor *monitor = + calloc(1, sizeof(struct wlr_drm_backend_monitor)); + if (!monitor) { + wlr_log_errno(WLR_ERROR, "Allocation failed"); + return NULL; + } + + monitor->multi = multi; + monitor->primary_drm = primary_drm; + monitor->session = session; + + monitor->session_add_drm_card.notify = handle_add_drm_card; + wl_signal_add(&session->events.add_drm_card, &monitor->session_add_drm_card); + + monitor->session_destroy.notify = handle_session_destroy; + wl_signal_add(&session->events.destroy, &monitor->session_destroy); + + monitor->primary_drm_destroy.notify = handle_primary_drm_destroy; + wl_signal_add(&primary_drm->events.destroy, &monitor->primary_drm_destroy); + + monitor->multi_destroy.notify = handle_multi_destroy; + wl_signal_add(&multi->events.destroy, &monitor->multi_destroy); + + return monitor; +} diff --git a/backend/session/session.c b/backend/session/session.c index e83a8b4c1a..7d6d080da9 100644 --- a/backend/session/session.c +++ b/backend/session/session.c @@ -370,7 +370,7 @@ bool wlr_session_change_vt(struct wlr_session *session, unsigned vt) { /* Tests if 'path' is KMS compatible by trying to open it. Returns the opened * device on success. */ -static struct wlr_device *open_if_kms(struct wlr_session *restrict session, +struct wlr_device *session_open_if_kms(struct wlr_session *restrict session, const char *restrict path) { if (!path) { return NULL; @@ -406,7 +406,7 @@ static ssize_t explicit_find_gpus(struct wlr_session *session, break; } - ret[i] = open_if_kms(session, ptr); + ret[i] = session_open_if_kms(session, ptr); if (!ret[i]) { wlr_log(WLR_ERROR, "Unable to open %s as DRM device", ptr); } else { @@ -542,7 +542,7 @@ ssize_t wlr_session_find_gpus(struct wlr_session *session, } struct wlr_device *wlr_dev = - open_if_kms(session, udev_device_get_devnode(dev)); + session_open_if_kms(session, udev_device_get_devnode(dev)); if (!wlr_dev) { udev_device_unref(dev); continue; diff --git a/include/backend/drm/monitor.h b/include/backend/drm/monitor.h new file mode 100644 index 0000000000..518171932e --- /dev/null +++ b/include/backend/drm/monitor.h @@ -0,0 +1,24 @@ +#ifndef BACKEND_DRM_MONITOR_H +#define BACKEND_DRM_MONITOR_H + +#include + +/** + * Helper to create new DRM sub-backends on GPU hotplug. + */ +struct wlr_drm_backend_monitor { + struct wlr_backend *multi; + struct wlr_backend *primary_drm; + struct wlr_session *session; + + struct wl_listener multi_destroy; + struct wl_listener primary_drm_destroy; + struct wl_listener session_destroy; + struct wl_listener session_add_drm_card; +}; + +struct wlr_drm_backend_monitor *drm_backend_monitor_create( + struct wlr_backend *multi, struct wlr_backend *primary_drm, + struct wlr_session *session); + +#endif diff --git a/include/backend/session/session.h b/include/backend/session/session.h index ebe6fc7000..5eca7f576d 100644 --- a/include/backend/session/session.h +++ b/include/backend/session/session.h @@ -11,4 +11,7 @@ bool libseat_change_vt(struct wlr_session *base, unsigned vt); void session_init(struct wlr_session *session); +struct wlr_device *session_open_if_kms(struct wlr_session *restrict session, + const char *restrict path); + #endif From 0215dffba51b63dc394ee028039ac811e1330c60 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Thu, 9 Dec 2021 23:17:16 +0100 Subject: [PATCH 114/188] scene: send surface enter/leave output events Co-authored-by: Simon Ser --- include/wlr/types/wlr_scene.h | 6 ++ types/scene/wlr_scene.c | 111 +++++++++++++++++++++++++++++++--- 2 files changed, 107 insertions(+), 10 deletions(-) diff --git a/include/wlr/types/wlr_scene.h b/include/wlr/types/wlr_scene.h index bca0b269e3..1238653d33 100644 --- a/include/wlr/types/wlr_scene.h +++ b/include/wlr/types/wlr_scene.h @@ -76,6 +76,8 @@ struct wlr_scene_surface { // private state + int prev_width, prev_height; + struct wl_listener surface_destroy; struct wl_listener surface_commit; }; @@ -201,6 +203,10 @@ struct wlr_scene_tree *wlr_scene_tree_create(struct wlr_scene_node *parent); * Add a node displaying a single surface to the scene-graph. * * The child sub-surfaces are ignored. + * + * wlr_surface_send_enter()/wlr_surface_send_leave() will be called + * automatically based on the position of the surface and outputs in + * the scene. */ struct wlr_scene_surface *wlr_scene_surface_create(struct wlr_scene_node *parent, struct wlr_surface *surface); diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index b1db7b8f20..d2f3f4a7fd 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -39,6 +39,13 @@ static struct wlr_scene_buffer *scene_buffer_from_node( return (struct wlr_scene_buffer *)node; } +static struct wlr_scene *scene_node_get_root(struct wlr_scene_node *node) { + while (node->parent != NULL) { + node = node->parent; + } + return scene_root_from_node(node); +} + static void scene_node_state_init(struct wlr_scene_node_state *state) { wl_list_init(&state->children); wl_list_init(&state->link); @@ -85,11 +92,11 @@ void wlr_scene_node_destroy(struct wlr_scene_node *node) { scene_node_damage_whole(node); scene_node_finish(node); + struct wlr_scene *scene = scene_node_get_root(node); + struct wlr_scene_output *scene_output; switch (node->type) { case WLR_SCENE_NODE_ROOT:; - struct wlr_scene *scene = scene_root_from_node(node); - - struct wlr_scene_output *scene_output, *scene_output_tmp; + struct wlr_scene_output *scene_output_tmp; wl_list_for_each_safe(scene_output, scene_output_tmp, &scene->outputs, link) { wlr_scene_output_destroy(scene_output); } @@ -102,8 +109,16 @@ void wlr_scene_node_destroy(struct wlr_scene_node *node) { break; case WLR_SCENE_NODE_SURFACE:; struct wlr_scene_surface *scene_surface = wlr_scene_surface_from_node(node); + + wl_list_for_each(scene_output, &scene->outputs, link) { + // This is a noop if wlr_surface_send_enter() wasn't previously called for + // the given output. + wlr_surface_send_leave(scene_surface->surface, scene_output->output); + } + wl_list_remove(&scene_surface->surface_commit.link); wl_list_remove(&scene_surface->surface_destroy.link); + free(scene_surface); break; case WLR_SCENE_NODE_RECT:; @@ -146,11 +161,60 @@ static void scene_surface_handle_surface_destroy(struct wl_listener *listener, wlr_scene_node_destroy(&scene_surface->node); } -static struct wlr_scene *scene_node_get_root(struct wlr_scene_node *node) { - while (node->parent != NULL) { - node = node->parent; +// This function must be called whenever the coordinates/dimensions of a scene +// surface or scene output change. It is not necessary to call when a scene +// surface's node is enabled/disabled or obscured by other nodes. To quote the +// protocol: "The surface might be hidden even if no leave event has been sent." +static void scene_surface_update_outputs( + struct wlr_scene_surface *scene_surface, + int lx, int ly, struct wlr_scene *scene) { + struct wlr_box surface_box = { + .x = lx, + .y = ly, + .width = scene_surface->surface->current.width, + .height = scene_surface->surface->current.height, + }; + + struct wlr_scene_output *scene_output; + wl_list_for_each(scene_output, &scene->outputs, link) { + struct wlr_box output_box = { + .x = scene_output->x, + .y = scene_output->y, + }; + wlr_output_effective_resolution(scene_output->output, + &output_box.width, &output_box.height); + + // These enter/leave functions are a noop if the event has already been + // sent for the given output. + struct wlr_box intersection; + if (wlr_box_intersection(&intersection, &surface_box, &output_box)) { + wlr_surface_send_enter(scene_surface->surface, scene_output->output); + } else { + wlr_surface_send_leave(scene_surface->surface, scene_output->output); + } + } +} + +static void scene_node_update_surface_outputs_iterator( + struct wlr_scene_node *node, int lx, int ly, struct wlr_scene *scene) { + if (node->type == WLR_SCENE_NODE_SURFACE) { + struct wlr_scene_surface *scene_surface = + wlr_scene_surface_from_node(node); + scene_surface_update_outputs(scene_surface, lx, ly, scene); + } + + struct wlr_scene_node *child; + wl_list_for_each(child, &node->state.children, state.link) { + scene_node_update_surface_outputs_iterator(child, lx + child->state.x, + ly + child->state.y, scene); } - return scene_root_from_node(node); +} + +static void scene_node_update_surface_outputs(struct wlr_scene_node *node) { + struct wlr_scene *scene = scene_node_get_root(node); + int lx, ly; + wlr_scene_node_coords(node, &lx, &ly); + scene_node_update_surface_outputs_iterator(node, lx, ly, scene); } static void scene_surface_handle_surface_commit(struct wl_listener *listener, @@ -163,12 +227,21 @@ static void scene_surface_handle_surface_commit(struct wl_listener *listener, return; } + struct wlr_scene *scene = scene_node_get_root(&scene_surface->node); + int lx, ly; - if (!wlr_scene_node_coords(&scene_surface->node, &lx, &ly)) { - return; + bool enabled = wlr_scene_node_coords(&scene_surface->node, &lx, &ly); + + if (surface->current.width != scene_surface->prev_width || + surface->current.height != scene_surface->prev_height) { + scene_surface_update_outputs(scene_surface, lx, ly, scene); + scene_surface->prev_width = surface->current.width; + scene_surface->prev_height = surface->current.height; } - struct wlr_scene *scene = scene_node_get_root(&scene_surface->node); + if (!enabled) { + return; + } struct wlr_scene_output *scene_output; wl_list_for_each(scene_output, &scene->outputs, link) { @@ -212,6 +285,8 @@ struct wlr_scene_surface *wlr_scene_surface_create(struct wlr_scene_node *parent scene_node_damage_whole(&scene_surface->node); + scene_node_update_surface_outputs(&scene_surface->node); + return scene_surface; } @@ -438,6 +513,8 @@ void wlr_scene_node_set_position(struct wlr_scene_node *node, int x, int y) { node->state.x = x; node->state.y = y; scene_node_damage_whole(node); + + scene_node_update_surface_outputs(node); } void wlr_scene_node_place_above(struct wlr_scene_node *node, @@ -511,6 +588,8 @@ void wlr_scene_node_reparent(struct wlr_scene_node *node, wl_list_insert(new_parent->state.children.prev, &node->state.link); scene_node_damage_whole(node); + + scene_node_update_surface_outputs(node); } bool wlr_scene_node_coords(struct wlr_scene_node *node, @@ -822,9 +901,19 @@ struct wlr_scene_output *wlr_scene_output_create(struct wlr_scene *scene, return scene_output; } +static void scene_output_send_leave_iterator(struct wlr_surface *surface, + int sx, int sy, void *data) { + struct wlr_output *output = data; + wlr_surface_send_leave(surface, output); +} + void wlr_scene_output_destroy(struct wlr_scene_output *scene_output) { wlr_addon_finish(&scene_output->addon); wl_list_remove(&scene_output->link); + + wlr_scene_output_for_each_surface(scene_output, + scene_output_send_leave_iterator, scene_output->output); + free(scene_output); } @@ -849,6 +938,8 @@ void wlr_scene_output_set_position(struct wlr_scene_output *scene_output, scene_output->x = lx; scene_output->y = ly; wlr_output_damage_add_whole(scene_output->damage); + + scene_node_update_surface_outputs(&scene_output->scene->node); } struct check_scanout_data { From fb1f613510b871bcc117179a9d3a32aabc4508c0 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Sat, 11 Dec 2021 12:25:53 +0100 Subject: [PATCH 115/188] scene: add primary output to wlr_scene_surface This allows compositors to avoid sending multiple frame done events to a surface that is rendered on multiple outputs at once. This may also be used in the same way for presentation feedback. --- include/wlr/types/wlr_scene.h | 8 ++++++++ types/scene/wlr_scene.c | 13 +++++++++++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/include/wlr/types/wlr_scene.h b/include/wlr/types/wlr_scene.h index 1238653d33..6c2928eedf 100644 --- a/include/wlr/types/wlr_scene.h +++ b/include/wlr/types/wlr_scene.h @@ -74,6 +74,14 @@ struct wlr_scene_surface { struct wlr_scene_node node; struct wlr_surface *surface; + /** + * The output that the largest area of this surface is displayed on. + * This may be NULL if the surface is not currently displayed on any + * outputs. This is the output that should be used for frame callbacks, + * presentation feedback, etc. + */ + struct wlr_output *primary_output; + // private state int prev_width, prev_height; diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index d2f3f4a7fd..42697074a8 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -175,6 +175,9 @@ static void scene_surface_update_outputs( .height = scene_surface->surface->current.height, }; + int largest_overlap = 0; + scene_surface->primary_output = NULL; + struct wlr_scene_output *scene_output; wl_list_for_each(scene_output, &scene->outputs, link) { struct wlr_box output_box = { @@ -184,10 +187,16 @@ static void scene_surface_update_outputs( wlr_output_effective_resolution(scene_output->output, &output_box.width, &output_box.height); - // These enter/leave functions are a noop if the event has already been - // sent for the given output. struct wlr_box intersection; if (wlr_box_intersection(&intersection, &surface_box, &output_box)) { + int overlap = intersection.width * intersection.height; + if (overlap > largest_overlap) { + largest_overlap = overlap; + scene_surface->primary_output = scene_output->output; + } + + // These enter/leave functions are a noop if the event has already been + // sent for the given output. wlr_surface_send_enter(scene_surface->surface, scene_output->output); } else { wlr_surface_send_leave(scene_surface->surface, scene_output->output); From fecde72be3ebdc5ad0aa526db7405aaf1eddf847 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Mon, 13 Dec 2021 16:11:19 +0100 Subject: [PATCH 116/188] scene: add wlr_scene_send_frame_done() --- include/wlr/types/wlr_scene.h | 7 +++++++ types/scene/wlr_scene.c | 25 +++++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/include/wlr/types/wlr_scene.h b/include/wlr/types/wlr_scene.h index 6c2928eedf..f388a65879 100644 --- a/include/wlr/types/wlr_scene.h +++ b/include/wlr/types/wlr_scene.h @@ -201,6 +201,13 @@ struct wlr_scene *wlr_scene_create(void); */ void wlr_scene_render_output(struct wlr_scene *scene, struct wlr_output *output, int lx, int ly, pixman_region32_t *damage); +/** + * Call wlr_surface_send_frame_done() on all surfaces in the scene rendered by + * wlr_scene_render_output() for which wlr_scene_surface->primary_output + * matches the given output. + */ +void wlr_scene_send_frame_done(struct wlr_scene *scene, + struct wlr_output *output, struct timespec *now); /** * Add a node displaying nothing but its children. diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 42697074a8..daaf8d97e0 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -876,6 +876,31 @@ void wlr_scene_render_output(struct wlr_scene *scene, struct wlr_output *output, pixman_region32_fini(&full_region); } +static void scene_send_frame_done_iterator(struct wlr_scene_node *node, + struct wlr_output *output, struct timespec *now) { + if (!node->state.enabled) { + return; + } + + if (node->type == WLR_SCENE_NODE_SURFACE) { + struct wlr_scene_surface *scene_surface = + wlr_scene_surface_from_node(node); + if (scene_surface->primary_output == output) { + wlr_surface_send_frame_done(scene_surface->surface, now); + } + } + + struct wlr_scene_node *child; + wl_list_for_each(child, &node->state.children, state.link) { + scene_send_frame_done_iterator(child, output, now); + } +} + +void wlr_scene_send_frame_done(struct wlr_scene *scene, + struct wlr_output *output, struct timespec *now) { + scene_send_frame_done_iterator(&scene->node, output, now); +} + static void scene_output_handle_destroy(struct wlr_addon *addon) { struct wlr_scene_output *scene_output = wl_container_of(addon, scene_output, addon); From ad01cdf0b218a8d49698bf0ff85b84a4540a4f6f Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Mon, 13 Dec 2021 16:13:03 +0100 Subject: [PATCH 117/188] tinywl: use wlr_scene_send_frame_done() --- tinywl/tinywl.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/tinywl/tinywl.c b/tinywl/tinywl.c index 487757409e..581fcfca38 100644 --- a/tinywl/tinywl.c +++ b/tinywl/tinywl.c @@ -510,29 +510,21 @@ static void server_cursor_frame(struct wl_listener *listener, void *data) { wlr_seat_pointer_notify_frame(server->seat); } -// TODO: We should avoid sending the frame done event twice if a surface -// appears on multiple outputs. -// https://github.com/swaywm/wlroots/issues/3210 -static void send_frame_done(struct wlr_surface *surface, - int sx, int sy, void *data) { - wlr_surface_send_frame_done(surface, data); -} - static void output_frame(struct wl_listener *listener, void *data) { /* This function is called every time an output is ready to display a frame, * generally at the output's refresh rate (e.g. 60Hz). */ - struct tinywl_output *output = - wl_container_of(listener, output, frame); + struct tinywl_output *output = wl_container_of(listener, output, frame); + struct wlr_scene *scene = output->server->scene; struct wlr_scene_output *scene_output = wlr_scene_get_scene_output( - output->server->scene, output->wlr_output); + scene, output->wlr_output); /* Render the scene if needed and commit the output */ wlr_scene_output_commit(scene_output); struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); - wlr_scene_output_for_each_surface(scene_output, send_frame_done, &now); + wlr_scene_send_frame_done(scene, output->wlr_output, &now); } static void server_new_output(struct wl_listener *listener, void *data) { From 1c3e0816f3cc4c653b24256486234d02fcf59e02 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Mon, 13 Dec 2021 17:23:47 +0100 Subject: [PATCH 118/188] scene: fix wlr_scene_send_frame_done() API This doesn't work if scene outputs are not used as the primary output of scene surfaces will always be NULL. Therefore, take a wlr_scene_output instead of separate wlr_scene and wlr_output arguments and rename the function to wlr_scene_output_send_frame_done(). The actual behavior of the function is unchanged. --- include/wlr/types/wlr_scene.h | 15 +++++------ tinywl/tinywl.c | 2 +- types/scene/wlr_scene.c | 51 ++++++++++++++++++----------------- 3 files changed, 34 insertions(+), 34 deletions(-) diff --git a/include/wlr/types/wlr_scene.h b/include/wlr/types/wlr_scene.h index f388a65879..d2af9c5af4 100644 --- a/include/wlr/types/wlr_scene.h +++ b/include/wlr/types/wlr_scene.h @@ -201,13 +201,6 @@ struct wlr_scene *wlr_scene_create(void); */ void wlr_scene_render_output(struct wlr_scene *scene, struct wlr_output *output, int lx, int ly, pixman_region32_t *damage); -/** - * Call wlr_surface_send_frame_done() on all surfaces in the scene rendered by - * wlr_scene_render_output() for which wlr_scene_surface->primary_output - * matches the given output. - */ -void wlr_scene_send_frame_done(struct wlr_scene *scene, - struct wlr_output *output, struct timespec *now); /** * Add a node displaying nothing but its children. @@ -295,7 +288,13 @@ void wlr_scene_output_set_position(struct wlr_scene_output *scene_output, * Render and commit an output. */ bool wlr_scene_output_commit(struct wlr_scene_output *scene_output); - +/** + * Call wlr_surface_send_frame_done() on all surfaces in the scene rendered by + * wlr_scene_output_commit() for which wlr_scene_surface->primary_output + * matches the given scene_output. + */ +void wlr_scene_output_send_frame_done(struct wlr_scene_output *scene_output, + struct timespec *now); /** * Call `iterator` on each surface in the scene-graph visible on the output, * with the surface's position in layout coordinates. The function is called diff --git a/tinywl/tinywl.c b/tinywl/tinywl.c index 581fcfca38..faa0a9a130 100644 --- a/tinywl/tinywl.c +++ b/tinywl/tinywl.c @@ -524,7 +524,7 @@ static void output_frame(struct wl_listener *listener, void *data) { struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); - wlr_scene_send_frame_done(scene, output->wlr_output, &now); + wlr_scene_output_send_frame_done(scene_output, &now); } static void server_new_output(struct wl_listener *listener, void *data) { diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index daaf8d97e0..3d19acefc8 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -876,31 +876,6 @@ void wlr_scene_render_output(struct wlr_scene *scene, struct wlr_output *output, pixman_region32_fini(&full_region); } -static void scene_send_frame_done_iterator(struct wlr_scene_node *node, - struct wlr_output *output, struct timespec *now) { - if (!node->state.enabled) { - return; - } - - if (node->type == WLR_SCENE_NODE_SURFACE) { - struct wlr_scene_surface *scene_surface = - wlr_scene_surface_from_node(node); - if (scene_surface->primary_output == output) { - wlr_surface_send_frame_done(scene_surface->surface, now); - } - } - - struct wlr_scene_node *child; - wl_list_for_each(child, &node->state.children, state.link) { - scene_send_frame_done_iterator(child, output, now); - } -} - -void wlr_scene_send_frame_done(struct wlr_scene *scene, - struct wlr_output *output, struct timespec *now) { - scene_send_frame_done_iterator(&scene->node, output, now); -} - static void scene_output_handle_destroy(struct wlr_addon *addon) { struct wlr_scene_output *scene_output = wl_container_of(addon, scene_output, addon); @@ -1121,6 +1096,32 @@ bool wlr_scene_output_commit(struct wlr_scene_output *scene_output) { return wlr_output_commit(output); } +static void scene_output_send_frame_done_iterator(struct wlr_scene_node *node, + struct wlr_output *output, struct timespec *now) { + if (!node->state.enabled) { + return; + } + + if (node->type == WLR_SCENE_NODE_SURFACE) { + struct wlr_scene_surface *scene_surface = + wlr_scene_surface_from_node(node); + if (scene_surface->primary_output == output) { + wlr_surface_send_frame_done(scene_surface->surface, now); + } + } + + struct wlr_scene_node *child; + wl_list_for_each(child, &node->state.children, state.link) { + scene_output_send_frame_done_iterator(child, output, now); + } +} + +void wlr_scene_output_send_frame_done(struct wlr_scene_output *scene_output, + struct timespec *now) { + scene_output_send_frame_done_iterator(&scene_output->scene->node, + scene_output->output, now); +} + static void scene_output_for_each_surface(const struct wlr_box *output_box, struct wlr_scene_node *node, int lx, int ly, wlr_surface_iterator_func_t user_iterator, void *user_data) { From 31914928d22934a09d2f19ef996500c324bd5ff2 Mon Sep 17 00:00:00 2001 From: David Rosca Date: Tue, 14 Dec 2021 08:15:45 +0100 Subject: [PATCH 119/188] seat: Only resend keyboard/pointer enter to focused clients Otherwise it will send enter events to clients that already have keyboard/pointer focus. Notably Qt applications warns about this. --- types/seat/wlr_seat_keyboard.c | 2 +- types/seat/wlr_seat_pointer.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/types/seat/wlr_seat_keyboard.c b/types/seat/wlr_seat_keyboard.c index e97b482529..57dbfe2f58 100644 --- a/types/seat/wlr_seat_keyboard.c +++ b/types/seat/wlr_seat_keyboard.c @@ -415,7 +415,7 @@ void seat_client_create_keyboard(struct wlr_seat_client *seat_client, seat_client->seat->keyboard_state.focused_surface; // Send an enter event if there is a focused client/surface stored - if (focused_client != NULL && focused_surface != NULL) { + if (focused_client == seat_client && focused_surface != NULL) { uint32_t *keycodes = keyboard->keycodes; size_t num_keycodes = keyboard->num_keycodes; diff --git a/types/seat/wlr_seat_pointer.c b/types/seat/wlr_seat_pointer.c index 27ece5e881..2fa911ee09 100644 --- a/types/seat/wlr_seat_pointer.c +++ b/types/seat/wlr_seat_pointer.c @@ -446,7 +446,7 @@ void seat_client_create_pointer(struct wlr_seat_client *seat_client, seat_client->seat->pointer_state.focused_surface; // Send an enter event if there is a focused client/surface stored - if (focused_client != NULL && focused_surface != NULL) { + if (focused_client == seat_client && focused_surface != NULL) { double sx = seat_client->seat->pointer_state.sx; double sy = seat_client->seat->pointer_state.sy; From 4c59f7d46a949548caa55805b00922f846d58525 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20G=C3=BCnther?= Date: Thu, 11 Nov 2021 15:31:27 +0100 Subject: [PATCH 120/188] xdg-activation: Allow to submit tokens Allows the compositor to submit tokens to the pool of currently active tokens. This can be useful when the launcher doesn't use or support xdg-activation-v1 by itself - e.g. when it is X11 based or use gtk_shell1. --- include/wlr/types/wlr_xdg_activation_v1.h | 4 ++++ types/wlr_xdg_activation_v1.c | 20 ++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/include/wlr/types/wlr_xdg_activation_v1.h b/include/wlr/types/wlr_xdg_activation_v1.h index 4635621518..97801af2ad 100644 --- a/include/wlr/types/wlr_xdg_activation_v1.h +++ b/include/wlr/types/wlr_xdg_activation_v1.h @@ -81,4 +81,8 @@ struct wlr_xdg_activation_token_v1 *wlr_xdg_activation_v1_find_token( const char *wlr_xdg_activation_token_v1_get_name( struct wlr_xdg_activation_token_v1 *token); +// Add a token to the pool of known tokens +struct wlr_xdg_activation_token_v1 *wlr_xdg_activation_v1_add_token( + struct wlr_xdg_activation_v1 *activation, const char *token_str); + #endif diff --git a/types/wlr_xdg_activation_v1.c b/types/wlr_xdg_activation_v1.c index 64864d882b..208ada28c9 100644 --- a/types/wlr_xdg_activation_v1.c +++ b/types/wlr_xdg_activation_v1.c @@ -407,3 +407,23 @@ const char *wlr_xdg_activation_token_v1_get_name( struct wlr_xdg_activation_token_v1 *token) { return token->token; } + +struct wlr_xdg_activation_token_v1 *wlr_xdg_activation_v1_add_token( + struct wlr_xdg_activation_v1 *activation, const char *token_str) { + assert(token_str); + + struct wlr_xdg_activation_token_v1 *token = calloc(1, sizeof(*token)); + if (token == NULL) { + return NULL; + } + wl_list_init(&token->link); + wl_list_init(&token->seat_destroy.link); + wl_list_init(&token->surface_destroy.link); + + token->activation = activation; + token->token = strdup(token_str); + + wl_list_insert(&activation->tokens, &token->link); + + return token; +} From 4377b5529279aa9dab64256d22ad0f2e9009843c Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 29 Jun 2021 19:12:50 +0200 Subject: [PATCH 121/188] util/global: remove wl_display arg from wlr_global_destroy_safe Since [1], we can get the wl_display directly from the wl_global. [1]: https://gitlab.freedesktop.org/wayland/wayland/-/commit/2b22160fb690a76247aa9bd0be3069ff43e8239f --- include/util/global.h | 3 +-- types/output/output.c | 2 +- types/seat/wlr_seat.c | 2 +- types/wlr_drm_lease_v1.c | 2 +- util/global.c | 4 ++-- 5 files changed, 6 insertions(+), 7 deletions(-) diff --git a/include/util/global.h b/include/util/global.h index ea0fda83b4..1c979ab110 100644 --- a/include/util/global.h +++ b/include/util/global.h @@ -9,7 +9,6 @@ * Globals that are created and destroyed on the fly need special handling to * prevent race conditions with wl_registry. Use this function to destroy them. */ -void wlr_global_destroy_safe(struct wl_global *global, - struct wl_display *display); +void wlr_global_destroy_safe(struct wl_global *global); #endif diff --git a/types/output/output.c b/types/output/output.c index 900cfda676..85590ab632 100644 --- a/types/output/output.c +++ b/types/output/output.c @@ -140,7 +140,7 @@ void wlr_output_destroy_global(struct wlr_output *output) { wl_list_init(wl_resource_get_link(resource)); } - wlr_global_destroy_safe(output->global, output->display); + wlr_global_destroy_safe(output->global); output->global = NULL; } diff --git a/types/seat/wlr_seat.c b/types/seat/wlr_seat.c index f931474436..4933133c63 100644 --- a/types/seat/wlr_seat.c +++ b/types/seat/wlr_seat.c @@ -191,7 +191,7 @@ void wlr_seat_destroy(struct wlr_seat *seat) { } } - wlr_global_destroy_safe(seat->global, seat->display); + wlr_global_destroy_safe(seat->global); free(seat->pointer_state.default_grab); free(seat->keyboard_state.default_grab); free(seat->touch_state.default_grab); diff --git a/types/wlr_drm_lease_v1.c b/types/wlr_drm_lease_v1.c index 7063e0d312..7cb9974d79 100644 --- a/types/wlr_drm_lease_v1.c +++ b/types/wlr_drm_lease_v1.c @@ -127,7 +127,7 @@ static void drm_lease_device_v1_destroy( } wl_list_remove(&device->link); - wlr_global_destroy_safe(device->global, device->manager->display); + wlr_global_destroy_safe(device->global); free(device); } diff --git a/util/global.c b/util/global.c index a0d84ed303..fa99a99466 100644 --- a/util/global.c +++ b/util/global.c @@ -14,8 +14,7 @@ static int destroy_global(void *_data) { return 0; } -void wlr_global_destroy_safe(struct wl_global *global, - struct wl_display *display) { +void wlr_global_destroy_safe(struct wl_global *global) { // Don't destroy the global immediately. If the global has been created // recently, clients might try to bind to it after we've destroyed it. // Instead, remove the global so that clients stop seeing it and wait an @@ -25,6 +24,7 @@ void wlr_global_destroy_safe(struct wl_global *global, wl_global_remove(global); wl_global_set_user_data(global, NULL); // safety net + struct wl_display *display = wl_global_get_display(global); struct wl_event_loop *event_loop = wl_display_get_event_loop(display); struct destroy_global_data *data = calloc(1, sizeof(*data)); if (data == NULL) { From a15c327718dafb1b7b7f1213c8878187b6351dba Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 3 Nov 2021 16:44:42 +0100 Subject: [PATCH 122/188] backend/drm: use drmModeFormatModifierBlobIterNext This avoids open-coding our own logic. The resulting code is more readable. References: https://gitlab.freedesktop.org/mesa/drm/-/merge_requests/146 --- backend/drm/drm.c | 14 +++----------- meson.build | 2 +- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/backend/drm/drm.c b/backend/drm/drm.c index 1a46b49598..43de54e553 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -142,17 +142,9 @@ static bool add_plane(struct wlr_drm_backend *drm, 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); - } - } + drmModeFormatModifierIterator iter = {0}; + while (drmModeFormatModifierBlobIterNext(blob, &iter)) { + wlr_drm_format_set_add(&p->formats, iter.fmt, iter.mod); } drmModeFreePropertyBlob(blob); diff --git a/meson.build b/meson.build index a84f3df79c..bdb6df1933 100644 --- a/meson.build +++ b/meson.build @@ -104,7 +104,7 @@ wayland_server = dependency('wayland-server', default_options: wayland_project_options, ) -drm = dependency('libdrm', version: '>=2.4.105') +drm = dependency('libdrm', version: '>=2.4.108') gbm = dependency('gbm', version: '>=17.1.0') xkbcommon = dependency('xkbcommon') udev = dependency('libudev') From bedfec94bb57cf7dc32f03fce463a30d3370bb20 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 2 Oct 2021 17:24:08 +0200 Subject: [PATCH 123/188] backend/drm: use drmCloseBufferHandle This has been added in [1] and allows us to close buffer handles without manually calling drmIoctl. [1]: https://gitlab.freedesktop.org/mesa/drm/-/merge_requests/192 --- backend/drm/legacy.c | 8 ++++++-- backend/drm/renderer.c | 4 +++- backend/drm/util.c | 11 ----------- include/backend/drm/util.h | 7 ------- 4 files changed, 9 insertions(+), 21 deletions(-) diff --git a/backend/drm/legacy.c b/backend/drm/legacy.c index cb672023bf..84e8a914f4 100644 --- a/backend/drm/legacy.c +++ b/backend/drm/legacy.c @@ -151,9 +151,13 @@ static bool legacy_crtc_commit(struct wlr_drm_connector *conn, int ret = drmModeSetCursor(drm->fd, crtc->id, cursor_handle, cursor_width, cursor_height); - close_bo_handle(drm->fd, cursor_handle); + int set_cursor_errno = errno; + if (drmCloseBufferHandle(drm->fd, cursor_handle) != 0) { + wlr_log_errno(WLR_ERROR, "drmCloseBufferHandle failed"); + } if (ret != 0) { - wlr_drm_conn_log_errno(conn, WLR_DEBUG, "drmModeSetCursor failed"); + wlr_drm_conn_log(conn, WLR_DEBUG, "drmModeSetCursor failed: %s", + strerror(set_cursor_errno)); return false; } diff --git a/backend/drm/renderer.c b/backend/drm/renderer.c index 815ba98187..792de938fb 100644 --- a/backend/drm/renderer.c +++ b/backend/drm/renderer.c @@ -271,7 +271,9 @@ static void close_all_bo_handles(struct wlr_drm_backend *drm, continue; } - close_bo_handle(drm->fd, handles[i]); + if (drmCloseBufferHandle(drm->fd, handles[i]) != 0) { + wlr_log_errno(WLR_ERROR, "drmCloseBufferHandle failed"); + } } } diff --git a/backend/drm/util.c b/backend/drm/util.c index 407c19a5e0..3cf656fb3a 100644 --- a/backend/drm/util.c +++ b/backend/drm/util.c @@ -320,14 +320,3 @@ size_t match_obj(size_t num_objs, const uint32_t objs[static restrict num_objs], match_obj_(&st, 0, 0, 0, 0); return st.score; } - -void close_bo_handle(int drm_fd, uint32_t handle) { - if (handle == 0) { - return; - } - - struct drm_gem_close args = { .handle = handle }; - if (drmIoctl(drm_fd, DRM_IOCTL_GEM_CLOSE, &args) != 0) { - wlr_log_errno(WLR_ERROR, "drmIoctl(GEM_CLOSE) failed"); - } -} diff --git a/include/backend/drm/util.h b/include/backend/drm/util.h index 6e97505c36..b4cdee7d7b 100644 --- a/include/backend/drm/util.h +++ b/include/backend/drm/util.h @@ -36,11 +36,4 @@ 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]); -/** - * Close a GEM buffer handle. - * - * TODO: replace with drmCloseBufferHandle. - */ -void close_bo_handle(int drm_fd, uint32_t handle); - #endif From c0b120a30caae4cc538d1f83b722950d3d71a921 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 2 Oct 2021 17:29:42 +0200 Subject: [PATCH 124/188] build: add subproject fallback for libdrm --- meson.build | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/meson.build b/meson.build index bdb6df1933..67fe86fd4a 100644 --- a/meson.build +++ b/meson.build @@ -104,7 +104,27 @@ wayland_server = dependency('wayland-server', default_options: wayland_project_options, ) -drm = dependency('libdrm', version: '>=2.4.108') +drm = dependency('libdrm', + version: '>=2.4.108', + fallback: ['libdrm', 'ext_libdrm'], + default_options: [ + 'libkms=false', + 'intel=false', + 'radeon=false', + 'amdgpu=false', + 'nouveau=false', + 'vmwgfx=false', + 'omap=false', + 'exynos=false', + 'freedreno=false', + 'tegra=false', + 'vc4=false', + 'etnaviv=false', + 'cairo-tests=false', + 'man-pages=false', + 'valgrind=false', + ], +) gbm = dependency('gbm', version: '>=17.1.0') xkbcommon = dependency('xkbcommon') udev = dependency('libudev') From 07ccc6e0b357dd3c7be0939957657d0a03738b9d Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Tue, 14 Dec 2021 18:35:44 +0100 Subject: [PATCH 125/188] scene: add wlr_scene_set_presentation() This helper automates sending presentation feedback to clients based on the primary output of scene surfaces. --- include/wlr/types/wlr_scene.h | 14 ++++++++++++ types/scene/wlr_scene.c | 42 +++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/include/wlr/types/wlr_scene.h b/include/wlr/types/wlr_scene.h index d2af9c5af4..e0b370adf6 100644 --- a/include/wlr/types/wlr_scene.h +++ b/include/wlr/types/wlr_scene.h @@ -62,6 +62,12 @@ struct wlr_scene { struct wlr_scene_node node; struct wl_list outputs; // wlr_scene_output.link + + // private state + + // May be NULL + struct wlr_presentation *presentation; + struct wl_listener presentation_destroy; }; /** A sub-tree in the scene-graph. */ @@ -201,6 +207,14 @@ struct wlr_scene *wlr_scene_create(void); */ void wlr_scene_render_output(struct wlr_scene *scene, struct wlr_output *output, int lx, int ly, pixman_region32_t *damage); +/** + * Handle presentation feedback for all surfaces in the scene, assuming that + * scene outputs and the scene rendering functions are used. + * + * Asserts that a wlr_presentation hasn't already been set for the scene. + */ +void wlr_scene_set_presentation(struct wlr_scene *scene, + struct wlr_presentation *presentation); /** * Add a node displaying nothing but its children. diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 3d19acefc8..7b55096389 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -101,6 +102,8 @@ void wlr_scene_node_destroy(struct wlr_scene_node *node) { wlr_scene_output_destroy(scene_output); } + wl_list_remove(&scene->presentation_destroy.link); + free(scene); break; case WLR_SCENE_NODE_TREE:; @@ -141,6 +144,7 @@ struct wlr_scene *wlr_scene_create(void) { } scene_node_init(&scene->node, WLR_SCENE_NODE_ROOT, NULL); wl_list_init(&scene->outputs); + wl_list_init(&scene->presentation_destroy.link); return scene; } @@ -767,6 +771,9 @@ static void render_texture(struct wlr_output *output, struct render_data { struct wlr_output *output; pixman_region32_t *damage; + + // May be NULL + struct wlr_presentation *presentation; }; static void render_node_iterator(struct wlr_scene_node *node, @@ -808,6 +815,11 @@ static void render_node_iterator(struct wlr_scene_node *node, render_texture(output, output_damage, texture, &src_box, &dst_box, matrix); + + if (data->presentation != NULL && scene_surface->primary_output == output) { + wlr_presentation_surface_sampled_on_output(data->presentation, + surface, output); + } break; case WLR_SCENE_NODE_RECT:; struct wlr_scene_rect *scene_rect = scene_rect_from_node(node); @@ -867,6 +879,7 @@ void wlr_scene_render_output(struct wlr_scene *scene, struct wlr_output *output, struct render_data data = { .output = output, .damage = damage, + .presentation = scene->presentation, }; scene_node_for_each_node(&scene->node, -lx, -ly, render_node_iterator, &data); @@ -876,6 +889,23 @@ void wlr_scene_render_output(struct wlr_scene *scene, struct wlr_output *output, pixman_region32_fini(&full_region); } +static void scene_handle_presentation_destroy(struct wl_listener *listener, + void *data) { + struct wlr_scene *scene = + wl_container_of(listener, scene, presentation_destroy); + wl_list_remove(&scene->presentation_destroy.link); + wl_list_init(&scene->presentation_destroy.link); + scene->presentation = NULL; +} + +void wlr_scene_set_presentation(struct wlr_scene *scene, + struct wlr_presentation *presentation) { + assert(scene->presentation == NULL); + scene->presentation = presentation; + scene->presentation_destroy.notify = scene_handle_presentation_destroy; + wl_signal_add(&presentation->events.destroy, &scene->presentation_destroy); +} + static void scene_output_handle_destroy(struct wlr_addon *addon) { struct wlr_scene_output *scene_output = wl_container_of(addon, scene_output, addon); @@ -1028,6 +1058,18 @@ static bool scene_output_scanout(struct wlr_scene_output *scene_output) { return false; } + struct wlr_presentation *presentation = scene_output->scene->presentation; + if (presentation != NULL && node->type == WLR_SCENE_NODE_SURFACE) { + struct wlr_scene_surface *scene_surface = + wlr_scene_surface_from_node(node); + // Since outputs may overlap, we still need to check this even though + // we know that the surface size matches the size of this output. + if (scene_surface->primary_output == output) { + wlr_presentation_surface_sampled_on_output(presentation, + scene_surface->surface, output); + } + } + return wlr_output_commit(output); } From 8e566f716c464d886f1aed7e57b5bf26c3502426 Mon Sep 17 00:00:00 2001 From: Stacy Harper Date: Tue, 14 Dec 2021 18:09:31 +0100 Subject: [PATCH 126/188] layer-shell: don't set committed flag if the property didn't change This fixes configure loop in Sway when clients re-send same properties on every configure event. Original issue: https://todo.sr.ht/~mil/sxmo-tickets/413 --- types/wlr_layer_shell_v1.c | 44 +++++++++++++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/types/wlr_layer_shell_v1.c b/types/wlr_layer_shell_v1.c index 4495472a1f..456dbc0eb5 100644 --- a/types/wlr_layer_shell_v1.c +++ b/types/wlr_layer_shell_v1.c @@ -103,7 +103,14 @@ static void layer_surface_handle_set_size(struct wl_client *client, if (!surface) { return; } - surface->pending.committed |= WLR_LAYER_SURFACE_V1_STATE_DESIRED_SIZE; + + if (surface->current.desired_width == width + && surface->current.desired_height == height) { + surface->pending.committed &= ~WLR_LAYER_SURFACE_V1_STATE_DESIRED_SIZE; + } else { + surface->pending.committed |= WLR_LAYER_SURFACE_V1_STATE_DESIRED_SIZE; + } + surface->pending.desired_width = width; surface->pending.desired_height = height; } @@ -125,7 +132,13 @@ static void layer_surface_handle_set_anchor(struct wl_client *client, if (!surface) { return; } - surface->pending.committed |= WLR_LAYER_SURFACE_V1_STATE_ANCHOR; + + if (surface->current.anchor == anchor) { + surface->pending.committed &= ~WLR_LAYER_SURFACE_V1_STATE_ANCHOR; + } else { + surface->pending.committed |= WLR_LAYER_SURFACE_V1_STATE_ANCHOR; + } + surface->pending.anchor = anchor; } @@ -136,7 +149,13 @@ static void layer_surface_handle_set_exclusive_zone(struct wl_client *client, if (!surface) { return; } - surface->pending.committed |= WLR_LAYER_SURFACE_V1_STATE_EXCLUSIVE_ZONE; + + if (surface->current.exclusive_zone == zone) { + surface->pending.committed &= ~WLR_LAYER_SURFACE_V1_STATE_EXCLUSIVE_ZONE; + } else { + surface->pending.committed |= WLR_LAYER_SURFACE_V1_STATE_EXCLUSIVE_ZONE; + } + surface->pending.exclusive_zone = zone; } @@ -148,7 +167,16 @@ static void layer_surface_handle_set_margin( if (!surface) { return; } - surface->pending.committed |= WLR_LAYER_SURFACE_V1_STATE_MARGIN; + + if (surface->current.margin.top == (uint32_t) top + && surface->current.margin.right == (uint32_t) right + && surface->current.margin.bottom == (uint32_t) bottom + && surface->current.margin.left == (uint32_t) left) { + surface->pending.committed &= ~WLR_LAYER_SURFACE_V1_STATE_MARGIN; + } else { + surface->pending.committed |= WLR_LAYER_SURFACE_V1_STATE_MARGIN; + } + surface->pending.margin.top = top; surface->pending.margin.right = right; surface->pending.margin.bottom = bottom; @@ -209,7 +237,13 @@ static void layer_surface_set_layer(struct wl_client *client, "Invalid layer %" PRIu32, layer); return; } - surface->pending.committed |= WLR_LAYER_SURFACE_V1_STATE_LAYER; + + if (surface->current.layer == layer) { + surface->pending.committed &= ~WLR_LAYER_SURFACE_V1_STATE_LAYER; + } else { + surface->pending.committed |= WLR_LAYER_SURFACE_V1_STATE_LAYER; + } + surface->pending.layer = layer; } From 9f41627aa10a94d9427bc315fa3d363a61b94d7c Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 8 Dec 2021 01:43:15 +0100 Subject: [PATCH 127/188] backend/wayland: add basic linux-dmabuf feedback support This patch makes it so we bind to zwp_linux_dmabuf_v1 version 4 and we use it to grab the main device. v4 sends supported formats via a table so we need to handle this as well. v4 allows wlroots to remove the requirement for Mesa's internal wl_drm interface. --- backend/wayland/backend.c | 166 ++++++++++++++++++++++++++++++++++++-- meson.build | 2 +- 2 files changed, 162 insertions(+), 6 deletions(-) diff --git a/backend/wayland/backend.c b/backend/wayland/backend.c index 63d6a7df6a..1df753b93c 100644 --- a/backend/wayland/backend.c +++ b/backend/wayland/backend.c @@ -4,6 +4,8 @@ #include #include #include +#include +#include #include #include @@ -30,6 +32,21 @@ #include "tablet-unstable-v2-client-protocol.h" #include "relative-pointer-unstable-v1-client-protocol.h" +struct wlr_wl_linux_dmabuf_feedback_v1 { + struct wlr_wl_backend *backend; + dev_t main_device_id; + struct wlr_wl_linux_dmabuf_v1_table_entry *format_table; + size_t format_table_size; + + dev_t tranche_target_device_id; +}; + +struct wlr_wl_linux_dmabuf_v1_table_entry { + uint32_t format; + uint32_t pad; /* unused */ + uint64_t modifier; +}; + struct wlr_wl_backend *get_wl_backend_from_backend(struct wlr_backend *backend) { assert(wlr_backend_is_wl(backend)); return (struct wlr_wl_backend *)backend; @@ -108,6 +125,119 @@ static const struct zwp_linux_dmabuf_v1_listener linux_dmabuf_v1_listener = { .modifier = linux_dmabuf_v1_handle_modifier, }; +static void linux_dmabuf_feedback_v1_handle_done(void *data, + struct zwp_linux_dmabuf_feedback_v1 *feedback) { + // This space is intentionally left blank +} + +static void linux_dmabuf_feedback_v1_handle_format_table(void *data, + struct zwp_linux_dmabuf_feedback_v1 *feedback, int fd, uint32_t size) { + struct wlr_wl_linux_dmabuf_feedback_v1 *feedback_data = data; + + feedback_data->format_table = NULL; + + void *table_data = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); + if (table_data == MAP_FAILED) { + wlr_log_errno(WLR_ERROR, "failed to mmap DMA-BUF format table"); + } else { + feedback_data->format_table = table_data; + feedback_data->format_table_size = size; + } + close(fd); +} + +static void linux_dmabuf_feedback_v1_handle_main_device(void *data, + struct zwp_linux_dmabuf_feedback_v1 *feedback, + struct wl_array *dev_id_arr) { + struct wlr_wl_linux_dmabuf_feedback_v1 *feedback_data = data; + + dev_t dev_id; + assert(dev_id_arr->size == sizeof(dev_id)); + memcpy(&dev_id, dev_id_arr->data, sizeof(dev_id)); + + feedback_data->main_device_id = dev_id; + + drmDevice *device = NULL; + if (drmGetDeviceFromDevId(dev_id, 0, &device) != 0) { + wlr_log_errno(WLR_ERROR, "drmGetDeviceFromDevId failed"); + return; + } + + const char *name = NULL; + if (device->available_nodes & (1 << DRM_NODE_RENDER)) { + name = device->nodes[DRM_NODE_RENDER]; + } else { + // Likely a split display/render setup. Pick the primary node and hope + // Mesa will open the right render node under-the-hood. + assert(device->available_nodes & (1 << DRM_NODE_PRIMARY)); + name = device->nodes[DRM_NODE_PRIMARY]; + wlr_log(WLR_DEBUG, "DRM device %s has no render node, " + "falling back to primary node", name); + } + + feedback_data->backend->drm_render_name = strdup(name); + + drmFreeDevice(&device); +} + +static void linux_dmabuf_feedback_v1_handle_tranche_done(void *data, + struct zwp_linux_dmabuf_feedback_v1 *feedback) { + struct wlr_wl_linux_dmabuf_feedback_v1 *feedback_data = data; + feedback_data->tranche_target_device_id = 0; +} + +static void linux_dmabuf_feedback_v1_handle_tranche_target_device(void *data, + struct zwp_linux_dmabuf_feedback_v1 *feedback, + struct wl_array *dev_id_arr) { + struct wlr_wl_linux_dmabuf_feedback_v1 *feedback_data = data; + + dev_t dev_id; + assert(dev_id_arr->size == sizeof(dev_id)); + memcpy(&dev_id, dev_id_arr->data, sizeof(dev_id)); + + feedback_data->tranche_target_device_id = dev_id; +} + +static void linux_dmabuf_feedback_v1_handle_tranche_formats(void *data, + struct zwp_linux_dmabuf_feedback_v1 *feedback, + struct wl_array *indices_arr) { + struct wlr_wl_linux_dmabuf_feedback_v1 *feedback_data = data; + + if (feedback_data->format_table == NULL) { + return; + } + if (feedback_data->tranche_target_device_id != feedback_data->main_device_id) { + return; + } + + size_t table_cap = feedback_data->format_table_size / + sizeof(struct wlr_wl_linux_dmabuf_v1_table_entry); + uint16_t *index_ptr; + wl_array_for_each(index_ptr, indices_arr) { + assert(*index_ptr < table_cap); + const struct wlr_wl_linux_dmabuf_v1_table_entry *entry = + &feedback_data->format_table[*index_ptr]; + wlr_drm_format_set_add(&feedback_data->backend->linux_dmabuf_v1_formats, + entry->format, entry->modifier); + } +} + +static void linux_dmabuf_feedback_v1_handle_tranche_flags(void *data, + struct zwp_linux_dmabuf_feedback_v1 *feedback, uint32_t flags) { + // TODO: handle SCANOUT flag +} + +static const struct zwp_linux_dmabuf_feedback_v1_listener + linux_dmabuf_feedback_v1_listener = { + .done = linux_dmabuf_feedback_v1_handle_done, + .format_table = linux_dmabuf_feedback_v1_handle_format_table, + .main_device = linux_dmabuf_feedback_v1_handle_main_device, + .tranche_done = linux_dmabuf_feedback_v1_handle_tranche_done, + .tranche_target_device = linux_dmabuf_feedback_v1_handle_tranche_target_device, + .tranche_formats = linux_dmabuf_feedback_v1_handle_tranche_formats, + .tranche_flags = linux_dmabuf_feedback_v1_handle_tranche_flags, +}; + static bool device_has_name(const drmDevice *device, const char *name) { for (size_t i = 0; i < DRM_NODE_MAX; i++) { if (!(device->available_nodes & (1 << i))) { @@ -172,8 +302,6 @@ static char *get_render_name(const char *name) { static void legacy_drm_handle_device(void *data, struct wl_drm *drm, const char *name) { struct wlr_wl_backend *wl = data; - - // TODO: get FD from linux-dmabuf hints instead wl->drm_render_name = get_render_name(name); } @@ -245,7 +373,7 @@ static void registry_global(void *data, struct wl_registry *registry, } 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_interface, version >= 4 ? 4 : version); zwp_linux_dmabuf_v1_add_listener(wl->zwp_linux_dmabuf_v1, &linux_dmabuf_v1_listener, wl); } else if (strcmp(iface, zwp_relative_pointer_manager_v1_interface.name) == 0) { @@ -428,10 +556,9 @@ struct wlr_backend *wlr_wl_backend_create(struct wl_display *display, wlr_log_errno(WLR_ERROR, "Could not obtain reference to remote registry"); goto error_display; } - wl_registry_add_listener(wl->registry, ®istry_listener, wl); + wl_display_roundtrip(wl->remote_display); // get globals - wl_display_roundtrip(wl->remote_display); // get linux-dmabuf formats if (!wl->compositor) { wlr_log(WLR_ERROR, @@ -444,6 +571,35 @@ struct wlr_backend *wlr_wl_backend_create(struct wl_display *display, goto error_registry; } + struct zwp_linux_dmabuf_feedback_v1 *linux_dmabuf_feedback_v1 = NULL; + struct wlr_wl_linux_dmabuf_feedback_v1 feedback_data = { .backend = wl }; + if (wl->zwp_linux_dmabuf_v1 != NULL && + zwp_linux_dmabuf_v1_get_version(wl->zwp_linux_dmabuf_v1) >= + ZWP_LINUX_DMABUF_V1_GET_DEFAULT_FEEDBACK_SINCE_VERSION) { + linux_dmabuf_feedback_v1 = + zwp_linux_dmabuf_v1_get_default_feedback(wl->zwp_linux_dmabuf_v1); + if (linux_dmabuf_feedback_v1 == NULL) { + wlr_log(WLR_ERROR, "Allocation failed"); + goto error_registry; + } + zwp_linux_dmabuf_feedback_v1_add_listener(linux_dmabuf_feedback_v1, + &linux_dmabuf_feedback_v1_listener, &feedback_data); + + if (wl->legacy_drm != NULL) { + wl_drm_destroy(wl->legacy_drm); + wl->legacy_drm = NULL; + } + } + + wl_display_roundtrip(wl->remote_display); // get linux-dmabuf formats + + if (feedback_data.format_table != NULL) { + munmap(feedback_data.format_table, feedback_data.format_table_size); + } + if (linux_dmabuf_feedback_v1 != NULL) { + zwp_linux_dmabuf_feedback_v1_destroy(linux_dmabuf_feedback_v1); + } + struct wl_event_loop *loop = wl_display_get_event_loop(wl->local_display); int fd = wl_display_get_fd(wl->remote_display); wl->remote_display_src = wl_event_loop_add_fd(loop, fd, WL_EVENT_READABLE, diff --git a/meson.build b/meson.build index 67fe86fd4a..681b07cc49 100644 --- a/meson.build +++ b/meson.build @@ -105,7 +105,7 @@ wayland_server = dependency('wayland-server', ) drm = dependency('libdrm', - version: '>=2.4.108', + version: '>=2.4.109', fallback: ['libdrm', 'ext_libdrm'], default_options: [ 'libkms=false', From 7360810f2e5c71fe143b25b63517ab1a6475ce0a Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Fri, 17 Dec 2021 11:54:06 +0100 Subject: [PATCH 128/188] build: bump to version 0.16.0 --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 681b07cc49..a12cd19bff 100644 --- a/meson.build +++ b/meson.build @@ -1,7 +1,7 @@ project( 'wlroots', 'c', - version: '0.15.0', + version: '0.16.0', license: 'MIT', meson_version: '>=0.58.1', default_options: [ From 92d137c78ac05b565bfa06b5659aea52992e0dd7 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Wed, 15 Dec 2021 16:30:32 +0100 Subject: [PATCH 129/188] layer-shell: fix type of margins These currently use uint32_t while they are an int32_t in the protocol. --- include/wlr/types/wlr_layer_shell_v1.h | 2 +- types/wlr_layer_shell_v1.c | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/wlr/types/wlr_layer_shell_v1.h b/include/wlr/types/wlr_layer_shell_v1.h index 694fb3b29d..79e435a00b 100644 --- a/include/wlr/types/wlr_layer_shell_v1.h +++ b/include/wlr/types/wlr_layer_shell_v1.h @@ -58,7 +58,7 @@ struct wlr_layer_surface_v1_state { uint32_t anchor; int32_t exclusive_zone; struct { - uint32_t top, right, bottom, left; + int32_t top, right, bottom, left; } margin; enum zwlr_layer_surface_v1_keyboard_interactivity keyboard_interactive; uint32_t desired_width, desired_height; diff --git a/types/wlr_layer_shell_v1.c b/types/wlr_layer_shell_v1.c index 456dbc0eb5..d7a2123e9a 100644 --- a/types/wlr_layer_shell_v1.c +++ b/types/wlr_layer_shell_v1.c @@ -168,10 +168,10 @@ static void layer_surface_handle_set_margin( return; } - if (surface->current.margin.top == (uint32_t) top - && surface->current.margin.right == (uint32_t) right - && surface->current.margin.bottom == (uint32_t) bottom - && surface->current.margin.left == (uint32_t) left) { + if (surface->current.margin.top == top + && surface->current.margin.right == right + && surface->current.margin.bottom == bottom + && surface->current.margin.left == left) { surface->pending.committed &= ~WLR_LAYER_SURFACE_V1_STATE_MARGIN; } else { surface->pending.committed |= WLR_LAYER_SURFACE_V1_STATE_MARGIN; From 562b24b9fc8e2c2e3de23dfaa32af5e3637c6da0 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Fri, 17 Dec 2021 12:51:54 +0100 Subject: [PATCH 130/188] build: bump soversion According to [1] this should be done at each release with breaking ABI changes. [1]: https://gitlab.freedesktop.org/wlroots/wlroots/-/wikis/Core-contributor-guide#releasing-a-new-version Fixes: 7360810f2e5c ("build: bump to version 0.16.0") --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index a12cd19bff..24aa6f7a15 100644 --- a/meson.build +++ b/meson.build @@ -15,7 +15,7 @@ project( # necessary for bugfix releases. Increasing soversion is required because # wlroots never guarantees ABI stability -- only API stability is guaranteed # between minor releases. -soversion = 10 +soversion = 11 little_endian = host_machine.endian() == 'little' big_endian = host_machine.endian() == 'big' From 93e050c602df91b00454046b12dc7331a555c437 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Fri, 17 Dec 2021 15:37:07 +0100 Subject: [PATCH 131/188] Remove wlr_box.h redirection Compositors should've all been updated to use the new header by now. --- include/wlr/types/wlr_box.h | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 include/wlr/types/wlr_box.h diff --git a/include/wlr/types/wlr_box.h b/include/wlr/types/wlr_box.h deleted file mode 100644 index 8720cef8fc..0000000000 --- a/include/wlr/types/wlr_box.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef WLR_TYPES_WLR_BOX_H -#define WLR_TYPES_WLR_BOX_H - -#warning "wlr_box has been moved to wlr/util/box.h" -#include - -#endif From b5a019d5754064788471e9eba4ee9354c7cc4cd5 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 14 Dec 2021 12:10:31 +0100 Subject: [PATCH 132/188] build: simplify Meson subproject fallbacks All of these projects use meson.override_dependency() so we can stop referencing their internal variable name to grab the depndencies we need. --- backend/session/meson.build | 2 +- backend/wayland/meson.build | 2 +- meson.build | 4 ++-- protocol/meson.build | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/backend/session/meson.build b/backend/session/meson.build index 66c908a2ea..27915506ba 100644 --- a/backend/session/meson.build +++ b/backend/session/meson.build @@ -1,6 +1,6 @@ libseat = dependency('libseat', version: '>=0.2.0', - fallback: ['seatd', 'libseat'], + fallback: 'seatd', default_options: ['server=disabled', 'man-pages=disabled'], ) wlr_files += files('session.c') diff --git a/backend/wayland/meson.build b/backend/wayland/meson.build index 103d3f9fa6..1831bbb309 100644 --- a/backend/wayland/meson.build +++ b/backend/wayland/meson.build @@ -1,5 +1,5 @@ wayland_client = dependency('wayland-client', - fallback: ['wayland', 'wayland_client_dep'], + fallback: 'wayland', default_options: wayland_project_options, ) wlr_deps += wayland_client diff --git a/meson.build b/meson.build index 24aa6f7a15..f7fb659c42 100644 --- a/meson.build +++ b/meson.build @@ -100,13 +100,13 @@ internal_features = { wayland_project_options = ['tests=false', 'documentation=false'] wayland_server = dependency('wayland-server', version: '>=1.20', - fallback: ['wayland', 'wayland_server_dep'], + fallback: 'wayland', default_options: wayland_project_options, ) drm = dependency('libdrm', version: '>=2.4.109', - fallback: ['libdrm', 'ext_libdrm'], + fallback: 'libdrm', default_options: [ 'libkms=false', 'intel=false', diff --git a/protocol/meson.build b/protocol/meson.build index 3e34f78818..ae2b2ef489 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -1,6 +1,6 @@ wayland_protos = dependency('wayland-protocols', version: '>=1.24', - fallback: ['wayland-protocols', 'wayland_protocols'], + fallback: 'wayland-protocols', default_options: ['tests=false'], ) wl_protocol_dir = wayland_protos.get_variable('pkgdatadir') From ec2845750862cc0b175bef59de4305f6da91960a Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 19 Dec 2021 16:39:57 +0100 Subject: [PATCH 133/188] backend: error out in autocreate without libinput support The libinput backend is now optional. However, this means that a user building wlroots without the correct libinput dependencies will end up with a compositor which doesn't respond to input events. wlr_backend_autocreate is supposed to return a sensible setup, so in this case let's just error out and explain what happened. Users can suppress the check by setting WLR_LIBINPUT_NO_DEVICES=1 (already used to suppress the zero input device case inside the libinput backend). Compositors which really want to create a bare DRM backend can easily create it manually instead of using wlr_backend_autocreate. --- backend/backend.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/backend/backend.c b/backend/backend.c index 24150b13dd..bfb43ba07b 100644 --- a/backend/backend.c +++ b/backend/backend.c @@ -364,6 +364,19 @@ struct wlr_backend *wlr_backend_autocreate(struct wl_display *display) { return NULL; } wlr_multi_backend_add(backend, libinput); +#else + const char *no_devs = getenv("WLR_LIBINPUT_NO_DEVICES"); + if (no_devs && strcmp(no_devs, "1") == 0) { + wlr_log(WLR_INFO, "WLR_LIBINPUT_NO_DEVICES is set, " + "starting without libinput backend"); + } else { + wlr_log(WLR_ERROR, "libinput support is not compiled in, " + "refusing to start"); + wlr_log(WLR_ERROR, "Set WLR_LIBINPUT_NO_DEVICES=1 to suppress this check"); + wlr_session_destroy(multi->session); + wlr_backend_destroy(backend); + return NULL; + } #endif #if WLR_HAS_DRM_BACKEND From 823476e76ed166762095330a8f51eabc825febff Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Mon, 20 Dec 2021 20:41:44 +0000 Subject: [PATCH 134/188] wlr_texture: remove wlr_texture_from_wl_drm() from header This function was already removed in e5b5592a but it was forgotten to remove it from the header. --- include/wlr/render/wlr_texture.h | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/include/wlr/render/wlr_texture.h b/include/wlr/render/wlr_texture.h index 3495c4439b..1dbcba1dec 100644 --- a/include/wlr/render/wlr_texture.h +++ b/include/wlr/render/wlr_texture.h @@ -33,16 +33,6 @@ struct wlr_texture *wlr_texture_from_pixels(struct wlr_renderer *renderer, uint32_t fmt, uint32_t stride, uint32_t width, uint32_t height, const void *data); -/** - * Create a new texture from a wl_drm resource. The returned texture is - * immutable. - * - * Should not be called in a rendering block like renderer_begin()/end() or - * between attaching a renderer to an output and committing it. - */ -struct wlr_texture *wlr_texture_from_wl_drm(struct wlr_renderer *renderer, - struct wl_resource *data); - /** * Create a new texture from a DMA-BUF. The returned texture is immutable. * From 812951f5bc47f502429406e49f4e24f377b7799b Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 23 Dec 2021 16:30:24 +0100 Subject: [PATCH 135/188] scene: schedule an output frame on wl_surface.frame Some clients (e.g. mpv, Firefox) request a new wl_surface.frame callback without damaging their surface. When this happens, schedule a new output frame. Closes: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3350 --- types/scene/wlr_scene.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 7b55096389..a0c06b6bfc 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -236,10 +236,6 @@ static void scene_surface_handle_surface_commit(struct wl_listener *listener, wl_container_of(listener, scene_surface, surface_commit); struct wlr_surface *surface = scene_surface->surface; - if (!pixman_region32_not_empty(&surface->buffer_damage)) { - return; - } - struct wlr_scene *scene = scene_node_get_root(&scene_surface->node); int lx, ly; @@ -256,6 +252,17 @@ static void scene_surface_handle_surface_commit(struct wl_listener *listener, return; } + // Even if the surface hasn't submitted damage, schedule a new frame if + // the client has requested a wl_surface.frame callback. + if (!wl_list_empty(&surface->current.frame_callback_list) && + scene_surface->primary_output != NULL) { + wlr_output_schedule_frame(scene_surface->primary_output); + } + + if (!pixman_region32_not_empty(&surface->buffer_damage)) { + return; + } + struct wlr_scene_output *scene_output; wl_list_for_each(scene_output, &scene->outputs, link) { struct wlr_output *output = scene_output->output; From 9988eb3378dbc3301059aa9b5e1ff476354cb92b Mon Sep 17 00:00:00 2001 From: nyorain Date: Sun, 26 Dec 2021 13:21:54 +0100 Subject: [PATCH 136/188] vulkan: Fix imported image layout --- render/vulkan/renderer.c | 2 +- render/vulkan/texture.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/render/vulkan/renderer.c b/render/vulkan/renderer.c index a1d8d41efb..21b36bdd6f 100644 --- a/render/vulkan/renderer.c +++ b/render/vulkan/renderer.c @@ -598,7 +598,7 @@ static void vulkan_end(struct wlr_renderer *wlr_renderer) { wl_list_for_each_safe(texture, tmp_tex, &renderer->foreign_textures, foreign_link) { VkImageLayout src_layout = VK_IMAGE_LAYOUT_GENERAL; if (!texture->transitioned) { - src_layout = VK_IMAGE_LAYOUT_PREINITIALIZED; + src_layout = VK_IMAGE_LAYOUT_UNDEFINED; texture->transitioned = true; } diff --git a/render/vulkan/texture.c b/render/vulkan/texture.c index 76c3701136..b705603cfb 100644 --- a/render/vulkan/texture.c +++ b/render/vulkan/texture.c @@ -438,7 +438,7 @@ VkImage vulkan_import_dmabuf(struct wlr_vk_renderer *renderer, img_info.arrayLayers = 1; img_info.samples = VK_SAMPLE_COUNT_1_BIT; img_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - img_info.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED; + img_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; img_info.extent = (VkExtent3D) { attribs->width, attribs->height, 1 }; img_info.usage = for_render ? VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT : From 59b9518f072527ac59593e51df7f5d5331a34f0e Mon Sep 17 00:00:00 2001 From: Thomas Hebb Date: Wed, 5 Jan 2022 00:16:59 -0800 Subject: [PATCH 137/188] render/gles2: don't constrain shm formats to ones that support reading commit 44e8451cd93e ("render/gles2: hide shm formats without GL support") added the is_gles2_pixel_format_supported() function to render/gles2/pixel_format.c, whose stated purpose is to "check whether the renderer has the needed GL extensions to read a given pixel format." It then used that function to filter the pixel formats returned by get_gles2_shm_formats(). The result of this change is that RGB formats are no longer reported for GL drivers that don't implement EXT_read_format_bgra, even when those formats are supported for rendering (which they have to be for wlr_gles2_renderer_create() to succeed). This is a pretty clear regression, since wlr_renderer_init_wl_shm() fails when either of WL_SHM_FORMAT_ARGB8888 or WL_SHM_FORMAT_XRGB8888 are missing. To fix the regression, change is_gles2_pixel_format_supported() to accept all pixel formats that support rendering, regardless of whether we can read them or not, and move the check for EXT_read_format_bgra back into gles2_read_pixels(). (There's already a check for this extension in gles2_preferred_read_format(), so we're not breaking any abstraction that wasn't already broken.) Tested on the NVIDIA 495.46 proprietary driver, which doesn't support EXT_read_format_bgra. Fixes: 44e8451cd93e ("render/gles2: hide shm formats without GL support") --- render/gles2/pixel_format.c | 14 ++++++++++---- render/gles2/renderer.c | 6 ++++++ 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/render/gles2/pixel_format.c b/render/gles2/pixel_format.c index 31bb3908e9..b155bbbe91 100644 --- a/render/gles2/pixel_format.c +++ b/render/gles2/pixel_format.c @@ -98,6 +98,10 @@ static const struct wlr_gles2_pixel_format formats[] = { // TODO: more pixel formats +/* + * Return true if supported for texturing, even if other operations like + * reading aren't supported. + */ bool is_gles2_pixel_format_supported(const struct wlr_gles2_renderer *renderer, const struct wlr_gles2_pixel_format *format) { if (format->gl_type == GL_UNSIGNED_INT_2_10_10_10_REV_EXT @@ -108,10 +112,12 @@ bool is_gles2_pixel_format_supported(const struct wlr_gles2_renderer *renderer, && !renderer->exts.OES_texture_half_float_linear) { return false; } - if (format->gl_format == GL_BGRA_EXT - && !renderer->exts.EXT_read_format_bgra) { - return false; - } + /* + * Note that we don't need to check for GL_EXT_texture_format_BGRA8888 + * here, since we've already checked if we have it at renderer creation + * time and bailed out if not. We do the check there because Wayland + * requires all compositors to support SHM buffers in that format. + */ return true; } diff --git a/render/gles2/renderer.c b/render/gles2/renderer.c index 527d85bfaa..67b8ead475 100644 --- a/render/gles2/renderer.c +++ b/render/gles2/renderer.c @@ -441,6 +441,12 @@ static bool gles2_read_pixels(struct wlr_renderer *wlr_renderer, return false; } + if (fmt->gl_format == GL_BGRA_EXT && !renderer->exts.EXT_read_format_bgra) { + wlr_log(WLR_ERROR, + "Cannot read pixels: missing GL_EXT_read_format_bgra extension"); + return false; + } + const struct wlr_pixel_format_info *drm_fmt = drm_get_pixel_format_info(fmt->drm_format); assert(drm_fmt); From 83ab5055fd36bd0f8a0106257e45d8ed303636d8 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Sat, 1 Jan 2022 14:04:53 +0300 Subject: [PATCH 138/188] scene/subsurface_tree: fix handling subsurface destruction This commit renames map/unmap listeners to clarify that they handle subsurface events, and ensures the node is always destroyed before the subsurface. Without this patch, wl_list_remove() would operate on listener links in already freed memory. glibc is usually lenient to bugs like this, but musl isn't. --- types/scene/subsurface_tree.c | 65 +++++++++++++++++++++-------------- 1 file changed, 39 insertions(+), 26 deletions(-) diff --git a/types/scene/subsurface_tree.c b/types/scene/subsurface_tree.c index 3f22ee3d60..bb3c7ff7a0 100644 --- a/types/scene/subsurface_tree.c +++ b/types/scene/subsurface_tree.c @@ -13,15 +13,20 @@ struct wlr_scene_subsurface_tree { struct wlr_surface *surface; struct wlr_scene_surface *scene_surface; - struct wlr_scene_subsurface_tree *parent; // NULL for the top-level surface - struct wlr_addon surface_addon; // only set if there's a parent - struct wl_listener tree_destroy; struct wl_listener surface_destroy; struct wl_listener surface_commit; - struct wl_listener surface_map; - struct wl_listener surface_unmap; struct wl_listener surface_new_subsurface; + + struct wlr_scene_subsurface_tree *parent; // NULL for the top-level surface + + // Only valid if the surface is a sub-surface + + struct wlr_addon surface_addon; + + struct wl_listener subsurface_destroy; + struct wl_listener subsurface_map; + struct wl_listener subsurface_unmap; }; static void subsurface_tree_handle_tree_destroy(struct wl_listener *listener, @@ -31,23 +36,17 @@ static void subsurface_tree_handle_tree_destroy(struct wl_listener *listener, // tree and scene_surface will be cleaned up by scene_node_finish if (subsurface_tree->parent) { wlr_addon_finish(&subsurface_tree->surface_addon); + wl_list_remove(&subsurface_tree->subsurface_destroy.link); + wl_list_remove(&subsurface_tree->subsurface_map.link); + wl_list_remove(&subsurface_tree->subsurface_unmap.link); } wl_list_remove(&subsurface_tree->tree_destroy.link); wl_list_remove(&subsurface_tree->surface_destroy.link); wl_list_remove(&subsurface_tree->surface_commit.link); - wl_list_remove(&subsurface_tree->surface_map.link); - wl_list_remove(&subsurface_tree->surface_unmap.link); wl_list_remove(&subsurface_tree->surface_new_subsurface.link); free(subsurface_tree); } -static void subsurface_tree_handle_surface_destroy(struct wl_listener *listener, - void *data) { - struct wlr_scene_subsurface_tree *subsurface_tree = - wl_container_of(listener, subsurface_tree, surface_destroy); - wlr_scene_node_destroy(&subsurface_tree->tree->node); -} - static const struct wlr_addon_interface subsurface_tree_addon_impl; static struct wlr_scene_subsurface_tree *subsurface_tree_from_subsurface( @@ -97,6 +96,13 @@ static void subsurface_tree_reconfigure( } } +static void subsurface_tree_handle_surface_destroy(struct wl_listener *listener, + void *data) { + struct wlr_scene_subsurface_tree *subsurface_tree = + wl_container_of(listener, subsurface_tree, surface_destroy); + wlr_scene_node_destroy(&subsurface_tree->tree->node); +} + static void subsurface_tree_handle_surface_commit(struct wl_listener *listener, void *data) { struct wlr_scene_subsurface_tree *subsurface_tree = @@ -106,18 +112,25 @@ static void subsurface_tree_handle_surface_commit(struct wl_listener *listener, subsurface_tree_reconfigure(subsurface_tree); } -static void subsurface_tree_handle_surface_map(struct wl_listener *listener, +static void subsurface_tree_handle_subsurface_destroy(struct wl_listener *listener, void *data) { struct wlr_scene_subsurface_tree *subsurface_tree = - wl_container_of(listener, subsurface_tree, surface_map); + wl_container_of(listener, subsurface_tree, subsurface_destroy); + wlr_scene_node_destroy(&subsurface_tree->tree->node); +} + +static void subsurface_tree_handle_subsurface_map(struct wl_listener *listener, + void *data) { + struct wlr_scene_subsurface_tree *subsurface_tree = + wl_container_of(listener, subsurface_tree, subsurface_map); wlr_scene_node_set_enabled(&subsurface_tree->tree->node, true); } -static void subsurface_tree_handle_surface_unmap(struct wl_listener *listener, +static void subsurface_tree_handle_subsurface_unmap(struct wl_listener *listener, void *data) { struct wlr_scene_subsurface_tree *subsurface_tree = - wl_container_of(listener, subsurface_tree, surface_unmap); + wl_container_of(listener, subsurface_tree, subsurface_unmap); wlr_scene_node_set_enabled(&subsurface_tree->tree->node, false); } @@ -151,8 +164,14 @@ static bool subsurface_tree_create_subsurface( wlr_addon_init(&child->surface_addon, &subsurface->surface->addons, parent, &subsurface_tree_addon_impl); - wl_signal_add(&subsurface->events.map, &child->surface_map); - wl_signal_add(&subsurface->events.unmap, &child->surface_unmap); + child->subsurface_destroy.notify = subsurface_tree_handle_subsurface_destroy; + wl_signal_add(&subsurface->events.destroy, &child->subsurface_destroy); + + child->subsurface_map.notify = subsurface_tree_handle_subsurface_map; + wl_signal_add(&subsurface->events.map, &child->subsurface_map); + + child->subsurface_unmap.notify = subsurface_tree_handle_subsurface_unmap; + wl_signal_add(&subsurface->events.unmap, &child->subsurface_unmap); return true; } @@ -214,12 +233,6 @@ static struct wlr_scene_subsurface_tree *scene_surface_tree_create( subsurface_tree->surface_commit.notify = subsurface_tree_handle_surface_commit; wl_signal_add(&surface->events.commit, &subsurface_tree->surface_commit); - subsurface_tree->surface_map.notify = subsurface_tree_handle_surface_map; - wl_list_init(&subsurface_tree->surface_map.link); - - subsurface_tree->surface_unmap.notify = subsurface_tree_handle_surface_unmap; - wl_list_init(&subsurface_tree->surface_unmap.link); - subsurface_tree->surface_new_subsurface.notify = subsurface_tree_handle_surface_new_subsurface; wl_signal_add(&surface->events.new_subsurface, From 6cdf843a8cef420255e0a55c842530184abb3fe4 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 9 Jan 2022 12:01:58 +0100 Subject: [PATCH 139/188] readme: drop mention of the Sway project wlroots has historically been started as a Sway project, but these days many wlroots contributors are working on other compositors. wlroots now also has its own namespace on gitlab.freedesktop.org. Let's remove the mention about Sway in the README, to make it clearer that Sway isn't treated in a special manner when it comes to wlroots development. --- README.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 2f9adf71e6..418aab94b1 100644 --- a/README.md +++ b/README.md @@ -32,9 +32,8 @@ to implement yourself. Check out our [wiki] to get started with wlroots. Join our IRC channel: [#sway-devel on Libera Chat]. -wlroots is developed under the direction of the [sway] project. A variety of -[wrapper libraries] are available for using it with your favorite programming -language. +A variety of [wrapper libraries] are available for using it with your favorite +programming language. ## Building @@ -77,7 +76,6 @@ See [CONTRIBUTING.md]. [Wayland]: https://wayland.freedesktop.org/ [wiki]: https://gitlab.freedesktop.org/wlroots/wlroots/-/wikis/Getting-started [#sway-devel on Libera Chat]: https://web.libera.chat/gamja/?channels=#sway-devel -[Sway]: https://github.com/swaywm/sway [wrapper libraries]: https://gitlab.freedesktop.org/wlroots/wlroots/-/wikis/Projects-which-use-wlroots#wrapper-libraries [libseat]: https://git.sr.ht/~kennylevinsen/seatd [CONTRIBUTING.md]: https://gitlab.freedesktop.org/wlroots/wlroots/-/blob/master/CONTRIBUTING.md From b6f43ab2e16931c5604b7e570edf481b3392fd25 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Thu, 13 Jan 2022 11:55:09 +0300 Subject: [PATCH 140/188] subcompositor: split out from compositor --- include/wlr/types/wlr_compositor.h | 15 ---- include/wlr/types/wlr_subcompositor.h | 37 ++++++++ tinywl/tinywl.c | 5 +- types/meson.build | 1 + types/wlr_compositor.c | 112 ++---------------------- types/wlr_subcompositor.c | 120 ++++++++++++++++++++++++++ types/wlr_surface.c | 1 + 7 files changed, 168 insertions(+), 123 deletions(-) create mode 100644 include/wlr/types/wlr_subcompositor.h create mode 100644 types/wlr_subcompositor.c diff --git a/include/wlr/types/wlr_compositor.h b/include/wlr/types/wlr_compositor.h index 04391c741e..756f6f30dd 100644 --- a/include/wlr/types/wlr_compositor.h +++ b/include/wlr/types/wlr_compositor.h @@ -14,16 +14,10 @@ struct wlr_surface; -struct wlr_subcompositor { - struct wl_global *global; -}; - struct wlr_compositor { struct wl_global *global; struct wlr_renderer *renderer; - struct wlr_subcompositor subcompositor; - struct wl_listener display_destroy; struct { @@ -35,13 +29,4 @@ struct wlr_compositor { struct wlr_compositor *wlr_compositor_create(struct wl_display *display, struct wlr_renderer *renderer); -bool wlr_surface_is_subsurface(struct wlr_surface *surface); - -/** - * Get a subsurface from a surface. Can return NULL if the subsurface has been - * destroyed. - */ -struct wlr_subsurface *wlr_subsurface_from_wlr_surface( - struct wlr_surface *surface); - #endif diff --git a/include/wlr/types/wlr_subcompositor.h b/include/wlr/types/wlr_subcompositor.h new file mode 100644 index 0000000000..f9c887bb7d --- /dev/null +++ b/include/wlr/types/wlr_subcompositor.h @@ -0,0 +1,37 @@ +/* + * This an unstable interface of wlroots. No guarantees are made regarding the + * future consistency of this API. + */ +#ifndef WLR_USE_UNSTABLE +#error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" +#endif + +#ifndef WLR_TYPES_WLR_SUBCOMPOSITOR_H +#define WLR_TYPES_WLR_SUBCOMPOSITOR_H + +#include + +struct wlr_surface; + +struct wlr_subcompositor { + struct wl_global *global; + + struct wl_listener display_destroy; + + struct { + struct wl_signal destroy; + } events; +}; + +bool wlr_surface_is_subsurface(struct wlr_surface *surface); + +/** + * Get a subsurface from a surface. Can return NULL if the subsurface has been + * destroyed. + */ +struct wlr_subsurface *wlr_subsurface_from_wlr_surface( + struct wlr_surface *surface); + +struct wlr_subcompositor *wlr_subcompositor_create(struct wl_display *display); + +#endif diff --git a/tinywl/tinywl.c b/tinywl/tinywl.c index faa0a9a130..722abd10c6 100644 --- a/tinywl/tinywl.c +++ b/tinywl/tinywl.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -756,12 +757,14 @@ int main(int argc, char *argv[]) { server.renderer); /* This creates some hands-off wlroots interfaces. The compositor is - * necessary for clients to allocate surfaces and the data device manager + * necessary for clients to allocate surfaces, the subcompositor allows to + * assign the role of subsurfaces to surfaces and the data device manager * handles the clipboard. Each of these wlroots interfaces has room for you * to dig your fingers in and play with their behavior if you want. Note that * the clients cannot set the selection directly without compositor approval, * see the handling of the request_set_selection event below.*/ wlr_compositor_create(server.wl_display, server.renderer); + wlr_subcompositor_create(server.wl_display); wlr_data_device_manager_create(server.wl_display); /* Creates an output layout, which a wlroots utility for working with an diff --git a/types/meson.build b/types/meson.build index 1694ac94a5..8aa93a4acc 100644 --- a/types/meson.build +++ b/types/meson.build @@ -58,6 +58,7 @@ wlr_files += files( 'wlr_relative_pointer_v1.c', 'wlr_screencopy_v1.c', 'wlr_server_decoration.c', + 'wlr_subcompositor.c', 'wlr_surface.c', 'wlr_switch.c', 'wlr_tablet_pad.c', diff --git a/types/wlr_compositor.c b/types/wlr_compositor.c index 7ad684543d..65549627d2 100644 --- a/types/wlr_compositor.c +++ b/types/wlr_compositor.c @@ -3,108 +3,11 @@ #include #include #include -#include #include "types/wlr_region.h" #include "types/wlr_surface.h" #include "util/signal.h" #define COMPOSITOR_VERSION 4 -#define SUBCOMPOSITOR_VERSION 1 - -extern const struct wlr_surface_role subsurface_role; - -bool wlr_surface_is_subsurface(struct wlr_surface *surface) { - return surface->role == &subsurface_role; -} - -struct wlr_subsurface *wlr_subsurface_from_wlr_surface( - struct wlr_surface *surface) { - assert(wlr_surface_is_subsurface(surface)); - return (struct wlr_subsurface *)surface->role_data; -} - -static void subcompositor_handle_destroy(struct wl_client *client, - struct wl_resource *resource) { - wl_resource_destroy(resource); -} - -static void subcompositor_handle_get_subsurface(struct wl_client *client, - struct wl_resource *resource, uint32_t id, - struct wl_resource *surface_resource, - struct wl_resource *parent_resource) { - struct wlr_surface *surface = wlr_surface_from_resource(surface_resource); - struct wlr_surface *parent = wlr_surface_from_resource(parent_resource); - - static const char msg[] = "get_subsurface: wl_subsurface@"; - - if (surface == parent) { - wl_resource_post_error(resource, - WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE, - "%s%" PRIu32 ": wl_surface@%" PRIu32 " cannot be its own parent", - msg, id, wl_resource_get_id(surface_resource)); - return; - } - - if (wlr_surface_is_subsurface(surface) && - wlr_subsurface_from_wlr_surface(surface) != NULL) { - wl_resource_post_error(resource, - WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE, - "%s%" PRIu32 ": wl_surface@%" PRIu32 " is already a sub-surface", - msg, id, wl_resource_get_id(surface_resource)); - return; - } - - if (wlr_surface_get_root_surface(parent) == surface) { - wl_resource_post_error(resource, - WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE, - "%s%" PRIu32 ": wl_surface@%" PRIu32 " is an ancestor of parent", - msg, id, wl_resource_get_id(surface_resource)); - return; - } - - if (!wlr_surface_set_role(surface, &subsurface_role, NULL, - resource, WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE)) { - return; - } - - subsurface_create(surface, parent, wl_resource_get_version(resource), id); -} - -static const struct wl_subcompositor_interface subcompositor_impl = { - .destroy = subcompositor_handle_destroy, - .get_subsurface = subcompositor_handle_get_subsurface, -}; - -static void subcompositor_bind(struct wl_client *client, void *data, - uint32_t version, uint32_t id) { - struct wlr_subcompositor *subcompositor = data; - struct wl_resource *resource = - wl_resource_create(client, &wl_subcompositor_interface, 1, id); - if (resource == NULL) { - wl_client_post_no_memory(client); - return; - } - wl_resource_set_implementation(resource, &subcompositor_impl, - subcompositor, NULL); -} - -static bool subcompositor_init(struct wlr_subcompositor *subcompositor, - struct wl_display *display) { - subcompositor->global = wl_global_create(display, - &wl_subcompositor_interface, SUBCOMPOSITOR_VERSION, subcompositor, - subcompositor_bind); - if (subcompositor->global == NULL) { - wlr_log_errno(WLR_ERROR, "Could not allocate subcompositor global"); - return false; - } - - return true; -} - -static void subcompositor_finish(struct wlr_subcompositor *subcompositor) { - wl_global_destroy(subcompositor->global); -} - static const struct wl_compositor_interface compositor_impl; @@ -152,11 +55,11 @@ static void compositor_bind(struct wl_client *wl_client, void *data, wl_resource_set_implementation(resource, &compositor_impl, compositor, NULL); } -static void handle_display_destroy(struct wl_listener *listener, void *data) { +static void compositor_handle_display_destroy( + struct wl_listener *listener, void *data) { struct wlr_compositor *compositor = wl_container_of(listener, compositor, display_destroy); - wlr_signal_emit_safe(&compositor->events.destroy, compositor); - subcompositor_finish(&compositor->subcompositor); + wlr_signal_emit_safe(&compositor->events.destroy, NULL); wl_list_remove(&compositor->display_destroy.link); wl_global_destroy(compositor->global); free(compositor); @@ -164,10 +67,8 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_compositor *wlr_compositor_create(struct wl_display *display, struct wlr_renderer *renderer) { - struct wlr_compositor *compositor = - calloc(1, sizeof(struct wlr_compositor)); + struct wlr_compositor *compositor = calloc(1, sizeof(*compositor)); if (!compositor) { - wlr_log_errno(WLR_ERROR, "Could not allocate wlr compositor"); return NULL; } @@ -175,7 +76,6 @@ struct wlr_compositor *wlr_compositor_create(struct wl_display *display, COMPOSITOR_VERSION, compositor, compositor_bind); if (!compositor->global) { free(compositor); - wlr_log_errno(WLR_ERROR, "Could not allocate compositor global"); return NULL; } compositor->renderer = renderer; @@ -183,9 +83,7 @@ struct wlr_compositor *wlr_compositor_create(struct wl_display *display, wl_signal_init(&compositor->events.new_surface); wl_signal_init(&compositor->events.destroy); - subcompositor_init(&compositor->subcompositor, display); - - compositor->display_destroy.notify = handle_display_destroy; + compositor->display_destroy.notify = compositor_handle_display_destroy; wl_display_add_destroy_listener(display, &compositor->display_destroy); return compositor; diff --git a/types/wlr_subcompositor.c b/types/wlr_subcompositor.c new file mode 100644 index 0000000000..f5a6248652 --- /dev/null +++ b/types/wlr_subcompositor.c @@ -0,0 +1,120 @@ +#include +#include +#include +#include +#include +#include "types/wlr_region.h" +#include "types/wlr_surface.h" +#include "util/signal.h" + +#define SUBCOMPOSITOR_VERSION 1 + +extern const struct wlr_surface_role subsurface_role; + +bool wlr_surface_is_subsurface(struct wlr_surface *surface) { + return surface->role == &subsurface_role; +} + +struct wlr_subsurface *wlr_subsurface_from_wlr_surface( + struct wlr_surface *surface) { + assert(wlr_surface_is_subsurface(surface)); + return (struct wlr_subsurface *)surface->role_data; +} + +static void subcompositor_handle_destroy(struct wl_client *client, + struct wl_resource *resource) { + wl_resource_destroy(resource); +} + +static void subcompositor_handle_get_subsurface(struct wl_client *client, + struct wl_resource *resource, uint32_t id, + struct wl_resource *surface_resource, + struct wl_resource *parent_resource) { + struct wlr_surface *surface = wlr_surface_from_resource(surface_resource); + struct wlr_surface *parent = wlr_surface_from_resource(parent_resource); + + static const char msg[] = "get_subsurface: wl_subsurface@"; + + if (surface == parent) { + wl_resource_post_error(resource, + WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE, + "%s%" PRIu32 ": wl_surface@%" PRIu32 " cannot be its own parent", + msg, id, wl_resource_get_id(surface_resource)); + return; + } + + if (wlr_surface_is_subsurface(surface) && + wlr_subsurface_from_wlr_surface(surface) != NULL) { + wl_resource_post_error(resource, + WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE, + "%s%" PRIu32 ": wl_surface@%" PRIu32 " is already a sub-surface", + msg, id, wl_resource_get_id(surface_resource)); + return; + } + + if (wlr_surface_get_root_surface(parent) == surface) { + wl_resource_post_error(resource, + WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE, + "%s%" PRIu32 ": wl_surface@%" PRIu32 " is an ancestor of parent", + msg, id, wl_resource_get_id(surface_resource)); + return; + } + + if (!wlr_surface_set_role(surface, &subsurface_role, NULL, + resource, WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE)) { + return; + } + + subsurface_create(surface, parent, wl_resource_get_version(resource), id); +} + +static const struct wl_subcompositor_interface subcompositor_impl = { + .destroy = subcompositor_handle_destroy, + .get_subsurface = subcompositor_handle_get_subsurface, +}; + +static void subcompositor_bind(struct wl_client *client, void *data, + uint32_t version, uint32_t id) { + struct wlr_subcompositor *subcompositor = data; + struct wl_resource *resource = + wl_resource_create(client, &wl_subcompositor_interface, 1, id); + if (resource == NULL) { + wl_client_post_no_memory(client); + return; + } + wl_resource_set_implementation(resource, &subcompositor_impl, + subcompositor, NULL); +} + +static void subcompositor_handle_display_destroy( + struct wl_listener *listener, void *data) { + struct wlr_subcompositor *subcompositor = + wl_container_of(listener, subcompositor, display_destroy); + wlr_signal_emit_safe(&subcompositor->events.destroy, NULL); + wl_list_remove(&subcompositor->display_destroy.link); + wl_global_destroy(subcompositor->global); + free(subcompositor); +} + +struct wlr_subcompositor *wlr_subcompositor_create(struct wl_display *display) { + struct wlr_subcompositor *subcompositor = + calloc(1, sizeof(*subcompositor)); + if (!subcompositor) { + return NULL; + } + + subcompositor->global = wl_global_create(display, + &wl_subcompositor_interface, SUBCOMPOSITOR_VERSION, + subcompositor, subcompositor_bind); + if (!subcompositor->global) { + free(subcompositor); + return NULL; + } + + wl_signal_init(&subcompositor->events.destroy); + + subcompositor->display_destroy.notify = subcompositor_handle_display_destroy; + wl_display_add_destroy_listener(display, &subcompositor->display_destroy); + + return subcompositor; +} diff --git a/types/wlr_surface.c b/types/wlr_surface.c index d5d4957149..fc4a509c31 100644 --- a/types/wlr_surface.c +++ b/types/wlr_surface.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include From 4ec683ad1c62fe47e0a3bc238446ae8e79f08246 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Thu, 13 Jan 2022 11:55:09 +0300 Subject: [PATCH 141/188] surface: introduce events.client_commit wlr_surface.events.client_commit is fired when wl_surface.commit request is received. --- include/wlr/types/wlr_surface.h | 2 ++ types/wlr_surface.c | 46 ++++++++++++++++++--------------- 2 files changed, 27 insertions(+), 21 deletions(-) diff --git a/include/wlr/types/wlr_surface.h b/include/wlr/types/wlr_surface.h index 59168eaa36..ca1a55ac08 100644 --- a/include/wlr/types/wlr_surface.h +++ b/include/wlr/types/wlr_surface.h @@ -140,6 +140,7 @@ struct wlr_surface { void *role_data; // role-specific data struct { + struct wl_signal client_commit; struct wl_signal commit; struct wl_signal new_subsurface; struct wl_signal destroy; @@ -188,6 +189,7 @@ struct wlr_subsurface { bool added; struct wl_listener surface_destroy; + struct wl_listener surface_client_commit; struct wl_listener parent_destroy; struct { diff --git a/types/wlr_surface.c b/types/wlr_surface.c index fc4a509c31..1b49a21b81 100644 --- a/types/wlr_surface.c +++ b/types/wlr_surface.c @@ -520,32 +520,13 @@ static void subsurface_parent_commit(struct wlr_subsurface *subsurface) { } } -static void subsurface_commit(struct wlr_subsurface *subsurface) { - struct wlr_surface *surface = subsurface->surface; - - if (subsurface_is_synchronized(subsurface)) { - if (subsurface->has_cache) { - // We already lock a previous commit. The prevents any future - // commit to be applied before we release the previous commit. - return; - } - subsurface->has_cache = true; - subsurface->cached_seq = wlr_surface_lock_pending(surface); - } -} - static void surface_handle_commit(struct wl_client *client, struct wl_resource *resource) { struct wlr_surface *surface = wlr_surface_from_resource(resource); - - struct wlr_subsurface *subsurface = wlr_surface_is_subsurface(surface) ? - wlr_subsurface_from_wlr_surface(surface) : NULL; - if (subsurface != NULL) { - subsurface_commit(subsurface); - } - surface_finalize_pending(surface); + wlr_signal_emit_safe(&surface->events.client_commit, NULL); + if (surface->role && surface->role->precommit) { surface->role->precommit(surface); } @@ -668,6 +649,7 @@ static void subsurface_destroy(struct wlr_subsurface *subsurface) { wlr_signal_emit_safe(&subsurface->events.destroy, subsurface); wl_list_remove(&subsurface->surface_destroy.link); + wl_list_remove(&subsurface->surface_client_commit.link); if (subsurface->parent) { wl_list_remove(&subsurface->current.link); @@ -747,6 +729,7 @@ struct wlr_surface *surface_create(struct wl_client *client, surface_state_init(&surface->pending); surface->pending.seq = 1; + wl_signal_init(&surface->events.client_commit); wl_signal_init(&surface->events.commit); wl_signal_init(&surface->events.destroy); wl_signal_init(&surface->events.new_subsurface); @@ -1108,6 +1091,23 @@ static void subsurface_handle_surface_destroy(struct wl_listener *listener, subsurface_destroy(subsurface); } +static void subsurface_handle_surface_client_commit( + struct wl_listener *listener, void *data) { + struct wlr_subsurface *subsurface = + wl_container_of(listener, subsurface, surface_client_commit); + struct wlr_surface *surface = subsurface->surface; + + if (subsurface_is_synchronized(subsurface)) { + if (subsurface->has_cache) { + // We already lock a previous commit. The prevents any future + // commit to be applied before we release the previous commit. + return; + } + subsurface->has_cache = true; + subsurface->cached_seq = wlr_surface_lock_pending(surface); + } +} + struct wlr_subsurface *subsurface_create(struct wlr_surface *surface, struct wlr_surface *parent, uint32_t version, uint32_t id) { struct wl_client *client = wl_resource_get_client(surface->resource); @@ -1136,6 +1136,10 @@ struct wlr_subsurface *subsurface_create(struct wlr_surface *surface, wl_signal_add(&surface->events.destroy, &subsurface->surface_destroy); subsurface->surface_destroy.notify = subsurface_handle_surface_destroy; + wl_signal_add(&surface->events.client_commit, + &subsurface->surface_client_commit); + subsurface->surface_client_commit.notify = + subsurface_handle_surface_client_commit; // link parent subsurface->parent = parent; From 36b5d5888cc48c40857b6d9275917cd57d0dfa0c Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Thu, 13 Jan 2022 11:55:09 +0300 Subject: [PATCH 142/188] surface: move impl to types/wlr_{sub,}compositor.c --- include/types/wlr_surface.h | 20 - types/meson.build | 1 - types/wlr_compositor.c | 1061 +++++++++++++++++++++++++- types/wlr_subcompositor.c | 375 ++++++++- types/wlr_surface.c | 1442 ----------------------------------- 5 files changed, 1433 insertions(+), 1466 deletions(-) delete mode 100644 include/types/wlr_surface.h delete mode 100644 types/wlr_surface.c diff --git a/include/types/wlr_surface.h b/include/types/wlr_surface.h deleted file mode 100644 index b8c7d02d26..0000000000 --- a/include/types/wlr_surface.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef TYPES_WLR_SURFACE_H -#define TYPES_WLR_SURFACE_H - -#include - -struct wlr_renderer; - -/** - * Create a new surface resource with the provided new ID. - */ -struct wlr_surface *surface_create(struct wl_client *client, - uint32_t version, uint32_t id, struct wlr_renderer *renderer); - -/** - * Create a new subsurface resource with the provided new ID. - */ -struct wlr_subsurface *subsurface_create(struct wlr_surface *surface, - struct wlr_surface *parent, uint32_t version, uint32_t id); - -#endif diff --git a/types/meson.build b/types/meson.build index 8aa93a4acc..476bffe82d 100644 --- a/types/meson.build +++ b/types/meson.build @@ -59,7 +59,6 @@ wlr_files += files( 'wlr_screencopy_v1.c', 'wlr_server_decoration.c', 'wlr_subcompositor.c', - 'wlr_surface.c', 'wlr_switch.c', 'wlr_tablet_pad.c', 'wlr_tablet_tool.c', diff --git a/types/wlr_compositor.c b/types/wlr_compositor.c index 65549627d2..4b4b34f995 100644 --- a/types/wlr_compositor.c +++ b/types/wlr_compositor.c @@ -1,13 +1,1072 @@ #include #include #include +#include +#include #include +#include +#include +#include #include +#include +#include +#include #include "types/wlr_region.h" -#include "types/wlr_surface.h" #include "util/signal.h" +#include "util/time.h" #define COMPOSITOR_VERSION 4 +#define CALLBACK_VERSION 1 + +static int min(int fst, int snd) { + if (fst < snd) { + return fst; + } else { + return snd; + } +} + +static int max(int fst, int snd) { + if (fst > snd) { + return fst; + } else { + return snd; + } +} + +static void surface_handle_destroy(struct wl_client *client, + struct wl_resource *resource) { + wl_resource_destroy(resource); +} + +static void surface_handle_attach(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *buffer_resource, int32_t dx, int32_t dy) { + struct wlr_surface *surface = wlr_surface_from_resource(resource); + + struct wlr_buffer *buffer = NULL; + if (buffer_resource != NULL) { + buffer = wlr_buffer_from_resource(buffer_resource); + if (buffer == NULL) { + wl_resource_post_error(buffer_resource, 0, "unknown buffer type"); + return; + } + } + + surface->pending.committed |= WLR_SURFACE_STATE_BUFFER; + surface->pending.dx = dx; + surface->pending.dy = dy; + + wlr_buffer_unlock(surface->pending.buffer); + surface->pending.buffer = buffer; +} + +static void surface_handle_damage(struct wl_client *client, + struct wl_resource *resource, + int32_t x, int32_t y, int32_t width, int32_t height) { + struct wlr_surface *surface = wlr_surface_from_resource(resource); + if (width < 0 || height < 0) { + return; + } + surface->pending.committed |= WLR_SURFACE_STATE_SURFACE_DAMAGE; + pixman_region32_union_rect(&surface->pending.surface_damage, + &surface->pending.surface_damage, + x, y, width, height); +} + +static void callback_handle_resource_destroy(struct wl_resource *resource) { + wl_list_remove(wl_resource_get_link(resource)); +} + +static void surface_handle_frame(struct wl_client *client, + struct wl_resource *resource, uint32_t callback) { + struct wlr_surface *surface = wlr_surface_from_resource(resource); + + struct wl_resource *callback_resource = wl_resource_create(client, + &wl_callback_interface, CALLBACK_VERSION, callback); + if (callback_resource == NULL) { + wl_resource_post_no_memory(resource); + return; + } + wl_resource_set_implementation(callback_resource, NULL, NULL, + callback_handle_resource_destroy); + + wl_list_insert(surface->pending.frame_callback_list.prev, + wl_resource_get_link(callback_resource)); + + surface->pending.committed |= WLR_SURFACE_STATE_FRAME_CALLBACK_LIST; +} + +static void surface_handle_set_opaque_region(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *region_resource) { + struct wlr_surface *surface = wlr_surface_from_resource(resource); + surface->pending.committed |= WLR_SURFACE_STATE_OPAQUE_REGION; + if (region_resource) { + pixman_region32_t *region = wlr_region_from_resource(region_resource); + pixman_region32_copy(&surface->pending.opaque, region); + } else { + pixman_region32_clear(&surface->pending.opaque); + } +} + +static void surface_handle_set_input_region(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *region_resource) { + struct wlr_surface *surface = wlr_surface_from_resource(resource); + surface->pending.committed |= WLR_SURFACE_STATE_INPUT_REGION; + if (region_resource) { + pixman_region32_t *region = wlr_region_from_resource(region_resource); + pixman_region32_copy(&surface->pending.input, region); + } else { + pixman_region32_fini(&surface->pending.input); + pixman_region32_init_rect(&surface->pending.input, + INT32_MIN, INT32_MIN, UINT32_MAX, UINT32_MAX); + } +} + +static void surface_state_transformed_buffer_size(struct wlr_surface_state *state, + int *out_width, int *out_height) { + int width = state->buffer_width; + int height = state->buffer_height; + if ((state->transform & WL_OUTPUT_TRANSFORM_90) != 0) { + int tmp = width; + width = height; + height = tmp; + } + *out_width = width; + *out_height = height; +} + +/** + * Computes the surface viewport source size, ie. the size after applying the + * surface's scale, transform and cropping (via the viewport's source + * rectangle) but before applying the viewport scaling (via the viewport's + * destination rectangle). + */ +static void surface_state_viewport_src_size(struct wlr_surface_state *state, + int *out_width, int *out_height) { + if (state->buffer_width == 0 && state->buffer_height == 0) { + *out_width = *out_height = 0; + return; + } + + if (state->viewport.has_src) { + *out_width = state->viewport.src.width; + *out_height = state->viewport.src.height; + } else { + surface_state_transformed_buffer_size(state, + out_width, out_height); + *out_width /= state->scale; + *out_height /= state->scale; + } +} + +static void surface_finalize_pending(struct wlr_surface *surface) { + struct wlr_surface_state *pending = &surface->pending; + + if ((pending->committed & WLR_SURFACE_STATE_BUFFER)) { + if (pending->buffer != NULL) { + pending->buffer_width = pending->buffer->width; + pending->buffer_height = pending->buffer->height; + } else { + pending->buffer_width = pending->buffer_height = 0; + } + } + + if (!pending->viewport.has_src && + (pending->buffer_width % pending->scale != 0 || + pending->buffer_height % pending->scale != 0)) { + // TODO: send WL_SURFACE_ERROR_INVALID_SIZE error once this issue is + // resolved: + // https://gitlab.freedesktop.org/wayland/wayland/-/issues/194 + wlr_log(WLR_DEBUG, "Client bug: submitted a buffer whose size (%dx%d) " + "is not divisible by scale (%d)", pending->buffer_width, + pending->buffer_height, pending->scale); + } + + if (pending->viewport.has_dst) { + if (pending->buffer_width == 0 && pending->buffer_height == 0) { + pending->width = pending->height = 0; + } else { + pending->width = pending->viewport.dst_width; + pending->height = pending->viewport.dst_height; + } + } else { + surface_state_viewport_src_size(pending, &pending->width, &pending->height); + } + + pixman_region32_intersect_rect(&pending->surface_damage, + &pending->surface_damage, 0, 0, pending->width, pending->height); + + pixman_region32_intersect_rect(&pending->buffer_damage, + &pending->buffer_damage, 0, 0, pending->buffer_width, + pending->buffer_height); +} + +static void surface_update_damage(pixman_region32_t *buffer_damage, + struct wlr_surface_state *current, struct wlr_surface_state *pending) { + pixman_region32_clear(buffer_damage); + + if (pending->width != current->width || + pending->height != current->height) { + // Damage the whole buffer on resize + pixman_region32_union_rect(buffer_damage, buffer_damage, 0, 0, + pending->buffer_width, pending->buffer_height); + } else { + // Copy over surface damage + buffer damage + pixman_region32_t surface_damage; + pixman_region32_init(&surface_damage); + + pixman_region32_copy(&surface_damage, &pending->surface_damage); + + if (pending->viewport.has_dst) { + int src_width, src_height; + surface_state_viewport_src_size(pending, &src_width, &src_height); + float scale_x = (float)pending->viewport.dst_width / src_width; + float scale_y = (float)pending->viewport.dst_height / src_height; + wlr_region_scale_xy(&surface_damage, &surface_damage, + 1.0 / scale_x, 1.0 / scale_y); + } + if (pending->viewport.has_src) { + // This is lossy: do a best-effort conversion + pixman_region32_translate(&surface_damage, + floor(pending->viewport.src.x), + floor(pending->viewport.src.y)); + } + + wlr_region_scale(&surface_damage, &surface_damage, pending->scale); + + int width, height; + surface_state_transformed_buffer_size(pending, &width, &height); + wlr_region_transform(&surface_damage, &surface_damage, + wlr_output_transform_invert(pending->transform), + width, height); + + pixman_region32_union(buffer_damage, + &pending->buffer_damage, &surface_damage); + + pixman_region32_fini(&surface_damage); + } +} + +/** + * Append pending state to current state and clear pending state. + */ +static void surface_state_move(struct wlr_surface_state *state, + struct wlr_surface_state *next) { + state->width = next->width; + state->height = next->height; + state->buffer_width = next->buffer_width; + state->buffer_height = next->buffer_height; + + if (next->committed & WLR_SURFACE_STATE_SCALE) { + state->scale = next->scale; + } + if (next->committed & WLR_SURFACE_STATE_TRANSFORM) { + state->transform = next->transform; + } + if (next->committed & WLR_SURFACE_STATE_BUFFER) { + state->dx = next->dx; + state->dy = next->dy; + next->dx = next->dy = 0; + + wlr_buffer_unlock(state->buffer); + state->buffer = NULL; + if (next->buffer) { + state->buffer = wlr_buffer_lock(next->buffer); + } + wlr_buffer_unlock(next->buffer); + next->buffer = NULL; + } else { + state->dx = state->dy = 0; + } + if (next->committed & WLR_SURFACE_STATE_SURFACE_DAMAGE) { + pixman_region32_copy(&state->surface_damage, &next->surface_damage); + pixman_region32_clear(&next->surface_damage); + } else { + pixman_region32_clear(&state->surface_damage); + } + if (next->committed & WLR_SURFACE_STATE_BUFFER_DAMAGE) { + pixman_region32_copy(&state->buffer_damage, &next->buffer_damage); + pixman_region32_clear(&next->buffer_damage); + } else { + pixman_region32_clear(&state->buffer_damage); + } + if (next->committed & WLR_SURFACE_STATE_OPAQUE_REGION) { + pixman_region32_copy(&state->opaque, &next->opaque); + } + if (next->committed & WLR_SURFACE_STATE_INPUT_REGION) { + pixman_region32_copy(&state->input, &next->input); + } + if (next->committed & WLR_SURFACE_STATE_VIEWPORT) { + memcpy(&state->viewport, &next->viewport, sizeof(state->viewport)); + } + if (next->committed & WLR_SURFACE_STATE_FRAME_CALLBACK_LIST) { + wl_list_insert_list(&state->frame_callback_list, + &next->frame_callback_list); + wl_list_init(&next->frame_callback_list); + } + + state->committed |= next->committed; + next->committed = 0; + + state->seq = next->seq; + + state->cached_state_locks = next->cached_state_locks; + next->cached_state_locks = 0; +} + +static void surface_apply_damage(struct wlr_surface *surface) { + if (surface->current.buffer == NULL) { + // NULL commit + if (surface->buffer != NULL) { + wlr_buffer_unlock(&surface->buffer->base); + } + surface->buffer = NULL; + return; + } + + if (surface->buffer != NULL) { + if (wlr_client_buffer_apply_damage(surface->buffer, + surface->current.buffer, &surface->buffer_damage)) { + wlr_buffer_unlock(surface->current.buffer); + surface->current.buffer = NULL; + return; + } + } + + struct wlr_client_buffer *buffer = wlr_client_buffer_create( + surface->current.buffer, surface->renderer); + + wlr_buffer_unlock(surface->current.buffer); + surface->current.buffer = NULL; + + if (buffer == NULL) { + wlr_log(WLR_ERROR, "Failed to upload buffer"); + return; + } + + if (surface->buffer != NULL) { + wlr_buffer_unlock(&surface->buffer->base); + } + surface->buffer = buffer; +} + +static void surface_update_opaque_region(struct wlr_surface *surface) { + struct wlr_texture *texture = wlr_surface_get_texture(surface); + if (texture == NULL) { + pixman_region32_clear(&surface->opaque_region); + return; + } + + if (wlr_texture_is_opaque(texture)) { + pixman_region32_init_rect(&surface->opaque_region, + 0, 0, surface->current.width, surface->current.height); + return; + } + + pixman_region32_intersect_rect(&surface->opaque_region, + &surface->current.opaque, + 0, 0, surface->current.width, surface->current.height); +} + +static void surface_update_input_region(struct wlr_surface *surface) { + pixman_region32_intersect_rect(&surface->input_region, + &surface->current.input, + 0, 0, surface->current.width, surface->current.height); +} + +static void surface_state_init(struct wlr_surface_state *state); + +static void subsurface_parent_commit(struct wlr_subsurface *subsurface); + +static void surface_cache_pending(struct wlr_surface *surface) { + struct wlr_surface_state *cached = calloc(1, sizeof(*cached)); + if (!cached) { + wl_resource_post_no_memory(surface->resource); + return; + } + + surface_state_init(cached); + surface_state_move(cached, &surface->pending); + + wl_list_insert(surface->cached.prev, &cached->cached_state_link); + + surface->pending.seq++; +} + +static void surface_commit_state(struct wlr_surface *surface, + struct wlr_surface_state *next) { + assert(next->cached_state_locks == 0); + + bool invalid_buffer = next->committed & WLR_SURFACE_STATE_BUFFER; + + surface->sx += next->dx; + surface->sy += next->dy; + surface_update_damage(&surface->buffer_damage, &surface->current, next); + + pixman_region32_clear(&surface->external_damage); + if (surface->current.width > next->width || + surface->current.height > next->height || + next->dx != 0 || next->dy != 0) { + pixman_region32_union_rect(&surface->external_damage, + &surface->external_damage, -next->dx, -next->dy, + surface->current.width, surface->current.height); + } + + surface->previous.scale = surface->current.scale; + surface->previous.transform = surface->current.transform; + surface->previous.width = surface->current.width; + surface->previous.height = surface->current.height; + surface->previous.buffer_width = surface->current.buffer_width; + surface->previous.buffer_height = surface->current.buffer_height; + + surface_state_move(&surface->current, next); + + if (invalid_buffer) { + surface_apply_damage(surface); + } + surface_update_opaque_region(surface); + surface_update_input_region(surface); + + // commit subsurface order + struct wlr_subsurface *subsurface; + wl_list_for_each_reverse(subsurface, &surface->pending.subsurfaces_above, + pending.link) { + wl_list_remove(&subsurface->current.link); + wl_list_insert(&surface->current.subsurfaces_above, + &subsurface->current.link); + + subsurface_parent_commit(subsurface); + } + wl_list_for_each_reverse(subsurface, &surface->pending.subsurfaces_below, + pending.link) { + wl_list_remove(&subsurface->current.link); + wl_list_insert(&surface->current.subsurfaces_below, + &subsurface->current.link); + + subsurface_parent_commit(subsurface); + } + + // If we're committing the pending state, bump the pending sequence number + // here, to allow commit listeners to lock the new pending state. + if (next == &surface->pending) { + surface->pending.seq++; + } + + if (surface->role && surface->role->commit) { + surface->role->commit(surface); + } + + wlr_signal_emit_safe(&surface->events.commit, surface); +} + +static void collect_subsurface_damage_iter(struct wlr_surface *surface, + int sx, int sy, void *data) { + struct wlr_subsurface *subsurface = data; + pixman_region32_t *damage = &subsurface->parent->external_damage; + pixman_region32_union_rect(damage, damage, + subsurface->current.x + sx, + subsurface->current.y + sy, + surface->current.width, surface->current.height); +} + +// TODO: untangle from wlr_surface +static void subsurface_parent_commit(struct wlr_subsurface *subsurface) { + struct wlr_surface *surface = subsurface->surface; + + bool moved = subsurface->current.x != subsurface->pending.x || + subsurface->current.y != subsurface->pending.y; + if (subsurface->mapped && moved) { + wlr_surface_for_each_surface(surface, + collect_subsurface_damage_iter, subsurface); + } + + if (subsurface->synchronized && subsurface->has_cache) { + wlr_surface_unlock_cached(surface, subsurface->cached_seq); + subsurface->has_cache = false; + } + + subsurface->current.x = subsurface->pending.x; + subsurface->current.y = subsurface->pending.y; + if (subsurface->mapped && (moved || subsurface->reordered)) { + subsurface->reordered = false; + wlr_surface_for_each_surface(surface, + collect_subsurface_damage_iter, subsurface); + } + + if (!subsurface->added) { + subsurface->added = true; + wlr_signal_emit_safe(&subsurface->parent->events.new_subsurface, + subsurface); + } +} + +static void surface_handle_commit(struct wl_client *client, + struct wl_resource *resource) { + struct wlr_surface *surface = wlr_surface_from_resource(resource); + surface_finalize_pending(surface); + + wlr_signal_emit_safe(&surface->events.client_commit, NULL); + + if (surface->role && surface->role->precommit) { + surface->role->precommit(surface); + } + + if (surface->pending.cached_state_locks > 0 || !wl_list_empty(&surface->cached)) { + surface_cache_pending(surface); + } else { + surface_commit_state(surface, &surface->pending); + } +} + +static void surface_handle_set_buffer_transform(struct wl_client *client, + struct wl_resource *resource, int32_t transform) { + if (transform < WL_OUTPUT_TRANSFORM_NORMAL || + transform > WL_OUTPUT_TRANSFORM_FLIPPED_270) { + wl_resource_post_error(resource, WL_SURFACE_ERROR_INVALID_TRANSFORM, + "Specified transform value (%d) is invalid", transform); + return; + } + struct wlr_surface *surface = wlr_surface_from_resource(resource); + surface->pending.committed |= WLR_SURFACE_STATE_TRANSFORM; + surface->pending.transform = transform; +} + +static void surface_handle_set_buffer_scale(struct wl_client *client, + struct wl_resource *resource, int32_t scale) { + if (scale <= 0) { + wl_resource_post_error(resource, WL_SURFACE_ERROR_INVALID_SCALE, + "Specified scale value (%d) is not positive", scale); + return; + } + struct wlr_surface *surface = wlr_surface_from_resource(resource); + surface->pending.committed |= WLR_SURFACE_STATE_SCALE; + surface->pending.scale = scale; +} + +static void surface_handle_damage_buffer(struct wl_client *client, + struct wl_resource *resource, + int32_t x, int32_t y, int32_t width, + int32_t height) { + struct wlr_surface *surface = wlr_surface_from_resource(resource); + if (width < 0 || height < 0) { + return; + } + surface->pending.committed |= WLR_SURFACE_STATE_BUFFER_DAMAGE; + pixman_region32_union_rect(&surface->pending.buffer_damage, + &surface->pending.buffer_damage, + x, y, width, height); +} + +static const struct wl_surface_interface surface_implementation = { + .destroy = surface_handle_destroy, + .attach = surface_handle_attach, + .damage = surface_handle_damage, + .frame = surface_handle_frame, + .set_opaque_region = surface_handle_set_opaque_region, + .set_input_region = surface_handle_set_input_region, + .commit = surface_handle_commit, + .set_buffer_transform = surface_handle_set_buffer_transform, + .set_buffer_scale = surface_handle_set_buffer_scale, + .damage_buffer = surface_handle_damage_buffer +}; + +struct wlr_surface *wlr_surface_from_resource(struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, &wl_surface_interface, + &surface_implementation)); + return wl_resource_get_user_data(resource); +} + +static void surface_state_init(struct wlr_surface_state *state) { + state->scale = 1; + state->transform = WL_OUTPUT_TRANSFORM_NORMAL; + + wl_list_init(&state->subsurfaces_above); + wl_list_init(&state->subsurfaces_below); + + wl_list_init(&state->frame_callback_list); + + pixman_region32_init(&state->surface_damage); + pixman_region32_init(&state->buffer_damage); + pixman_region32_init(&state->opaque); + pixman_region32_init_rect(&state->input, + INT32_MIN, INT32_MIN, UINT32_MAX, UINT32_MAX); +} + +static void surface_state_finish(struct wlr_surface_state *state) { + wlr_buffer_unlock(state->buffer); + + struct wl_resource *resource, *tmp; + wl_resource_for_each_safe(resource, tmp, &state->frame_callback_list) { + wl_resource_destroy(resource); + } + + pixman_region32_fini(&state->surface_damage); + pixman_region32_fini(&state->buffer_damage); + pixman_region32_fini(&state->opaque); + pixman_region32_fini(&state->input); +} + +static void surface_state_destroy_cached(struct wlr_surface_state *state) { + surface_state_finish(state); + wl_list_remove(&state->cached_state_link); + free(state); +} + +static void surface_output_destroy(struct wlr_surface_output *surface_output); + +static void surface_handle_resource_destroy(struct wl_resource *resource) { + struct wlr_surface *surface = wlr_surface_from_resource(resource); + + struct wlr_surface_output *surface_output, *surface_output_tmp; + wl_list_for_each_safe(surface_output, surface_output_tmp, + &surface->current_outputs, link) { + surface_output_destroy(surface_output); + } + + wlr_signal_emit_safe(&surface->events.destroy, surface); + + wlr_addon_set_finish(&surface->addons); + + struct wlr_surface_state *cached, *cached_tmp; + wl_list_for_each_safe(cached, cached_tmp, &surface->cached, cached_state_link) { + surface_state_destroy_cached(cached); + } + + wl_list_remove(&surface->renderer_destroy.link); + surface_state_finish(&surface->pending); + surface_state_finish(&surface->current); + pixman_region32_fini(&surface->buffer_damage); + pixman_region32_fini(&surface->external_damage); + pixman_region32_fini(&surface->opaque_region); + pixman_region32_fini(&surface->input_region); + if (surface->buffer != NULL) { + wlr_buffer_unlock(&surface->buffer->base); + } + free(surface); +} + +static void surface_handle_renderer_destroy(struct wl_listener *listener, + void *data) { + struct wlr_surface *surface = + wl_container_of(listener, surface, renderer_destroy); + wl_resource_destroy(surface->resource); +} + +static struct wlr_surface *surface_create(struct wl_client *client, + uint32_t version, uint32_t id, struct wlr_renderer *renderer) { + struct wlr_surface *surface = calloc(1, sizeof(struct wlr_surface)); + if (!surface) { + wl_client_post_no_memory(client); + return NULL; + } + surface->resource = wl_resource_create(client, &wl_surface_interface, + version, id); + if (surface->resource == NULL) { + free(surface); + wl_client_post_no_memory(client); + return NULL; + } + wl_resource_set_implementation(surface->resource, &surface_implementation, + surface, surface_handle_resource_destroy); + + wlr_log(WLR_DEBUG, "New wlr_surface %p (res %p)", surface, surface->resource); + + surface->renderer = renderer; + + surface_state_init(&surface->current); + surface_state_init(&surface->pending); + surface->pending.seq = 1; + + wl_signal_init(&surface->events.client_commit); + wl_signal_init(&surface->events.commit); + wl_signal_init(&surface->events.destroy); + wl_signal_init(&surface->events.new_subsurface); + wl_list_init(&surface->current_outputs); + wl_list_init(&surface->cached); + pixman_region32_init(&surface->buffer_damage); + pixman_region32_init(&surface->external_damage); + pixman_region32_init(&surface->opaque_region); + pixman_region32_init(&surface->input_region); + wlr_addon_set_init(&surface->addons); + + wl_signal_add(&renderer->events.destroy, &surface->renderer_destroy); + surface->renderer_destroy.notify = surface_handle_renderer_destroy; + + return surface; +} + +struct wlr_texture *wlr_surface_get_texture(struct wlr_surface *surface) { + if (surface->buffer == NULL) { + return NULL; + } + return surface->buffer->texture; +} + +bool wlr_surface_has_buffer(struct wlr_surface *surface) { + return wlr_surface_get_texture(surface) != NULL; +} + +bool wlr_surface_set_role(struct wlr_surface *surface, + const struct wlr_surface_role *role, void *role_data, + struct wl_resource *error_resource, uint32_t error_code) { + assert(role != NULL); + + if (surface->role != NULL && surface->role != role) { + if (error_resource != NULL) { + wl_resource_post_error(error_resource, error_code, + "Cannot assign role %s to wl_surface@%" PRIu32 ", already has role %s\n", + role->name, wl_resource_get_id(surface->resource), + surface->role->name); + } + return false; + } + if (surface->role_data != NULL && surface->role_data != role_data) { + wl_resource_post_error(error_resource, error_code, + "Cannot reassign role %s to wl_surface@%" PRIu32 "," + "role object still exists", role->name, + wl_resource_get_id(surface->resource)); + return false; + } + + surface->role = role; + surface->role_data = role_data; + return true; +} + +uint32_t wlr_surface_lock_pending(struct wlr_surface *surface) { + surface->pending.cached_state_locks++; + return surface->pending.seq; +} + +void wlr_surface_unlock_cached(struct wlr_surface *surface, uint32_t seq) { + if (surface->pending.seq == seq) { + assert(surface->pending.cached_state_locks > 0); + surface->pending.cached_state_locks--; + return; + } + + bool found = false; + struct wlr_surface_state *cached; + wl_list_for_each(cached, &surface->cached, cached_state_link) { + if (cached->seq == seq) { + found = true; + break; + } + } + assert(found); + + assert(cached->cached_state_locks > 0); + cached->cached_state_locks--; + + if (cached->cached_state_locks != 0) { + return; + } + + if (cached->cached_state_link.prev != &surface->cached) { + // This isn't the first cached state. This means we're blocked on a + // previous cached state. + return; + } + + // TODO: consider merging all committed states together + struct wlr_surface_state *next, *tmp; + wl_list_for_each_safe(next, tmp, &surface->cached, cached_state_link) { + if (next->cached_state_locks > 0) { + break; + } + + surface_commit_state(surface, next); + surface_state_destroy_cached(next); + } +} + +struct wlr_surface *wlr_surface_get_root_surface(struct wlr_surface *surface) { + while (wlr_surface_is_subsurface(surface)) { + struct wlr_subsurface *subsurface = + wlr_subsurface_from_wlr_surface(surface); + if (subsurface == NULL) { + break; + } + if (subsurface->parent == NULL) { + return NULL; + } + surface = subsurface->parent; + } + return surface; +} + +bool wlr_surface_point_accepts_input(struct wlr_surface *surface, + double sx, double sy) { + return sx >= 0 && sx < surface->current.width && + sy >= 0 && sy < surface->current.height && + pixman_region32_contains_point(&surface->current.input, floor(sx), floor(sy), NULL); +} + +struct wlr_surface *wlr_surface_surface_at(struct wlr_surface *surface, + double sx, double sy, double *sub_x, double *sub_y) { + struct wlr_subsurface *subsurface; + wl_list_for_each_reverse(subsurface, &surface->current.subsurfaces_above, + current.link) { + if (!subsurface->mapped) { + continue; + } + + double _sub_x = subsurface->current.x; + double _sub_y = subsurface->current.y; + struct wlr_surface *sub = wlr_surface_surface_at(subsurface->surface, + sx - _sub_x, sy - _sub_y, sub_x, sub_y); + if (sub != NULL) { + return sub; + } + } + + if (wlr_surface_point_accepts_input(surface, sx, sy)) { + if (sub_x) { + *sub_x = sx; + } + if (sub_y) { + *sub_y = sy; + } + return surface; + } + + wl_list_for_each_reverse(subsurface, &surface->current.subsurfaces_below, + current.link) { + if (!subsurface->mapped) { + continue; + } + + double _sub_x = subsurface->current.x; + double _sub_y = subsurface->current.y; + struct wlr_surface *sub = wlr_surface_surface_at(subsurface->surface, + sx - _sub_x, sy - _sub_y, sub_x, sub_y); + if (sub != NULL) { + return sub; + } + } + + return NULL; +} + +static void surface_output_destroy(struct wlr_surface_output *surface_output) { + wl_list_remove(&surface_output->bind.link); + wl_list_remove(&surface_output->destroy.link); + wl_list_remove(&surface_output->link); + + free(surface_output); +} + +static void surface_handle_output_bind(struct wl_listener *listener, + void *data) { + struct wlr_output_event_bind *evt = data; + struct wlr_surface_output *surface_output = + wl_container_of(listener, surface_output, bind); + struct wl_client *client = wl_resource_get_client( + surface_output->surface->resource); + if (client == wl_resource_get_client(evt->resource)) { + wl_surface_send_enter(surface_output->surface->resource, evt->resource); + } +} + +static void surface_handle_output_destroy(struct wl_listener *listener, + void *data) { + struct wlr_surface_output *surface_output = + wl_container_of(listener, surface_output, destroy); + surface_output_destroy(surface_output); +} + +void wlr_surface_send_enter(struct wlr_surface *surface, + struct wlr_output *output) { + struct wl_client *client = wl_resource_get_client(surface->resource); + struct wlr_surface_output *surface_output; + struct wl_resource *resource; + + wl_list_for_each(surface_output, &surface->current_outputs, link) { + if (surface_output->output == output) { + return; + } + } + + surface_output = calloc(1, sizeof(struct wlr_surface_output)); + if (surface_output == NULL) { + return; + } + surface_output->bind.notify = surface_handle_output_bind; + surface_output->destroy.notify = surface_handle_output_destroy; + + wl_signal_add(&output->events.bind, &surface_output->bind); + wl_signal_add(&output->events.destroy, &surface_output->destroy); + + surface_output->surface = surface; + surface_output->output = output; + wl_list_insert(&surface->current_outputs, &surface_output->link); + + wl_resource_for_each(resource, &output->resources) { + if (client == wl_resource_get_client(resource)) { + wl_surface_send_enter(surface->resource, resource); + } + } +} + +void wlr_surface_send_leave(struct wlr_surface *surface, + struct wlr_output *output) { + struct wl_client *client = wl_resource_get_client(surface->resource); + struct wlr_surface_output *surface_output, *tmp; + struct wl_resource *resource; + + wl_list_for_each_safe(surface_output, tmp, + &surface->current_outputs, link) { + if (surface_output->output == output) { + surface_output_destroy(surface_output); + wl_resource_for_each(resource, &output->resources) { + if (client == wl_resource_get_client(resource)) { + wl_surface_send_leave(surface->resource, resource); + } + } + break; + } + } +} + +void wlr_surface_send_frame_done(struct wlr_surface *surface, + const struct timespec *when) { + struct wl_resource *resource, *tmp; + wl_resource_for_each_safe(resource, tmp, + &surface->current.frame_callback_list) { + wl_callback_send_done(resource, timespec_to_msec(when)); + wl_resource_destroy(resource); + } +} + +static void surface_for_each_surface(struct wlr_surface *surface, int x, int y, + wlr_surface_iterator_func_t iterator, void *user_data) { + struct wlr_subsurface *subsurface; + wl_list_for_each(subsurface, &surface->current.subsurfaces_below, current.link) { + if (!subsurface->mapped) { + continue; + } + + struct wlr_subsurface_parent_state *state = &subsurface->current; + int sx = state->x; + int sy = state->y; + + surface_for_each_surface(subsurface->surface, x + sx, y + sy, + iterator, user_data); + } + + iterator(surface, x, y, user_data); + + wl_list_for_each(subsurface, &surface->current.subsurfaces_above, current.link) { + if (!subsurface->mapped) { + continue; + } + + struct wlr_subsurface_parent_state *state = &subsurface->current; + int sx = state->x; + int sy = state->y; + + surface_for_each_surface(subsurface->surface, x + sx, y + sy, + iterator, user_data); + } +} + +void wlr_surface_for_each_surface(struct wlr_surface *surface, + wlr_surface_iterator_func_t iterator, void *user_data) { + surface_for_each_surface(surface, 0, 0, iterator, user_data); +} + +struct bound_acc { + int32_t min_x, min_y; + int32_t max_x, max_y; +}; + +static void handle_bounding_box_surface(struct wlr_surface *surface, + int x, int y, void *data) { + struct bound_acc *acc = data; + + acc->min_x = min(x, acc->min_x); + acc->min_y = min(y, acc->min_y); + + acc->max_x = max(x + surface->current.width, acc->max_x); + acc->max_y = max(y + surface->current.height, acc->max_y); +} + +void wlr_surface_get_extends(struct wlr_surface *surface, struct wlr_box *box) { + struct bound_acc acc = { + .min_x = 0, + .min_y = 0, + .max_x = surface->current.width, + .max_y = surface->current.height, + }; + + wlr_surface_for_each_surface(surface, handle_bounding_box_surface, &acc); + + box->x = acc.min_x; + box->y = acc.min_y; + box->width = acc.max_x - acc.min_x; + box->height = acc.max_y - acc.min_y; +} + +static void crop_region(pixman_region32_t *dst, pixman_region32_t *src, + const struct wlr_box *box) { + pixman_region32_intersect_rect(dst, src, + box->x, box->y, box->width, box->height); + pixman_region32_translate(dst, -box->x, -box->y); +} + +void wlr_surface_get_effective_damage(struct wlr_surface *surface, + pixman_region32_t *damage) { + pixman_region32_clear(damage); + + // Transform and copy the buffer damage in terms of surface coordinates. + wlr_region_transform(damage, &surface->buffer_damage, + surface->current.transform, surface->current.buffer_width, + surface->current.buffer_height); + wlr_region_scale(damage, damage, 1.0 / (float)surface->current.scale); + + if (surface->current.viewport.has_src) { + struct wlr_box src_box = { + .x = floor(surface->current.viewport.src.x), + .y = floor(surface->current.viewport.src.y), + .width = ceil(surface->current.viewport.src.width), + .height = ceil(surface->current.viewport.src.height), + }; + crop_region(damage, damage, &src_box); + } + if (surface->current.viewport.has_dst) { + int src_width, src_height; + surface_state_viewport_src_size(&surface->current, + &src_width, &src_height); + float scale_x = (float)surface->current.viewport.dst_width / src_width; + float scale_y = (float)surface->current.viewport.dst_height / src_height; + wlr_region_scale_xy(damage, damage, scale_x, scale_y); + } + + pixman_region32_union(damage, damage, &surface->external_damage); +} + +void wlr_surface_get_buffer_source_box(struct wlr_surface *surface, + struct wlr_fbox *box) { + box->x = box->y = 0; + box->width = surface->current.buffer_width; + box->height = surface->current.buffer_height; + + if (surface->current.viewport.has_src) { + box->x = surface->current.viewport.src.x * surface->current.scale; + box->y = surface->current.viewport.src.y * surface->current.scale; + box->width = surface->current.viewport.src.width * surface->current.scale; + box->height = surface->current.viewport.src.height * surface->current.scale; + + int width, height; + surface_state_transformed_buffer_size(&surface->current, &width, &height); + wlr_fbox_transform(box, box, + wlr_output_transform_invert(surface->current.transform), + width, height); + } +} static const struct wl_compositor_interface compositor_impl; diff --git a/types/wlr_subcompositor.c b/types/wlr_subcompositor.c index f5a6248652..45d0349d21 100644 --- a/types/wlr_subcompositor.c +++ b/types/wlr_subcompositor.c @@ -4,12 +4,383 @@ #include #include #include "types/wlr_region.h" -#include "types/wlr_surface.h" #include "util/signal.h" #define SUBCOMPOSITOR_VERSION 1 -extern const struct wlr_surface_role subsurface_role; +static bool subsurface_is_synchronized(struct wlr_subsurface *subsurface) { + while (subsurface != NULL) { + if (subsurface->synchronized) { + return true; + } + + if (!subsurface->parent) { + return false; + } + + if (!wlr_surface_is_subsurface(subsurface->parent)) { + break; + } + subsurface = wlr_subsurface_from_wlr_surface(subsurface->parent); + } + + return false; +} + +static void subsurface_unmap(struct wlr_subsurface *subsurface); + +static void subsurface_destroy(struct wlr_subsurface *subsurface) { + if (subsurface == NULL) { + return; + } + + if (subsurface->has_cache) { + wlr_surface_unlock_cached(subsurface->surface, + subsurface->cached_seq); + } + + subsurface_unmap(subsurface); + + wlr_signal_emit_safe(&subsurface->events.destroy, subsurface); + + wl_list_remove(&subsurface->surface_destroy.link); + wl_list_remove(&subsurface->surface_client_commit.link); + + if (subsurface->parent) { + wl_list_remove(&subsurface->current.link); + wl_list_remove(&subsurface->pending.link); + wl_list_remove(&subsurface->parent_destroy.link); + } + + wl_resource_set_user_data(subsurface->resource, NULL); + if (subsurface->surface) { + subsurface->surface->role_data = NULL; + } + free(subsurface); +} + +static const struct wl_subsurface_interface subsurface_implementation; + +static struct wlr_subsurface *subsurface_from_resource( + struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, &wl_subsurface_interface, + &subsurface_implementation)); + return wl_resource_get_user_data(resource); +} + +static void subsurface_resource_destroy(struct wl_resource *resource) { + struct wlr_subsurface *subsurface = subsurface_from_resource(resource); + subsurface_destroy(subsurface); +} + +static void subsurface_handle_destroy(struct wl_client *client, + struct wl_resource *resource) { + wl_resource_destroy(resource); +} + +static void subsurface_handle_set_position(struct wl_client *client, + struct wl_resource *resource, int32_t x, int32_t y) { + struct wlr_subsurface *subsurface = subsurface_from_resource(resource); + if (subsurface == NULL) { + return; + } + + subsurface->pending.x = x; + subsurface->pending.y = y; +} + +static struct wlr_subsurface *subsurface_find_sibling( + struct wlr_subsurface *subsurface, struct wlr_surface *surface) { + struct wlr_surface *parent = subsurface->parent; + + struct wlr_subsurface *sibling; + wl_list_for_each(sibling, &parent->pending.subsurfaces_below, pending.link) { + if (sibling->surface == surface && sibling != subsurface) { + return sibling; + } + } + wl_list_for_each(sibling, &parent->pending.subsurfaces_above, pending.link) { + if (sibling->surface == surface && sibling != subsurface) { + return sibling; + } + } + + return NULL; +} + +static void subsurface_handle_place_above(struct wl_client *client, + struct wl_resource *resource, struct wl_resource *sibling_resource) { + struct wlr_subsurface *subsurface = subsurface_from_resource(resource); + if (subsurface == NULL) { + return; + } + + struct wlr_surface *sibling_surface = + wlr_surface_from_resource(sibling_resource); + + struct wl_list *node; + if (sibling_surface == subsurface->parent) { + node = &subsurface->parent->pending.subsurfaces_above; + } else { + struct wlr_subsurface *sibling = + subsurface_find_sibling(subsurface, sibling_surface); + if (!sibling) { + wl_resource_post_error(subsurface->resource, + WL_SUBSURFACE_ERROR_BAD_SURFACE, + "%s: wl_surface@%" PRIu32 "is not a parent or sibling", + "place_above", wl_resource_get_id(sibling_resource)); + return; + } + node = &sibling->pending.link; + } + + wl_list_remove(&subsurface->pending.link); + wl_list_insert(node, &subsurface->pending.link); + + subsurface->reordered = true; +} + +static void subsurface_handle_place_below(struct wl_client *client, + struct wl_resource *resource, struct wl_resource *sibling_resource) { + struct wlr_subsurface *subsurface = subsurface_from_resource(resource); + if (subsurface == NULL) { + return; + } + + struct wlr_surface *sibling_surface = + wlr_surface_from_resource(sibling_resource); + + struct wl_list *node; + if (sibling_surface == subsurface->parent) { + node = &subsurface->parent->pending.subsurfaces_below; + } else { + struct wlr_subsurface *sibling = + subsurface_find_sibling(subsurface, sibling_surface); + if (!sibling) { + wl_resource_post_error(subsurface->resource, + WL_SUBSURFACE_ERROR_BAD_SURFACE, + "%s: wl_surface@%" PRIu32 " is not a parent or sibling", + "place_below", wl_resource_get_id(sibling_resource)); + return; + } + node = &sibling->pending.link; + } + + wl_list_remove(&subsurface->pending.link); + wl_list_insert(node->prev, &subsurface->pending.link); + + subsurface->reordered = true; +} + +static void subsurface_handle_set_sync(struct wl_client *client, + struct wl_resource *resource) { + struct wlr_subsurface *subsurface = subsurface_from_resource(resource); + if (subsurface == NULL) { + return; + } + + subsurface->synchronized = true; +} + +static void subsurface_handle_set_desync(struct wl_client *client, + struct wl_resource *resource) { + struct wlr_subsurface *subsurface = subsurface_from_resource(resource); + if (subsurface == NULL) { + return; + } + + if (subsurface->synchronized) { + subsurface->synchronized = false; + + if (!subsurface_is_synchronized(subsurface) && + subsurface->has_cache) { + wlr_surface_unlock_cached(subsurface->surface, + subsurface->cached_seq); + subsurface->has_cache = false; + } + } +} + +static const struct wl_subsurface_interface subsurface_implementation = { + .destroy = subsurface_handle_destroy, + .set_position = subsurface_handle_set_position, + .place_above = subsurface_handle_place_above, + .place_below = subsurface_handle_place_below, + .set_sync = subsurface_handle_set_sync, + .set_desync = subsurface_handle_set_desync, +}; + +/** + * Checks if this subsurface needs to be marked as mapped. This can happen if: + * - The subsurface has a buffer + * - Its parent is mapped + */ +static void subsurface_consider_map(struct wlr_subsurface *subsurface, + bool check_parent) { + if (subsurface->mapped || !wlr_surface_has_buffer(subsurface->surface)) { + return; + } + + if (check_parent) { + if (subsurface->parent == NULL) { + return; + } + if (wlr_surface_is_subsurface(subsurface->parent)) { + struct wlr_subsurface *parent = + wlr_subsurface_from_wlr_surface(subsurface->parent); + if (parent == NULL || !parent->mapped) { + return; + } + } + } + + // Now we can map the subsurface + wlr_signal_emit_safe(&subsurface->events.map, subsurface); + subsurface->mapped = true; + + // Try mapping all children too + struct wlr_subsurface *child; + wl_list_for_each(child, &subsurface->surface->current.subsurfaces_below, + current.link) { + subsurface_consider_map(child, false); + } + wl_list_for_each(child, &subsurface->surface->current.subsurfaces_above, + current.link) { + subsurface_consider_map(child, false); + } +} + +static void subsurface_unmap(struct wlr_subsurface *subsurface) { + if (!subsurface->mapped) { + return; + } + + wlr_signal_emit_safe(&subsurface->events.unmap, subsurface); + subsurface->mapped = false; + + // Unmap all children + struct wlr_subsurface *child; + wl_list_for_each(child, &subsurface->surface->current.subsurfaces_below, + current.link) { + subsurface_unmap(child); + } + wl_list_for_each(child, &subsurface->surface->current.subsurfaces_above, + current.link) { + subsurface_unmap(child); + } +} + +static void subsurface_role_commit(struct wlr_surface *surface) { + struct wlr_subsurface *subsurface = + wlr_subsurface_from_wlr_surface(surface); + if (subsurface == NULL) { + return; + } + + subsurface_consider_map(subsurface, true); +} + +static void subsurface_role_precommit(struct wlr_surface *surface) { + struct wlr_subsurface *subsurface = + wlr_subsurface_from_wlr_surface(surface); + if (subsurface == NULL) { + return; + } + + if (surface->pending.committed & WLR_SURFACE_STATE_BUFFER && + surface->pending.buffer == NULL) { + // This is a NULL commit + subsurface_unmap(subsurface); + } +} + +const struct wlr_surface_role subsurface_role = { + .name = "wl_subsurface", + .commit = subsurface_role_commit, + .precommit = subsurface_role_precommit, +}; + +static void subsurface_handle_parent_destroy(struct wl_listener *listener, + void *data) { + struct wlr_subsurface *subsurface = + wl_container_of(listener, subsurface, parent_destroy); + subsurface_unmap(subsurface); + wl_list_remove(&subsurface->current.link); + wl_list_remove(&subsurface->pending.link); + wl_list_remove(&subsurface->parent_destroy.link); + subsurface->parent = NULL; +} + +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); +} + +static void subsurface_handle_surface_client_commit( + struct wl_listener *listener, void *data) { + struct wlr_subsurface *subsurface = + wl_container_of(listener, subsurface, surface_client_commit); + struct wlr_surface *surface = subsurface->surface; + + if (subsurface_is_synchronized(subsurface)) { + if (subsurface->has_cache) { + // We already lock a previous commit. The prevents any future + // commit to be applied before we release the previous commit. + return; + } + subsurface->has_cache = true; + subsurface->cached_seq = wlr_surface_lock_pending(surface); + } +} + +static struct wlr_subsurface *subsurface_create(struct wlr_surface *surface, + struct wlr_surface *parent, uint32_t version, uint32_t id) { + struct wl_client *client = wl_resource_get_client(surface->resource); + + struct wlr_subsurface *subsurface = + calloc(1, sizeof(struct wlr_subsurface)); + if (!subsurface) { + wl_client_post_no_memory(client); + return NULL; + } + subsurface->synchronized = true; + subsurface->surface = surface; + subsurface->resource = + wl_resource_create(client, &wl_subsurface_interface, version, id); + if (subsurface->resource == NULL) { + free(subsurface); + wl_client_post_no_memory(client); + return NULL; + } + wl_resource_set_implementation(subsurface->resource, + &subsurface_implementation, subsurface, subsurface_resource_destroy); + + wl_signal_init(&subsurface->events.destroy); + wl_signal_init(&subsurface->events.map); + wl_signal_init(&subsurface->events.unmap); + + wl_signal_add(&surface->events.destroy, &subsurface->surface_destroy); + subsurface->surface_destroy.notify = subsurface_handle_surface_destroy; + wl_signal_add(&surface->events.client_commit, + &subsurface->surface_client_commit); + subsurface->surface_client_commit.notify = + subsurface_handle_surface_client_commit; + + // link parent + subsurface->parent = parent; + wl_signal_add(&parent->events.destroy, &subsurface->parent_destroy); + subsurface->parent_destroy.notify = subsurface_handle_parent_destroy; + + wl_list_init(&subsurface->current.link); + wl_list_insert(parent->pending.subsurfaces_above.prev, + &subsurface->pending.link); + + surface->role_data = subsurface; + + return subsurface; +} bool wlr_surface_is_subsurface(struct wlr_surface *surface) { return surface->role == &subsurface_role; diff --git a/types/wlr_surface.c b/types/wlr_surface.c deleted file mode 100644 index 1b49a21b81..0000000000 --- a/types/wlr_surface.c +++ /dev/null @@ -1,1442 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "types/wlr_surface.h" -#include "util/signal.h" -#include "util/time.h" - -#define CALLBACK_VERSION 1 - -static int min(int fst, int snd) { - if (fst < snd) { - return fst; - } else { - return snd; - } -} - -static int max(int fst, int snd) { - if (fst > snd) { - return fst; - } else { - return snd; - } -} - -static void surface_handle_destroy(struct wl_client *client, - struct wl_resource *resource) { - wl_resource_destroy(resource); -} - -static void surface_handle_attach(struct wl_client *client, - struct wl_resource *resource, - struct wl_resource *buffer_resource, int32_t dx, int32_t dy) { - struct wlr_surface *surface = wlr_surface_from_resource(resource); - - struct wlr_buffer *buffer = NULL; - if (buffer_resource != NULL) { - buffer = wlr_buffer_from_resource(buffer_resource); - if (buffer == NULL) { - wl_resource_post_error(buffer_resource, 0, "unknown buffer type"); - return; - } - } - - surface->pending.committed |= WLR_SURFACE_STATE_BUFFER; - surface->pending.dx = dx; - surface->pending.dy = dy; - - wlr_buffer_unlock(surface->pending.buffer); - surface->pending.buffer = buffer; -} - -static void surface_handle_damage(struct wl_client *client, - struct wl_resource *resource, - int32_t x, int32_t y, int32_t width, int32_t height) { - struct wlr_surface *surface = wlr_surface_from_resource(resource); - if (width < 0 || height < 0) { - return; - } - surface->pending.committed |= WLR_SURFACE_STATE_SURFACE_DAMAGE; - pixman_region32_union_rect(&surface->pending.surface_damage, - &surface->pending.surface_damage, - x, y, width, height); -} - -static void callback_handle_resource_destroy(struct wl_resource *resource) { - wl_list_remove(wl_resource_get_link(resource)); -} - -static void surface_handle_frame(struct wl_client *client, - struct wl_resource *resource, uint32_t callback) { - struct wlr_surface *surface = wlr_surface_from_resource(resource); - - struct wl_resource *callback_resource = wl_resource_create(client, - &wl_callback_interface, CALLBACK_VERSION, callback); - if (callback_resource == NULL) { - wl_resource_post_no_memory(resource); - return; - } - wl_resource_set_implementation(callback_resource, NULL, NULL, - callback_handle_resource_destroy); - - wl_list_insert(surface->pending.frame_callback_list.prev, - wl_resource_get_link(callback_resource)); - - surface->pending.committed |= WLR_SURFACE_STATE_FRAME_CALLBACK_LIST; -} - -static void surface_handle_set_opaque_region(struct wl_client *client, - struct wl_resource *resource, - struct wl_resource *region_resource) { - struct wlr_surface *surface = wlr_surface_from_resource(resource); - surface->pending.committed |= WLR_SURFACE_STATE_OPAQUE_REGION; - if (region_resource) { - pixman_region32_t *region = wlr_region_from_resource(region_resource); - pixman_region32_copy(&surface->pending.opaque, region); - } else { - pixman_region32_clear(&surface->pending.opaque); - } -} - -static void surface_handle_set_input_region(struct wl_client *client, - struct wl_resource *resource, - struct wl_resource *region_resource) { - struct wlr_surface *surface = wlr_surface_from_resource(resource); - surface->pending.committed |= WLR_SURFACE_STATE_INPUT_REGION; - if (region_resource) { - pixman_region32_t *region = wlr_region_from_resource(region_resource); - pixman_region32_copy(&surface->pending.input, region); - } else { - pixman_region32_fini(&surface->pending.input); - pixman_region32_init_rect(&surface->pending.input, - INT32_MIN, INT32_MIN, UINT32_MAX, UINT32_MAX); - } -} - -static void surface_state_transformed_buffer_size(struct wlr_surface_state *state, - int *out_width, int *out_height) { - int width = state->buffer_width; - int height = state->buffer_height; - if ((state->transform & WL_OUTPUT_TRANSFORM_90) != 0) { - int tmp = width; - width = height; - height = tmp; - } - *out_width = width; - *out_height = height; -} - -/** - * Computes the surface viewport source size, ie. the size after applying the - * surface's scale, transform and cropping (via the viewport's source - * rectangle) but before applying the viewport scaling (via the viewport's - * destination rectangle). - */ -static void surface_state_viewport_src_size(struct wlr_surface_state *state, - int *out_width, int *out_height) { - if (state->buffer_width == 0 && state->buffer_height == 0) { - *out_width = *out_height = 0; - return; - } - - if (state->viewport.has_src) { - *out_width = state->viewport.src.width; - *out_height = state->viewport.src.height; - } else { - surface_state_transformed_buffer_size(state, - out_width, out_height); - *out_width /= state->scale; - *out_height /= state->scale; - } -} - -static void surface_finalize_pending(struct wlr_surface *surface) { - struct wlr_surface_state *pending = &surface->pending; - - if ((pending->committed & WLR_SURFACE_STATE_BUFFER)) { - if (pending->buffer != NULL) { - pending->buffer_width = pending->buffer->width; - pending->buffer_height = pending->buffer->height; - } else { - pending->buffer_width = pending->buffer_height = 0; - } - } - - if (!pending->viewport.has_src && - (pending->buffer_width % pending->scale != 0 || - pending->buffer_height % pending->scale != 0)) { - // TODO: send WL_SURFACE_ERROR_INVALID_SIZE error once this issue is - // resolved: - // https://gitlab.freedesktop.org/wayland/wayland/-/issues/194 - wlr_log(WLR_DEBUG, "Client bug: submitted a buffer whose size (%dx%d) " - "is not divisible by scale (%d)", pending->buffer_width, - pending->buffer_height, pending->scale); - } - - if (pending->viewport.has_dst) { - if (pending->buffer_width == 0 && pending->buffer_height == 0) { - pending->width = pending->height = 0; - } else { - pending->width = pending->viewport.dst_width; - pending->height = pending->viewport.dst_height; - } - } else { - surface_state_viewport_src_size(pending, &pending->width, &pending->height); - } - - pixman_region32_intersect_rect(&pending->surface_damage, - &pending->surface_damage, 0, 0, pending->width, pending->height); - - pixman_region32_intersect_rect(&pending->buffer_damage, - &pending->buffer_damage, 0, 0, pending->buffer_width, - pending->buffer_height); -} - -static void surface_update_damage(pixman_region32_t *buffer_damage, - struct wlr_surface_state *current, struct wlr_surface_state *pending) { - pixman_region32_clear(buffer_damage); - - if (pending->width != current->width || - pending->height != current->height) { - // Damage the whole buffer on resize - pixman_region32_union_rect(buffer_damage, buffer_damage, 0, 0, - pending->buffer_width, pending->buffer_height); - } else { - // Copy over surface damage + buffer damage - pixman_region32_t surface_damage; - pixman_region32_init(&surface_damage); - - pixman_region32_copy(&surface_damage, &pending->surface_damage); - - if (pending->viewport.has_dst) { - int src_width, src_height; - surface_state_viewport_src_size(pending, &src_width, &src_height); - float scale_x = (float)pending->viewport.dst_width / src_width; - float scale_y = (float)pending->viewport.dst_height / src_height; - wlr_region_scale_xy(&surface_damage, &surface_damage, - 1.0 / scale_x, 1.0 / scale_y); - } - if (pending->viewport.has_src) { - // This is lossy: do a best-effort conversion - pixman_region32_translate(&surface_damage, - floor(pending->viewport.src.x), - floor(pending->viewport.src.y)); - } - - wlr_region_scale(&surface_damage, &surface_damage, pending->scale); - - int width, height; - surface_state_transformed_buffer_size(pending, &width, &height); - wlr_region_transform(&surface_damage, &surface_damage, - wlr_output_transform_invert(pending->transform), - width, height); - - pixman_region32_union(buffer_damage, - &pending->buffer_damage, &surface_damage); - - pixman_region32_fini(&surface_damage); - } -} - -/** - * Append pending state to current state and clear pending state. - */ -static void surface_state_move(struct wlr_surface_state *state, - struct wlr_surface_state *next) { - state->width = next->width; - state->height = next->height; - state->buffer_width = next->buffer_width; - state->buffer_height = next->buffer_height; - - if (next->committed & WLR_SURFACE_STATE_SCALE) { - state->scale = next->scale; - } - if (next->committed & WLR_SURFACE_STATE_TRANSFORM) { - state->transform = next->transform; - } - if (next->committed & WLR_SURFACE_STATE_BUFFER) { - state->dx = next->dx; - state->dy = next->dy; - next->dx = next->dy = 0; - - wlr_buffer_unlock(state->buffer); - state->buffer = NULL; - if (next->buffer) { - state->buffer = wlr_buffer_lock(next->buffer); - } - wlr_buffer_unlock(next->buffer); - next->buffer = NULL; - } else { - state->dx = state->dy = 0; - } - if (next->committed & WLR_SURFACE_STATE_SURFACE_DAMAGE) { - pixman_region32_copy(&state->surface_damage, &next->surface_damage); - pixman_region32_clear(&next->surface_damage); - } else { - pixman_region32_clear(&state->surface_damage); - } - if (next->committed & WLR_SURFACE_STATE_BUFFER_DAMAGE) { - pixman_region32_copy(&state->buffer_damage, &next->buffer_damage); - pixman_region32_clear(&next->buffer_damage); - } else { - pixman_region32_clear(&state->buffer_damage); - } - if (next->committed & WLR_SURFACE_STATE_OPAQUE_REGION) { - pixman_region32_copy(&state->opaque, &next->opaque); - } - if (next->committed & WLR_SURFACE_STATE_INPUT_REGION) { - pixman_region32_copy(&state->input, &next->input); - } - if (next->committed & WLR_SURFACE_STATE_VIEWPORT) { - memcpy(&state->viewport, &next->viewport, sizeof(state->viewport)); - } - if (next->committed & WLR_SURFACE_STATE_FRAME_CALLBACK_LIST) { - wl_list_insert_list(&state->frame_callback_list, - &next->frame_callback_list); - wl_list_init(&next->frame_callback_list); - } - - state->committed |= next->committed; - next->committed = 0; - - state->seq = next->seq; - - state->cached_state_locks = next->cached_state_locks; - next->cached_state_locks = 0; -} - -static void surface_apply_damage(struct wlr_surface *surface) { - if (surface->current.buffer == NULL) { - // NULL commit - if (surface->buffer != NULL) { - wlr_buffer_unlock(&surface->buffer->base); - } - surface->buffer = NULL; - return; - } - - if (surface->buffer != NULL) { - if (wlr_client_buffer_apply_damage(surface->buffer, - surface->current.buffer, &surface->buffer_damage)) { - wlr_buffer_unlock(surface->current.buffer); - surface->current.buffer = NULL; - return; - } - } - - struct wlr_client_buffer *buffer = wlr_client_buffer_create( - surface->current.buffer, surface->renderer); - - wlr_buffer_unlock(surface->current.buffer); - surface->current.buffer = NULL; - - if (buffer == NULL) { - wlr_log(WLR_ERROR, "Failed to upload buffer"); - return; - } - - if (surface->buffer != NULL) { - wlr_buffer_unlock(&surface->buffer->base); - } - surface->buffer = buffer; -} - -static void surface_update_opaque_region(struct wlr_surface *surface) { - struct wlr_texture *texture = wlr_surface_get_texture(surface); - if (texture == NULL) { - pixman_region32_clear(&surface->opaque_region); - return; - } - - if (wlr_texture_is_opaque(texture)) { - pixman_region32_init_rect(&surface->opaque_region, - 0, 0, surface->current.width, surface->current.height); - return; - } - - pixman_region32_intersect_rect(&surface->opaque_region, - &surface->current.opaque, - 0, 0, surface->current.width, surface->current.height); -} - -static void surface_update_input_region(struct wlr_surface *surface) { - pixman_region32_intersect_rect(&surface->input_region, - &surface->current.input, - 0, 0, surface->current.width, surface->current.height); -} - -static void surface_state_init(struct wlr_surface_state *state); - -static void subsurface_parent_commit(struct wlr_subsurface *subsurface); - -static void surface_cache_pending(struct wlr_surface *surface) { - struct wlr_surface_state *cached = calloc(1, sizeof(*cached)); - if (!cached) { - wl_resource_post_no_memory(surface->resource); - return; - } - - surface_state_init(cached); - surface_state_move(cached, &surface->pending); - - wl_list_insert(surface->cached.prev, &cached->cached_state_link); - - surface->pending.seq++; -} - -static void surface_commit_state(struct wlr_surface *surface, - struct wlr_surface_state *next) { - assert(next->cached_state_locks == 0); - - bool invalid_buffer = next->committed & WLR_SURFACE_STATE_BUFFER; - - surface->sx += next->dx; - surface->sy += next->dy; - surface_update_damage(&surface->buffer_damage, &surface->current, next); - - pixman_region32_clear(&surface->external_damage); - if (surface->current.width > next->width || - surface->current.height > next->height || - next->dx != 0 || next->dy != 0) { - pixman_region32_union_rect(&surface->external_damage, - &surface->external_damage, -next->dx, -next->dy, - surface->current.width, surface->current.height); - } - - surface->previous.scale = surface->current.scale; - surface->previous.transform = surface->current.transform; - surface->previous.width = surface->current.width; - surface->previous.height = surface->current.height; - surface->previous.buffer_width = surface->current.buffer_width; - surface->previous.buffer_height = surface->current.buffer_height; - - surface_state_move(&surface->current, next); - - if (invalid_buffer) { - surface_apply_damage(surface); - } - surface_update_opaque_region(surface); - surface_update_input_region(surface); - - // commit subsurface order - struct wlr_subsurface *subsurface; - wl_list_for_each_reverse(subsurface, &surface->pending.subsurfaces_above, - pending.link) { - wl_list_remove(&subsurface->current.link); - wl_list_insert(&surface->current.subsurfaces_above, - &subsurface->current.link); - - subsurface_parent_commit(subsurface); - } - wl_list_for_each_reverse(subsurface, &surface->pending.subsurfaces_below, - pending.link) { - wl_list_remove(&subsurface->current.link); - wl_list_insert(&surface->current.subsurfaces_below, - &subsurface->current.link); - - subsurface_parent_commit(subsurface); - } - - // If we're committing the pending state, bump the pending sequence number - // here, to allow commit listeners to lock the new pending state. - if (next == &surface->pending) { - surface->pending.seq++; - } - - if (surface->role && surface->role->commit) { - surface->role->commit(surface); - } - - wlr_signal_emit_safe(&surface->events.commit, surface); -} - -static bool subsurface_is_synchronized(struct wlr_subsurface *subsurface) { - while (subsurface != NULL) { - if (subsurface->synchronized) { - return true; - } - - if (!subsurface->parent) { - return false; - } - - if (!wlr_surface_is_subsurface(subsurface->parent)) { - break; - } - subsurface = wlr_subsurface_from_wlr_surface(subsurface->parent); - } - - return false; -} - -static void collect_subsurface_damage_iter(struct wlr_surface *surface, - int sx, int sy, void *data) { - struct wlr_subsurface *subsurface = data; - pixman_region32_t *damage = &subsurface->parent->external_damage; - pixman_region32_union_rect(damage, damage, - subsurface->current.x + sx, - subsurface->current.y + sy, - surface->current.width, surface->current.height); -} - -static void subsurface_parent_commit(struct wlr_subsurface *subsurface) { - struct wlr_surface *surface = subsurface->surface; - - bool moved = subsurface->current.x != subsurface->pending.x || - subsurface->current.y != subsurface->pending.y; - if (subsurface->mapped && moved) { - wlr_surface_for_each_surface(surface, - collect_subsurface_damage_iter, subsurface); - } - - if (subsurface->synchronized && subsurface->has_cache) { - wlr_surface_unlock_cached(surface, subsurface->cached_seq); - subsurface->has_cache = false; - } - - subsurface->current.x = subsurface->pending.x; - subsurface->current.y = subsurface->pending.y; - if (subsurface->mapped && (moved || subsurface->reordered)) { - subsurface->reordered = false; - wlr_surface_for_each_surface(surface, - collect_subsurface_damage_iter, subsurface); - } - - if (!subsurface->added) { - subsurface->added = true; - wlr_signal_emit_safe(&subsurface->parent->events.new_subsurface, - subsurface); - } -} - -static void surface_handle_commit(struct wl_client *client, - struct wl_resource *resource) { - struct wlr_surface *surface = wlr_surface_from_resource(resource); - surface_finalize_pending(surface); - - wlr_signal_emit_safe(&surface->events.client_commit, NULL); - - if (surface->role && surface->role->precommit) { - surface->role->precommit(surface); - } - - if (surface->pending.cached_state_locks > 0 || !wl_list_empty(&surface->cached)) { - surface_cache_pending(surface); - } else { - surface_commit_state(surface, &surface->pending); - } -} - -static void surface_handle_set_buffer_transform(struct wl_client *client, - struct wl_resource *resource, int32_t transform) { - if (transform < WL_OUTPUT_TRANSFORM_NORMAL || - transform > WL_OUTPUT_TRANSFORM_FLIPPED_270) { - wl_resource_post_error(resource, WL_SURFACE_ERROR_INVALID_TRANSFORM, - "Specified transform value (%d) is invalid", transform); - return; - } - struct wlr_surface *surface = wlr_surface_from_resource(resource); - surface->pending.committed |= WLR_SURFACE_STATE_TRANSFORM; - surface->pending.transform = transform; -} - -static void surface_handle_set_buffer_scale(struct wl_client *client, - struct wl_resource *resource, int32_t scale) { - if (scale <= 0) { - wl_resource_post_error(resource, WL_SURFACE_ERROR_INVALID_SCALE, - "Specified scale value (%d) is not positive", scale); - return; - } - struct wlr_surface *surface = wlr_surface_from_resource(resource); - surface->pending.committed |= WLR_SURFACE_STATE_SCALE; - surface->pending.scale = scale; -} - -static void surface_handle_damage_buffer(struct wl_client *client, - struct wl_resource *resource, - int32_t x, int32_t y, int32_t width, - int32_t height) { - struct wlr_surface *surface = wlr_surface_from_resource(resource); - if (width < 0 || height < 0) { - return; - } - surface->pending.committed |= WLR_SURFACE_STATE_BUFFER_DAMAGE; - pixman_region32_union_rect(&surface->pending.buffer_damage, - &surface->pending.buffer_damage, - x, y, width, height); -} - -static const struct wl_surface_interface surface_implementation = { - .destroy = surface_handle_destroy, - .attach = surface_handle_attach, - .damage = surface_handle_damage, - .frame = surface_handle_frame, - .set_opaque_region = surface_handle_set_opaque_region, - .set_input_region = surface_handle_set_input_region, - .commit = surface_handle_commit, - .set_buffer_transform = surface_handle_set_buffer_transform, - .set_buffer_scale = surface_handle_set_buffer_scale, - .damage_buffer = surface_handle_damage_buffer -}; - -struct wlr_surface *wlr_surface_from_resource(struct wl_resource *resource) { - assert(wl_resource_instance_of(resource, &wl_surface_interface, - &surface_implementation)); - return wl_resource_get_user_data(resource); -} - -static void surface_state_init(struct wlr_surface_state *state) { - state->scale = 1; - state->transform = WL_OUTPUT_TRANSFORM_NORMAL; - - wl_list_init(&state->subsurfaces_above); - wl_list_init(&state->subsurfaces_below); - - wl_list_init(&state->frame_callback_list); - - pixman_region32_init(&state->surface_damage); - pixman_region32_init(&state->buffer_damage); - pixman_region32_init(&state->opaque); - pixman_region32_init_rect(&state->input, - INT32_MIN, INT32_MIN, UINT32_MAX, UINT32_MAX); -} - -static void surface_state_finish(struct wlr_surface_state *state) { - wlr_buffer_unlock(state->buffer); - - struct wl_resource *resource, *tmp; - wl_resource_for_each_safe(resource, tmp, &state->frame_callback_list) { - wl_resource_destroy(resource); - } - - pixman_region32_fini(&state->surface_damage); - pixman_region32_fini(&state->buffer_damage); - pixman_region32_fini(&state->opaque); - pixman_region32_fini(&state->input); -} - -static void surface_state_destroy_cached(struct wlr_surface_state *state) { - surface_state_finish(state); - wl_list_remove(&state->cached_state_link); - free(state); -} - -static void subsurface_unmap(struct wlr_subsurface *subsurface); - -static void subsurface_destroy(struct wlr_subsurface *subsurface) { - if (subsurface == NULL) { - return; - } - - if (subsurface->has_cache) { - wlr_surface_unlock_cached(subsurface->surface, - subsurface->cached_seq); - } - - subsurface_unmap(subsurface); - - wlr_signal_emit_safe(&subsurface->events.destroy, subsurface); - - wl_list_remove(&subsurface->surface_destroy.link); - wl_list_remove(&subsurface->surface_client_commit.link); - - if (subsurface->parent) { - wl_list_remove(&subsurface->current.link); - wl_list_remove(&subsurface->pending.link); - wl_list_remove(&subsurface->parent_destroy.link); - } - - wl_resource_set_user_data(subsurface->resource, NULL); - if (subsurface->surface) { - subsurface->surface->role_data = NULL; - } - free(subsurface); -} - -static void surface_output_destroy(struct wlr_surface_output *surface_output); - -static void surface_handle_resource_destroy(struct wl_resource *resource) { - struct wlr_surface *surface = wlr_surface_from_resource(resource); - - struct wlr_surface_output *surface_output, *surface_output_tmp; - wl_list_for_each_safe(surface_output, surface_output_tmp, - &surface->current_outputs, link) { - surface_output_destroy(surface_output); - } - - wlr_signal_emit_safe(&surface->events.destroy, surface); - - wlr_addon_set_finish(&surface->addons); - - struct wlr_surface_state *cached, *cached_tmp; - wl_list_for_each_safe(cached, cached_tmp, &surface->cached, cached_state_link) { - surface_state_destroy_cached(cached); - } - - wl_list_remove(&surface->renderer_destroy.link); - surface_state_finish(&surface->pending); - surface_state_finish(&surface->current); - pixman_region32_fini(&surface->buffer_damage); - pixman_region32_fini(&surface->external_damage); - pixman_region32_fini(&surface->opaque_region); - pixman_region32_fini(&surface->input_region); - if (surface->buffer != NULL) { - wlr_buffer_unlock(&surface->buffer->base); - } - free(surface); -} - -static void surface_handle_renderer_destroy(struct wl_listener *listener, - void *data) { - struct wlr_surface *surface = - wl_container_of(listener, surface, renderer_destroy); - wl_resource_destroy(surface->resource); -} - -struct wlr_surface *surface_create(struct wl_client *client, - uint32_t version, uint32_t id, struct wlr_renderer *renderer) { - struct wlr_surface *surface = calloc(1, sizeof(struct wlr_surface)); - if (!surface) { - wl_client_post_no_memory(client); - return NULL; - } - surface->resource = wl_resource_create(client, &wl_surface_interface, - version, id); - if (surface->resource == NULL) { - free(surface); - wl_client_post_no_memory(client); - return NULL; - } - wl_resource_set_implementation(surface->resource, &surface_implementation, - surface, surface_handle_resource_destroy); - - wlr_log(WLR_DEBUG, "New wlr_surface %p (res %p)", surface, surface->resource); - - surface->renderer = renderer; - - surface_state_init(&surface->current); - surface_state_init(&surface->pending); - surface->pending.seq = 1; - - wl_signal_init(&surface->events.client_commit); - wl_signal_init(&surface->events.commit); - wl_signal_init(&surface->events.destroy); - wl_signal_init(&surface->events.new_subsurface); - wl_list_init(&surface->current_outputs); - wl_list_init(&surface->cached); - pixman_region32_init(&surface->buffer_damage); - pixman_region32_init(&surface->external_damage); - pixman_region32_init(&surface->opaque_region); - pixman_region32_init(&surface->input_region); - wlr_addon_set_init(&surface->addons); - - wl_signal_add(&renderer->events.destroy, &surface->renderer_destroy); - surface->renderer_destroy.notify = surface_handle_renderer_destroy; - - return surface; -} - -struct wlr_texture *wlr_surface_get_texture(struct wlr_surface *surface) { - if (surface->buffer == NULL) { - return NULL; - } - return surface->buffer->texture; -} - -bool wlr_surface_has_buffer(struct wlr_surface *surface) { - return wlr_surface_get_texture(surface) != NULL; -} - -bool wlr_surface_set_role(struct wlr_surface *surface, - const struct wlr_surface_role *role, void *role_data, - struct wl_resource *error_resource, uint32_t error_code) { - assert(role != NULL); - - if (surface->role != NULL && surface->role != role) { - if (error_resource != NULL) { - wl_resource_post_error(error_resource, error_code, - "Cannot assign role %s to wl_surface@%" PRIu32 ", already has role %s\n", - role->name, wl_resource_get_id(surface->resource), - surface->role->name); - } - return false; - } - if (surface->role_data != NULL && surface->role_data != role_data) { - wl_resource_post_error(error_resource, error_code, - "Cannot reassign role %s to wl_surface@%" PRIu32 "," - "role object still exists", role->name, - wl_resource_get_id(surface->resource)); - return false; - } - - surface->role = role; - surface->role_data = role_data; - return true; -} - -uint32_t wlr_surface_lock_pending(struct wlr_surface *surface) { - surface->pending.cached_state_locks++; - return surface->pending.seq; -} - -void wlr_surface_unlock_cached(struct wlr_surface *surface, uint32_t seq) { - if (surface->pending.seq == seq) { - assert(surface->pending.cached_state_locks > 0); - surface->pending.cached_state_locks--; - return; - } - - bool found = false; - struct wlr_surface_state *cached; - wl_list_for_each(cached, &surface->cached, cached_state_link) { - if (cached->seq == seq) { - found = true; - break; - } - } - assert(found); - - assert(cached->cached_state_locks > 0); - cached->cached_state_locks--; - - if (cached->cached_state_locks != 0) { - return; - } - - if (cached->cached_state_link.prev != &surface->cached) { - // This isn't the first cached state. This means we're blocked on a - // previous cached state. - return; - } - - // TODO: consider merging all committed states together - struct wlr_surface_state *next, *tmp; - wl_list_for_each_safe(next, tmp, &surface->cached, cached_state_link) { - if (next->cached_state_locks > 0) { - break; - } - - surface_commit_state(surface, next); - surface_state_destroy_cached(next); - } -} - -static const struct wl_subsurface_interface subsurface_implementation; - -static struct wlr_subsurface *subsurface_from_resource( - struct wl_resource *resource) { - assert(wl_resource_instance_of(resource, &wl_subsurface_interface, - &subsurface_implementation)); - return wl_resource_get_user_data(resource); -} - -static void subsurface_resource_destroy(struct wl_resource *resource) { - struct wlr_subsurface *subsurface = subsurface_from_resource(resource); - subsurface_destroy(subsurface); -} - -static void subsurface_handle_destroy(struct wl_client *client, - struct wl_resource *resource) { - wl_resource_destroy(resource); -} - -static void subsurface_handle_set_position(struct wl_client *client, - struct wl_resource *resource, int32_t x, int32_t y) { - struct wlr_subsurface *subsurface = subsurface_from_resource(resource); - if (subsurface == NULL) { - return; - } - - subsurface->pending.x = x; - subsurface->pending.y = y; -} - -static struct wlr_subsurface *subsurface_find_sibling( - struct wlr_subsurface *subsurface, struct wlr_surface *surface) { - struct wlr_surface *parent = subsurface->parent; - - struct wlr_subsurface *sibling; - wl_list_for_each(sibling, &parent->pending.subsurfaces_below, pending.link) { - if (sibling->surface == surface && sibling != subsurface) { - return sibling; - } - } - wl_list_for_each(sibling, &parent->pending.subsurfaces_above, pending.link) { - if (sibling->surface == surface && sibling != subsurface) { - return sibling; - } - } - - return NULL; -} - -static void subsurface_handle_place_above(struct wl_client *client, - struct wl_resource *resource, struct wl_resource *sibling_resource) { - struct wlr_subsurface *subsurface = subsurface_from_resource(resource); - if (subsurface == NULL) { - return; - } - - struct wlr_surface *sibling_surface = - wlr_surface_from_resource(sibling_resource); - - struct wl_list *node; - if (sibling_surface == subsurface->parent) { - node = &subsurface->parent->pending.subsurfaces_above; - } else { - struct wlr_subsurface *sibling = - subsurface_find_sibling(subsurface, sibling_surface); - if (!sibling) { - wl_resource_post_error(subsurface->resource, - WL_SUBSURFACE_ERROR_BAD_SURFACE, - "%s: wl_surface@%" PRIu32 "is not a parent or sibling", - "place_above", wl_resource_get_id(sibling_resource)); - return; - } - node = &sibling->pending.link; - } - - wl_list_remove(&subsurface->pending.link); - wl_list_insert(node, &subsurface->pending.link); - - subsurface->reordered = true; -} - -static void subsurface_handle_place_below(struct wl_client *client, - struct wl_resource *resource, struct wl_resource *sibling_resource) { - struct wlr_subsurface *subsurface = subsurface_from_resource(resource); - if (subsurface == NULL) { - return; - } - - struct wlr_surface *sibling_surface = - wlr_surface_from_resource(sibling_resource); - - struct wl_list *node; - if (sibling_surface == subsurface->parent) { - node = &subsurface->parent->pending.subsurfaces_below; - } else { - struct wlr_subsurface *sibling = - subsurface_find_sibling(subsurface, sibling_surface); - if (!sibling) { - wl_resource_post_error(subsurface->resource, - WL_SUBSURFACE_ERROR_BAD_SURFACE, - "%s: wl_surface@%" PRIu32 " is not a parent or sibling", - "place_below", wl_resource_get_id(sibling_resource)); - return; - } - node = &sibling->pending.link; - } - - wl_list_remove(&subsurface->pending.link); - wl_list_insert(node->prev, &subsurface->pending.link); - - subsurface->reordered = true; -} - -static void subsurface_handle_set_sync(struct wl_client *client, - struct wl_resource *resource) { - struct wlr_subsurface *subsurface = subsurface_from_resource(resource); - if (subsurface == NULL) { - return; - } - - subsurface->synchronized = true; -} - -static void subsurface_handle_set_desync(struct wl_client *client, - struct wl_resource *resource) { - struct wlr_subsurface *subsurface = subsurface_from_resource(resource); - if (subsurface == NULL) { - return; - } - - if (subsurface->synchronized) { - subsurface->synchronized = false; - - if (!subsurface_is_synchronized(subsurface) && - subsurface->has_cache) { - wlr_surface_unlock_cached(subsurface->surface, - subsurface->cached_seq); - subsurface->has_cache = false; - } - } -} - -static const struct wl_subsurface_interface subsurface_implementation = { - .destroy = subsurface_handle_destroy, - .set_position = subsurface_handle_set_position, - .place_above = subsurface_handle_place_above, - .place_below = subsurface_handle_place_below, - .set_sync = subsurface_handle_set_sync, - .set_desync = subsurface_handle_set_desync, -}; - -/** - * Checks if this subsurface needs to be marked as mapped. This can happen if: - * - The subsurface has a buffer - * - Its parent is mapped - */ -static void subsurface_consider_map(struct wlr_subsurface *subsurface, - bool check_parent) { - if (subsurface->mapped || !wlr_surface_has_buffer(subsurface->surface)) { - return; - } - - if (check_parent) { - if (subsurface->parent == NULL) { - return; - } - if (wlr_surface_is_subsurface(subsurface->parent)) { - struct wlr_subsurface *parent = - wlr_subsurface_from_wlr_surface(subsurface->parent); - if (parent == NULL || !parent->mapped) { - return; - } - } - } - - // Now we can map the subsurface - wlr_signal_emit_safe(&subsurface->events.map, subsurface); - subsurface->mapped = true; - - // Try mapping all children too - struct wlr_subsurface *child; - wl_list_for_each(child, &subsurface->surface->current.subsurfaces_below, - current.link) { - subsurface_consider_map(child, false); - } - wl_list_for_each(child, &subsurface->surface->current.subsurfaces_above, - current.link) { - subsurface_consider_map(child, false); - } -} - -static void subsurface_unmap(struct wlr_subsurface *subsurface) { - if (!subsurface->mapped) { - return; - } - - wlr_signal_emit_safe(&subsurface->events.unmap, subsurface); - subsurface->mapped = false; - - // Unmap all children - struct wlr_subsurface *child; - wl_list_for_each(child, &subsurface->surface->current.subsurfaces_below, - current.link) { - subsurface_unmap(child); - } - wl_list_for_each(child, &subsurface->surface->current.subsurfaces_above, - current.link) { - subsurface_unmap(child); - } -} - -static void subsurface_role_commit(struct wlr_surface *surface) { - struct wlr_subsurface *subsurface = - wlr_subsurface_from_wlr_surface(surface); - if (subsurface == NULL) { - return; - } - - subsurface_consider_map(subsurface, true); -} - -static void subsurface_role_precommit(struct wlr_surface *surface) { - struct wlr_subsurface *subsurface = - wlr_subsurface_from_wlr_surface(surface); - if (subsurface == NULL) { - return; - } - - if (surface->pending.committed & WLR_SURFACE_STATE_BUFFER && - surface->pending.buffer == NULL) { - // This is a NULL commit - subsurface_unmap(subsurface); - } -} - -const struct wlr_surface_role subsurface_role = { - .name = "wl_subsurface", - .commit = subsurface_role_commit, - .precommit = subsurface_role_precommit, -}; - -static void subsurface_handle_parent_destroy(struct wl_listener *listener, - void *data) { - struct wlr_subsurface *subsurface = - wl_container_of(listener, subsurface, parent_destroy); - subsurface_unmap(subsurface); - wl_list_remove(&subsurface->current.link); - wl_list_remove(&subsurface->pending.link); - wl_list_remove(&subsurface->parent_destroy.link); - subsurface->parent = NULL; -} - -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); -} - -static void subsurface_handle_surface_client_commit( - struct wl_listener *listener, void *data) { - struct wlr_subsurface *subsurface = - wl_container_of(listener, subsurface, surface_client_commit); - struct wlr_surface *surface = subsurface->surface; - - if (subsurface_is_synchronized(subsurface)) { - if (subsurface->has_cache) { - // We already lock a previous commit. The prevents any future - // commit to be applied before we release the previous commit. - return; - } - subsurface->has_cache = true; - subsurface->cached_seq = wlr_surface_lock_pending(surface); - } -} - -struct wlr_subsurface *subsurface_create(struct wlr_surface *surface, - struct wlr_surface *parent, uint32_t version, uint32_t id) { - struct wl_client *client = wl_resource_get_client(surface->resource); - - struct wlr_subsurface *subsurface = - calloc(1, sizeof(struct wlr_subsurface)); - if (!subsurface) { - wl_client_post_no_memory(client); - return NULL; - } - subsurface->synchronized = true; - subsurface->surface = surface; - subsurface->resource = - wl_resource_create(client, &wl_subsurface_interface, version, id); - if (subsurface->resource == NULL) { - free(subsurface); - wl_client_post_no_memory(client); - return NULL; - } - wl_resource_set_implementation(subsurface->resource, - &subsurface_implementation, subsurface, subsurface_resource_destroy); - - wl_signal_init(&subsurface->events.destroy); - wl_signal_init(&subsurface->events.map); - wl_signal_init(&subsurface->events.unmap); - - wl_signal_add(&surface->events.destroy, &subsurface->surface_destroy); - subsurface->surface_destroy.notify = subsurface_handle_surface_destroy; - wl_signal_add(&surface->events.client_commit, - &subsurface->surface_client_commit); - subsurface->surface_client_commit.notify = - subsurface_handle_surface_client_commit; - - // link parent - subsurface->parent = parent; - wl_signal_add(&parent->events.destroy, &subsurface->parent_destroy); - subsurface->parent_destroy.notify = subsurface_handle_parent_destroy; - - wl_list_init(&subsurface->current.link); - wl_list_insert(parent->pending.subsurfaces_above.prev, - &subsurface->pending.link); - - surface->role_data = subsurface; - - return subsurface; -} - - -struct wlr_surface *wlr_surface_get_root_surface(struct wlr_surface *surface) { - while (wlr_surface_is_subsurface(surface)) { - struct wlr_subsurface *subsurface = - wlr_subsurface_from_wlr_surface(surface); - if (subsurface == NULL) { - break; - } - if (subsurface->parent == NULL) { - return NULL; - } - surface = subsurface->parent; - } - return surface; -} - -bool wlr_surface_point_accepts_input(struct wlr_surface *surface, - double sx, double sy) { - return sx >= 0 && sx < surface->current.width && - sy >= 0 && sy < surface->current.height && - pixman_region32_contains_point(&surface->current.input, floor(sx), floor(sy), NULL); -} - -struct wlr_surface *wlr_surface_surface_at(struct wlr_surface *surface, - double sx, double sy, double *sub_x, double *sub_y) { - struct wlr_subsurface *subsurface; - wl_list_for_each_reverse(subsurface, &surface->current.subsurfaces_above, - current.link) { - if (!subsurface->mapped) { - continue; - } - - double _sub_x = subsurface->current.x; - double _sub_y = subsurface->current.y; - struct wlr_surface *sub = wlr_surface_surface_at(subsurface->surface, - sx - _sub_x, sy - _sub_y, sub_x, sub_y); - if (sub != NULL) { - return sub; - } - } - - if (wlr_surface_point_accepts_input(surface, sx, sy)) { - if (sub_x) { - *sub_x = sx; - } - if (sub_y) { - *sub_y = sy; - } - return surface; - } - - wl_list_for_each_reverse(subsurface, &surface->current.subsurfaces_below, - current.link) { - if (!subsurface->mapped) { - continue; - } - - double _sub_x = subsurface->current.x; - double _sub_y = subsurface->current.y; - struct wlr_surface *sub = wlr_surface_surface_at(subsurface->surface, - sx - _sub_x, sy - _sub_y, sub_x, sub_y); - if (sub != NULL) { - return sub; - } - } - - return NULL; -} - -static void surface_output_destroy(struct wlr_surface_output *surface_output) { - wl_list_remove(&surface_output->bind.link); - wl_list_remove(&surface_output->destroy.link); - wl_list_remove(&surface_output->link); - - free(surface_output); -} - -static void surface_handle_output_bind(struct wl_listener *listener, - void *data) { - struct wlr_output_event_bind *evt = data; - struct wlr_surface_output *surface_output = - wl_container_of(listener, surface_output, bind); - struct wl_client *client = wl_resource_get_client( - surface_output->surface->resource); - if (client == wl_resource_get_client(evt->resource)) { - wl_surface_send_enter(surface_output->surface->resource, evt->resource); - } -} - -static void surface_handle_output_destroy(struct wl_listener *listener, - void *data) { - struct wlr_surface_output *surface_output = - wl_container_of(listener, surface_output, destroy); - surface_output_destroy(surface_output); -} - -void wlr_surface_send_enter(struct wlr_surface *surface, - struct wlr_output *output) { - struct wl_client *client = wl_resource_get_client(surface->resource); - struct wlr_surface_output *surface_output; - struct wl_resource *resource; - - wl_list_for_each(surface_output, &surface->current_outputs, link) { - if (surface_output->output == output) { - return; - } - } - - surface_output = calloc(1, sizeof(struct wlr_surface_output)); - if (surface_output == NULL) { - return; - } - surface_output->bind.notify = surface_handle_output_bind; - surface_output->destroy.notify = surface_handle_output_destroy; - - wl_signal_add(&output->events.bind, &surface_output->bind); - wl_signal_add(&output->events.destroy, &surface_output->destroy); - - surface_output->surface = surface; - surface_output->output = output; - wl_list_insert(&surface->current_outputs, &surface_output->link); - - wl_resource_for_each(resource, &output->resources) { - if (client == wl_resource_get_client(resource)) { - wl_surface_send_enter(surface->resource, resource); - } - } -} - -void wlr_surface_send_leave(struct wlr_surface *surface, - struct wlr_output *output) { - struct wl_client *client = wl_resource_get_client(surface->resource); - struct wlr_surface_output *surface_output, *tmp; - struct wl_resource *resource; - - wl_list_for_each_safe(surface_output, tmp, - &surface->current_outputs, link) { - if (surface_output->output == output) { - surface_output_destroy(surface_output); - wl_resource_for_each(resource, &output->resources) { - if (client == wl_resource_get_client(resource)) { - wl_surface_send_leave(surface->resource, resource); - } - } - break; - } - } -} - -void wlr_surface_send_frame_done(struct wlr_surface *surface, - const struct timespec *when) { - struct wl_resource *resource, *tmp; - wl_resource_for_each_safe(resource, tmp, - &surface->current.frame_callback_list) { - wl_callback_send_done(resource, timespec_to_msec(when)); - wl_resource_destroy(resource); - } -} - -static void surface_for_each_surface(struct wlr_surface *surface, int x, int y, - wlr_surface_iterator_func_t iterator, void *user_data) { - struct wlr_subsurface *subsurface; - wl_list_for_each(subsurface, &surface->current.subsurfaces_below, current.link) { - if (!subsurface->mapped) { - continue; - } - - struct wlr_subsurface_parent_state *state = &subsurface->current; - int sx = state->x; - int sy = state->y; - - surface_for_each_surface(subsurface->surface, x + sx, y + sy, - iterator, user_data); - } - - iterator(surface, x, y, user_data); - - wl_list_for_each(subsurface, &surface->current.subsurfaces_above, current.link) { - if (!subsurface->mapped) { - continue; - } - - struct wlr_subsurface_parent_state *state = &subsurface->current; - int sx = state->x; - int sy = state->y; - - surface_for_each_surface(subsurface->surface, x + sx, y + sy, - iterator, user_data); - } -} - -void wlr_surface_for_each_surface(struct wlr_surface *surface, - wlr_surface_iterator_func_t iterator, void *user_data) { - surface_for_each_surface(surface, 0, 0, iterator, user_data); -} - -struct bound_acc { - int32_t min_x, min_y; - int32_t max_x, max_y; -}; - -static void handle_bounding_box_surface(struct wlr_surface *surface, - int x, int y, void *data) { - struct bound_acc *acc = data; - - acc->min_x = min(x, acc->min_x); - acc->min_y = min(y, acc->min_y); - - acc->max_x = max(x + surface->current.width, acc->max_x); - acc->max_y = max(y + surface->current.height, acc->max_y); -} - -void wlr_surface_get_extends(struct wlr_surface *surface, struct wlr_box *box) { - struct bound_acc acc = { - .min_x = 0, - .min_y = 0, - .max_x = surface->current.width, - .max_y = surface->current.height, - }; - - wlr_surface_for_each_surface(surface, handle_bounding_box_surface, &acc); - - box->x = acc.min_x; - box->y = acc.min_y; - box->width = acc.max_x - acc.min_x; - box->height = acc.max_y - acc.min_y; -} - -static void crop_region(pixman_region32_t *dst, pixman_region32_t *src, - const struct wlr_box *box) { - pixman_region32_intersect_rect(dst, src, - box->x, box->y, box->width, box->height); - pixman_region32_translate(dst, -box->x, -box->y); -} - -void wlr_surface_get_effective_damage(struct wlr_surface *surface, - pixman_region32_t *damage) { - pixman_region32_clear(damage); - - // Transform and copy the buffer damage in terms of surface coordinates. - wlr_region_transform(damage, &surface->buffer_damage, - surface->current.transform, surface->current.buffer_width, - surface->current.buffer_height); - wlr_region_scale(damage, damage, 1.0 / (float)surface->current.scale); - - if (surface->current.viewport.has_src) { - struct wlr_box src_box = { - .x = floor(surface->current.viewport.src.x), - .y = floor(surface->current.viewport.src.y), - .width = ceil(surface->current.viewport.src.width), - .height = ceil(surface->current.viewport.src.height), - }; - crop_region(damage, damage, &src_box); - } - if (surface->current.viewport.has_dst) { - int src_width, src_height; - surface_state_viewport_src_size(&surface->current, - &src_width, &src_height); - float scale_x = (float)surface->current.viewport.dst_width / src_width; - float scale_y = (float)surface->current.viewport.dst_height / src_height; - wlr_region_scale_xy(damage, damage, scale_x, scale_y); - } - - pixman_region32_union(damage, damage, &surface->external_damage); -} - -void wlr_surface_get_buffer_source_box(struct wlr_surface *surface, - struct wlr_fbox *box) { - box->x = box->y = 0; - box->width = surface->current.buffer_width; - box->height = surface->current.buffer_height; - - if (surface->current.viewport.has_src) { - box->x = surface->current.viewport.src.x * surface->current.scale; - box->y = surface->current.viewport.src.y * surface->current.scale; - box->width = surface->current.viewport.src.width * surface->current.scale; - box->height = surface->current.viewport.src.height * surface->current.scale; - - int width, height; - surface_state_transformed_buffer_size(&surface->current, &width, &height); - wlr_fbox_transform(box, box, - wlr_output_transform_invert(surface->current.transform), - width, height); - } -} From e94e16ba5dd95e1fb27cdf46da9e3b42b4869521 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Thu, 13 Jan 2022 11:55:09 +0300 Subject: [PATCH 143/188] surface: move decl to wlr_{sub,}compositor.h --- include/wlr/types/wlr_compositor.h | 273 +++++++++++++++++++++- include/wlr/types/wlr_subcompositor.h | 40 ++++ include/wlr/types/wlr_surface.h | 324 +------------------------- 3 files changed, 313 insertions(+), 324 deletions(-) diff --git a/include/wlr/types/wlr_compositor.h b/include/wlr/types/wlr_compositor.h index 756f6f30dd..0146a8ab59 100644 --- a/include/wlr/types/wlr_compositor.h +++ b/include/wlr/types/wlr_compositor.h @@ -9,10 +9,161 @@ #ifndef WLR_TYPES_WLR_COMPOSITOR_H #define WLR_TYPES_WLR_COMPOSITOR_H +#include +#include +#include +#include #include -#include +#include +#include +#include -struct wlr_surface; +enum wlr_surface_state_field { + WLR_SURFACE_STATE_BUFFER = 1 << 0, + WLR_SURFACE_STATE_SURFACE_DAMAGE = 1 << 1, + WLR_SURFACE_STATE_BUFFER_DAMAGE = 1 << 2, + WLR_SURFACE_STATE_OPAQUE_REGION = 1 << 3, + WLR_SURFACE_STATE_INPUT_REGION = 1 << 4, + WLR_SURFACE_STATE_TRANSFORM = 1 << 5, + WLR_SURFACE_STATE_SCALE = 1 << 6, + WLR_SURFACE_STATE_FRAME_CALLBACK_LIST = 1 << 7, + WLR_SURFACE_STATE_VIEWPORT = 1 << 8, +}; + +struct wlr_surface_state { + uint32_t committed; // enum wlr_surface_state_field + // Sequence number of the surface state. Incremented on each commit, may + // overflow. + uint32_t seq; + + struct wlr_buffer *buffer; + int32_t dx, dy; // relative to previous position + pixman_region32_t surface_damage, buffer_damage; // clipped to bounds + pixman_region32_t opaque, input; + enum wl_output_transform transform; + int32_t scale; + struct wl_list frame_callback_list; // wl_resource + + int width, height; // in surface-local coordinates + int buffer_width, buffer_height; + + struct wl_list subsurfaces_below; + struct wl_list subsurfaces_above; + + /** + * The viewport is applied after the surface transform and scale. + * + * If has_src is true, the surface content is cropped to the provided + * rectangle. If has_dst is true, the surface content is scaled to the + * provided rectangle. + */ + struct { + bool has_src, has_dst; + // In coordinates after scale/transform are applied, but before the + // destination rectangle is applied + struct wlr_fbox src; + int dst_width, dst_height; // in surface-local coordinates + } viewport; + + // Number of locks that prevent this surface state from being committed. + size_t cached_state_locks; + struct wl_list cached_state_link; // wlr_surface.cached +}; + +struct wlr_surface_role { + const char *name; + void (*commit)(struct wlr_surface *surface); + void (*precommit)(struct wlr_surface *surface); +}; + +struct wlr_surface_output { + struct wlr_surface *surface; + struct wlr_output *output; + + struct wl_list link; // wlr_surface::current_outputs + struct wl_listener bind; + struct wl_listener destroy; +}; + +struct wlr_surface { + struct wl_resource *resource; + struct wlr_renderer *renderer; + /** + * The surface's buffer, if any. A surface has an attached buffer when it + * commits with a non-null buffer in its pending state. A surface will not + * have a buffer if it has never committed one, has committed a null buffer, + * or something went wrong with uploading the buffer. + */ + struct wlr_client_buffer *buffer; + /** + * The buffer position, in surface-local units. + */ + int sx, sy; + /** + * The last commit's buffer damage, in buffer-local coordinates. This + * contains both the damage accumulated by the client via + * `wlr_surface_state.surface_damage` and `wlr_surface_state.buffer_damage`. + * If the buffer has been resized, the whole buffer is damaged. + * + * This region needs to be scaled and transformed into output coordinates, + * just like the buffer's texture. In addition, if the buffer has shrunk the + * old size needs to be damaged and if the buffer has moved the old and new + * positions need to be damaged. + */ + pixman_region32_t buffer_damage; + /** + * The last commit's damage caused by surface and its subsurfaces' + * movement, in surface-local coordinates. + */ + pixman_region32_t external_damage; + /** + * The current opaque region, in surface-local coordinates. It is clipped to + * the surface bounds. If the surface's buffer is using a fully opaque + * format, this is set to the whole surface. + */ + pixman_region32_t opaque_region; + /** + * The current input region, in surface-local coordinates. It is clipped to + * the surface bounds. + */ + pixman_region32_t input_region; + /** + * `current` contains the current, committed surface state. `pending` + * accumulates state changes from the client between commits and shouldn't + * be accessed by the compositor directly. + */ + struct wlr_surface_state current, pending; + + struct wl_list cached; // wlr_surface_state.cached_link + + const struct wlr_surface_role *role; // the lifetime-bound role or NULL + void *role_data; // role-specific data + + struct { + struct wl_signal client_commit; + struct wl_signal commit; + struct wl_signal new_subsurface; + struct wl_signal destroy; + } events; + + struct wl_list current_outputs; // wlr_surface_output::link + + struct wlr_addon_set addons; + void *data; + + // private state + + struct wl_listener renderer_destroy; + + struct { + int32_t scale; + enum wl_output_transform transform; + int width, height; + int buffer_width, buffer_height; + } previous; +}; + +struct wlr_renderer; struct wlr_compositor { struct wl_global *global; @@ -26,6 +177,124 @@ struct wlr_compositor { } events; }; +typedef void (*wlr_surface_iterator_func_t)(struct wlr_surface *surface, + int sx, int sy, void *data); + +/** + * Set the lifetime role for this surface. Returns 0 on success or -1 if the + * role cannot be set. + */ +bool wlr_surface_set_role(struct wlr_surface *surface, + const struct wlr_surface_role *role, void *role_data, + struct wl_resource *error_resource, uint32_t error_code); + +/** + * Whether or not this surface currently has an attached buffer. A surface has + * an attached buffer when it commits with a non-null buffer in its pending + * state. A surface will not have a buffer if it has never committed one, has + * committed a null buffer, or something went wrong with uploading the buffer. + */ +bool wlr_surface_has_buffer(struct wlr_surface *surface); + +/** + * Get the texture of the buffer currently attached to this surface. Returns + * NULL if no buffer is currently attached or if something went wrong with + * uploading the buffer. + */ +struct wlr_texture *wlr_surface_get_texture(struct wlr_surface *surface); + +/** + * Get the root of the subsurface tree for this surface. Can return NULL if + * a surface in the tree has been destroyed. + */ +struct wlr_surface *wlr_surface_get_root_surface(struct wlr_surface *surface); + +/** + * Check if the surface accepts input events at the given surface-local + * coordinates. Does not check the surface's subsurfaces. + */ +bool wlr_surface_point_accepts_input(struct wlr_surface *surface, + double sx, double sy); + +/** + * Find a surface in this surface's tree that accepts input events and has all + * parents mapped (except this surface, which can be unmapped) at the given + * surface-local coordinates. Returns the surface and coordinates in the leaf + * surface coordinate system or NULL if no surface is found at that location. + */ +struct wlr_surface *wlr_surface_surface_at(struct wlr_surface *surface, + double sx, double sy, double *sub_x, double *sub_y); + +void wlr_surface_send_enter(struct wlr_surface *surface, + struct wlr_output *output); + +void wlr_surface_send_leave(struct wlr_surface *surface, + struct wlr_output *output); + +void wlr_surface_send_frame_done(struct wlr_surface *surface, + const struct timespec *when); + +/** + * Get the bounding box that contains the surface and all subsurfaces in + * surface coordinates. + * X and y may be negative, if there are subsurfaces with negative position. + */ +void wlr_surface_get_extends(struct wlr_surface *surface, struct wlr_box *box); + +/** + * Get the wlr_surface corresponding to a wl_surface resource. This asserts + * that the resource is a valid wl_surface resource created by wlroots and + * will never return NULL. + */ +struct wlr_surface *wlr_surface_from_resource(struct wl_resource *resource); + +/** + * Call `iterator` on each mapped surface in the surface tree (whether or not + * this surface is mapped), with the surface's position relative to the root + * surface. The function is called from root to leaves (in rendering order). + */ +void wlr_surface_for_each_surface(struct wlr_surface *surface, + wlr_surface_iterator_func_t iterator, void *user_data); + +/** + * Get the effective surface damage in surface-local coordinate space. Besides + * buffer damage, this includes damage induced by resizing and moving the + * surface and its subsurfaces. The resulting damage is not expected to be + * bounded by the surface itself. + */ +void wlr_surface_get_effective_damage(struct wlr_surface *surface, + pixman_region32_t *damage); + +/** + * Get the source rectangle describing the region of the buffer that needs to + * be sampled to render this surface's current state. The box is in + * buffer-local coordinates. + * + * If the viewport's source rectangle is unset, the position is zero and the + * size is the buffer's. + */ +void wlr_surface_get_buffer_source_box(struct wlr_surface *surface, + struct wlr_fbox *box); + +/** + * Acquire a lock for the pending surface state. + * + * The state won't be committed before the caller releases the lock. Instead, + * the state becomes cached. The caller needs to use wlr_surface_unlock_cached + * to release the lock. + * + * Returns a surface commit sequence number for the cached state. + */ +uint32_t wlr_surface_lock_pending(struct wlr_surface *surface); + +/** + * Release a lock for a cached state. + * + * Callers should not assume that the cached state will immediately be + * committed. Another caller may still have an active lock. + */ +void wlr_surface_unlock_cached(struct wlr_surface *surface, uint32_t seq); + struct wlr_compositor *wlr_compositor_create(struct wl_display *display, struct wlr_renderer *renderer); diff --git a/include/wlr/types/wlr_subcompositor.h b/include/wlr/types/wlr_subcompositor.h index f9c887bb7d..d8e57ed835 100644 --- a/include/wlr/types/wlr_subcompositor.h +++ b/include/wlr/types/wlr_subcompositor.h @@ -9,10 +9,50 @@ #ifndef WLR_TYPES_WLR_SUBCOMPOSITOR_H #define WLR_TYPES_WLR_SUBCOMPOSITOR_H +#include +#include #include struct wlr_surface; +/** + * The sub-surface state describing the sub-surface's relationship with its + * parent. Contrary to other states, this one is not applied on surface commit. + * Instead, it's applied on parent surface commit. + */ +struct wlr_subsurface_parent_state { + int32_t x, y; + struct wl_list link; +}; + +struct wlr_subsurface { + struct wl_resource *resource; + struct wlr_surface *surface; + struct wlr_surface *parent; + + struct wlr_subsurface_parent_state current, pending; + + uint32_t cached_seq; + bool has_cache; + + bool synchronized; + bool reordered; + bool mapped; + bool added; + + struct wl_listener surface_destroy; + struct wl_listener surface_client_commit; + struct wl_listener parent_destroy; + + struct { + struct wl_signal destroy; + struct wl_signal map; + struct wl_signal unmap; + } events; + + void *data; +}; + struct wlr_subcompositor { struct wl_global *global; diff --git a/include/wlr/types/wlr_surface.h b/include/wlr/types/wlr_surface.h index ca1a55ac08..4a6801e8e8 100644 --- a/include/wlr/types/wlr_surface.h +++ b/include/wlr/types/wlr_surface.h @@ -1,322 +1,2 @@ -/* - * This an unstable interface of wlroots. No guarantees are made regarding the - * future consistency of this API. - */ -#ifndef WLR_USE_UNSTABLE -#error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" -#endif - -#ifndef WLR_TYPES_WLR_SURFACE_H -#define WLR_TYPES_WLR_SURFACE_H - -#include -#include -#include -#include -#include -#include -#include -#include - -enum wlr_surface_state_field { - WLR_SURFACE_STATE_BUFFER = 1 << 0, - WLR_SURFACE_STATE_SURFACE_DAMAGE = 1 << 1, - WLR_SURFACE_STATE_BUFFER_DAMAGE = 1 << 2, - WLR_SURFACE_STATE_OPAQUE_REGION = 1 << 3, - WLR_SURFACE_STATE_INPUT_REGION = 1 << 4, - WLR_SURFACE_STATE_TRANSFORM = 1 << 5, - WLR_SURFACE_STATE_SCALE = 1 << 6, - WLR_SURFACE_STATE_FRAME_CALLBACK_LIST = 1 << 7, - WLR_SURFACE_STATE_VIEWPORT = 1 << 8, -}; - -struct wlr_surface_state { - uint32_t committed; // enum wlr_surface_state_field - // Sequence number of the surface state. Incremented on each commit, may - // overflow. - uint32_t seq; - - struct wlr_buffer *buffer; - int32_t dx, dy; // relative to previous position - pixman_region32_t surface_damage, buffer_damage; // clipped to bounds - pixman_region32_t opaque, input; - enum wl_output_transform transform; - int32_t scale; - struct wl_list frame_callback_list; // wl_resource - - int width, height; // in surface-local coordinates - int buffer_width, buffer_height; - - struct wl_list subsurfaces_below; - struct wl_list subsurfaces_above; - - /** - * The viewport is applied after the surface transform and scale. - * - * If has_src is true, the surface content is cropped to the provided - * rectangle. If has_dst is true, the surface content is scaled to the - * provided rectangle. - */ - struct { - bool has_src, has_dst; - // In coordinates after scale/transform are applied, but before the - // destination rectangle is applied - struct wlr_fbox src; - int dst_width, dst_height; // in surface-local coordinates - } viewport; - - // Number of locks that prevent this surface state from being committed. - size_t cached_state_locks; - struct wl_list cached_state_link; // wlr_surface.cached -}; - -struct wlr_surface_role { - const char *name; - void (*commit)(struct wlr_surface *surface); - void (*precommit)(struct wlr_surface *surface); -}; - -struct wlr_surface_output { - struct wlr_surface *surface; - struct wlr_output *output; - - struct wl_list link; // wlr_surface::current_outputs - struct wl_listener bind; - struct wl_listener destroy; -}; - -struct wlr_surface { - struct wl_resource *resource; - struct wlr_renderer *renderer; - /** - * The surface's buffer, if any. A surface has an attached buffer when it - * commits with a non-null buffer in its pending state. A surface will not - * have a buffer if it has never committed one, has committed a null buffer, - * or something went wrong with uploading the buffer. - */ - struct wlr_client_buffer *buffer; - /** - * The buffer position, in surface-local units. - */ - int sx, sy; - /** - * The last commit's buffer damage, in buffer-local coordinates. This - * contains both the damage accumulated by the client via - * `wlr_surface_state.surface_damage` and `wlr_surface_state.buffer_damage`. - * If the buffer has been resized, the whole buffer is damaged. - * - * This region needs to be scaled and transformed into output coordinates, - * just like the buffer's texture. In addition, if the buffer has shrunk the - * old size needs to be damaged and if the buffer has moved the old and new - * positions need to be damaged. - */ - pixman_region32_t buffer_damage; - /** - * The last commit's damage caused by surface and its subsurfaces' - * movement, in surface-local coordinates. - */ - pixman_region32_t external_damage; - /** - * The current opaque region, in surface-local coordinates. It is clipped to - * the surface bounds. If the surface's buffer is using a fully opaque - * format, this is set to the whole surface. - */ - pixman_region32_t opaque_region; - /** - * The current input region, in surface-local coordinates. It is clipped to - * the surface bounds. - */ - pixman_region32_t input_region; - /** - * `current` contains the current, committed surface state. `pending` - * accumulates state changes from the client between commits and shouldn't - * be accessed by the compositor directly. - */ - struct wlr_surface_state current, pending; - - struct wl_list cached; // wlr_surface_state.cached_link - - const struct wlr_surface_role *role; // the lifetime-bound role or NULL - void *role_data; // role-specific data - - struct { - struct wl_signal client_commit; - struct wl_signal commit; - struct wl_signal new_subsurface; - struct wl_signal destroy; - } events; - - struct wl_list current_outputs; // wlr_surface_output::link - - struct wlr_addon_set addons; - void *data; - - // private state - - struct wl_listener renderer_destroy; - - struct { - int32_t scale; - enum wl_output_transform transform; - int width, height; - int buffer_width, buffer_height; - } previous; -}; - -/** - * The sub-surface state describing the sub-surface's relationship with its - * parent. Contrary to other states, this one is not applied on surface commit. - * Instead, it's applied on parent surface commit. - */ -struct wlr_subsurface_parent_state { - int32_t x, y; - struct wl_list link; -}; - -struct wlr_subsurface { - struct wl_resource *resource; - struct wlr_surface *surface; - struct wlr_surface *parent; - - struct wlr_subsurface_parent_state current, pending; - - uint32_t cached_seq; - bool has_cache; - - bool synchronized; - bool reordered; - bool mapped; - bool added; - - struct wl_listener surface_destroy; - struct wl_listener surface_client_commit; - struct wl_listener parent_destroy; - - struct { - struct wl_signal destroy; - struct wl_signal map; - struct wl_signal unmap; - } events; - - void *data; -}; - -typedef void (*wlr_surface_iterator_func_t)(struct wlr_surface *surface, - int sx, int sy, void *data); - -/** - * Set the lifetime role for this surface. Returns 0 on success or -1 if the - * role cannot be set. - */ -bool wlr_surface_set_role(struct wlr_surface *surface, - const struct wlr_surface_role *role, void *role_data, - struct wl_resource *error_resource, uint32_t error_code); - -/** - * Whether or not this surface currently has an attached buffer. A surface has - * an attached buffer when it commits with a non-null buffer in its pending - * state. A surface will not have a buffer if it has never committed one, has - * committed a null buffer, or something went wrong with uploading the buffer. - */ -bool wlr_surface_has_buffer(struct wlr_surface *surface); - -/** - * Get the texture of the buffer currently attached to this surface. Returns - * NULL if no buffer is currently attached or if something went wrong with - * uploading the buffer. - */ -struct wlr_texture *wlr_surface_get_texture(struct wlr_surface *surface); - -/** - * Get the root of the subsurface tree for this surface. Can return NULL if - * a surface in the tree has been destroyed. - */ -struct wlr_surface *wlr_surface_get_root_surface(struct wlr_surface *surface); - -/** - * Check if the surface accepts input events at the given surface-local - * coordinates. Does not check the surface's subsurfaces. - */ -bool wlr_surface_point_accepts_input(struct wlr_surface *surface, - double sx, double sy); - -/** - * Find a surface in this surface's tree that accepts input events and has all - * parents mapped (except this surface, which can be unmapped) at the given - * surface-local coordinates. Returns the surface and coordinates in the leaf - * surface coordinate system or NULL if no surface is found at that location. - */ -struct wlr_surface *wlr_surface_surface_at(struct wlr_surface *surface, - double sx, double sy, double *sub_x, double *sub_y); - -void wlr_surface_send_enter(struct wlr_surface *surface, - struct wlr_output *output); - -void wlr_surface_send_leave(struct wlr_surface *surface, - struct wlr_output *output); - -void wlr_surface_send_frame_done(struct wlr_surface *surface, - const struct timespec *when); - -/** - * Get the bounding box that contains the surface and all subsurfaces in - * surface coordinates. - * X and y may be negative, if there are subsurfaces with negative position. - */ -void wlr_surface_get_extends(struct wlr_surface *surface, struct wlr_box *box); - -/** - * Get the wlr_surface corresponding to a wl_surface resource. This asserts - * that the resource is a valid wl_surface resource created by wlroots and - * will never return NULL. - */ -struct wlr_surface *wlr_surface_from_resource(struct wl_resource *resource); - -/** - * Call `iterator` on each mapped surface in the surface tree (whether or not - * this surface is mapped), with the surface's position relative to the root - * surface. The function is called from root to leaves (in rendering order). - */ -void wlr_surface_for_each_surface(struct wlr_surface *surface, - wlr_surface_iterator_func_t iterator, void *user_data); - -/** - * Get the effective surface damage in surface-local coordinate space. Besides - * buffer damage, this includes damage induced by resizing and moving the - * surface and its subsurfaces. The resulting damage is not expected to be - * bounded by the surface itself. - */ -void wlr_surface_get_effective_damage(struct wlr_surface *surface, - pixman_region32_t *damage); - -/** - * Get the source rectangle describing the region of the buffer that needs to - * be sampled to render this surface's current state. The box is in - * buffer-local coordinates. - * - * If the viewport's source rectangle is unset, the position is zero and the - * size is the buffer's. - */ -void wlr_surface_get_buffer_source_box(struct wlr_surface *surface, - struct wlr_fbox *box); - -/** - * Acquire a lock for the pending surface state. - * - * The state won't be committed before the caller releases the lock. Instead, - * the state becomes cached. The caller needs to use wlr_surface_unlock_cached - * to release the lock. - * - * Returns a surface commit sequence number for the cached state. - */ -uint32_t wlr_surface_lock_pending(struct wlr_surface *surface); - -/** - * Release a lock for a cached state. - * - * Callers should not assume that the cached state will immediately be - * committed. Another caller may still have an active lock. - */ -void wlr_surface_unlock_cached(struct wlr_surface *surface, uint32_t seq); - -#endif +#include +#include From 617eb4fb9338c6960feb95b1a734c9cdd6bc5c46 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Thu, 13 Jan 2022 11:55:09 +0300 Subject: [PATCH 144/188] surface: deprecate wlr_surface.h --- examples/fullscreen-shell.c | 1 - examples/scene-graph.c | 1 - include/wlr/types/wlr_layer_shell_v1.h | 2 +- include/wlr/types/wlr_pointer_gestures_v1.h | 3 ++- include/wlr/types/wlr_scene.h | 2 +- include/wlr/types/wlr_seat.h | 3 ++- include/wlr/types/wlr_surface.h | 2 ++ include/wlr/types/wlr_text_input_v3.h | 3 ++- include/wlr/types/wlr_xdg_shell.h | 1 + types/data_device/wlr_data_device.c | 1 + types/data_device/wlr_drag.c | 1 + types/output/cursor.c | 2 +- types/output/output.c | 2 +- types/scene/subsurface_tree.c | 1 + types/scene/wlr_scene.c | 2 +- types/seat/wlr_seat_keyboard.c | 1 + types/seat/wlr_seat_pointer.c | 1 + types/seat/wlr_seat_touch.c | 1 + types/tablet_v2/wlr_tablet_v2_pad.c | 1 + types/tablet_v2/wlr_tablet_v2_tablet.c | 1 + types/tablet_v2/wlr_tablet_v2_tool.c | 1 + types/wlr_compositor.c | 1 - types/wlr_foreign_toplevel_management_v1.c | 1 + types/wlr_fullscreen_shell_v1.c | 2 +- types/wlr_idle_inhibit_v1.c | 2 +- types/wlr_input_method_v2.c | 2 +- types/wlr_keyboard_shortcuts_inhibit_v1.c | 1 + types/wlr_layer_shell_v1.c | 2 +- types/wlr_linux_dmabuf_v1.c | 2 +- types/wlr_pointer_constraints_v1.c | 1 + types/wlr_pointer_gestures_v1.c | 1 + types/wlr_presentation_time.c | 2 +- types/wlr_server_decoration.c | 2 +- types/wlr_subcompositor.c | 2 +- types/wlr_text_input_v3.c | 1 + types/wlr_viewporter.c | 2 +- types/wlr_xdg_activation_v1.c | 2 +- xwayland/xwm.c | 2 +- 38 files changed, 39 insertions(+), 22 deletions(-) diff --git a/examples/fullscreen-shell.c b/examples/fullscreen-shell.c index 5a9d4c926d..bc4bb7a86c 100644 --- a/examples/fullscreen-shell.c +++ b/examples/fullscreen-shell.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include diff --git a/examples/scene-graph.c b/examples/scene-graph.c index bd2003f53f..aa542996df 100644 --- a/examples/scene-graph.c +++ b/examples/scene-graph.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include diff --git a/include/wlr/types/wlr_layer_shell_v1.h b/include/wlr/types/wlr_layer_shell_v1.h index 79e435a00b..370fad837a 100644 --- a/include/wlr/types/wlr_layer_shell_v1.h +++ b/include/wlr/types/wlr_layer_shell_v1.h @@ -12,7 +12,7 @@ #include #include #include -#include +#include #include "wlr-layer-shell-unstable-v1-protocol.h" /** diff --git a/include/wlr/types/wlr_pointer_gestures_v1.h b/include/wlr/types/wlr_pointer_gestures_v1.h index 0d91a517dc..b05b1a30fa 100644 --- a/include/wlr/types/wlr_pointer_gestures_v1.h +++ b/include/wlr/types/wlr_pointer_gestures_v1.h @@ -11,7 +11,8 @@ #include #include -#include + +struct wlr_surface; struct wlr_pointer_gestures_v1 { struct wl_global *global; diff --git a/include/wlr/types/wlr_scene.h b/include/wlr/types/wlr_scene.h index e0b370adf6..7dfccbf9b0 100644 --- a/include/wlr/types/wlr_scene.h +++ b/include/wlr/types/wlr_scene.h @@ -21,7 +21,7 @@ #include #include -#include +#include struct wlr_output; struct wlr_output_layout; diff --git a/include/wlr/types/wlr_seat.h b/include/wlr/types/wlr_seat.h index 631b7dd3de..41f3918b09 100644 --- a/include/wlr/types/wlr_seat.h +++ b/include/wlr/types/wlr_seat.h @@ -14,7 +14,8 @@ #include #include #include -#include + +struct wlr_surface; #define WLR_SERIAL_RINGSET_SIZE 128 diff --git a/include/wlr/types/wlr_surface.h b/include/wlr/types/wlr_surface.h index 4a6801e8e8..9612dc085f 100644 --- a/include/wlr/types/wlr_surface.h +++ b/include/wlr/types/wlr_surface.h @@ -1,2 +1,4 @@ +#warning "wlr/types/wlr_surface.h has been deprecated and will be removed in the future. Use wlr/types/wlr_compositor.h and wlr/types/wlr_subcompositor.h." + #include #include diff --git a/include/wlr/types/wlr_text_input_v3.h b/include/wlr/types/wlr_text_input_v3.h index c9ee0b2212..5bac69a65e 100644 --- a/include/wlr/types/wlr_text_input_v3.h +++ b/include/wlr/types/wlr_text_input_v3.h @@ -11,9 +11,10 @@ #include #include -#include #include +struct wlr_surface; + enum wlr_text_input_v3_features { WLR_TEXT_INPUT_V3_FEATURE_SURROUNDING_TEXT = 1 << 0, WLR_TEXT_INPUT_V3_FEATURE_CONTENT_TYPE = 1 << 1, diff --git a/include/wlr/types/wlr_xdg_shell.h b/include/wlr/types/wlr_xdg_shell.h index cf42e82a7c..8d1a6fce60 100644 --- a/include/wlr/types/wlr_xdg_shell.h +++ b/include/wlr/types/wlr_xdg_shell.h @@ -10,6 +10,7 @@ #define WLR_TYPES_WLR_XDG_SHELL_H #include +#include #include #include #include "xdg-shell-protocol.h" diff --git a/types/data_device/wlr_data_device.c b/types/data_device/wlr_data_device.c index 6cd84ec0ab..ceaf80e72f 100644 --- a/types/data_device/wlr_data_device.c +++ b/types/data_device/wlr_data_device.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include diff --git a/types/data_device/wlr_drag.c b/types/data_device/wlr_drag.c index 1952dcb884..9865d93092 100644 --- a/types/data_device/wlr_drag.c +++ b/types/data_device/wlr_drag.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include diff --git a/types/output/cursor.c b/types/output/cursor.c index efbf650326..f3055c5c47 100644 --- a/types/output/cursor.c +++ b/types/output/cursor.c @@ -3,8 +3,8 @@ #include #include #include +#include #include -#include #include #include "render/allocator/allocator.h" #include "render/swapchain.h" diff --git a/types/output/output.c b/types/output/output.c index 85590ab632..0062013f74 100644 --- a/types/output/output.c +++ b/types/output/output.c @@ -4,8 +4,8 @@ #include #include #include +#include #include -#include #include #include "render/allocator/allocator.h" #include "render/swapchain.h" diff --git a/types/scene/subsurface_tree.c b/types/scene/subsurface_tree.c index bb3c7ff7a0..8384db8fbe 100644 --- a/types/scene/subsurface_tree.c +++ b/types/scene/subsurface_tree.c @@ -1,6 +1,7 @@ #include #include #include +#include #include /** diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index a0c06b6bfc..9353fef5c1 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -3,11 +3,11 @@ #include #include #include +#include #include #include #include #include -#include #include #include #include "util/signal.h" diff --git a/types/seat/wlr_seat_keyboard.c b/types/seat/wlr_seat_keyboard.c index 57dbfe2f58..9ed5fb3454 100644 --- a/types/seat/wlr_seat_keyboard.c +++ b/types/seat/wlr_seat_keyboard.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include diff --git a/types/seat/wlr_seat_pointer.c b/types/seat/wlr_seat_pointer.c index 2fa911ee09..ce4ca54e2a 100644 --- a/types/seat/wlr_seat_pointer.c +++ b/types/seat/wlr_seat_pointer.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include "types/wlr_seat.h" diff --git a/types/seat/wlr_seat_touch.c b/types/seat/wlr_seat_touch.c index 195928a3bf..99c50cf88a 100644 --- a/types/seat/wlr_seat_touch.c +++ b/types/seat/wlr_seat_touch.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include "types/wlr_seat.h" diff --git a/types/tablet_v2/wlr_tablet_v2_pad.c b/types/tablet_v2/wlr_tablet_v2_pad.c index 020cba57cf..58094f575f 100644 --- a/types/tablet_v2/wlr_tablet_v2_pad.c +++ b/types/tablet_v2/wlr_tablet_v2_pad.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include diff --git a/types/tablet_v2/wlr_tablet_v2_tablet.c b/types/tablet_v2/wlr_tablet_v2_tablet.c index bf2a915938..1d3c08fd4d 100644 --- a/types/tablet_v2/wlr_tablet_v2_tablet.c +++ b/types/tablet_v2/wlr_tablet_v2_tablet.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include diff --git a/types/tablet_v2/wlr_tablet_v2_tool.c b/types/tablet_v2/wlr_tablet_v2_tool.c index 51efc1e846..9e5b9658a1 100644 --- a/types/tablet_v2/wlr_tablet_v2_tool.c +++ b/types/tablet_v2/wlr_tablet_v2_tool.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include diff --git a/types/wlr_compositor.c b/types/wlr_compositor.c index 4b4b34f995..96122c1c51 100644 --- a/types/wlr_compositor.c +++ b/types/wlr_compositor.c @@ -7,7 +7,6 @@ #include #include #include -#include #include #include #include diff --git a/types/wlr_foreign_toplevel_management_v1.c b/types/wlr_foreign_toplevel_management_v1.c index 49805ef9f6..9bf811a44b 100644 --- a/types/wlr_foreign_toplevel_management_v1.c +++ b/types/wlr_foreign_toplevel_management_v1.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include diff --git a/types/wlr_fullscreen_shell_v1.c b/types/wlr_fullscreen_shell_v1.c index 59636e4c74..7e9f85539d 100644 --- a/types/wlr_fullscreen_shell_v1.c +++ b/types/wlr_fullscreen_shell_v1.c @@ -1,8 +1,8 @@ #include #include +#include #include #include -#include #include #include "util/signal.h" diff --git a/types/wlr_idle_inhibit_v1.c b/types/wlr_idle_inhibit_v1.c index 5acf740e58..61a66dc1f6 100644 --- a/types/wlr_idle_inhibit_v1.c +++ b/types/wlr_idle_inhibit_v1.c @@ -3,8 +3,8 @@ #include #include #include +#include #include -#include #include #include "idle-inhibit-unstable-v1-protocol.h" diff --git a/types/wlr_input_method_v2.c b/types/wlr_input_method_v2.c index 7ccd31ee02..c9390e9790 100644 --- a/types/wlr_input_method_v2.c +++ b/types/wlr_input_method_v2.c @@ -6,8 +6,8 @@ #include #include #include +#include #include -#include #include #include #include "input-method-unstable-v2-protocol.h" diff --git a/types/wlr_keyboard_shortcuts_inhibit_v1.c b/types/wlr_keyboard_shortcuts_inhibit_v1.c index fe4e64b04e..247d350d8b 100644 --- a/types/wlr_keyboard_shortcuts_inhibit_v1.c +++ b/types/wlr_keyboard_shortcuts_inhibit_v1.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include "keyboard-shortcuts-inhibit-unstable-v1-protocol.h" diff --git a/types/wlr_layer_shell_v1.c b/types/wlr_layer_shell_v1.c index d7a2123e9a..983f2186ee 100644 --- a/types/wlr_layer_shell_v1.c +++ b/types/wlr_layer_shell_v1.c @@ -3,9 +3,9 @@ #include #include #include +#include #include #include -#include #include #include #include "util/signal.h" diff --git a/types/wlr_linux_dmabuf_v1.c b/types/wlr_linux_dmabuf_v1.c index ae9bfeb907..5138e469fd 100644 --- a/types/wlr_linux_dmabuf_v1.c +++ b/types/wlr_linux_dmabuf_v1.c @@ -5,8 +5,8 @@ #include #include #include +#include #include -#include #include #include "linux-dmabuf-unstable-v1-protocol.h" #include "render/drm_format_set.h" diff --git a/types/wlr_pointer_constraints_v1.c b/types/wlr_pointer_constraints_v1.c index d608b778ab..e90daf72e7 100644 --- a/types/wlr_pointer_constraints_v1.c +++ b/types/wlr_pointer_constraints_v1.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include diff --git a/types/wlr_pointer_gestures_v1.c b/types/wlr_pointer_gestures_v1.c index 6b09a764ff..b8229dfc45 100644 --- a/types/wlr_pointer_gestures_v1.c +++ b/types/wlr_pointer_gestures_v1.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include diff --git a/types/wlr_presentation_time.c b/types/wlr_presentation_time.c index 36b7fe95f8..a6facbdbe2 100644 --- a/types/wlr_presentation_time.c +++ b/types/wlr_presentation_time.c @@ -3,8 +3,8 @@ #include #include #include +#include #include -#include #include #include "presentation-time-protocol.h" #include "util/signal.h" diff --git a/types/wlr_server_decoration.c b/types/wlr_server_decoration.c index 39ba624aa6..a80793939a 100644 --- a/types/wlr_server_decoration.c +++ b/types/wlr_server_decoration.c @@ -1,7 +1,7 @@ #include #include +#include #include -#include #include #include "server-decoration-protocol.h" #include "util/signal.h" diff --git a/types/wlr_subcompositor.c b/types/wlr_subcompositor.c index 45d0349d21..8a9e6207af 100644 --- a/types/wlr_subcompositor.c +++ b/types/wlr_subcompositor.c @@ -1,8 +1,8 @@ #include #include #include +#include #include -#include #include "types/wlr_region.h" #include "util/signal.h" diff --git a/types/wlr_text_input_v3.c b/types/wlr_text_input_v3.c index f5267b359d..65a8ebd6ff 100644 --- a/types/wlr_text_input_v3.c +++ b/types/wlr_text_input_v3.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include "text-input-unstable-v3-protocol.h" diff --git a/types/wlr_viewporter.c b/types/wlr_viewporter.c index f07c18febf..097af85769 100644 --- a/types/wlr_viewporter.c +++ b/types/wlr_viewporter.c @@ -1,6 +1,6 @@ #include #include -#include +#include #include #include #include "util/signal.h" diff --git a/types/wlr_xdg_activation_v1.c b/types/wlr_xdg_activation_v1.c index 208ada28c9..02ba9e07f2 100644 --- a/types/wlr_xdg_activation_v1.c +++ b/types/wlr_xdg_activation_v1.c @@ -2,8 +2,8 @@ #include #include #include +#include #include -#include #include #include #include "util/signal.h" diff --git a/xwayland/xwm.c b/xwayland/xwm.c index 313bfc0a79..f0a010d9ca 100644 --- a/xwayland/xwm.c +++ b/xwayland/xwm.c @@ -5,9 +5,9 @@ #include #include #include +#include #include #include -#include #include #include #include From 50827ed7f5f01ed2f03f67c5e9e55e13ede06748 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Thu, 13 Jan 2022 14:08:54 +0300 Subject: [PATCH 145/188] surface: improve role precommit hook Now the role precommit hook is called before the commit, not on wl_surface.commit request, and takes a state which is to be applied. --- include/types/wlr_xdg_shell.h | 3 ++- include/wlr/types/wlr_compositor.h | 3 ++- types/wlr_compositor.c | 8 ++++---- types/wlr_layer_shell_v1.c | 6 +++--- types/wlr_subcompositor.c | 6 +++--- types/xdg_shell/wlr_xdg_surface.c | 6 +++--- xwayland/xwm.c | 6 +++--- 7 files changed, 20 insertions(+), 18 deletions(-) diff --git a/include/types/wlr_xdg_shell.h b/include/types/wlr_xdg_shell.h index 509b1d2c1f..8b2c66cc25 100644 --- a/include/types/wlr_xdg_shell.h +++ b/include/types/wlr_xdg_shell.h @@ -20,7 +20,8 @@ void unmap_xdg_surface(struct wlr_xdg_surface *surface); void reset_xdg_surface(struct wlr_xdg_surface *xdg_surface); void destroy_xdg_surface(struct wlr_xdg_surface *surface); void handle_xdg_surface_commit(struct wlr_surface *wlr_surface); -void handle_xdg_surface_precommit(struct wlr_surface *wlr_surface); +void handle_xdg_surface_precommit(struct wlr_surface *wlr_surface, + const struct wlr_surface_state *state); void create_xdg_positioner(struct wlr_xdg_client *client, uint32_t id); struct wlr_xdg_positioner_resource *get_xdg_positioner_from_resource( diff --git a/include/wlr/types/wlr_compositor.h b/include/wlr/types/wlr_compositor.h index 0146a8ab59..071610b8f4 100644 --- a/include/wlr/types/wlr_compositor.h +++ b/include/wlr/types/wlr_compositor.h @@ -73,7 +73,8 @@ struct wlr_surface_state { struct wlr_surface_role { const char *name; void (*commit)(struct wlr_surface *surface); - void (*precommit)(struct wlr_surface *surface); + void (*precommit)(struct wlr_surface *surface, + const struct wlr_surface_state *state); }; struct wlr_surface_output { diff --git a/types/wlr_compositor.c b/types/wlr_compositor.c index 96122c1c51..6c4eb61b33 100644 --- a/types/wlr_compositor.c +++ b/types/wlr_compositor.c @@ -399,6 +399,10 @@ static void surface_commit_state(struct wlr_surface *surface, struct wlr_surface_state *next) { assert(next->cached_state_locks == 0); + if (surface->role && surface->role->precommit) { + surface->role->precommit(surface, next); + } + bool invalid_buffer = next->committed & WLR_SURFACE_STATE_BUFFER; surface->sx += next->dx; @@ -509,10 +513,6 @@ static void surface_handle_commit(struct wl_client *client, wlr_signal_emit_safe(&surface->events.client_commit, NULL); - if (surface->role && surface->role->precommit) { - surface->role->precommit(surface); - } - if (surface->pending.cached_state_locks > 0 || !wl_list_empty(&surface->cached)) { surface_cache_pending(surface); } else { diff --git a/types/wlr_layer_shell_v1.c b/types/wlr_layer_shell_v1.c index 983f2186ee..b2c82955f6 100644 --- a/types/wlr_layer_shell_v1.c +++ b/types/wlr_layer_shell_v1.c @@ -375,15 +375,15 @@ static void layer_surface_role_commit(struct wlr_surface *wlr_surface) { } } -static void layer_surface_role_precommit(struct wlr_surface *wlr_surface) { +static void layer_surface_role_precommit(struct wlr_surface *wlr_surface, + const struct wlr_surface_state *state) { struct wlr_layer_surface_v1 *surface = wlr_layer_surface_v1_from_wlr_surface(wlr_surface); if (surface == NULL) { return; } - if (wlr_surface->pending.committed & WLR_SURFACE_STATE_BUFFER && - wlr_surface->pending.buffer == NULL) { + if (state->committed & WLR_SURFACE_STATE_BUFFER && state->buffer == NULL) { // This is a NULL commit if (surface->configured && surface->mapped) { layer_surface_unmap(surface); diff --git a/types/wlr_subcompositor.c b/types/wlr_subcompositor.c index 8a9e6207af..397baf8c22 100644 --- a/types/wlr_subcompositor.c +++ b/types/wlr_subcompositor.c @@ -280,15 +280,15 @@ static void subsurface_role_commit(struct wlr_surface *surface) { subsurface_consider_map(subsurface, true); } -static void subsurface_role_precommit(struct wlr_surface *surface) { +static void subsurface_role_precommit(struct wlr_surface *surface, + const struct wlr_surface_state *state) { struct wlr_subsurface *subsurface = wlr_subsurface_from_wlr_surface(surface); if (subsurface == NULL) { return; } - if (surface->pending.committed & WLR_SURFACE_STATE_BUFFER && - surface->pending.buffer == NULL) { + if (state->committed & WLR_SURFACE_STATE_BUFFER && state->buffer == NULL) { // This is a NULL commit subsurface_unmap(subsurface); } diff --git a/types/xdg_shell/wlr_xdg_surface.c b/types/xdg_shell/wlr_xdg_surface.c index c45baabb99..d54c058e05 100644 --- a/types/xdg_shell/wlr_xdg_surface.c +++ b/types/xdg_shell/wlr_xdg_surface.c @@ -338,15 +338,15 @@ void handle_xdg_surface_commit(struct wlr_surface *wlr_surface) { } } -void handle_xdg_surface_precommit(struct wlr_surface *wlr_surface) { +void handle_xdg_surface_precommit(struct wlr_surface *wlr_surface, + const struct wlr_surface_state *state) { struct wlr_xdg_surface *surface = wlr_xdg_surface_from_wlr_surface(wlr_surface); if (surface == NULL) { return; } - if (wlr_surface->pending.committed & WLR_SURFACE_STATE_BUFFER && - wlr_surface->pending.buffer == NULL) { + if (state->committed & WLR_SURFACE_STATE_BUFFER && state->buffer == NULL) { // This is a NULL commit if (surface->configured && surface->mapped) { unmap_xdg_surface(surface); diff --git a/xwayland/xwm.c b/xwayland/xwm.c index f0a010d9ca..0c19926900 100644 --- a/xwayland/xwm.c +++ b/xwayland/xwm.c @@ -852,15 +852,15 @@ static void xwayland_surface_role_commit(struct wlr_surface *wlr_surface) { } } -static void xwayland_surface_role_precommit(struct wlr_surface *wlr_surface) { +static void xwayland_surface_role_precommit(struct wlr_surface *wlr_surface, + const struct wlr_surface_state *state) { assert(wlr_surface->role == &xwayland_surface_role); struct wlr_xwayland_surface *surface = wlr_surface->role_data; if (surface == NULL) { return; } - if (wlr_surface->pending.committed & WLR_SURFACE_STATE_BUFFER && - wlr_surface->pending.buffer == NULL) { + if (state->committed & WLR_SURFACE_STATE_BUFFER && state->buffer == NULL) { // This is a NULL commit if (surface->mapped) { wlr_signal_emit_safe(&surface->events.unmap, surface); From 89dc9a44968fbd3fe8a08a41858d1537ee145668 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Fri, 14 Jan 2022 20:46:20 +0100 Subject: [PATCH 146/188] tinywl: fix check whether client is focused or not Currently this check is too strict and denies the move/resize request if a subsurface of the client has pointer focus. --- tinywl/tinywl.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tinywl/tinywl.c b/tinywl/tinywl.c index 722abd10c6..6f628836cb 100644 --- a/tinywl/tinywl.c +++ b/tinywl/tinywl.c @@ -612,7 +612,8 @@ static void begin_interactive(struct tinywl_view *view, struct tinywl_server *server = view->server; struct wlr_surface *focused_surface = server->seat->pointer_state.focused_surface; - if (view->xdg_surface->surface != focused_surface) { + if (view->xdg_surface->surface != + wlr_surface_get_root_surface(focused_surface)) { /* Deny move/resize requests from unfocused clients. */ return; } From 5091118bed82394de5a151d658e895bb44059b61 Mon Sep 17 00:00:00 2001 From: Tadeo Kondrak Date: Thu, 30 Dec 2021 22:49:48 -0700 Subject: [PATCH 147/188] input_method_v2: improve mapping detection Detect NULL commits before the surface is actually committed, allowing the surface to be properly damaged on unmap. --- types/wlr_input_method_v2.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/types/wlr_input_method_v2.c b/types/wlr_input_method_v2.c index c9390e9790..559927eb16 100644 --- a/types/wlr_input_method_v2.c +++ b/types/wlr_input_method_v2.c @@ -157,9 +157,22 @@ static void popup_surface_surface_role_commit(struct wlr_surface *surface) { && popup_surface->input_method->client_active); } +static void popup_surface_surface_role_precommit(struct wlr_surface *surface, + const struct wlr_surface_state *state) { + struct wlr_input_popup_surface_v2 *popup_surface = surface->role_data; + if (popup_surface == NULL) { + return; + } + if (state->committed & WLR_SURFACE_STATE_BUFFER && state->buffer == NULL) { + // This is a NULL commit + popup_surface_set_mapped(popup_surface, false); + } +} + static const struct wlr_surface_role input_popup_surface_v2_role = { .name = "zwp_input_popup_surface_v2", .commit = popup_surface_surface_role_commit, + .precommit = popup_surface_surface_role_precommit, }; bool wlr_surface_is_input_popup_surface_v2(struct wlr_surface *surface) { From 1bd0ea3a809bdba092ef051120bb6d32f79c0ffb Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Mon, 17 Jan 2022 19:11:08 +0100 Subject: [PATCH 148/188] foreign-toplevel: send enter if needed on output bind Currently the output enter event is never sent if the client has not yet bound the output, which happens every time the compositor creates a new output. To fix this, listen for the output bind event and inform clients as if needed. --- .../wlr_foreign_toplevel_management_v1.h | 7 +++++-- types/wlr_foreign_toplevel_management_v1.c | 21 +++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/include/wlr/types/wlr_foreign_toplevel_management_v1.h b/include/wlr/types/wlr_foreign_toplevel_management_v1.h index 6ea9d47fdb..d9030b2eac 100644 --- a/include/wlr/types/wlr_foreign_toplevel_management_v1.h +++ b/include/wlr/types/wlr_foreign_toplevel_management_v1.h @@ -36,10 +36,13 @@ enum wlr_foreign_toplevel_handle_v1_state { struct wlr_foreign_toplevel_handle_v1_output { struct wl_list link; // wlr_foreign_toplevel_handle_v1::outputs - struct wl_listener output_destroy; struct wlr_output *output; - struct wlr_foreign_toplevel_handle_v1 *toplevel; + + // private state + + struct wl_listener output_bind; + struct wl_listener output_destroy; }; struct wlr_foreign_toplevel_handle_v1 { diff --git a/types/wlr_foreign_toplevel_management_v1.c b/types/wlr_foreign_toplevel_management_v1.c index 9bf811a44b..a700a115b8 100644 --- a/types/wlr_foreign_toplevel_management_v1.c +++ b/types/wlr_foreign_toplevel_management_v1.c @@ -257,6 +257,23 @@ static void toplevel_send_output(struct wlr_foreign_toplevel_handle_v1 *toplevel toplevel_update_idle_source(toplevel); } +static void toplevel_handle_output_bind(struct wl_listener *listener, + void *data) { + struct wlr_foreign_toplevel_handle_v1_output *toplevel_output = + wl_container_of(listener, toplevel_output, output_bind); + struct wlr_output_event_bind *event = data; + struct wl_client *client = wl_resource_get_client(event->resource); + + struct wl_resource *resource; + wl_resource_for_each(resource, &toplevel_output->toplevel->resources) { + if (wl_resource_get_client(resource) == client) { + send_output_to_resource(resource, toplevel_output->output, true); + } + } + + toplevel_update_idle_source(toplevel_output->toplevel); +} + static void toplevel_handle_output_destroy(struct wl_listener *listener, void *data) { struct wlr_foreign_toplevel_handle_v1_output *toplevel_output = @@ -286,6 +303,9 @@ void wlr_foreign_toplevel_handle_v1_output_enter( toplevel_output->toplevel = toplevel; wl_list_insert(&toplevel->outputs, &toplevel_output->link); + toplevel_output->output_bind.notify = toplevel_handle_output_bind; + wl_signal_add(&output->events.bind, &toplevel_output->output_bind); + toplevel_output->output_destroy.notify = toplevel_handle_output_destroy; wl_signal_add(&output->events.destroy, &toplevel_output->output_destroy); @@ -295,6 +315,7 @@ void wlr_foreign_toplevel_handle_v1_output_enter( static void toplevel_output_destroy( struct wlr_foreign_toplevel_handle_v1_output *toplevel_output) { wl_list_remove(&toplevel_output->link); + wl_list_remove(&toplevel_output->output_bind.link); wl_list_remove(&toplevel_output->output_destroy.link); free(toplevel_output); } From 8656c772489b9f7bde3b9e9c91c872a1d3d82f27 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Wed, 19 Jan 2022 04:38:19 -0500 Subject: [PATCH 149/188] scene_graph: use wlr_scene_output_send_frame_done --- examples/scene-graph.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/examples/scene-graph.c b/examples/scene-graph.c index aa542996df..a8ac668447 100644 --- a/examples/scene-graph.c +++ b/examples/scene-graph.c @@ -64,11 +64,7 @@ static void output_handle_frame(struct wl_listener *listener, void *data) { struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); - - struct surface *surface; - wl_list_for_each(surface, &output->server->surfaces, link) { - wlr_surface_send_frame_done(surface->wlr, &now); - } + wlr_scene_output_send_frame_done(output->scene_output, &now); } static void server_handle_new_output(struct wl_listener *listener, void *data) { From 1bc6f7f243f385881773f1034cf75d186d4b665f Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Wed, 19 Jan 2022 04:42:01 -0500 Subject: [PATCH 150/188] scene_graph: remove unused outputs list --- examples/scene-graph.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/examples/scene-graph.c b/examples/scene-graph.c index a8ac668447..cf1372e721 100644 --- a/examples/scene-graph.c +++ b/examples/scene-graph.c @@ -29,7 +29,6 @@ struct server { struct wlr_allocator *allocator; struct wlr_scene *scene; - struct wl_list outputs; struct wl_list surfaces; struct wl_listener new_output; @@ -79,7 +78,6 @@ static void server_handle_new_output(struct wl_listener *listener, void *data) { 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); output->scene_output = wlr_scene_output_create(server->scene, wlr_output); @@ -172,7 +170,6 @@ int main(int argc, char *argv[]) { wlr_xdg_shell_create(server.display); - wl_list_init(&server.outputs); wl_list_init(&server.surfaces); server.new_output.notify = server_handle_new_output; From c22ea3eb9969de6cd141f280e14539d5c4b6bd3f Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Wed, 19 Jan 2022 04:46:30 -0500 Subject: [PATCH 151/188] scene_graph: Simplify computation for offset of new surfaces. This became possible after the usage of wlr_surface_send_frame_done. --- examples/scene-graph.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/examples/scene-graph.c b/examples/scene-graph.c index cf1372e721..c3ce058934 100644 --- a/examples/scene-graph.c +++ b/examples/scene-graph.c @@ -29,7 +29,7 @@ struct server { struct wlr_allocator *allocator; struct wlr_scene *scene; - struct wl_list surfaces; + uint32_t surface_offset; struct wl_listener new_output; struct wl_listener new_surface; @@ -111,7 +111,8 @@ static void server_handle_new_surface(struct wl_listener *listener, struct server *server = wl_container_of(listener, server, new_surface); struct wlr_surface *wlr_surface = data; - int pos = 50 * wl_list_length(&server->surfaces); + int pos = server->surface_offset; + server->surface_offset += 50; struct surface *surface = calloc(1, sizeof(struct surface)); surface->wlr = wlr_surface; @@ -127,7 +128,6 @@ static void server_handle_new_surface(struct wl_listener *listener, surface->scene_surface = wlr_scene_surface_create(&server->scene->node, wlr_surface); - wl_list_insert(server->surfaces.prev, &surface->link); wlr_scene_node_set_position(&surface->scene_surface->node, pos + border_width, pos + border_width); @@ -155,6 +155,7 @@ int main(int argc, char *argv[]) { } struct server server = {0}; + server.surface_offset = 0; server.display = wl_display_create(); server.backend = wlr_backend_autocreate(server.display); server.scene = wlr_scene_create(); @@ -170,8 +171,6 @@ int main(int argc, char *argv[]) { wlr_xdg_shell_create(server.display); - wl_list_init(&server.surfaces); - server.new_output.notify = server_handle_new_output; wl_signal_add(&server.backend->events.new_output, &server.new_output); From d8d30463ac3c094b243369761b70f21ff9c59ab9 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 19 Jan 2022 13:08:46 +0100 Subject: [PATCH 152/188] render/vulkan: log physical device driver name This can be useful to figure out why a required feature is missing, e.g. as in [1]. We check VK_EXT_physical_device_drm availability after printing the driver name. [1]: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3358 --- render/vulkan/vulkan.c | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/render/vulkan/vulkan.c b/render/vulkan/vulkan.c index 4932ec4da4..7e9e939623 100644 --- a/render/vulkan/vulkan.c +++ b/render/vulkan/vulkan.c @@ -343,22 +343,40 @@ VkPhysicalDevice vulkan_find_drm_phdev(struct wlr_vk_instance *ini, int drm_fd) } const char *name = VK_EXT_PHYSICAL_DEVICE_DRM_EXTENSION_NAME; - if (find_extensions(avail_ext_props, avail_extc, &name, 1) != NULL) { - wlr_log(WLR_DEBUG, " Ignoring physical device \"%s\": " - "VK_EXT_physical_device_drm not supported", - phdev_props.deviceName); - continue; - } + bool has_drm_props = find_extensions(avail_ext_props, avail_extc, &name, 1) == NULL; + name = VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME; + bool has_driver_props = find_extensions(avail_ext_props, avail_extc, &name, 1) == NULL; + + VkPhysicalDeviceProperties2 props = {0}; + props.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; VkPhysicalDeviceDrmPropertiesEXT drm_props = {0}; drm_props.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRM_PROPERTIES_EXT; + if (has_drm_props) { + drm_props.pNext = props.pNext; + props.pNext = &drm_props; + } - VkPhysicalDeviceProperties2 props = {0}; - props.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; - props.pNext = &drm_props; + VkPhysicalDeviceDriverPropertiesKHR driver_props = {0}; + driver_props.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES; + if (has_driver_props) { + driver_props.pNext = props.pNext; + props.pNext = &driver_props; + } vkGetPhysicalDeviceProperties2(phdev, &props); + if (has_driver_props) { + wlr_log(WLR_INFO, " Driver name: %s (%s)", driver_props.driverName, driver_props.driverInfo); + } + + if (!has_drm_props) { + wlr_log(WLR_DEBUG, " Ignoring physical device \"%s\": " + "VK_EXT_physical_device_drm not supported", + phdev_props.deviceName); + continue; + } + dev_t primary_devid = makedev(drm_props.primaryMajor, drm_props.primaryMinor); dev_t render_devid = makedev(drm_props.renderMajor, drm_props.renderMinor); if (primary_devid == drm_stat.st_rdev || From cfba4c634497949d490fb8a72f457ee5c809de09 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 20 Jan 2022 15:09:15 +0100 Subject: [PATCH 153/188] editorconfig: set max_line_length See [1]. CONTRIBUTING.md says: > Try to keep your lines under 80 columns, but you can go up to 100 if it > improves readability. [1]: https://github.com/editorconfig/editorconfig/wiki/EditorConfig-Properties#max_line_length --- .editorconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/.editorconfig b/.editorconfig index f392d812a9..9dcb30d7d0 100644 --- a/.editorconfig +++ b/.editorconfig @@ -7,6 +7,7 @@ charset = utf-8 trim_trailing_whitespace = true indent_style = tab indent_size = 4 +max_line_length = 80 [*.xml] indent_style = space From 7ce966a5d4c73cfe37f3538e887f2ec915b1a1dc Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 20 Jan 2022 09:55:21 +0100 Subject: [PATCH 154/188] subcompositor: document subsurface_from_resource --- types/wlr_subcompositor.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/types/wlr_subcompositor.c b/types/wlr_subcompositor.c index 397baf8c22..e9d53f5370 100644 --- a/types/wlr_subcompositor.c +++ b/types/wlr_subcompositor.c @@ -61,6 +61,12 @@ static void subsurface_destroy(struct wlr_subsurface *subsurface) { static const struct wl_subsurface_interface subsurface_implementation; +/** + * Get a wlr_subsurface from a wl_subsurface resource. + * + * Returns NULL if the subsurface is inert (e.g. the wl_surface object got + * destroyed). + */ static struct wlr_subsurface *subsurface_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &wl_subsurface_interface, From 1d1b84541015d7b2914bdc69013f2a04548f4321 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 20 Jan 2022 10:03:31 +0100 Subject: [PATCH 155/188] subcompositor: destroy subsurface with parent When the parent surface is destroyed, also destroy the child wl_subsurface. No need to handle the wlr_subsurface.parent == NULL case anymore. Closes: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/1709 --- types/wlr_compositor.c | 5 +---- types/wlr_subcompositor.c | 38 ++++++++++++-------------------------- 2 files changed, 13 insertions(+), 30 deletions(-) diff --git a/types/wlr_compositor.c b/types/wlr_compositor.c index 6c4eb61b33..e7b922a5fc 100644 --- a/types/wlr_compositor.c +++ b/types/wlr_compositor.c @@ -478,7 +478,7 @@ static void collect_subsurface_damage_iter(struct wlr_surface *surface, // TODO: untangle from wlr_surface static void subsurface_parent_commit(struct wlr_subsurface *subsurface) { struct wlr_surface *surface = subsurface->surface; - + bool moved = subsurface->current.x != subsurface->pending.x || subsurface->current.y != subsurface->pending.y; if (subsurface->mapped && moved) { @@ -789,9 +789,6 @@ struct wlr_surface *wlr_surface_get_root_surface(struct wlr_surface *surface) { if (subsurface == NULL) { break; } - if (subsurface->parent == NULL) { - return NULL; - } surface = subsurface->parent; } return surface; diff --git a/types/wlr_subcompositor.c b/types/wlr_subcompositor.c index e9d53f5370..8ee5a9496d 100644 --- a/types/wlr_subcompositor.c +++ b/types/wlr_subcompositor.c @@ -14,10 +14,6 @@ static bool subsurface_is_synchronized(struct wlr_subsurface *subsurface) { return true; } - if (!subsurface->parent) { - return false; - } - if (!wlr_surface_is_subsurface(subsurface->parent)) { break; } @@ -45,12 +41,9 @@ static void subsurface_destroy(struct wlr_subsurface *subsurface) { wl_list_remove(&subsurface->surface_destroy.link); wl_list_remove(&subsurface->surface_client_commit.link); - - if (subsurface->parent) { - wl_list_remove(&subsurface->current.link); - wl_list_remove(&subsurface->pending.link); - wl_list_remove(&subsurface->parent_destroy.link); - } + wl_list_remove(&subsurface->current.link); + wl_list_remove(&subsurface->pending.link); + wl_list_remove(&subsurface->parent_destroy.link); wl_resource_set_user_data(subsurface->resource, NULL); if (subsurface->surface) { @@ -64,8 +57,8 @@ static const struct wl_subsurface_interface subsurface_implementation; /** * Get a wlr_subsurface from a wl_subsurface resource. * - * Returns NULL if the subsurface is inert (e.g. the wl_surface object got - * destroyed). + * Returns NULL if the subsurface is inert (e.g. the wl_surface object or the + * parent surface got destroyed). */ static struct wlr_subsurface *subsurface_from_resource( struct wl_resource *resource) { @@ -227,17 +220,12 @@ static void subsurface_consider_map(struct wlr_subsurface *subsurface, return; } - if (check_parent) { - if (subsurface->parent == NULL) { + if (check_parent && wlr_surface_is_subsurface(subsurface->parent)) { + struct wlr_subsurface *parent = + wlr_subsurface_from_wlr_surface(subsurface->parent); + if (parent == NULL || !parent->mapped) { return; } - if (wlr_surface_is_subsurface(subsurface->parent)) { - struct wlr_subsurface *parent = - wlr_subsurface_from_wlr_surface(subsurface->parent); - if (parent == NULL || !parent->mapped) { - return; - } - } } // Now we can map the subsurface @@ -310,11 +298,9 @@ static void subsurface_handle_parent_destroy(struct wl_listener *listener, void *data) { struct wlr_subsurface *subsurface = wl_container_of(listener, subsurface, parent_destroy); - subsurface_unmap(subsurface); - wl_list_remove(&subsurface->current.link); - wl_list_remove(&subsurface->pending.link); - wl_list_remove(&subsurface->parent_destroy.link); - subsurface->parent = NULL; + // Once the parent is destroyed, the client has no way to use the + // wl_subsurface object anymore, so we can destroy it. + subsurface_destroy(subsurface); } static void subsurface_handle_surface_destroy(struct wl_listener *listener, From da2491d4163e1d8e627d00c8ae594c7f8003472e Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Sat, 22 Jan 2022 11:35:22 +0300 Subject: [PATCH 156/188] compositor: damage the whole buffer on viewport src change wp_viewporter protocol doesn't seem to say anything about damage, but Firefox assumes that wp_viewport::set_source alone is enough to damage the whole surface, and that assumption kinda makes sense, so let's do that. --- types/wlr_compositor.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/types/wlr_compositor.c b/types/wlr_compositor.c index e7b922a5fc..bb4275ef39 100644 --- a/types/wlr_compositor.c +++ b/types/wlr_compositor.c @@ -208,8 +208,12 @@ static void surface_update_damage(pixman_region32_t *buffer_damage, pixman_region32_clear(buffer_damage); if (pending->width != current->width || - pending->height != current->height) { - // Damage the whole buffer on resize + pending->height != current->height || + pending->viewport.src.x != current->viewport.src.x || + pending->viewport.src.y != current->viewport.src.y || + pending->viewport.src.width != current->viewport.src.width || + pending->viewport.src.height != current->viewport.src.height) { + // Damage the whole buffer on resize or viewport source box change pixman_region32_union_rect(buffer_damage, buffer_damage, 0, 0, pending->buffer_width, pending->buffer_height); } else { From 77951968dc9df7214c04c33f4905a9a7aa92f60c Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Tue, 25 Jan 2022 22:30:36 +0300 Subject: [PATCH 157/188] subsurface: unlock cached state on commit if desynced wl_subsurface::set_desync description states: "If cached state exists when wl_surface.commit is called in desynchronized mode, the pending state is added to the cached state, and applied as a whole." This commit reintroduces an implementation of said behavior, previously removed in 7daf6da9ac05be2cb74c0983e3caee0b21db75d4. Strictly speaking, this logic isn't fully correct, as the cached state and the pending state are applied individually instead, if the cached state isn't locked by anything else. However, the end result is still the same. This commit fixes the issue with Firefox permission popups. --- types/wlr_subcompositor.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/types/wlr_subcompositor.c b/types/wlr_subcompositor.c index 8ee5a9496d..2b9b97cd23 100644 --- a/types/wlr_subcompositor.c +++ b/types/wlr_subcompositor.c @@ -324,6 +324,9 @@ static void subsurface_handle_surface_client_commit( } subsurface->has_cache = true; subsurface->cached_seq = wlr_surface_lock_pending(surface); + } else if (subsurface->has_cache) { + wlr_surface_unlock_cached(surface, subsurface->cached_seq); + subsurface->has_cache = false; } } From 2c59435e8223339070f963263f2caebf07620078 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Fri, 28 Jan 2022 17:38:39 +0100 Subject: [PATCH 158/188] xdg-output: remove dead code wlroots current requires wayland-protocols 1.24, so this if is no longer needed and hasn't been for a while. --- types/wlr_xdg_output_v1.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/types/wlr_xdg_output_v1.c b/types/wlr_xdg_output_v1.c index 819ea6c63a..8d5fee485a 100644 --- a/types/wlr_xdg_output_v1.c +++ b/types/wlr_xdg_output_v1.c @@ -253,12 +253,6 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_xdg_output_manager_v1 *wlr_xdg_output_manager_v1_create( struct wl_display *display, struct wlr_output_layout *layout) { - // TODO: require wayland-protocols 1.18 and remove this condition - int version = OUTPUT_MANAGER_VERSION; - if (version > zxdg_output_manager_v1_interface.version) { - version = zxdg_output_manager_v1_interface.version; - } - struct wlr_xdg_output_manager_v1 *manager = calloc(1, sizeof(struct wlr_xdg_output_manager_v1)); if (manager == NULL) { @@ -266,7 +260,7 @@ struct wlr_xdg_output_manager_v1 *wlr_xdg_output_manager_v1_create( } manager->layout = layout; manager->global = wl_global_create(display, - &zxdg_output_manager_v1_interface, version, manager, + &zxdg_output_manager_v1_interface, OUTPUT_MANAGER_VERSION, manager, output_manager_bind); if (!manager->global) { free(manager); From 0fce935dd8719f7173515f20b4bc23bceac67088 Mon Sep 17 00:00:00 2001 From: Andri Yngvason Date: Sun, 31 Oct 2021 20:09:46 +0000 Subject: [PATCH 159/188] buffer: Implement get_shm for shm_client_buffer --- types/wlr_buffer.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/types/wlr_buffer.c b/types/wlr_buffer.c index fa281c6820..3193f8969c 100644 --- a/types/wlr_buffer.c +++ b/types/wlr_buffer.c @@ -343,6 +343,21 @@ static void shm_client_buffer_destroy(struct wlr_buffer *wlr_buffer) { free(buffer); } +static bool shm_client_buffer_get_shm(struct wlr_buffer *wlr_buffer, + struct wlr_shm_attributes *attribs) { + struct wlr_shm_client_buffer *buffer = + shm_client_buffer_from_buffer(wlr_buffer); + + attribs->fd = -1; + attribs->width = wl_shm_buffer_get_width(buffer->shm_buffer); + attribs->height = wl_shm_buffer_get_height(buffer->shm_buffer); + attribs->offset = 0; + attribs->stride = wl_shm_buffer_get_stride(buffer->shm_buffer); + attribs->format = wl_shm_buffer_get_format(buffer->shm_buffer); + + return true; +} + static bool shm_client_buffer_begin_data_ptr_access(struct wlr_buffer *wlr_buffer, uint32_t flags, void **data, uint32_t *format, size_t *stride) { struct wlr_shm_client_buffer *buffer = @@ -368,6 +383,7 @@ static void shm_client_buffer_end_data_ptr_access(struct wlr_buffer *wlr_buffer) static const struct wlr_buffer_impl shm_client_buffer_impl = { .destroy = shm_client_buffer_destroy, + .get_shm = shm_client_buffer_get_shm, .begin_data_ptr_access = shm_client_buffer_begin_data_ptr_access, .end_data_ptr_access = shm_client_buffer_end_data_ptr_access, }; From 47914da18fb7f8e5a1c4ea58ba35ae1e37881133 Mon Sep 17 00:00:00 2001 From: Andri Yngvason Date: Sat, 30 Oct 2021 18:52:47 +0000 Subject: [PATCH 160/188] Implement zext-screencopy-unstable-v1 --- include/wlr/types/wlr_zext_screencopy_v1.h | 78 +++ protocol/meson.build | 1 + protocol/screencopy-unstable-v1.xml | 234 +++++++ types/meson.build | 1 + types/wlr_zext_screencopy_v1.c | 759 +++++++++++++++++++++ 5 files changed, 1073 insertions(+) create mode 100644 include/wlr/types/wlr_zext_screencopy_v1.h create mode 100644 protocol/screencopy-unstable-v1.xml create mode 100644 types/wlr_zext_screencopy_v1.c diff --git a/include/wlr/types/wlr_zext_screencopy_v1.h b/include/wlr/types/wlr_zext_screencopy_v1.h new file mode 100644 index 0000000000..132b3eb743 --- /dev/null +++ b/include/wlr/types/wlr_zext_screencopy_v1.h @@ -0,0 +1,78 @@ +/* + * This an unstable interface of wlroots. No guarantees are made regarding the + * future consistency of this API. + */ +#ifndef WLR_USE_UNSTABLE +#error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" +#endif + +#ifndef WLR_TYPES_WLR_ZEXT_SCREENCOPY_V1_H +#define WLR_TYPES_WLR_ZEXT_SCREENCOPY_V1_H + +#include +#include +#include + +struct wlr_buffer; + +struct wlr_zext_screencopy_manager_v1 { + struct wl_global *global; + + struct wl_listener display_destroy; + + struct { + struct wl_signal destroy; + } events; + + void *data; +}; + +enum wlr_zext_screencopy_surface_v1_type { + WLR_ZEXT_SCREENCOPY_SURFACE_V1_TYPE_OUTPUT, + WLR_ZEXT_SCREENCOPY_SURFACE_V1_TYPE_OUTPUT_CURSOR, +}; + +enum wlr_zext_screencopy_surface_v1_state { + WLR_ZEXT_SCREENCOPY_SURFACE_V1_STATE_WAITING_FOR_BUFFER_FORMATS, + WLR_ZEXT_SCREENCOPY_SURFACE_V1_STATE_READY, +}; + +struct wlr_zext_screencopy_surface_v1 { + struct wl_resource *resource; + + enum wlr_zext_screencopy_surface_v1_type type; + enum wlr_zext_screencopy_surface_v1_state state; + + uint32_t wl_shm_format; + int wl_shm_stride; + + uint32_t dmabuf_format; + + /* Buffer and info staged for next commit */ + struct wl_resource *staged_buffer_resource; + struct pixman_region32 staged_buffer_damage; + struct wl_listener staged_buffer_destroy; + + uint32_t options; + + /* Currently attached buffer and info */ + struct wl_resource *buffer_resource; + struct pixman_region32 buffer_damage; + struct wl_listener buffer_destroy; + + /* Accumulated frame damage for the surface */ + struct pixman_region32 frame_damage; + + struct wlr_output *output; + + struct wl_listener output_precommit; + struct wl_listener output_commit; + struct wl_listener output_destroy; + + void *data; +}; + +struct wlr_zext_screencopy_manager_v1 *wlr_zext_screencopy_manager_v1_create( + struct wl_display *display); + +#endif diff --git a/protocol/meson.build b/protocol/meson.build index ae2b2ef489..0d8d81836d 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -53,6 +53,7 @@ protocols = { 'wlr-output-power-management-unstable-v1': 'wlr-output-power-management-unstable-v1.xml', 'wlr-screencopy-unstable-v1': 'wlr-screencopy-unstable-v1.xml', 'wlr-virtual-pointer-unstable-v1': 'wlr-virtual-pointer-unstable-v1.xml', + 'screencopy-unstable-v1': 'screencopy-unstable-v1.xml', } protocols_code = {} diff --git a/protocol/screencopy-unstable-v1.xml b/protocol/screencopy-unstable-v1.xml new file mode 100644 index 0000000000..1c6c92ec09 --- /dev/null +++ b/protocol/screencopy-unstable-v1.xml @@ -0,0 +1,234 @@ + + + + Copyright © 2021 Andri Yngvason + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice (including the next + paragraph) shall be included in all copies or substantial portions of the + Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + + + + This protocol allows clients to ask the compositor to copy part of the + screen content to a client buffer. + + Warning! The protocol described in this file is experimental and + backward incompatible changes may be made. Backward compatible changes + may be added together with the corresponding interface version bump. + Backward incompatible changes are done by bumping the version number in + the protocol and interface names and resetting the interface version. + Once the protocol is to be declared stable, the 'z' prefix and the + version number in the protocol and interface names are removed and the + interface version number is reset. + + + + + This object is a manager which offers requests to start capturing from a + source. + + + + + Create a capturing surface for an output + + + + + + + + Create a capturing surface for an output's cursor + + + + + + + + + This object represents a surface that's being captured. + + After a screencopy surface is created, buffer_info events will be emitted + from the compositor to tell the client which buffer types and formats are + supported for reading from the surface. + + When the client knows all the buffer attributes, it can create a buffer, + attach it to the screencopy surface using the "attach_buffer" request, + set the buffer damage using the "damage_buffer" request and then call + the "commit" request. + + After "commit" has been called, the next time that a buffer is committed + by the compositor, the contents of that buffer will be copied to the one + committed to the screencopy surface. A series of events will be generated, + ending with the "ready" event, which means that the buffer is ready to be + used and a buffer may be committed to the surface again. + + The "failed" event may be sent at any time. When this happens, the client + must destroy the surface. Depending on the failure reason, the client can + create a new surface to replace it. + + + + + + + + + + + + + + + + + + + + + + + + + Provides information about buffer parameters that need to be used for + this surface. This event is sent for every supported buffer type after + the surface is created. After all supported buffers have been + enumerated, an event with type = none is sent. All other parameters + should be set to 0. + + The stride parameter is invalid for dmabuf and may be set to 0. + + + + + + + + + + + Attach a buffer to the surface. + + + + + + + Apply damage to the buffer which is to be committed next. + + This is for optimisation purposes. The compositor may use this + information to reduce copying. + + The client must submit damage if it's using multiple buffers. Otherwise, + the server might not copy into damaged regions of the buffer. + + + + + + + + + + Commit the screencopy surface. The frame will be copied to the surface + on next frame commit. + + If the "copy_on_damage" flag is set, no copies are carried out until the + source surface is damaged. + + + + + + + Destroys the surface. This request can be sent at any time by the + client. + + + + + + This event is sent before the ready event and holds the output transform + of the source buffer. + + + + + + + This event is sent before the ready event. It may be generated multiple + times for each commit. + + The arguments describe a box around an area that has changed since the + last ready event. + + + + + + + + + + This event is only relevant when a cursor is being captured. It may be + generated at any time, but it should at least be generated once before + the first ready event of the surface. + + + + + + + + + + This event indicates that the attempted frame copy has failed. + + After receiving this event, the client should destroy the object. + + + + + + + This event indicates the presentation time of the frame when applicable. + + The timestamp is expressed as tv_sec_hi, tv_sec_lo, tv_nsec triples, + each component being an unsigned 32-bit value. Whole seconds are in + tv_sec which is a 64-bit value combined from tv_sec_hi and tv_sec_lo, + and the additional fractional part in tv_nsec as nanoseconds. Hence, + for valid timestamps tv_nsec must be in [0, 999999999]. The seconds part + may have an arbitrary offset at start. + + + + + + + + + Called as soon as the frame is copied, indicating it is available + for reading. + + + + diff --git a/types/meson.build b/types/meson.build index 476bffe82d..2ab4db8687 100644 --- a/types/meson.build +++ b/types/meson.build @@ -74,6 +74,7 @@ wlr_files += files( 'wlr_xdg_foreign_v2.c', 'wlr_xdg_foreign_registry.c', 'wlr_xdg_output_v1.c', + 'wlr_zext_screencopy_v1.c', ) if features.get('drm-backend') diff --git a/types/wlr_zext_screencopy_v1.c b/types/wlr_zext_screencopy_v1.c new file mode 100644 index 0000000000..30a1a3d113 --- /dev/null +++ b/types/wlr_zext_screencopy_v1.c @@ -0,0 +1,759 @@ +#include +#include +#include +#include +#include +#include + +#include "util/signal.h" +#include "render/wlr_renderer.h" + +#include +#include +#include + +#include "screencopy-unstable-v1-protocol.h" + +#define ZEXT_SCREENCOPY_MANAGER_VERSION 1 + +static const struct zext_screencopy_manager_v1_interface manager_impl; +static const struct zext_screencopy_surface_v1_interface surface_impl; + +static struct wlr_zext_screencopy_surface_v1 *surface_from_resource( + struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, + &zext_screencopy_surface_v1_interface, &surface_impl)); + return wl_resource_get_user_data(resource); +} + +static struct wlr_zext_screencopy_manager_v1 *manager_from_resource( + struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, + &zext_screencopy_manager_v1_interface, &manager_impl)); + return wl_resource_get_user_data(resource); +} + +static void surface_destroy(struct wlr_zext_screencopy_surface_v1 *surface) { + if (!surface) { + return; + } + + pixman_region32_fini(&surface->frame_damage); + pixman_region32_fini(&surface->buffer_damage); + pixman_region32_fini(&surface->staged_buffer_damage); + + wl_list_remove(&surface->output_precommit.link); + wl_list_remove(&surface->output_commit.link); + wl_list_remove(&surface->output_destroy.link); + + if (surface->staged_buffer_resource) { + wl_list_remove(&surface->staged_buffer_destroy.link); + } + + if (surface->buffer_resource) { + wl_list_remove(&surface->buffer_destroy.link); + } + + wl_resource_set_user_data(surface->resource, NULL); + free(surface); +} + +static struct wlr_output *surface_check_output( + struct wlr_zext_screencopy_surface_v1 *surface) { + if (!surface->output) { + zext_screencopy_surface_v1_send_failed(surface->resource, + ZEXT_SCREENCOPY_SURFACE_V1_FAILURE_REASON_OUTPUT_MISSING); + surface_destroy(surface); + return NULL; + } + + if (!surface->output->enabled) { + zext_screencopy_surface_v1_send_failed(surface->resource, + ZEXT_SCREENCOPY_SURFACE_V1_FAILURE_REASON_OUTPUT_DISABLED); + surface_destroy(surface); + return NULL; + } + + return surface->output; +} + +static bool surface_get_source_dimensions( + struct wlr_zext_screencopy_surface_v1 *surface, + uint32_t *width, uint32_t *height) { + struct wlr_output *output = surface->output; + struct wlr_output_cursor *cursor = output->hardware_cursor; + + switch (surface->type) { + case WLR_ZEXT_SCREENCOPY_SURFACE_V1_TYPE_OUTPUT: + *width = output->width; + *height = output->height; + return true; + case WLR_ZEXT_SCREENCOPY_SURFACE_V1_TYPE_OUTPUT_CURSOR: + if (!cursor || !cursor->enabled) + return false; + + *width = cursor->width; + *height = cursor->height; + return true; + default: + abort(); + break; + } + + return false; +} + +static void surface_handle_staged_buffer_destroy(struct wl_listener *listener, + void *data) { + struct wlr_zext_screencopy_surface_v1 *surface = + wl_container_of(listener, surface, staged_buffer_destroy); + surface->staged_buffer_resource = NULL; + wl_list_remove(&surface->staged_buffer_destroy.link); +} + +static void surface_handle_committed_buffer_destroy(struct wl_listener *listener, + void *data) { + struct wlr_zext_screencopy_surface_v1 *surface = + wl_container_of(listener, surface, buffer_destroy); + surface->buffer_resource = NULL; + wl_list_remove(&surface->buffer_destroy.link); +} + +static void surface_attach_buffer(struct wl_client *client, + struct wl_resource *surface_resource, + struct wl_resource *buffer_resource) { + struct wlr_zext_screencopy_surface_v1 *surface = + surface_from_resource(surface_resource); + if (!surface) { + return; + } + + if (surface->staged_buffer_resource) { + wl_list_remove(&surface->staged_buffer_destroy.link); + } + + surface->staged_buffer_resource = buffer_resource; + if (buffer_resource) { + wl_resource_add_destroy_listener(buffer_resource, + &surface->staged_buffer_destroy); + surface->staged_buffer_destroy.notify = + surface_handle_staged_buffer_destroy; + } +} + +static void surface_damage_buffer(struct wl_client *client, + struct wl_resource *surface_resource, uint32_t x, uint32_t y, + uint32_t width, uint32_t height) { + struct wlr_zext_screencopy_surface_v1 *surface = + surface_from_resource(surface_resource); + if (!surface) { + return; + } + + pixman_region32_union_rect(&surface->staged_buffer_damage, + &surface->staged_buffer_damage, x, y, width, height); +} + +static void surface_commit(struct wl_client *client, + struct wl_resource *surface_resource, uint32_t options) { + struct wlr_zext_screencopy_surface_v1 *surface = + surface_from_resource(surface_resource); + if (!surface) { + return; + } + + struct wlr_output *output = surface_check_output(surface); + if (!output) { + return; + } + + uint32_t src_width, src_height; + if (!surface_get_source_dimensions(surface, &src_width, &src_height)) { + zext_screencopy_surface_v1_send_failed(surface->resource, + ZEXT_SCREENCOPY_SURFACE_V1_FAILURE_REASON_CURSOR_MISSING); + surface_destroy(surface); + return; + } + + surface->buffer_resource = surface->staged_buffer_resource; + surface->staged_buffer_resource = NULL; + + wl_list_remove(&surface->staged_buffer_destroy.link); + wl_resource_add_destroy_listener(surface->buffer_resource, + &surface->buffer_destroy); + surface->buffer_destroy.notify = surface_handle_committed_buffer_destroy; + + pixman_region32_copy(&surface->buffer_damage, + &surface->staged_buffer_damage); + pixman_region32_clear(&surface->staged_buffer_damage); + pixman_region32_intersect_rect(&surface->buffer_damage, + &surface->buffer_damage, 0, 0, src_width, src_height); + + surface->options = options; + + if (options & ZEXT_SCREENCOPY_SURFACE_V1_OPTIONS_SCHEDULE_FRAME) { + wlr_output_schedule_frame(output); + } +} + +static void surface_handle_destroy(struct wl_client *client, + struct wl_resource *surface_resource) { + struct wlr_zext_screencopy_surface_v1 *surface = + surface_from_resource(surface_resource); + surface_destroy(surface); +} + +static const struct zext_screencopy_surface_v1_interface surface_impl = { + .attach_buffer = surface_attach_buffer, + .damage_buffer = surface_damage_buffer, + .commit = surface_commit, + .destroy = surface_handle_destroy, +}; + +static void surface_handle_resource_destroy(struct wl_resource *resource) { + struct wlr_zext_screencopy_surface_v1 *surface = + surface_from_resource(resource); + surface_destroy(surface); +} + +static void surface_handle_output_destroy(struct wl_listener *listener, + void *data) { + struct wlr_zext_screencopy_surface_v1 *surface = data; + surface->output = NULL; +} + +static uint32_t get_dmabuf_format(struct wlr_buffer *buffer) { + struct wlr_dmabuf_attributes attr = { 0 }; + if (!wlr_buffer_get_dmabuf(buffer, &attr)) { + return DRM_FORMAT_INVALID; + } + return attr.format; +} + +static uint32_t get_buffer_preferred_read_format(struct wlr_buffer *buffer, + struct wlr_renderer *renderer) +{ + if (!renderer->impl->preferred_read_format || !renderer->impl->read_pixels) { + return DRM_FORMAT_INVALID; + } + + if (!renderer_bind_buffer(renderer, buffer)) { + return DRM_FORMAT_INVALID; + } + + uint32_t format = renderer->impl->preferred_read_format(renderer); + + renderer_bind_buffer(renderer, NULL); + + return format; +} + +static void surface_handle_output_precommit_formats( + struct wlr_zext_screencopy_surface_v1 *surface, + struct wlr_output_event_precommit *event) { + struct wlr_output *output = surface_check_output(surface); + if (!output) { + return; + } + + // Nothing to do here... +} + +static void surface_accumulate_frame_damage( + struct wlr_zext_screencopy_surface_v1 *surface, + struct wlr_output *output) +{ + struct pixman_region32 *region = &surface->frame_damage; + + if (output->pending.committed & WLR_OUTPUT_STATE_DAMAGE) { + // If the compositor submitted damage, copy it over + pixman_region32_union(region, region, &output->pending.damage); + pixman_region32_intersect_rect(region, region, 0, 0, + output->width, output->height); + } else if (output->pending.committed & WLR_OUTPUT_STATE_BUFFER) { + // If the compositor did not submit damage but did submit a + // buffer damage everything + pixman_region32_union_rect(region, region, 0, 0, + output->width, output->height); + } +} + +static void surface_damage_whole( + struct wlr_zext_screencopy_surface_v1 *surface) { + struct pixman_region32 *region = &surface->frame_damage; + uint32_t src_width, src_height; + if (!surface_get_source_dimensions(surface, &src_width, &src_height)) { + zext_screencopy_surface_v1_send_failed(surface->resource, + ZEXT_SCREENCOPY_SURFACE_V1_FAILURE_REASON_CURSOR_MISSING); + surface_destroy(surface); + return; + } + pixman_region32_union_rect(region, region, 0, 0, src_width, src_height); +} + +static void surface_handle_output_precommit_ready( + struct wlr_zext_screencopy_surface_v1 *surface, + struct wlr_output_event_precommit *event) { + struct wlr_output *output = surface_check_output(surface); + if (!output) { + return; + } + + switch (surface->type) { + case WLR_ZEXT_SCREENCOPY_SURFACE_V1_TYPE_OUTPUT: + surface_accumulate_frame_damage(surface, output); + break; + case WLR_ZEXT_SCREENCOPY_SURFACE_V1_TYPE_OUTPUT_CURSOR: + surface_damage_whole(surface); + break; + } +} + +static void surface_handle_output_precommit(struct wl_listener *listener, + void *data) { + struct wlr_zext_screencopy_surface_v1 *surface = + wl_container_of(listener, surface, output_precommit); + struct wlr_output_event_precommit *event = data; + + assert(surface->output); + + switch(surface->state) { + case WLR_ZEXT_SCREENCOPY_SURFACE_V1_STATE_WAITING_FOR_BUFFER_FORMATS: + surface_handle_output_precommit_formats(surface, event); + break; + case WLR_ZEXT_SCREENCOPY_SURFACE_V1_STATE_READY: + surface_handle_output_precommit_ready(surface, event); + break; + default: + abort(); + break; + } +} + +static void surface_handle_output_commit_formats( + struct wlr_zext_screencopy_surface_v1 *surface, + struct wlr_output_event_commit *event) { + struct wlr_output *output = surface_check_output(surface); + if (!output) { + return; + } + + struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend); + + struct wlr_buffer *buffer = NULL; + switch (surface->type) { + case WLR_ZEXT_SCREENCOPY_SURFACE_V1_TYPE_OUTPUT: + buffer = event->buffer; + break; + case WLR_ZEXT_SCREENCOPY_SURFACE_V1_TYPE_OUTPUT_CURSOR: + buffer = output->cursor_front_buffer; + break; + } + assert(buffer); + + surface->wl_shm_format = + get_buffer_preferred_read_format(buffer, renderer); + surface->wl_shm_stride = buffer->width * 4; // TODO: This assumes things... + + surface->dmabuf_format = get_dmabuf_format(buffer); + + uint32_t src_width, src_height; + if (!surface_get_source_dimensions(surface, &src_width, &src_height)) { + zext_screencopy_surface_v1_send_failed(surface->resource, + ZEXT_SCREENCOPY_SURFACE_V1_FAILURE_REASON_CURSOR_MISSING); + surface_destroy(surface); + return; + } + + if (surface->wl_shm_format != DRM_FORMAT_INVALID) { + assert(surface->wl_shm_stride); + + zext_screencopy_surface_v1_send_buffer_info(surface->resource, + ZEXT_SCREENCOPY_SURFACE_V1_BUFFER_TYPE_WL_SHM, + surface->wl_shm_format, src_width, src_height, + surface->wl_shm_stride); + } + + if (surface->dmabuf_format != DRM_FORMAT_INVALID) { + zext_screencopy_surface_v1_send_buffer_info(surface->resource, + ZEXT_SCREENCOPY_SURFACE_V1_BUFFER_TYPE_DMABUF, + surface->dmabuf_format, src_width, src_height, + 0); + } + + zext_screencopy_surface_v1_send_buffer_info(surface->resource, + ZEXT_SCREENCOPY_SURFACE_V1_BUFFER_TYPE_NONE, 0, 0, 0, 0); + + surface->state = WLR_ZEXT_SCREENCOPY_SURFACE_V1_STATE_READY; +} + +static void surface_send_cursor_info( + struct wlr_zext_screencopy_surface_v1 *surface) { + struct wlr_output *output = surface->output; + struct wlr_output_cursor *cursor = output->hardware_cursor; + + zext_screencopy_surface_v1_send_cursor_info(surface->resource, + cursor->x, cursor->y, cursor->hotspot_x, + cursor->hotspot_y); +} + +static void surface_send_transform(struct wlr_zext_screencopy_surface_v1 *surface) { + enum wl_output_transform transform = surface->output->transform; + zext_screencopy_surface_v1_send_transform(surface->resource, transform); +} + +static bool surface_copy_wl_shm(struct wlr_zext_screencopy_surface_v1 *surface, + struct wlr_buffer *dst_buffer, struct wlr_shm_attributes *attr, + struct wlr_buffer *src_buffer) { + struct wlr_output *output = surface->output; + struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend); + + if (dst_buffer->width != src_buffer->width || + dst_buffer->height != src_buffer->height) { + return false; + } + + uint32_t preferred_format = get_buffer_preferred_read_format(src_buffer, + renderer); + if (preferred_format != surface->wl_shm_format) { + return false; + } + + int32_t width = dst_buffer->width; + int32_t height = dst_buffer->height; + void *data = NULL; + uint32_t format = DRM_FORMAT_INVALID; + size_t stride = 0; + + if (!wlr_buffer_begin_data_ptr_access(dst_buffer, + WLR_BUFFER_DATA_PTR_ACCESS_WRITE, &data, &format, + &stride)) { + return false; + } + + uint32_t renderer_flags = 0; + bool ok; + ok = wlr_renderer_begin_with_buffer(renderer, src_buffer); + ok = ok && wlr_renderer_read_pixels(renderer, format, &renderer_flags, + stride, width, height, 0, 0, 0, 0, data); + wlr_renderer_end(renderer); + // TODO: if renderer_flags & WLR_RENDERER_READ_PIXELS_Y_INVERT: + // add vertical flip to transform + wlr_buffer_end_data_ptr_access(dst_buffer); + + return ok; +} + +static bool blit_dmabuf(struct wlr_renderer *renderer, + struct wlr_buffer *dst_buffer, + struct wlr_buffer *src_buffer) { + struct wlr_texture *src_tex = + wlr_texture_from_buffer(renderer, src_buffer); + if (!src_tex) { + return false; + } + + float mat[9]; + wlr_matrix_identity(mat); + wlr_matrix_scale(mat, dst_buffer->width, dst_buffer->height); + + if (!wlr_renderer_begin_with_buffer(renderer, dst_buffer)) { + goto error_renderer_begin; + } + + wlr_renderer_clear(renderer, (float[]){ 0.0, 0.0, 0.0, 0.0 }); + wlr_render_texture_with_matrix(renderer, src_tex, mat, 1.0f); + + wlr_renderer_end(renderer); + + wlr_texture_destroy(src_tex); + return true; + +error_renderer_begin: + wlr_texture_destroy(src_tex); + return false; +} + +static bool surface_copy_dmabuf(struct wlr_zext_screencopy_surface_v1 *surface, + struct wlr_buffer *dst_buffer, struct wlr_dmabuf_attributes *attr, + struct wlr_buffer *src_buffer) { + struct wlr_output *output = surface->output; + struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend); + + if (dst_buffer->width != src_buffer->width || + dst_buffer->height != src_buffer->height) { + return false; + } + + if (attr->format != surface->dmabuf_format) + return false; + + return blit_dmabuf(renderer, dst_buffer, src_buffer); +} + +static bool surface_copy(struct wlr_zext_screencopy_surface_v1 *surface, + struct wlr_buffer *src_buffer) { + struct wlr_buffer *dst_buffer = + wlr_buffer_from_resource(surface->buffer_resource); + if (!dst_buffer) { + goto failure; + } + + struct wlr_shm_attributes shm_attr = { 0 }; + if (wlr_buffer_get_shm(dst_buffer, &shm_attr)) { + if (!surface_copy_wl_shm(surface, dst_buffer, &shm_attr, + src_buffer)) { + goto failure; + } + } + + struct wlr_dmabuf_attributes dmabuf_attr = { 0 }; + if (wlr_buffer_get_dmabuf(dst_buffer, &dmabuf_attr)) { + if (!surface_copy_dmabuf(surface, dst_buffer, &dmabuf_attr, + src_buffer)) { + goto failure; + } + } + + return true; + +failure: + zext_screencopy_surface_v1_send_failed(surface->resource, + ZEXT_SCREENCOPY_SURFACE_V1_FAILURE_REASON_INVALID_BUFFER); + surface_destroy(surface); + return false; +} + +static void surface_send_damage(struct wlr_zext_screencopy_surface_v1 *surface) { + // TODO: send fine-grained damage events + struct pixman_box32 *damage_box = + pixman_region32_extents(&surface->frame_damage); + + int damage_x = damage_box->x1; + int damage_y = damage_box->y1; + int damage_width = damage_box->x2 - damage_box->x1; + int damage_height = damage_box->y2 - damage_box->y1; + + zext_screencopy_surface_v1_send_damage(surface->resource, + damage_x, damage_y, damage_width, damage_height); +} + +static void surface_send_presentation_time( + struct wlr_zext_screencopy_surface_v1 *surface, + struct timespec *when) +{ + time_t tv_sec = when->tv_sec; + uint32_t tv_sec_hi = (sizeof(tv_sec) > 4) ? tv_sec >> 32 : 0; + uint32_t tv_sec_lo = tv_sec & 0xFFFFFFFF; + zext_screencopy_surface_v1_send_presentation_time(surface->resource, + tv_sec_hi, tv_sec_lo, when->tv_nsec); +} + +static void surface_handle_output_commit_ready( + struct wlr_zext_screencopy_surface_v1 *surface, + struct wlr_output_event_commit *event) { + struct wlr_output *output = surface_check_output(surface); + if (!output) { + return; + } + + if (!(event->committed & WLR_OUTPUT_STATE_BUFFER)) { + return; + } + + if (!surface->buffer_resource) { + return; + } + + if ((surface->options & ZEXT_SCREENCOPY_SURFACE_V1_OPTIONS_WAIT_FOR_DAMAGE) && + !pixman_region32_not_empty(&surface->frame_damage)) { + return; + } + + switch (surface->type) { + case WLR_ZEXT_SCREENCOPY_SURFACE_V1_TYPE_OUTPUT: + if (!surface_copy(surface, event->buffer)) { + return; + } + + break; + case WLR_ZEXT_SCREENCOPY_SURFACE_V1_TYPE_OUTPUT_CURSOR: + if (!surface_copy(surface, output->cursor_front_buffer)) { + return; + } + + surface_send_cursor_info(surface); + break; + default: + abort(); + break; + } + + surface_send_transform(surface); + surface_send_damage(surface); + surface_send_presentation_time(surface, event->when); + zext_screencopy_surface_v1_send_ready(surface->resource); + + pixman_region32_clear(&surface->frame_damage); + pixman_region32_clear(&surface->buffer_damage); + + wl_list_remove(&surface->buffer_destroy.link); + surface->buffer_resource = NULL; +} + +static void surface_handle_output_commit(struct wl_listener *listener, + void *data) { + struct wlr_zext_screencopy_surface_v1 *surface = + wl_container_of(listener, surface, output_commit); + struct wlr_output_event_commit *event = data; + + assert(surface->output); + + switch(surface->state) { + case WLR_ZEXT_SCREENCOPY_SURFACE_V1_STATE_WAITING_FOR_BUFFER_FORMATS: + surface_handle_output_commit_formats(surface, event); + break; + case WLR_ZEXT_SCREENCOPY_SURFACE_V1_STATE_READY: + surface_handle_output_commit_ready(surface, event); + break; + default: + abort(); + break; + } +} + +static void capture_output(struct wl_client *client, uint32_t version, + struct wlr_zext_screencopy_manager_v1 *manager, + uint32_t surface_id, struct wlr_output *output, + enum wlr_zext_screencopy_surface_v1_type type) { + struct wlr_zext_screencopy_surface_v1 *surface = + calloc(1, sizeof(struct wlr_zext_screencopy_surface_v1)); + if (!surface) { + wl_client_post_no_memory(client); + return; + } + + surface->type = type; + + surface->wl_shm_format = DRM_FORMAT_INVALID; + surface->dmabuf_format = DRM_FORMAT_INVALID; + + surface->resource = wl_resource_create(client, + &zext_screencopy_surface_v1_interface, version, + surface_id); + if (!surface->resource) { + free(surface); + wl_client_post_no_memory(client); + return; + } + wl_resource_set_implementation(surface->resource, &surface_impl, + surface, surface_handle_resource_destroy); + + wl_list_init(&surface->output_commit.link); + wl_list_init(&surface->output_destroy.link); + + surface->output = output; + + wl_signal_add(&surface->output->events.destroy, + &surface->output_destroy); + surface->output_destroy.notify = surface_handle_output_destroy; + + wl_signal_add(&surface->output->events.precommit, + &surface->output_precommit); + surface->output_precommit.notify = surface_handle_output_precommit; + + wl_signal_add(&surface->output->events.commit, + &surface->output_commit); + surface->output_commit.notify = surface_handle_output_commit; + + pixman_region32_init(&surface->buffer_damage); + pixman_region32_init(&surface->staged_buffer_damage); + pixman_region32_init(&surface->frame_damage); + + uint32_t src_width, src_height; + if (!surface_get_source_dimensions(surface, &src_width, &src_height)) { + zext_screencopy_surface_v1_send_failed(surface->resource, + ZEXT_SCREENCOPY_SURFACE_V1_FAILURE_REASON_CURSOR_MISSING); + surface_destroy(surface); + return; + } + + pixman_region32_union_rect(&surface->frame_damage, + &surface->frame_damage, 0, 0, src_width, src_height); + + // We need a new frame to check the buffer formats + wlr_output_schedule_frame(output); +} + +static void manager_capture_output(struct wl_client *client, + struct wl_resource *manager_resource, uint32_t surface_id, + struct wl_resource *output_resource) { + struct wlr_zext_screencopy_manager_v1 *manager = + manager_from_resource(manager_resource); + uint32_t version = wl_resource_get_version(manager_resource); + struct wlr_output *output = wlr_output_from_resource(output_resource); + + capture_output(client, version, manager, surface_id, output, + WLR_ZEXT_SCREENCOPY_SURFACE_V1_TYPE_OUTPUT); +} + +static void manager_capture_output_cursor(struct wl_client *client, + struct wl_resource *manager_resource, uint32_t surface_id, + struct wl_resource *output_resource) { + struct wlr_zext_screencopy_manager_v1 *manager = + manager_from_resource(manager_resource); + uint32_t version = wl_resource_get_version(manager_resource); + struct wlr_output *output = wlr_output_from_resource(output_resource); + + capture_output(client, version, manager, surface_id, output, + WLR_ZEXT_SCREENCOPY_SURFACE_V1_TYPE_OUTPUT_CURSOR); +} + +static const struct zext_screencopy_manager_v1_interface manager_impl = { + .capture_output = manager_capture_output, + .capture_output_cursor = manager_capture_output_cursor, +}; + +static void manager_bind(struct wl_client *client, void *data, + uint32_t version, uint32_t id) { + struct wl_resource *resource = wl_resource_create(client, + &zext_screencopy_manager_v1_interface, version, id); + if (!resource) { + wl_client_post_no_memory(client); + return; + } + wl_resource_set_implementation(resource, &manager_impl, data, NULL); +} + +static void handle_display_destroy(struct wl_listener *listener, void *data) { + struct wlr_zext_screencopy_manager_v1 *manager = + wl_container_of(listener, manager, display_destroy); + wlr_signal_emit_safe(&manager->events.destroy, manager); + wl_list_remove(&manager->display_destroy.link); + wl_global_destroy(manager->global); + free(manager); +} + +struct wlr_zext_screencopy_manager_v1 *wlr_zext_screencopy_manager_v1_create( + struct wl_display *display) { + struct wlr_zext_screencopy_manager_v1 *manager = + calloc(1, sizeof(struct wlr_zext_screencopy_manager_v1)); + if (!manager) { + return NULL; + } + + manager->global = wl_global_create(display, + &zext_screencopy_manager_v1_interface, + ZEXT_SCREENCOPY_MANAGER_VERSION, manager, manager_bind); + if (!manager->global) { + free(manager); + return NULL; + } + + wl_signal_init(&manager->events.destroy); + + manager->display_destroy.notify = handle_display_destroy; + wl_display_add_destroy_listener(display, &manager->display_destroy); + + return manager; +} From c5d89e4ba938aae214ff28a96d5db4c5be784bd4 Mon Sep 17 00:00:00 2001 From: Andri Yngvason Date: Sun, 31 Oct 2021 17:45:29 +0000 Subject: [PATCH 161/188] examples: screencopy: Use zext-screencopy-unstable-v1 --- examples/meson.build | 2 +- examples/screencopy.c | 109 +++++++++++++++++++++++++++++++----------- 2 files changed, 83 insertions(+), 28 deletions(-) diff --git a/examples/meson.build b/examples/meson.build index 26d103bbb2..1e047ba616 100644 --- a/examples/meson.build +++ b/examples/meson.build @@ -137,7 +137,7 @@ clients = { 'screencopy': { 'src': 'screencopy.c', 'dep': [libpng, rt], - 'proto': ['wlr-screencopy-unstable-v1'], + 'proto': ['screencopy-unstable-v1'], }, 'screencopy-dmabuf': { 'src': 'screencopy-dmabuf.c', diff --git a/examples/screencopy.c b/examples/screencopy.c index 42123e55eb..1bb64eb80a 100644 --- a/examples/screencopy.c +++ b/examples/screencopy.c @@ -36,8 +36,9 @@ #include #include #include +#include #include -#include "wlr-screencopy-unstable-v1-client-protocol.h" +#include "screencopy-unstable-v1-client-protocol.h" struct format { enum wl_shm_format wl_format; @@ -45,7 +46,7 @@ struct format { }; static struct wl_shm *shm = NULL; -static struct zwlr_screencopy_manager_v1 *screencopy_manager = NULL; +static struct zext_screencopy_manager_v1 *screencopy_manager = NULL; static struct wl_output *output = NULL; static struct { @@ -66,6 +67,15 @@ static const struct format formats[] = { {WL_SHM_FORMAT_ABGR8888, false}, }; +static enum wl_shm_format drm_format_to_wl_shm(uint32_t in) { + switch (in) { + case DRM_FORMAT_ARGB8888: return WL_SHM_FORMAT_ARGB8888; + case DRM_FORMAT_XRGB8888: return WL_SHM_FORMAT_XRGB8888; + } + + return in; +} + 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; @@ -105,10 +115,35 @@ static struct wl_buffer *create_shm_buffer(enum wl_shm_format fmt, 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; +static void commit_buffer(struct zext_screencopy_surface_v1 *surface) { + if (buffer.wl_buffer == NULL) { + fprintf(stderr, "no supported buffer format reported\n"); + exit(EXIT_FAILURE); + } + + zext_screencopy_surface_v1_attach_buffer(surface, buffer.wl_buffer); + zext_screencopy_surface_v1_damage_buffer(surface, 0, 0, buffer.width, + buffer.height); + + zext_screencopy_surface_v1_commit(surface, + ZEXT_SCREENCOPY_SURFACE_V1_OPTIONS_SCHEDULE_FRAME); +} + +static void surface_handle_buffer_info(void *data, + struct zext_screencopy_surface_v1 *surface, + enum zext_screencopy_surface_v1_buffer_type type, + uint32_t format, uint32_t width, uint32_t height, + uint32_t stride) { + if (type == ZEXT_SCREENCOPY_SURFACE_V1_BUFFER_TYPE_NONE) { + commit_buffer(surface); + return; + } + + if (type != ZEXT_SCREENCOPY_SURFACE_V1_BUFFER_TYPE_WL_SHM) { + return; + } + + buffer.format = drm_format_to_wl_shm(format); buffer.width = width; buffer.height = height; buffer.stride = stride; @@ -116,37 +151,57 @@ static void frame_handle_buffer(void *data, // Make sure the buffer is not allocated assert(!buffer.wl_buffer); buffer.wl_buffer = - create_shm_buffer(format, width, height, stride, &buffer.data); + create_shm_buffer(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 surface_handle_transform(void *data, + struct zext_screencopy_surface_v1 *surface, + int32_t transform) { + if (transform == WL_OUTPUT_TRANSFORM_FLIPPED_180) { + buffer.y_invert = true; + } } -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) { +static void surface_handle_ready(void *data, + struct zext_screencopy_surface_v1 *surface) { buffer_copy_done = true; } -static void frame_handle_failed(void *data, - struct zwlr_screencopy_frame_v1 *frame) { +static void surface_handle_failed(void *data, + struct zext_screencopy_surface_v1 *surface, + enum zext_screencopy_surface_v1_failure_reason reason) { 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 surface_handle_damage(void *data, + struct zext_screencopy_surface_v1 *surface, + uint32_t x, uint32_t y, uint32_t width, uint32_t height) { +} + +static void surface_handle_cursor_info(void *data, + struct zext_screencopy_surface_v1 *surface, + int32_t pos_x, int32_t pos_y, + int32_t hotspot_x, int32_t hotspot_y) { +} + +static void surface_handle_presentation_time(void *data, + struct zext_screencopy_surface_v1 *surface, + uint32_t sec_hi, uint32_t sec_lo, uint32_t nsec) { +} + +static const struct zext_screencopy_surface_v1_listener frame_listener = { + .buffer_info = surface_handle_buffer_info, + .damage = surface_handle_damage, + .cursor_info = surface_handle_cursor_info, + .presentation_time = surface_handle_presentation_time, + .transform = surface_handle_transform, + .ready = surface_handle_ready, + .failed = surface_handle_failed, }; static void handle_global(void *data, struct wl_registry *registry, @@ -156,9 +211,9 @@ static void handle_global(void *data, struct wl_registry *registry, } 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) { + zext_screencopy_manager_v1_interface.name) == 0) { screencopy_manager = wl_registry_bind(registry, name, - &zwlr_screencopy_manager_v1_interface, 1); + &zext_screencopy_manager_v1_interface, 1); } } @@ -249,9 +304,9 @@ int main(int argc, char *argv[]) { 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); + struct zext_screencopy_surface_v1 *surface = + zext_screencopy_manager_v1_capture_output(screencopy_manager, output); + zext_screencopy_surface_v1_add_listener(surface, &frame_listener, NULL); while (!buffer_copy_done && wl_display_dispatch(display) != -1) { // This space is intentionally left blank From 1f870d06f97ea58b12096b69e3757edd9d1616e5 Mon Sep 17 00:00:00 2001 From: Andri Yngvason Date: Sun, 31 Oct 2021 20:21:35 +0000 Subject: [PATCH 162/188] examples: screencopy: Add option to capture cursor --- examples/screencopy.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/examples/screencopy.c b/examples/screencopy.c index 1bb64eb80a..af87e6b051 100644 --- a/examples/screencopy.c +++ b/examples/screencopy.c @@ -48,6 +48,7 @@ struct format { static struct wl_shm *shm = NULL; static struct zext_screencopy_manager_v1 *screencopy_manager = NULL; static struct wl_output *output = NULL; +static bool capture_cursor = false; static struct { struct wl_buffer *wl_buffer; @@ -281,7 +282,11 @@ static void write_image(char *filename, enum wl_shm_format wl_fmt, int width, } int main(int argc, char *argv[]) { - struct wl_display * display = wl_display_connect(NULL); + if (argc >= 2 && strcmp(argv[1], "cursor") == 0) { + capture_cursor = true; + } + + struct wl_display *display = wl_display_connect(NULL); if (display == NULL) { perror("failed to create display"); return EXIT_FAILURE; @@ -304,7 +309,8 @@ int main(int argc, char *argv[]) { return EXIT_FAILURE; } - struct zext_screencopy_surface_v1 *surface = + struct zext_screencopy_surface_v1 *surface = capture_cursor ? + zext_screencopy_manager_v1_capture_output_cursor(screencopy_manager, output) : zext_screencopy_manager_v1_capture_output(screencopy_manager, output); zext_screencopy_surface_v1_add_listener(surface, &frame_listener, NULL); From 71f31df008170163cb9ec00844845201d030ac78 Mon Sep 17 00:00:00 2001 From: Andri Yngvason Date: Sun, 31 Oct 2021 18:06:38 +0000 Subject: [PATCH 163/188] examples: screencopy-dmabuf: Use zext-screencopy-unstable-v1 --- examples/meson.build | 2 +- examples/screencopy-dmabuf.c | 111 ++++++++++++++++++++++------------- 2 files changed, 71 insertions(+), 42 deletions(-) diff --git a/examples/meson.build b/examples/meson.build index 1e047ba616..bfabb0d275 100644 --- a/examples/meson.build +++ b/examples/meson.build @@ -143,7 +143,7 @@ clients = { 'src': 'screencopy-dmabuf.c', 'dep': [libpng, rt, gbm, drm], 'proto': [ - 'wlr-screencopy-unstable-v1', + 'screencopy-unstable-v1', 'linux-dmabuf-unstable-v1', ], }, diff --git a/examples/screencopy-dmabuf.c b/examples/screencopy-dmabuf.c index e7657e1a92..d1f19b2383 100644 --- a/examples/screencopy-dmabuf.c +++ b/examples/screencopy-dmabuf.c @@ -1,6 +1,6 @@ /* * Copyright © 2008 Kristian Høgsberg - * Copyright © 2020 Andri Yngvason + * Copyright © 2020-2021 Andri Yngvason * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -41,7 +41,7 @@ #include #include #include -#include "wlr-screencopy-unstable-v1-client-protocol.h" +#include "screencopy-unstable-v1-client-protocol.h" #include "linux-dmabuf-unstable-v1-client-protocol.h" struct format { @@ -53,7 +53,7 @@ static int drm_fd = -1; static struct gbm_device *gbm_device = NULL; static struct zwp_linux_dmabuf_v1 *dmabuf = NULL; -static struct zwlr_screencopy_manager_v1 *screencopy_manager = NULL; +static struct zext_screencopy_manager_v1 *screencopy_manager = NULL; static struct wl_output *output = NULL; static struct { @@ -100,9 +100,15 @@ static bool find_render_node(char *node, size_t maxlen) { static void dmabuf_created(void *data, struct zwp_linux_buffer_params_v1 *params, struct wl_buffer *wl_buffer) { + struct zext_screencopy_surface_v1 *surface = data; + buffer.wl_buffer = wl_buffer; - zwlr_screencopy_frame_v1_copy(data, buffer.wl_buffer); + zext_screencopy_surface_v1_attach_buffer(surface, buffer.wl_buffer); + zext_screencopy_surface_v1_damage_buffer(surface, 0, 0, buffer.width, + buffer.height); + zext_screencopy_surface_v1_commit(surface, + ZEXT_SCREENCOPY_SURFACE_V1_OPTIONS_SCHEDULE_FRAME); } static void dmabuf_failed(void *data, @@ -116,23 +122,8 @@ static const struct zwp_linux_buffer_params_v1_listener params_listener = { .failed = dmabuf_failed, }; -static void frame_handle_buffer(void *data, - struct zwlr_screencopy_frame_v1 *frame, uint32_t wl_format, - uint32_t width, uint32_t height, uint32_t stride) { - // Not implemented -} - -static void frame_handle_linux_dmabuf(void *data, - struct zwlr_screencopy_frame_v1 *frame, uint32_t fourcc, - uint32_t width, uint32_t height) { - buffer.width = width; - buffer.height = height; - buffer.format = fourcc; - have_linux_dmabuf = true; -} - -static void frame_handle_buffer_done(void *data, - struct zwlr_screencopy_frame_v1 *frame) { +static void surface_handle_buffer_done( + struct zext_screencopy_surface_v1 *surface) { assert(!buffer.bo); if (!have_linux_dmabuf) { @@ -159,36 +150,74 @@ static void frame_handle_buffer_done(void *data, zwp_linux_buffer_params_v1_add(params, fd, 0, off, bo_stride, mod >> 32, mod & 0xffffffff); - zwp_linux_buffer_params_v1_add_listener(params, ¶ms_listener, frame); + zwp_linux_buffer_params_v1_add_listener(params, ¶ms_listener, + surface); zwp_linux_buffer_params_v1_create(params, buffer.width, buffer.height, buffer.format, /* flags */ 0); } -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 surface_handle_buffer_info(void *data, + struct zext_screencopy_surface_v1 *surface, uint32_t type, + uint32_t fourcc, uint32_t width, uint32_t height, + uint32_t stride) { + + if (type == ZEXT_SCREENCOPY_SURFACE_V1_BUFFER_TYPE_NONE) { + surface_handle_buffer_done(surface); + return; + } + + if (type != ZEXT_SCREENCOPY_SURFACE_V1_BUFFER_TYPE_DMABUF) { + return; + } + + buffer.width = width; + buffer.height = height; + buffer.format = fourcc; + have_linux_dmabuf = true; +} + +static void surface_handle_damage(void *data, + struct zext_screencopy_surface_v1 *surface, + uint32_t x, uint32_t y, uint32_t width, uint32_t height) { } -static void frame_handle_ready(void *data, - struct zwlr_screencopy_frame_v1 *frame, uint32_t tv_sec_hi, +static void surface_handle_cursor_info(void *data, + struct zext_screencopy_surface_v1 *surface, + int32_t pos_x, int32_t pos_y, + int32_t hotspot_x, int32_t hotspot_y) { +} + +static void surface_handle_transform(void *data, + struct zext_screencopy_surface_v1 *frame, int32_t transform) { + buffer.y_invert = transform == WL_OUTPUT_TRANSFORM_FLIPPED_180; +} + +static void surface_handle_presentation_time(void *data, + struct zext_screencopy_surface_v1 *frame, uint32_t tv_sec_hi, uint32_t tv_sec_lo, uint32_t tv_nsec) { +} + +static void surface_handle_ready(void *data, + struct zext_screencopy_surface_v1 *surface) { buffer_copy_done = true; } -static void frame_handle_failed(void *data, - struct zwlr_screencopy_frame_v1 *frame) { +static void surface_handle_failed(void *data, + struct zext_screencopy_surface_v1 *surface, + enum zext_screencopy_surface_v1_failure_reason reason) { fprintf(stderr, "failed to copy frame\n"); exit(EXIT_FAILURE); } -static const struct zwlr_screencopy_frame_v1_listener frame_listener = { - .buffer = frame_handle_buffer, - .linux_dmabuf = frame_handle_linux_dmabuf, - .buffer_done = frame_handle_buffer_done, - .flags = frame_handle_flags, - .ready = frame_handle_ready, - .failed = frame_handle_failed, +static const struct zext_screencopy_surface_v1_listener surface_listener = { + .buffer_info = surface_handle_buffer_info, + .damage = surface_handle_damage, + .cursor_info = surface_handle_cursor_info, + .presentation_time = surface_handle_presentation_time, + .transform = surface_handle_transform, + .ready = surface_handle_ready, + .failed = surface_handle_failed, }; static void dmabuf_format(void *data, @@ -216,9 +245,9 @@ static void handle_global(void *data, struct wl_registry *registry, &zwp_linux_dmabuf_v1_interface, 3); zwp_linux_dmabuf_v1_add_listener(dmabuf, &dmabuf_listener, data); } else if (strcmp(interface, - zwlr_screencopy_manager_v1_interface.name) == 0) { + zext_screencopy_manager_v1_interface.name) == 0) { screencopy_manager = wl_registry_bind(registry, name, - &zwlr_screencopy_manager_v1_interface, 3); + &zext_screencopy_manager_v1_interface, 1); } } @@ -330,9 +359,9 @@ int main(int argc, char *argv[]) { 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); + struct zext_screencopy_surface_v1 *surface = + zext_screencopy_manager_v1_capture_output(screencopy_manager, output); + zext_screencopy_surface_v1_add_listener(surface, &surface_listener, NULL); while (!buffer_copy_done && wl_display_dispatch(display) != -1) { // This space is intentionally left blank From dc3b44b29c88ed62688c7234f3fd233a9f8206bc Mon Sep 17 00:00:00 2001 From: Andri Yngvason Date: Sun, 21 Nov 2021 15:16:52 +0000 Subject: [PATCH 164/188] WiP: protocol: ext-screencopy: Make improvements --- protocol/screencopy-unstable-v1.xml | 54 ++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 16 deletions(-) diff --git a/protocol/screencopy-unstable-v1.xml b/protocol/screencopy-unstable-v1.xml index 1c6c92ec09..cce4644d49 100644 --- a/protocol/screencopy-unstable-v1.xml +++ b/protocol/screencopy-unstable-v1.xml @@ -51,12 +51,12 @@ - - - Create a capturing surface for an output's cursor + + + Create a capturing surface for a cursor - + @@ -94,8 +94,8 @@ - - + + @@ -108,9 +108,7 @@ Provides information about buffer parameters that need to be used for this surface. This event is sent for every supported buffer type after - the surface is created. After all supported buffers have been - enumerated, an event with type = none is sent. All other parameters - should be set to 0. + the surface is created. The stride parameter is invalid for dmabuf and may be set to 0. @@ -121,6 +119,12 @@ + + + This event is sent once when all buffer info events have been sent. + + + Attach a buffer to the surface. @@ -137,6 +141,8 @@ The client must submit damage if it's using multiple buffers. Otherwise, the server might not copy into damaged regions of the buffer. + + These coordinates originate in the upper left corner of the buffer. @@ -146,11 +152,23 @@ - Commit the screencopy surface. The frame will be copied to the surface - on next frame commit. + Commit the screencopy surface. - If the "copy_on_damage" flag is set, no copies are carried out until the - source surface is damaged. + For output surfaces, the frame will be copied to the surface on next + output commit. A ready event is generated when the buffer is ready. + + For cursor surfaces, the cursor will be copied to the surface the moment + at which the cursor is applied to the output, whether it be software or + hardware composited. A ready event is generated when the buffer is ready + and that ready event should always happen before any pending output + surface ready event. + + If the "immediate" flag is set, the compositor must present a frame to + the client as soon as possible. + + If the "with_cursors" flag is set, cursors shall be composited onto the + surface. Otherwise, the compositor should try to leave them out, if + possible. @@ -177,6 +195,8 @@ The arguments describe a box around an area that has changed since the last ready event. + + These coordinates originate in the upper left corner of the buffer. @@ -186,9 +206,11 @@ - This event is only relevant when a cursor is being captured. It may be - generated at any time, but it should at least be generated once before - the first ready event of the surface. + This event is only relevant for cursor capturing surfaces. It is + generated on cursor surfaces to report the position of the cursor in + global coordinates. + + The hotspot position is relative to the cursor buffer. From cf30866b35bb94349f0e174da211d30ed37a516f Mon Sep 17 00:00:00 2001 From: Andri Yngvason Date: Sun, 28 Nov 2021 12:31:27 +0000 Subject: [PATCH 165/188] WiP: protocol: ext-screencopy: Include cursor on surface --- protocol/screencopy-unstable-v1.xml | 94 +++++++++++++++++++---------- 1 file changed, 62 insertions(+), 32 deletions(-) diff --git a/protocol/screencopy-unstable-v1.xml b/protocol/screencopy-unstable-v1.xml index cce4644d49..5c9ea7f04e 100644 --- a/protocol/screencopy-unstable-v1.xml +++ b/protocol/screencopy-unstable-v1.xml @@ -50,14 +50,6 @@ - - - - Create a capturing surface for a cursor - - - - @@ -89,26 +81,25 @@ - + - + - - - + + Provides information about buffer parameters that need to be used for - this surface. This event is sent for every supported buffer type after - the surface is created. + the main image. This event is sent for every supported buffer type + after the surface is created. The stride parameter is invalid for dmabuf and may be set to 0. @@ -119,6 +110,25 @@ + + + Provides information about buffer parameters that need to be used for + the cursor image. This event is sent for every supported buffer type + after the surface is created, and it may be different for each seat. + + The default seat will be referred to as "default" within this protocol, + whether it be named so by the compositor or not. + + The stride parameter is invalid for dmabuf and may be set to 0. + + + + + + + + + This event is sent once when all buffer info events have been sent. @@ -133,7 +143,7 @@ - + Apply damage to the buffer which is to be committed next. This is for optimisation purposes. The compositor may use this @@ -150,24 +160,35 @@ + + + Attach a named cursor buffer to the surface. + + + + + + + + Apply damage to a named cursor buffer which is to be committed next. + + The whole cursor buffer will be considered damaged. + + + + Commit the screencopy surface. - For output surfaces, the frame will be copied to the surface on next - output commit. A ready event is generated when the buffer is ready. - - For cursor surfaces, the cursor will be copied to the surface the moment - at which the cursor is applied to the output, whether it be software or - hardware composited. A ready event is generated when the buffer is ready - and that ready event should always happen before any pending output - surface ready event. + The frame will be copied to the surface on next output commit. A ready + event is generated when the buffer is ready. If the "immediate" flag is set, the compositor must present a frame to the client as soon as possible. - If the "with_cursors" flag is set, cursors shall be composited onto the - surface. Otherwise, the compositor should try to leave them out, if + If the "render_cursors" flag is set, cursors shall be composited onto the + main surface. Otherwise, the compositor should try to leave them out, if possible. @@ -206,12 +227,20 @@ - This event is only relevant for cursor capturing surfaces. It is - generated on cursor surfaces to report the position of the cursor in - global coordinates. + This event is generated for each cursor buffer that was attached to the + surface and for which the cursor is currently focused on the surface. + It is generated once for each cursor buffer before the ready event. + + Cursors outside the surface do not get captured and no event will be + generated for them. + + If the cursor image has changed, the cursor buffer will have been + updated and the "has_damage" argument will be set to 1; otherwise 0. The hotspot position is relative to the cursor buffer. + + @@ -227,9 +256,10 @@ - - - This event indicates the presentation time of the frame when applicable. + + + This event indicates the time at which the frame is committed to be + scanned out. The timestamp is expressed as tv_sec_hi, tv_sec_lo, tv_nsec triples, each component being an unsigned 32-bit value. Whole seconds are in From 6c4bcddf28cbcb02f4681d144795aa4135f62c5c Mon Sep 17 00:00:00 2001 From: Andri Yngvason Date: Sun, 28 Nov 2021 13:08:48 +0000 Subject: [PATCH 166/188] Revert "examples: screencopy: Add option to capture cursor" This reverts commit 4fe62d0149c582867482b6965c715e914ea2f646. --- examples/screencopy.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/examples/screencopy.c b/examples/screencopy.c index af87e6b051..1bb64eb80a 100644 --- a/examples/screencopy.c +++ b/examples/screencopy.c @@ -48,7 +48,6 @@ struct format { static struct wl_shm *shm = NULL; static struct zext_screencopy_manager_v1 *screencopy_manager = NULL; static struct wl_output *output = NULL; -static bool capture_cursor = false; static struct { struct wl_buffer *wl_buffer; @@ -282,11 +281,7 @@ static void write_image(char *filename, enum wl_shm_format wl_fmt, int width, } int main(int argc, char *argv[]) { - if (argc >= 2 && strcmp(argv[1], "cursor") == 0) { - capture_cursor = true; - } - - struct wl_display *display = wl_display_connect(NULL); + struct wl_display * display = wl_display_connect(NULL); if (display == NULL) { perror("failed to create display"); return EXIT_FAILURE; @@ -309,8 +304,7 @@ int main(int argc, char *argv[]) { return EXIT_FAILURE; } - struct zext_screencopy_surface_v1 *surface = capture_cursor ? - zext_screencopy_manager_v1_capture_output_cursor(screencopy_manager, output) : + struct zext_screencopy_surface_v1 *surface = zext_screencopy_manager_v1_capture_output(screencopy_manager, output); zext_screencopy_surface_v1_add_listener(surface, &frame_listener, NULL); From c7ee8750387faef2bdc73d41c8bf9e4865e2ad92 Mon Sep 17 00:00:00 2001 From: Andri Yngvason Date: Sun, 28 Nov 2021 13:17:51 +0000 Subject: [PATCH 167/188] examples: screencopy: Update after protocol change --- examples/screencopy.c | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/examples/screencopy.c b/examples/screencopy.c index 1bb64eb80a..6841735b9f 100644 --- a/examples/screencopy.c +++ b/examples/screencopy.c @@ -126,7 +126,8 @@ static void commit_buffer(struct zext_screencopy_surface_v1 *surface) { buffer.height); zext_screencopy_surface_v1_commit(surface, - ZEXT_SCREENCOPY_SURFACE_V1_OPTIONS_SCHEDULE_FRAME); + ZEXT_SCREENCOPY_SURFACE_V1_OPTIONS_IMMEDIATE | + ZEXT_SCREENCOPY_SURFACE_V1_OPTIONS_RENDER_CURSORS); } static void surface_handle_buffer_info(void *data, @@ -134,11 +135,6 @@ static void surface_handle_buffer_info(void *data, enum zext_screencopy_surface_v1_buffer_type type, uint32_t format, uint32_t width, uint32_t height, uint32_t stride) { - if (type == ZEXT_SCREENCOPY_SURFACE_V1_BUFFER_TYPE_NONE) { - commit_buffer(surface); - return; - } - if (type != ZEXT_SCREENCOPY_SURFACE_V1_BUFFER_TYPE_WL_SHM) { return; } @@ -158,6 +154,19 @@ static void surface_handle_buffer_info(void *data, } } +static void surface_handle_cursor_buffer_info(void *data, + struct zext_screencopy_surface_v1 *surface, const char *name, + enum zext_screencopy_surface_v1_buffer_type type, + uint32_t format, uint32_t width, uint32_t height, + uint32_t stride) { +} + +static void surface_handle_init_done(void *data, + struct zext_screencopy_surface_v1 *surface) { + commit_buffer(surface); + return; +} + static void surface_handle_transform(void *data, struct zext_screencopy_surface_v1 *surface, int32_t transform) { @@ -184,21 +193,23 @@ static void surface_handle_damage(void *data, } static void surface_handle_cursor_info(void *data, - struct zext_screencopy_surface_v1 *surface, - int32_t pos_x, int32_t pos_y, + struct zext_screencopy_surface_v1 *surface, const char *name, + int has_damage, int32_t pos_x, int32_t pos_y, int32_t hotspot_x, int32_t hotspot_y) { } -static void surface_handle_presentation_time(void *data, +static void surface_handle_commit_time(void *data, struct zext_screencopy_surface_v1 *surface, uint32_t sec_hi, uint32_t sec_lo, uint32_t nsec) { } static const struct zext_screencopy_surface_v1_listener frame_listener = { .buffer_info = surface_handle_buffer_info, + .cursor_buffer_info = surface_handle_cursor_buffer_info, + .init_done = surface_handle_init_done, .damage = surface_handle_damage, .cursor_info = surface_handle_cursor_info, - .presentation_time = surface_handle_presentation_time, + .commit_time = surface_handle_commit_time, .transform = surface_handle_transform, .ready = surface_handle_ready, .failed = surface_handle_failed, From 0f6feddb1b73f4e71a84d93dd4b5da96ff354737 Mon Sep 17 00:00:00 2001 From: Andri Yngvason Date: Sun, 28 Nov 2021 13:57:10 +0000 Subject: [PATCH 168/188] Remove the cursor stuff --- include/wlr/types/wlr_zext_screencopy_v1.h | 6 - types/wlr_zext_screencopy_v1.c | 154 +++++---------------- 2 files changed, 31 insertions(+), 129 deletions(-) diff --git a/include/wlr/types/wlr_zext_screencopy_v1.h b/include/wlr/types/wlr_zext_screencopy_v1.h index 132b3eb743..9f30403264 100644 --- a/include/wlr/types/wlr_zext_screencopy_v1.h +++ b/include/wlr/types/wlr_zext_screencopy_v1.h @@ -27,11 +27,6 @@ struct wlr_zext_screencopy_manager_v1 { void *data; }; -enum wlr_zext_screencopy_surface_v1_type { - WLR_ZEXT_SCREENCOPY_SURFACE_V1_TYPE_OUTPUT, - WLR_ZEXT_SCREENCOPY_SURFACE_V1_TYPE_OUTPUT_CURSOR, -}; - enum wlr_zext_screencopy_surface_v1_state { WLR_ZEXT_SCREENCOPY_SURFACE_V1_STATE_WAITING_FOR_BUFFER_FORMATS, WLR_ZEXT_SCREENCOPY_SURFACE_V1_STATE_READY, @@ -40,7 +35,6 @@ enum wlr_zext_screencopy_surface_v1_state { struct wlr_zext_screencopy_surface_v1 { struct wl_resource *resource; - enum wlr_zext_screencopy_surface_v1_type type; enum wlr_zext_screencopy_surface_v1_state state; uint32_t wl_shm_format; diff --git a/types/wlr_zext_screencopy_v1.c b/types/wlr_zext_screencopy_v1.c index 30a1a3d113..365dc3adbb 100644 --- a/types/wlr_zext_screencopy_v1.c +++ b/types/wlr_zext_screencopy_v1.c @@ -77,32 +77,6 @@ static struct wlr_output *surface_check_output( return surface->output; } -static bool surface_get_source_dimensions( - struct wlr_zext_screencopy_surface_v1 *surface, - uint32_t *width, uint32_t *height) { - struct wlr_output *output = surface->output; - struct wlr_output_cursor *cursor = output->hardware_cursor; - - switch (surface->type) { - case WLR_ZEXT_SCREENCOPY_SURFACE_V1_TYPE_OUTPUT: - *width = output->width; - *height = output->height; - return true; - case WLR_ZEXT_SCREENCOPY_SURFACE_V1_TYPE_OUTPUT_CURSOR: - if (!cursor || !cursor->enabled) - return false; - - *width = cursor->width; - *height = cursor->height; - return true; - default: - abort(); - break; - } - - return false; -} - static void surface_handle_staged_buffer_destroy(struct wl_listener *listener, void *data) { struct wlr_zext_screencopy_surface_v1 *surface = @@ -167,14 +141,6 @@ static void surface_commit(struct wl_client *client, return; } - uint32_t src_width, src_height; - if (!surface_get_source_dimensions(surface, &src_width, &src_height)) { - zext_screencopy_surface_v1_send_failed(surface->resource, - ZEXT_SCREENCOPY_SURFACE_V1_FAILURE_REASON_CURSOR_MISSING); - surface_destroy(surface); - return; - } - surface->buffer_resource = surface->staged_buffer_resource; surface->staged_buffer_resource = NULL; @@ -187,11 +153,12 @@ static void surface_commit(struct wl_client *client, &surface->staged_buffer_damage); pixman_region32_clear(&surface->staged_buffer_damage); pixman_region32_intersect_rect(&surface->buffer_damage, - &surface->buffer_damage, 0, 0, src_width, src_height); + &surface->buffer_damage, 0, 0, output->width, + output->height); surface->options = options; - if (options & ZEXT_SCREENCOPY_SURFACE_V1_OPTIONS_SCHEDULE_FRAME) { + if (options & ZEXT_SCREENCOPY_SURFACE_V1_OPTIONS_IMMEDIATE) { wlr_output_schedule_frame(output); } } @@ -278,18 +245,15 @@ static void surface_accumulate_frame_damage( } } +#if 0 static void surface_damage_whole( struct wlr_zext_screencopy_surface_v1 *surface) { struct pixman_region32 *region = &surface->frame_damage; - uint32_t src_width, src_height; - if (!surface_get_source_dimensions(surface, &src_width, &src_height)) { - zext_screencopy_surface_v1_send_failed(surface->resource, - ZEXT_SCREENCOPY_SURFACE_V1_FAILURE_REASON_CURSOR_MISSING); - surface_destroy(surface); - return; - } - pixman_region32_union_rect(region, region, 0, 0, src_width, src_height); + struct wlr_output *output = surface->output; + pixman_region32_union_rect(region, region, 0, 0, output->width, + output->height); } +#endif static void surface_handle_output_precommit_ready( struct wlr_zext_screencopy_surface_v1 *surface, @@ -299,14 +263,7 @@ static void surface_handle_output_precommit_ready( return; } - switch (surface->type) { - case WLR_ZEXT_SCREENCOPY_SURFACE_V1_TYPE_OUTPUT: - surface_accumulate_frame_damage(surface, output); - break; - case WLR_ZEXT_SCREENCOPY_SURFACE_V1_TYPE_OUTPUT_CURSOR: - surface_damage_whole(surface); - break; - } + surface_accumulate_frame_damage(surface, output); } static void surface_handle_output_precommit(struct wl_listener *listener, @@ -340,15 +297,7 @@ static void surface_handle_output_commit_formats( struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend); - struct wlr_buffer *buffer = NULL; - switch (surface->type) { - case WLR_ZEXT_SCREENCOPY_SURFACE_V1_TYPE_OUTPUT: - buffer = event->buffer; - break; - case WLR_ZEXT_SCREENCOPY_SURFACE_V1_TYPE_OUTPUT_CURSOR: - buffer = output->cursor_front_buffer; - break; - } + struct wlr_buffer *buffer = event->buffer; assert(buffer); surface->wl_shm_format = @@ -357,45 +306,37 @@ static void surface_handle_output_commit_formats( surface->dmabuf_format = get_dmabuf_format(buffer); - uint32_t src_width, src_height; - if (!surface_get_source_dimensions(surface, &src_width, &src_height)) { - zext_screencopy_surface_v1_send_failed(surface->resource, - ZEXT_SCREENCOPY_SURFACE_V1_FAILURE_REASON_CURSOR_MISSING); - surface_destroy(surface); - return; - } - if (surface->wl_shm_format != DRM_FORMAT_INVALID) { assert(surface->wl_shm_stride); zext_screencopy_surface_v1_send_buffer_info(surface->resource, ZEXT_SCREENCOPY_SURFACE_V1_BUFFER_TYPE_WL_SHM, - surface->wl_shm_format, src_width, src_height, - surface->wl_shm_stride); + surface->wl_shm_format, output->width, + output->height, surface->wl_shm_stride); } if (surface->dmabuf_format != DRM_FORMAT_INVALID) { zext_screencopy_surface_v1_send_buffer_info(surface->resource, ZEXT_SCREENCOPY_SURFACE_V1_BUFFER_TYPE_DMABUF, - surface->dmabuf_format, src_width, src_height, - 0); + surface->dmabuf_format, output->width, + output->height, 0); } - zext_screencopy_surface_v1_send_buffer_info(surface->resource, - ZEXT_SCREENCOPY_SURFACE_V1_BUFFER_TYPE_NONE, 0, 0, 0, 0); - + zext_screencopy_surface_v1_send_init_done(surface->resource); surface->state = WLR_ZEXT_SCREENCOPY_SURFACE_V1_STATE_READY; } +#if 0 static void surface_send_cursor_info( struct wlr_zext_screencopy_surface_v1 *surface) { struct wlr_output *output = surface->output; struct wlr_output_cursor *cursor = output->hardware_cursor; zext_screencopy_surface_v1_send_cursor_info(surface->resource, - cursor->x, cursor->y, cursor->hotspot_x, + "default", 1, cursor->x, cursor->y, cursor->hotspot_x, cursor->hotspot_y); } +#endif static void surface_send_transform(struct wlr_zext_screencopy_surface_v1 *surface) { enum wl_output_transform transform = surface->output->transform; @@ -538,14 +479,14 @@ static void surface_send_damage(struct wlr_zext_screencopy_surface_v1 *surface) damage_x, damage_y, damage_width, damage_height); } -static void surface_send_presentation_time( +static void surface_send_commit_time( struct wlr_zext_screencopy_surface_v1 *surface, struct timespec *when) { time_t tv_sec = when->tv_sec; uint32_t tv_sec_hi = (sizeof(tv_sec) > 4) ? tv_sec >> 32 : 0; uint32_t tv_sec_lo = tv_sec & 0xFFFFFFFF; - zext_screencopy_surface_v1_send_presentation_time(surface->resource, + zext_screencopy_surface_v1_send_commit_time(surface->resource, tv_sec_hi, tv_sec_lo, when->tv_nsec); } @@ -565,33 +506,24 @@ static void surface_handle_output_commit_ready( return; } - if ((surface->options & ZEXT_SCREENCOPY_SURFACE_V1_OPTIONS_WAIT_FOR_DAMAGE) && - !pixman_region32_not_empty(&surface->frame_damage)) { + if (!surface_copy(surface, event->buffer)) { return; } - switch (surface->type) { - case WLR_ZEXT_SCREENCOPY_SURFACE_V1_TYPE_OUTPUT: - if (!surface_copy(surface, event->buffer)) { - return; - } - - break; - case WLR_ZEXT_SCREENCOPY_SURFACE_V1_TYPE_OUTPUT_CURSOR: - if (!surface_copy(surface, output->cursor_front_buffer)) { + // TODO + /* + if (output->cursor_buffer) { + if (!surface_copy_cursor(surface, output->cursor_front_buffer)) { return; } surface_send_cursor_info(surface); - break; - default: - abort(); - break; } + */ surface_send_transform(surface); surface_send_damage(surface); - surface_send_presentation_time(surface, event->when); + surface_send_commit_time(surface, event->when); zext_screencopy_surface_v1_send_ready(surface->resource); pixman_region32_clear(&surface->frame_damage); @@ -624,8 +556,7 @@ static void surface_handle_output_commit(struct wl_listener *listener, static void capture_output(struct wl_client *client, uint32_t version, struct wlr_zext_screencopy_manager_v1 *manager, - uint32_t surface_id, struct wlr_output *output, - enum wlr_zext_screencopy_surface_v1_type type) { + uint32_t surface_id, struct wlr_output *output) { struct wlr_zext_screencopy_surface_v1 *surface = calloc(1, sizeof(struct wlr_zext_screencopy_surface_v1)); if (!surface) { @@ -633,8 +564,6 @@ static void capture_output(struct wl_client *client, uint32_t version, return; } - surface->type = type; - surface->wl_shm_format = DRM_FORMAT_INVALID; surface->dmabuf_format = DRM_FORMAT_INVALID; @@ -670,16 +599,9 @@ static void capture_output(struct wl_client *client, uint32_t version, pixman_region32_init(&surface->staged_buffer_damage); pixman_region32_init(&surface->frame_damage); - uint32_t src_width, src_height; - if (!surface_get_source_dimensions(surface, &src_width, &src_height)) { - zext_screencopy_surface_v1_send_failed(surface->resource, - ZEXT_SCREENCOPY_SURFACE_V1_FAILURE_REASON_CURSOR_MISSING); - surface_destroy(surface); - return; - } - pixman_region32_union_rect(&surface->frame_damage, - &surface->frame_damage, 0, 0, src_width, src_height); + &surface->frame_damage, 0, 0, output->width, + output->height); // We need a new frame to check the buffer formats wlr_output_schedule_frame(output); @@ -693,25 +615,11 @@ static void manager_capture_output(struct wl_client *client, uint32_t version = wl_resource_get_version(manager_resource); struct wlr_output *output = wlr_output_from_resource(output_resource); - capture_output(client, version, manager, surface_id, output, - WLR_ZEXT_SCREENCOPY_SURFACE_V1_TYPE_OUTPUT); -} - -static void manager_capture_output_cursor(struct wl_client *client, - struct wl_resource *manager_resource, uint32_t surface_id, - struct wl_resource *output_resource) { - struct wlr_zext_screencopy_manager_v1 *manager = - manager_from_resource(manager_resource); - uint32_t version = wl_resource_get_version(manager_resource); - struct wlr_output *output = wlr_output_from_resource(output_resource); - - capture_output(client, version, manager, surface_id, output, - WLR_ZEXT_SCREENCOPY_SURFACE_V1_TYPE_OUTPUT_CURSOR); + capture_output(client, version, manager, surface_id, output); } static const struct zext_screencopy_manager_v1_interface manager_impl = { .capture_output = manager_capture_output, - .capture_output_cursor = manager_capture_output_cursor, }; static void manager_bind(struct wl_client *client, void *data, From f89a834ddb990ba9751e120502993043f83aa9db Mon Sep 17 00:00:00 2001 From: Andri Yngvason Date: Sun, 28 Nov 2021 14:19:08 +0000 Subject: [PATCH 169/188] Put buffer into into a struct --- include/wlr/types/wlr_zext_screencopy_v1.h | 19 +++--- types/wlr_zext_screencopy_v1.c | 75 +++++++++++----------- 2 files changed, 48 insertions(+), 46 deletions(-) diff --git a/include/wlr/types/wlr_zext_screencopy_v1.h b/include/wlr/types/wlr_zext_screencopy_v1.h index 9f30403264..d99b9f1917 100644 --- a/include/wlr/types/wlr_zext_screencopy_v1.h +++ b/include/wlr/types/wlr_zext_screencopy_v1.h @@ -32,6 +32,12 @@ enum wlr_zext_screencopy_surface_v1_state { WLR_ZEXT_SCREENCOPY_SURFACE_V1_STATE_READY, }; +struct wlr_zext_screencopy_surface_v1_buffer { + struct wl_resource *resource; + struct pixman_region32 damage; + struct wl_listener destroy; +}; + struct wlr_zext_screencopy_surface_v1 { struct wl_resource *resource; @@ -42,17 +48,12 @@ struct wlr_zext_screencopy_surface_v1 { uint32_t dmabuf_format; - /* Buffer and info staged for next commit */ - struct wl_resource *staged_buffer_resource; - struct pixman_region32 staged_buffer_damage; - struct wl_listener staged_buffer_destroy; - uint32_t options; + struct wlr_zext_screencopy_surface_v1_buffer staged_buffer; + struct wlr_zext_screencopy_surface_v1_buffer current_buffer; - /* Currently attached buffer and info */ - struct wl_resource *buffer_resource; - struct pixman_region32 buffer_damage; - struct wl_listener buffer_destroy; + struct wlr_zext_screencopy_surface_v1_buffer staged_cursor_buffer; + struct wlr_zext_screencopy_surface_v1_buffer current_cursor_buffer; /* Accumulated frame damage for the surface */ struct pixman_region32 frame_damage; diff --git a/types/wlr_zext_screencopy_v1.c b/types/wlr_zext_screencopy_v1.c index 365dc3adbb..2e4d4a1a2e 100644 --- a/types/wlr_zext_screencopy_v1.c +++ b/types/wlr_zext_screencopy_v1.c @@ -39,19 +39,19 @@ static void surface_destroy(struct wlr_zext_screencopy_surface_v1 *surface) { } pixman_region32_fini(&surface->frame_damage); - pixman_region32_fini(&surface->buffer_damage); - pixman_region32_fini(&surface->staged_buffer_damage); + pixman_region32_fini(&surface->current_buffer.damage); + pixman_region32_fini(&surface->staged_buffer.damage); wl_list_remove(&surface->output_precommit.link); wl_list_remove(&surface->output_commit.link); wl_list_remove(&surface->output_destroy.link); - if (surface->staged_buffer_resource) { - wl_list_remove(&surface->staged_buffer_destroy.link); + if (surface->staged_buffer.resource) { + wl_list_remove(&surface->staged_buffer.destroy.link); } - if (surface->buffer_resource) { - wl_list_remove(&surface->buffer_destroy.link); + if (surface->current_buffer.resource) { + wl_list_remove(&surface->current_buffer.destroy.link); } wl_resource_set_user_data(surface->resource, NULL); @@ -80,17 +80,17 @@ static struct wlr_output *surface_check_output( static void surface_handle_staged_buffer_destroy(struct wl_listener *listener, void *data) { struct wlr_zext_screencopy_surface_v1 *surface = - wl_container_of(listener, surface, staged_buffer_destroy); - surface->staged_buffer_resource = NULL; - wl_list_remove(&surface->staged_buffer_destroy.link); + wl_container_of(listener, surface, staged_buffer.destroy); + surface->staged_buffer.resource = NULL; + wl_list_remove(&surface->staged_buffer.destroy.link); } static void surface_handle_committed_buffer_destroy(struct wl_listener *listener, void *data) { struct wlr_zext_screencopy_surface_v1 *surface = - wl_container_of(listener, surface, buffer_destroy); - surface->buffer_resource = NULL; - wl_list_remove(&surface->buffer_destroy.link); + wl_container_of(listener, surface, current_buffer.destroy); + surface->current_buffer.resource = NULL; + wl_list_remove(&surface->current_buffer.destroy.link); } static void surface_attach_buffer(struct wl_client *client, @@ -102,15 +102,15 @@ static void surface_attach_buffer(struct wl_client *client, return; } - if (surface->staged_buffer_resource) { - wl_list_remove(&surface->staged_buffer_destroy.link); + if (surface->staged_buffer.resource) { + wl_list_remove(&surface->staged_buffer.destroy.link); } - surface->staged_buffer_resource = buffer_resource; + surface->staged_buffer.resource = buffer_resource; if (buffer_resource) { wl_resource_add_destroy_listener(buffer_resource, - &surface->staged_buffer_destroy); - surface->staged_buffer_destroy.notify = + &surface->staged_buffer.destroy); + surface->staged_buffer.destroy.notify = surface_handle_staged_buffer_destroy; } } @@ -124,8 +124,8 @@ static void surface_damage_buffer(struct wl_client *client, return; } - pixman_region32_union_rect(&surface->staged_buffer_damage, - &surface->staged_buffer_damage, x, y, width, height); + pixman_region32_union_rect(&surface->staged_buffer.damage, + &surface->staged_buffer.damage, x, y, width, height); } static void surface_commit(struct wl_client *client, @@ -141,19 +141,20 @@ static void surface_commit(struct wl_client *client, return; } - surface->buffer_resource = surface->staged_buffer_resource; - surface->staged_buffer_resource = NULL; + surface->current_buffer.resource = surface->staged_buffer.resource; + surface->staged_buffer.resource = NULL; - wl_list_remove(&surface->staged_buffer_destroy.link); - wl_resource_add_destroy_listener(surface->buffer_resource, - &surface->buffer_destroy); - surface->buffer_destroy.notify = surface_handle_committed_buffer_destroy; + wl_list_remove(&surface->staged_buffer.destroy.link); + wl_resource_add_destroy_listener(surface->current_buffer.resource, + &surface->current_buffer.destroy); + surface->current_buffer.destroy.notify = + surface_handle_committed_buffer_destroy; - pixman_region32_copy(&surface->buffer_damage, - &surface->staged_buffer_damage); - pixman_region32_clear(&surface->staged_buffer_damage); - pixman_region32_intersect_rect(&surface->buffer_damage, - &surface->buffer_damage, 0, 0, output->width, + pixman_region32_copy(&surface->current_buffer.damage, + &surface->staged_buffer.damage); + pixman_region32_clear(&surface->staged_buffer.damage); + pixman_region32_intersect_rect(&surface->current_buffer.damage, + &surface->current_buffer.damage, 0, 0, output->width, output->height); surface->options = options; @@ -435,7 +436,7 @@ static bool surface_copy_dmabuf(struct wlr_zext_screencopy_surface_v1 *surface, static bool surface_copy(struct wlr_zext_screencopy_surface_v1 *surface, struct wlr_buffer *src_buffer) { struct wlr_buffer *dst_buffer = - wlr_buffer_from_resource(surface->buffer_resource); + wlr_buffer_from_resource(surface->current_buffer.resource); if (!dst_buffer) { goto failure; } @@ -502,7 +503,7 @@ static void surface_handle_output_commit_ready( return; } - if (!surface->buffer_resource) { + if (!surface->current_buffer.resource) { return; } @@ -527,10 +528,10 @@ static void surface_handle_output_commit_ready( zext_screencopy_surface_v1_send_ready(surface->resource); pixman_region32_clear(&surface->frame_damage); - pixman_region32_clear(&surface->buffer_damage); + pixman_region32_clear(&surface->current_buffer.damage); - wl_list_remove(&surface->buffer_destroy.link); - surface->buffer_resource = NULL; + wl_list_remove(&surface->current_buffer.destroy.link); + surface->current_buffer.resource = NULL; } static void surface_handle_output_commit(struct wl_listener *listener, @@ -595,8 +596,8 @@ static void capture_output(struct wl_client *client, uint32_t version, &surface->output_commit); surface->output_commit.notify = surface_handle_output_commit; - pixman_region32_init(&surface->buffer_damage); - pixman_region32_init(&surface->staged_buffer_damage); + pixman_region32_init(&surface->current_buffer.damage); + pixman_region32_init(&surface->staged_buffer.damage); pixman_region32_init(&surface->frame_damage); pixman_region32_union_rect(&surface->frame_damage, From f4cead034a051a52c0d14f98a231bb140c78a3ae Mon Sep 17 00:00:00 2001 From: Andri Yngvason Date: Sun, 28 Nov 2021 15:11:03 +0000 Subject: [PATCH 170/188] Add back cursor stuff --- include/wlr/types/wlr_zext_screencopy_v1.h | 8 +- types/wlr_zext_screencopy_v1.c | 130 ++++++++++++++++++--- 2 files changed, 118 insertions(+), 20 deletions(-) diff --git a/include/wlr/types/wlr_zext_screencopy_v1.h b/include/wlr/types/wlr_zext_screencopy_v1.h index d99b9f1917..21cfeb7d47 100644 --- a/include/wlr/types/wlr_zext_screencopy_v1.h +++ b/include/wlr/types/wlr_zext_screencopy_v1.h @@ -45,9 +45,12 @@ struct wlr_zext_screencopy_surface_v1 { uint32_t wl_shm_format; int wl_shm_stride; - uint32_t dmabuf_format; + uint32_t cursor_wl_shm_format; + int cursor_wl_shm_stride; + uint32_t cursor_dmabuf_format; + uint32_t options; struct wlr_zext_screencopy_surface_v1_buffer staged_buffer; struct wlr_zext_screencopy_surface_v1_buffer current_buffer; @@ -55,8 +58,9 @@ struct wlr_zext_screencopy_surface_v1 { struct wlr_zext_screencopy_surface_v1_buffer staged_cursor_buffer; struct wlr_zext_screencopy_surface_v1_buffer current_cursor_buffer; - /* Accumulated frame damage for the surface */ + /* Accumulated damage for the surface */ struct pixman_region32 frame_damage; + struct pixman_region32 cursor_damage; struct wlr_output *output; diff --git a/types/wlr_zext_screencopy_v1.c b/types/wlr_zext_screencopy_v1.c index 2e4d4a1a2e..ffb4c4aaa9 100644 --- a/types/wlr_zext_screencopy_v1.c +++ b/types/wlr_zext_screencopy_v1.c @@ -93,6 +93,22 @@ static void surface_handle_committed_buffer_destroy(struct wl_listener *listener wl_list_remove(&surface->current_buffer.destroy.link); } +static void surface_handle_staged_cursor_buffer_destroy(struct wl_listener *listener, + void *data) { + struct wlr_zext_screencopy_surface_v1 *surface = + wl_container_of(listener, surface, staged_cursor_buffer.destroy); + surface->staged_cursor_buffer.resource = NULL; + wl_list_remove(&surface->staged_cursor_buffer.destroy.link); +} + +static void surface_handle_committed_cursor_buffer_destroy( + struct wl_listener *listener, void *data) { + struct wlr_zext_screencopy_surface_v1 *surface = + wl_container_of(listener, surface, current_cursor_buffer.destroy); + surface->current_cursor_buffer.resource = NULL; + wl_list_remove(&surface->current_cursor_buffer.destroy.link); +} + static void surface_attach_buffer(struct wl_client *client, struct wl_resource *surface_resource, struct wl_resource *buffer_resource) { @@ -115,6 +131,31 @@ static void surface_attach_buffer(struct wl_client *client, } } +static void surface_attach_cursor_buffer(struct wl_client *client, + struct wl_resource *surface_resource, + struct wl_resource *buffer_resource, + const char *seat_name) { + struct wlr_zext_screencopy_surface_v1 *surface = + surface_from_resource(surface_resource); + if (!surface) { + return; + } + + // TODO: Do something with "seat_name" + + if (surface->staged_cursor_buffer.resource) { + wl_list_remove(&surface->staged_cursor_buffer.destroy.link); + } + + surface->staged_cursor_buffer.resource = buffer_resource; + if (buffer_resource) { + wl_resource_add_destroy_listener(buffer_resource, + &surface->staged_cursor_buffer.destroy); + surface->staged_cursor_buffer.destroy.notify = + surface_handle_staged_cursor_buffer_destroy; + } +} + static void surface_damage_buffer(struct wl_client *client, struct wl_resource *surface_resource, uint32_t x, uint32_t y, uint32_t width, uint32_t height) { @@ -141,6 +182,7 @@ static void surface_commit(struct wl_client *client, return; } + // Main buffer surface->current_buffer.resource = surface->staged_buffer.resource; surface->staged_buffer.resource = NULL; @@ -157,6 +199,24 @@ static void surface_commit(struct wl_client *client, &surface->current_buffer.damage, 0, 0, output->width, output->height); + // Cursor buffer + surface->current_cursor_buffer.resource = + surface->staged_cursor_buffer.resource; + surface->staged_cursor_buffer.resource = NULL; + + wl_list_remove(&surface->staged_cursor_buffer.destroy.link); + wl_resource_add_destroy_listener(surface->current_cursor_buffer.resource, + &surface->current_cursor_buffer.destroy); + surface->current_cursor_buffer.destroy.notify = + surface_handle_committed_cursor_buffer_destroy; + + pixman_region32_copy(&surface->current_cursor_buffer.damage, + &surface->staged_cursor_buffer.damage); + pixman_region32_clear(&surface->staged_cursor_buffer.damage); + pixman_region32_intersect_rect(&surface->current_cursor_buffer.damage, + &surface->current_cursor_buffer.damage, 0, 0, + output->width, output->height); + surface->options = options; if (options & ZEXT_SCREENCOPY_SURFACE_V1_OPTIONS_IMMEDIATE) { @@ -173,6 +233,7 @@ static void surface_handle_destroy(struct wl_client *client, static const struct zext_screencopy_surface_v1_interface surface_impl = { .attach_buffer = surface_attach_buffer, + .attach_cursor_buffer = surface_attach_cursor_buffer, .damage_buffer = surface_damage_buffer, .commit = surface_commit, .destroy = surface_handle_destroy, @@ -323,11 +384,40 @@ static void surface_handle_output_commit_formats( output->height, 0); } + // TODO: Is the hardware cursor buffer always available? + struct wlr_buffer *cursor_buffer = output->cursor_front_buffer; + if (cursor_buffer) { + surface->cursor_wl_shm_format = + get_buffer_preferred_read_format(cursor_buffer, renderer); + surface->cursor_wl_shm_stride = + cursor_buffer->width * 4; // TODO: This assumes things... + + surface->cursor_dmabuf_format = get_dmabuf_format(cursor_buffer); + } + + if (surface->cursor_wl_shm_format != DRM_FORMAT_INVALID) { + assert(surface->cursor_wl_shm_stride); + + zext_screencopy_surface_v1_send_cursor_buffer_info( + surface->resource, "default", + ZEXT_SCREENCOPY_SURFACE_V1_BUFFER_TYPE_WL_SHM, + surface->cursor_wl_shm_format, + cursor_buffer->width, cursor_buffer->height, + surface->cursor_wl_shm_stride); + } + + if (surface->cursor_dmabuf_format != DRM_FORMAT_INVALID) { + zext_screencopy_surface_v1_send_cursor_buffer_info( + surface->resource, "default", + ZEXT_SCREENCOPY_SURFACE_V1_BUFFER_TYPE_DMABUF, + surface->cursor_dmabuf_format, + cursor_buffer->width, cursor_buffer->height, 0); + } + zext_screencopy_surface_v1_send_init_done(surface->resource); surface->state = WLR_ZEXT_SCREENCOPY_SURFACE_V1_STATE_READY; } -#if 0 static void surface_send_cursor_info( struct wlr_zext_screencopy_surface_v1 *surface) { struct wlr_output *output = surface->output; @@ -337,7 +427,6 @@ static void surface_send_cursor_info( "default", 1, cursor->x, cursor->y, cursor->hotspot_x, cursor->hotspot_y); } -#endif static void surface_send_transform(struct wlr_zext_screencopy_surface_v1 *surface) { enum wl_output_transform transform = surface->output->transform; @@ -346,7 +435,7 @@ static void surface_send_transform(struct wlr_zext_screencopy_surface_v1 *surfac static bool surface_copy_wl_shm(struct wlr_zext_screencopy_surface_v1 *surface, struct wlr_buffer *dst_buffer, struct wlr_shm_attributes *attr, - struct wlr_buffer *src_buffer) { + struct wlr_buffer *src_buffer, uint32_t wl_shm_format) { struct wlr_output *output = surface->output; struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend); @@ -357,7 +446,7 @@ static bool surface_copy_wl_shm(struct wlr_zext_screencopy_surface_v1 *surface, uint32_t preferred_format = get_buffer_preferred_read_format(src_buffer, renderer); - if (preferred_format != surface->wl_shm_format) { + if (preferred_format != wl_shm_format) { return false; } @@ -418,7 +507,7 @@ static bool blit_dmabuf(struct wlr_renderer *renderer, static bool surface_copy_dmabuf(struct wlr_zext_screencopy_surface_v1 *surface, struct wlr_buffer *dst_buffer, struct wlr_dmabuf_attributes *attr, - struct wlr_buffer *src_buffer) { + struct wlr_buffer *src_buffer, uint32_t format) { struct wlr_output *output = surface->output; struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend); @@ -427,16 +516,18 @@ static bool surface_copy_dmabuf(struct wlr_zext_screencopy_surface_v1 *surface, return false; } - if (attr->format != surface->dmabuf_format) + if (attr->format != format) return false; return blit_dmabuf(renderer, dst_buffer, src_buffer); } static bool surface_copy(struct wlr_zext_screencopy_surface_v1 *surface, - struct wlr_buffer *src_buffer) { + struct wlr_zext_screencopy_surface_v1_buffer *surface_buffer, + struct wlr_buffer *src_buffer, uint32_t wl_shm_format, + uint32_t dmabuf_format) { struct wlr_buffer *dst_buffer = - wlr_buffer_from_resource(surface->current_buffer.resource); + wlr_buffer_from_resource(surface_buffer->resource); if (!dst_buffer) { goto failure; } @@ -444,7 +535,7 @@ static bool surface_copy(struct wlr_zext_screencopy_surface_v1 *surface, struct wlr_shm_attributes shm_attr = { 0 }; if (wlr_buffer_get_shm(dst_buffer, &shm_attr)) { if (!surface_copy_wl_shm(surface, dst_buffer, &shm_attr, - src_buffer)) { + src_buffer, wl_shm_format)) { goto failure; } } @@ -452,7 +543,7 @@ static bool surface_copy(struct wlr_zext_screencopy_surface_v1 *surface, struct wlr_dmabuf_attributes dmabuf_attr = { 0 }; if (wlr_buffer_get_dmabuf(dst_buffer, &dmabuf_attr)) { if (!surface_copy_dmabuf(surface, dst_buffer, &dmabuf_attr, - src_buffer)) { + src_buffer, dmabuf_format)) { goto failure; } } @@ -499,6 +590,7 @@ static void surface_handle_output_commit_ready( return; } + // TODO: We might have cursor damage but no new main buffer... if (!(event->committed & WLR_OUTPUT_STATE_BUFFER)) { return; } @@ -507,23 +599,23 @@ static void surface_handle_output_commit_ready( return; } - if (!surface_copy(surface, event->buffer)) { + if (!surface_copy(surface, &surface->current_buffer, event->buffer, + surface->wl_shm_format, surface->dmabuf_format)) { return; } - // TODO - /* - if (output->cursor_buffer) { - if (!surface_copy_cursor(surface, output->cursor_front_buffer)) { + if (surface->current_cursor_buffer.resource) { + if (!surface_copy(surface, &surface->current_cursor_buffer, + output->cursor_front_buffer, + surface->cursor_wl_shm_format, + surface->cursor_dmabuf_format)) { return; } - - surface_send_cursor_info(surface); } - */ surface_send_transform(surface); surface_send_damage(surface); + surface_send_cursor_info(surface); surface_send_commit_time(surface, event->when); zext_screencopy_surface_v1_send_ready(surface->resource); @@ -567,6 +659,8 @@ static void capture_output(struct wl_client *client, uint32_t version, surface->wl_shm_format = DRM_FORMAT_INVALID; surface->dmabuf_format = DRM_FORMAT_INVALID; + surface->cursor_wl_shm_format = DRM_FORMAT_INVALID; + surface->cursor_dmabuf_format = DRM_FORMAT_INVALID; surface->resource = wl_resource_create(client, &zext_screencopy_surface_v1_interface, version, From 1e5b0770471f0166d03e4298ea449c32fa1fefe3 Mon Sep 17 00:00:00 2001 From: Andri Yngvason Date: Sun, 28 Nov 2021 18:40:19 +0000 Subject: [PATCH 171/188] Implement more cursor stuff --- types/wlr_zext_screencopy_v1.c | 85 +++++++++++++++++++++++++--------- 1 file changed, 62 insertions(+), 23 deletions(-) diff --git a/types/wlr_zext_screencopy_v1.c b/types/wlr_zext_screencopy_v1.c index ffb4c4aaa9..63d5e7c712 100644 --- a/types/wlr_zext_screencopy_v1.c +++ b/types/wlr_zext_screencopy_v1.c @@ -7,6 +7,7 @@ #include "util/signal.h" #include "render/wlr_renderer.h" +#include "render/swapchain.h" #include #include @@ -41,6 +42,8 @@ static void surface_destroy(struct wlr_zext_screencopy_surface_v1 *surface) { pixman_region32_fini(&surface->frame_damage); pixman_region32_fini(&surface->current_buffer.damage); pixman_region32_fini(&surface->staged_buffer.damage); + pixman_region32_fini(&surface->staged_cursor_buffer.damage); + pixman_region32_fini(&surface->current_cursor_buffer.damage); wl_list_remove(&surface->output_precommit.link); wl_list_remove(&surface->output_commit.link); @@ -54,6 +57,14 @@ static void surface_destroy(struct wlr_zext_screencopy_surface_v1 *surface) { wl_list_remove(&surface->current_buffer.destroy.link); } + if (surface->staged_cursor_buffer.resource) { + wl_list_remove(&surface->staged_cursor_buffer.destroy.link); + } + + if (surface->current_cursor_buffer.resource) { + wl_list_remove(&surface->current_cursor_buffer.destroy.link); + } + wl_resource_set_user_data(surface->resource, NULL); free(surface); } @@ -169,6 +180,21 @@ static void surface_damage_buffer(struct wl_client *client, &surface->staged_buffer.damage, x, y, width, height); } +static void surface_damage_cursor_buffer(struct wl_client *client, + struct wl_resource *surface_resource, const char *seat_name) { + struct wlr_zext_screencopy_surface_v1 *surface = + surface_from_resource(surface_resource); + if (!surface) { + return; + } + + // TODO: Do something with "seat_name" + + pixman_region32_union_rect(&surface->staged_buffer.damage, + &surface->staged_buffer.damage, 0, 0, + INT32_MAX, INT32_MAX); +} + static void surface_commit(struct wl_client *client, struct wl_resource *surface_resource, uint32_t options) { struct wlr_zext_screencopy_surface_v1 *surface = @@ -213,9 +239,6 @@ static void surface_commit(struct wl_client *client, pixman_region32_copy(&surface->current_cursor_buffer.damage, &surface->staged_cursor_buffer.damage); pixman_region32_clear(&surface->staged_cursor_buffer.damage); - pixman_region32_intersect_rect(&surface->current_cursor_buffer.damage, - &surface->current_cursor_buffer.damage, 0, 0, - output->width, output->height); surface->options = options; @@ -235,6 +258,7 @@ static const struct zext_screencopy_surface_v1_interface surface_impl = { .attach_buffer = surface_attach_buffer, .attach_cursor_buffer = surface_attach_cursor_buffer, .damage_buffer = surface_damage_buffer, + .damage_cursor_buffer = surface_damage_cursor_buffer, .commit = surface_commit, .destroy = surface_handle_destroy, }; @@ -307,16 +331,6 @@ static void surface_accumulate_frame_damage( } } -#if 0 -static void surface_damage_whole( - struct wlr_zext_screencopy_surface_v1 *surface) { - struct pixman_region32 *region = &surface->frame_damage; - struct wlr_output *output = surface->output; - pixman_region32_union_rect(region, region, 0, 0, output->width, - output->height); -} -#endif - static void surface_handle_output_precommit_ready( struct wlr_zext_screencopy_surface_v1 *surface, struct wlr_output_event_precommit *event) { @@ -384,15 +398,27 @@ static void surface_handle_output_commit_formats( output->height, 0); } - // TODO: Is the hardware cursor buffer always available? + // TODO: If hardware cursors are not enabled, don't advertise cursor + // formats. + bool have_dummy_buffer = false; struct wlr_buffer *cursor_buffer = output->cursor_front_buffer; - if (cursor_buffer) { - surface->cursor_wl_shm_format = - get_buffer_preferred_read_format(cursor_buffer, renderer); - surface->cursor_wl_shm_stride = - cursor_buffer->width * 4; // TODO: This assumes things... + if (!cursor_buffer) { + // This is a hack so that we can get a cursor buffer when no + // cursor is set. + cursor_buffer = wlr_swapchain_acquire(output->cursor_swapchain, + NULL); + have_dummy_buffer = true; + } + + surface->cursor_wl_shm_format = + get_buffer_preferred_read_format(cursor_buffer, renderer); + surface->cursor_wl_shm_stride = + cursor_buffer->width * 4; // TODO: This assumes things... - surface->cursor_dmabuf_format = get_dmabuf_format(cursor_buffer); + surface->cursor_dmabuf_format = get_dmabuf_format(cursor_buffer); + + if (have_dummy_buffer) { + wlr_buffer_unlock(cursor_buffer); } if (surface->cursor_wl_shm_format != DRM_FORMAT_INVALID) { @@ -423,9 +449,18 @@ static void surface_send_cursor_info( struct wlr_output *output = surface->output; struct wlr_output_cursor *cursor = output->hardware_cursor; + if (!output->cursor_front_buffer || + !surface->current_cursor_buffer.resource) { + return; + } + + bool have_damage = true; + // TODO: +// bool have_damage = pixman_region32_not_empty(&surface->cursor_damage); + zext_screencopy_surface_v1_send_cursor_info(surface->resource, - "default", 1, cursor->x, cursor->y, cursor->hotspot_x, - cursor->hotspot_y); + "default", have_damage, cursor->x, cursor->y, + cursor->hotspot_x, cursor->hotspot_y); } static void surface_send_transform(struct wlr_zext_screencopy_surface_v1 *surface) { @@ -604,7 +639,8 @@ static void surface_handle_output_commit_ready( return; } - if (surface->current_cursor_buffer.resource) { + if (surface->output->cursor_front_buffer && + surface->current_cursor_buffer.resource) { if (!surface_copy(surface, &surface->current_cursor_buffer, output->cursor_front_buffer, surface->cursor_wl_shm_format, @@ -621,6 +657,7 @@ static void surface_handle_output_commit_ready( pixman_region32_clear(&surface->frame_damage); pixman_region32_clear(&surface->current_buffer.damage); + pixman_region32_clear(&surface->current_cursor_buffer.damage); wl_list_remove(&surface->current_buffer.destroy.link); surface->current_buffer.resource = NULL; @@ -692,6 +729,8 @@ static void capture_output(struct wl_client *client, uint32_t version, pixman_region32_init(&surface->current_buffer.damage); pixman_region32_init(&surface->staged_buffer.damage); + pixman_region32_init(&surface->current_cursor_buffer.damage); + pixman_region32_init(&surface->staged_cursor_buffer.damage); pixman_region32_init(&surface->frame_damage); pixman_region32_union_rect(&surface->frame_damage, From 962adc9a191bfb22edf5c460c155a8ccd797b229 Mon Sep 17 00:00:00 2001 From: Andri Yngvason Date: Sun, 28 Nov 2021 18:56:46 +0000 Subject: [PATCH 172/188] examples: screencopy-dmabuf: Update after protocol change --- examples/screencopy-dmabuf.c | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/examples/screencopy-dmabuf.c b/examples/screencopy-dmabuf.c index d1f19b2383..6b40c5879a 100644 --- a/examples/screencopy-dmabuf.c +++ b/examples/screencopy-dmabuf.c @@ -108,7 +108,8 @@ static void dmabuf_created(void *data, zext_screencopy_surface_v1_damage_buffer(surface, 0, 0, buffer.width, buffer.height); zext_screencopy_surface_v1_commit(surface, - ZEXT_SCREENCOPY_SURFACE_V1_OPTIONS_SCHEDULE_FRAME); + ZEXT_SCREENCOPY_SURFACE_V1_OPTIONS_IMMEDIATE | + ZEXT_SCREENCOPY_SURFACE_V1_OPTIONS_RENDER_CURSORS); } static void dmabuf_failed(void *data, @@ -162,11 +163,6 @@ static void surface_handle_buffer_info(void *data, uint32_t fourcc, uint32_t width, uint32_t height, uint32_t stride) { - if (type == ZEXT_SCREENCOPY_SURFACE_V1_BUFFER_TYPE_NONE) { - surface_handle_buffer_done(surface); - return; - } - if (type != ZEXT_SCREENCOPY_SURFACE_V1_BUFFER_TYPE_DMABUF) { return; } @@ -177,6 +173,17 @@ static void surface_handle_buffer_info(void *data, have_linux_dmabuf = true; } +static void surface_handle_cursor_buffer_info(void *data, + struct zext_screencopy_surface_v1 *surface, const char* name, + uint32_t type, uint32_t fourcc, uint32_t width, uint32_t height, + uint32_t stride) { +} + +static void surface_handle_init_done(void *data, + struct zext_screencopy_surface_v1 *surface) { + surface_handle_buffer_done(surface); +} + static void surface_handle_damage(void *data, struct zext_screencopy_surface_v1 *surface, uint32_t x, uint32_t y, uint32_t width, uint32_t height) { @@ -184,7 +191,7 @@ static void surface_handle_damage(void *data, static void surface_handle_cursor_info(void *data, struct zext_screencopy_surface_v1 *surface, - int32_t pos_x, int32_t pos_y, + const char *name, int damaged, int32_t pos_x, int32_t pos_y, int32_t hotspot_x, int32_t hotspot_y) { } @@ -193,7 +200,7 @@ static void surface_handle_transform(void *data, buffer.y_invert = transform == WL_OUTPUT_TRANSFORM_FLIPPED_180; } -static void surface_handle_presentation_time(void *data, +static void surface_handle_commit_time(void *data, struct zext_screencopy_surface_v1 *frame, uint32_t tv_sec_hi, uint32_t tv_sec_lo, uint32_t tv_nsec) { } @@ -212,9 +219,11 @@ static void surface_handle_failed(void *data, static const struct zext_screencopy_surface_v1_listener surface_listener = { .buffer_info = surface_handle_buffer_info, + .cursor_buffer_info = surface_handle_cursor_buffer_info, + .init_done = surface_handle_init_done, .damage = surface_handle_damage, .cursor_info = surface_handle_cursor_info, - .presentation_time = surface_handle_presentation_time, + .commit_time = surface_handle_commit_time, .transform = surface_handle_transform, .ready = surface_handle_ready, .failed = surface_handle_failed, From 38cd11574bfd29853efa031f3a6e60a1259ba459 Mon Sep 17 00:00:00 2001 From: Andri Yngvason Date: Sun, 28 Nov 2021 19:10:46 +0000 Subject: [PATCH 173/188] Fix a whoopsie --- types/wlr_zext_screencopy_v1.c | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/types/wlr_zext_screencopy_v1.c b/types/wlr_zext_screencopy_v1.c index 63d5e7c712..dd19a255bf 100644 --- a/types/wlr_zext_screencopy_v1.c +++ b/types/wlr_zext_screencopy_v1.c @@ -212,11 +212,14 @@ static void surface_commit(struct wl_client *client, surface->current_buffer.resource = surface->staged_buffer.resource; surface->staged_buffer.resource = NULL; - wl_list_remove(&surface->staged_buffer.destroy.link); - wl_resource_add_destroy_listener(surface->current_buffer.resource, - &surface->current_buffer.destroy); - surface->current_buffer.destroy.notify = - surface_handle_committed_buffer_destroy; + if (surface->current_buffer.resource) { + wl_list_remove(&surface->staged_buffer.destroy.link); + wl_resource_add_destroy_listener( + surface->current_buffer.resource, + &surface->current_buffer.destroy); + surface->current_buffer.destroy.notify = + surface_handle_committed_buffer_destroy; + } pixman_region32_copy(&surface->current_buffer.damage, &surface->staged_buffer.damage); @@ -230,11 +233,14 @@ static void surface_commit(struct wl_client *client, surface->staged_cursor_buffer.resource; surface->staged_cursor_buffer.resource = NULL; - wl_list_remove(&surface->staged_cursor_buffer.destroy.link); - wl_resource_add_destroy_listener(surface->current_cursor_buffer.resource, - &surface->current_cursor_buffer.destroy); - surface->current_cursor_buffer.destroy.notify = - surface_handle_committed_cursor_buffer_destroy; + if (surface->current_cursor_buffer.resource) { + wl_list_remove(&surface->staged_cursor_buffer.destroy.link); + wl_resource_add_destroy_listener( + surface->current_cursor_buffer.resource, + &surface->current_cursor_buffer.destroy); + surface->current_cursor_buffer.destroy.notify = + surface_handle_committed_cursor_buffer_destroy; + } pixman_region32_copy(&surface->current_cursor_buffer.damage, &surface->staged_cursor_buffer.damage); From 176710d1a194628e874f3772f9338b0a2952662e Mon Sep 17 00:00:00 2001 From: Andri Yngvason Date: Sun, 30 Jan 2022 20:25:18 +0000 Subject: [PATCH 174/188] protocol: screencopy: specify monotonic time --- protocol/screencopy-unstable-v1.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocol/screencopy-unstable-v1.xml b/protocol/screencopy-unstable-v1.xml index 5c9ea7f04e..8184253c77 100644 --- a/protocol/screencopy-unstable-v1.xml +++ b/protocol/screencopy-unstable-v1.xml @@ -259,7 +259,7 @@ This event indicates the time at which the frame is committed to be - scanned out. + scanned out in system monotonic time. The timestamp is expressed as tv_sec_hi, tv_sec_lo, tv_nsec triples, each component being an unsigned 32-bit value. Whole seconds are in From f311c001e1dae40587276fb79d3a5b46b8101ab9 Mon Sep 17 00:00:00 2001 From: Andri Yngvason Date: Sun, 30 Jan 2022 20:40:04 +0000 Subject: [PATCH 175/188] zext-screencopy: Handle API change --- types/wlr_zext_screencopy_v1.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/types/wlr_zext_screencopy_v1.c b/types/wlr_zext_screencopy_v1.c index dd19a255bf..5471478917 100644 --- a/types/wlr_zext_screencopy_v1.c +++ b/types/wlr_zext_screencopy_v1.c @@ -377,7 +377,7 @@ static void surface_handle_output_commit_formats( return; } - struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend); + struct wlr_renderer *renderer = output->renderer; struct wlr_buffer *buffer = event->buffer; assert(buffer); @@ -478,7 +478,7 @@ static bool surface_copy_wl_shm(struct wlr_zext_screencopy_surface_v1 *surface, struct wlr_buffer *dst_buffer, struct wlr_shm_attributes *attr, struct wlr_buffer *src_buffer, uint32_t wl_shm_format) { struct wlr_output *output = surface->output; - struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend); + struct wlr_renderer *renderer = output->renderer; if (dst_buffer->width != src_buffer->width || dst_buffer->height != src_buffer->height) { @@ -550,7 +550,7 @@ static bool surface_copy_dmabuf(struct wlr_zext_screencopy_surface_v1 *surface, struct wlr_buffer *dst_buffer, struct wlr_dmabuf_attributes *attr, struct wlr_buffer *src_buffer, uint32_t format) { struct wlr_output *output = surface->output; - struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend); + struct wlr_renderer *renderer = output->renderer; if (dst_buffer->width != src_buffer->width || dst_buffer->height != src_buffer->height) { From 1a89d0d3fe895fbcd770399abf13528681ba82cd Mon Sep 17 00:00:00 2001 From: Andri Yngvason Date: Wed, 2 Feb 2022 19:59:43 +0000 Subject: [PATCH 176/188] Scissor dmabufs based on buffer damage --- types/wlr_zext_screencopy_v1.c | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/types/wlr_zext_screencopy_v1.c b/types/wlr_zext_screencopy_v1.c index 5471478917..087c731cd0 100644 --- a/types/wlr_zext_screencopy_v1.c +++ b/types/wlr_zext_screencopy_v1.c @@ -4,6 +4,7 @@ #include #include #include +#include #include "util/signal.h" #include "render/wlr_renderer.h" @@ -503,6 +504,7 @@ static bool surface_copy_wl_shm(struct wlr_zext_screencopy_surface_v1 *surface, return false; } + // TODO: Only copy damaged region uint32_t renderer_flags = 0; bool ok; ok = wlr_renderer_begin_with_buffer(renderer, src_buffer); @@ -518,7 +520,7 @@ static bool surface_copy_wl_shm(struct wlr_zext_screencopy_surface_v1 *surface, static bool blit_dmabuf(struct wlr_renderer *renderer, struct wlr_buffer *dst_buffer, - struct wlr_buffer *src_buffer) { + struct wlr_buffer *src_buffer, struct wlr_box *clip_box) { struct wlr_texture *src_tex = wlr_texture_from_buffer(renderer, src_buffer); if (!src_tex) { @@ -533,8 +535,9 @@ static bool blit_dmabuf(struct wlr_renderer *renderer, goto error_renderer_begin; } - wlr_renderer_clear(renderer, (float[]){ 0.0, 0.0, 0.0, 0.0 }); + wlr_renderer_scissor(renderer, clip_box); wlr_render_texture_with_matrix(renderer, src_tex, mat, 1.0f); + wlr_renderer_scissor(renderer, NULL); wlr_renderer_end(renderer); @@ -560,7 +563,24 @@ static bool surface_copy_dmabuf(struct wlr_zext_screencopy_surface_v1 *surface, if (attr->format != format) return false; - return blit_dmabuf(renderer, dst_buffer, src_buffer); + // TODO: More fine grained damage regions + struct pixman_region32 damage; + + pixman_region32_init(&damage); + pixman_region32_union(&damage, &surface->frame_damage, + &surface->current_buffer.damage); + + pixman_box32_t *extents = pixman_region32_extents(&damage); + struct wlr_box clip_box = { + .x = extents->x1, + .y = extents->y1, + .width = extents->x2 - extents->x1, + .height = extents->y2 - extents->y1, + }; + + pixman_region32_fini(&damage); + + return blit_dmabuf(renderer, dst_buffer, src_buffer, &clip_box); } static bool surface_copy(struct wlr_zext_screencopy_surface_v1 *surface, From eaf5b6902d98e2ad94a7acfb80d5c5cfca78b34e Mon Sep 17 00:00:00 2001 From: Andri Yngvason Date: Thu, 3 Feb 2022 22:01:57 +0000 Subject: [PATCH 177/188] Work on cursor capturing --- include/wlr/types/wlr_zext_screencopy_v1.h | 8 +- protocol/screencopy-unstable-v1.xml | 24 ++++ types/wlr_zext_screencopy_v1.c | 139 ++++++++++++++------- 3 files changed, 126 insertions(+), 45 deletions(-) diff --git a/include/wlr/types/wlr_zext_screencopy_v1.h b/include/wlr/types/wlr_zext_screencopy_v1.h index 21cfeb7d47..c7734070c6 100644 --- a/include/wlr/types/wlr_zext_screencopy_v1.h +++ b/include/wlr/types/wlr_zext_screencopy_v1.h @@ -28,7 +28,7 @@ struct wlr_zext_screencopy_manager_v1 { }; enum wlr_zext_screencopy_surface_v1_state { - WLR_ZEXT_SCREENCOPY_SURFACE_V1_STATE_WAITING_FOR_BUFFER_FORMATS, + WLR_ZEXT_SCREENCOPY_SURFACE_V1_STATE_WAITING_FOR_BUFFER_FORMATS = 0, WLR_ZEXT_SCREENCOPY_SURFACE_V1_STATE_READY, }; @@ -43,6 +43,8 @@ struct wlr_zext_screencopy_surface_v1 { enum wlr_zext_screencopy_surface_v1_state state; + struct wlr_buffer *buffer; + uint32_t wl_shm_format; int wl_shm_stride; uint32_t dmabuf_format; @@ -51,6 +53,10 @@ struct wlr_zext_screencopy_surface_v1 { int cursor_wl_shm_stride; uint32_t cursor_dmabuf_format; + int cursor_width; + int cursor_height; + bool have_cursor; + uint32_t options; struct wlr_zext_screencopy_surface_v1_buffer staged_buffer; struct wlr_zext_screencopy_surface_v1_buffer current_buffer; diff --git a/protocol/screencopy-unstable-v1.xml b/protocol/screencopy-unstable-v1.xml index 8184253c77..6eec9adb6b 100644 --- a/protocol/screencopy-unstable-v1.xml +++ b/protocol/screencopy-unstable-v1.xml @@ -135,6 +135,13 @@ + + + Signals that some buffer attributes have changed. This event will be + followed by buffer info events and "init_done". + + + Attach a buffer to the surface. @@ -225,6 +232,23 @@ + + + Sent when a cursor enters the captured surface. It shall be generated + before the "cursor_info" event when and only when a cursor enters the + surface. + + + + + + + Sent when a cursor leaves the captured surface. No "cursor_info" event + is generated for for the given cursor. + + + + This event is generated for each cursor buffer that was attached to the diff --git a/types/wlr_zext_screencopy_v1.c b/types/wlr_zext_screencopy_v1.c index 087c731cd0..69fad066e9 100644 --- a/types/wlr_zext_screencopy_v1.c +++ b/types/wlr_zext_screencopy_v1.c @@ -370,63 +370,47 @@ static void surface_handle_output_precommit(struct wl_listener *listener, } } -static void surface_handle_output_commit_formats( - struct wlr_zext_screencopy_surface_v1 *surface, - struct wlr_output_event_commit *event) { +static bool surface_check_cursor_formats( + struct wlr_zext_screencopy_surface_v1 *surface) { struct wlr_output *output = surface_check_output(surface); if (!output) { - return; + return false; } - struct wlr_renderer *renderer = output->renderer; - - struct wlr_buffer *buffer = event->buffer; - assert(buffer); - - surface->wl_shm_format = - get_buffer_preferred_read_format(buffer, renderer); - surface->wl_shm_stride = buffer->width * 4; // TODO: This assumes things... + struct wlr_buffer *buffer = output->cursor_front_buffer; + if (!buffer) { + return false; + } - surface->dmabuf_format = get_dmabuf_format(buffer); + uint32_t dmabuf_format = get_dmabuf_format(buffer); - if (surface->wl_shm_format != DRM_FORMAT_INVALID) { - assert(surface->wl_shm_stride); + return buffer->width != surface->cursor_width || + buffer->height != surface->cursor_height || + surface->cursor_dmabuf_format != dmabuf_format; +} - zext_screencopy_surface_v1_send_buffer_info(surface->resource, - ZEXT_SCREENCOPY_SURFACE_V1_BUFFER_TYPE_WL_SHM, - surface->wl_shm_format, output->width, - output->height, surface->wl_shm_stride); +static void surface_advertise_cursor_formats( + struct wlr_zext_screencopy_surface_v1 *surface) { + struct wlr_output *output = surface_check_output(surface); + if (!output) { + return; } - if (surface->dmabuf_format != DRM_FORMAT_INVALID) { - zext_screencopy_surface_v1_send_buffer_info(surface->resource, - ZEXT_SCREENCOPY_SURFACE_V1_BUFFER_TYPE_DMABUF, - surface->dmabuf_format, output->width, - output->height, 0); + struct wlr_buffer *buffer = output->cursor_front_buffer; + if (!buffer) { + return; } - // TODO: If hardware cursors are not enabled, don't advertise cursor - // formats. - bool have_dummy_buffer = false; - struct wlr_buffer *cursor_buffer = output->cursor_front_buffer; - if (!cursor_buffer) { - // This is a hack so that we can get a cursor buffer when no - // cursor is set. - cursor_buffer = wlr_swapchain_acquire(output->cursor_swapchain, - NULL); - have_dummy_buffer = true; - } + struct wlr_renderer *renderer = output->renderer; surface->cursor_wl_shm_format = - get_buffer_preferred_read_format(cursor_buffer, renderer); + get_buffer_preferred_read_format(buffer, renderer); surface->cursor_wl_shm_stride = - cursor_buffer->width * 4; // TODO: This assumes things... - - surface->cursor_dmabuf_format = get_dmabuf_format(cursor_buffer); + buffer->width * 4; // TODO: This assumes things... + surface->cursor_dmabuf_format = get_dmabuf_format(buffer); - if (have_dummy_buffer) { - wlr_buffer_unlock(cursor_buffer); - } + surface->cursor_width = buffer->width; + surface->cursor_height = buffer->height; if (surface->cursor_wl_shm_format != DRM_FORMAT_INVALID) { assert(surface->cursor_wl_shm_stride); @@ -435,7 +419,7 @@ static void surface_handle_output_commit_formats( surface->resource, "default", ZEXT_SCREENCOPY_SURFACE_V1_BUFFER_TYPE_WL_SHM, surface->cursor_wl_shm_format, - cursor_buffer->width, cursor_buffer->height, + buffer->width, buffer->height, surface->cursor_wl_shm_stride); } @@ -444,10 +428,57 @@ static void surface_handle_output_commit_formats( surface->resource, "default", ZEXT_SCREENCOPY_SURFACE_V1_BUFFER_TYPE_DMABUF, surface->cursor_dmabuf_format, - cursor_buffer->width, cursor_buffer->height, 0); + buffer->width, buffer->height, 0); } zext_screencopy_surface_v1_send_init_done(surface->resource); + + if (output->cursor_front_buffer) { + zext_screencopy_surface_v1_send_cursor_enter(surface->resource, + "default"); + surface->have_cursor = true; + } +} + +static void surface_advertise_buffer_formats( + struct wlr_zext_screencopy_surface_v1 *surface, + struct wlr_buffer *buffer) { + struct wlr_output *output = surface_check_output(surface); + if (!output) { + return; + } + + struct wlr_renderer *renderer = output->renderer; + + surface->wl_shm_format = + get_buffer_preferred_read_format(buffer, renderer); + surface->wl_shm_stride = buffer->width * 4; // TODO: This assumes things... + + surface->dmabuf_format = get_dmabuf_format(buffer); + + if (surface->wl_shm_format != DRM_FORMAT_INVALID) { + assert(surface->wl_shm_stride); + + zext_screencopy_surface_v1_send_buffer_info(surface->resource, + ZEXT_SCREENCOPY_SURFACE_V1_BUFFER_TYPE_WL_SHM, + surface->wl_shm_format, output->width, + output->height, surface->wl_shm_stride); + } + + if (surface->dmabuf_format != DRM_FORMAT_INVALID) { + zext_screencopy_surface_v1_send_buffer_info(surface->resource, + ZEXT_SCREENCOPY_SURFACE_V1_BUFFER_TYPE_DMABUF, + surface->dmabuf_format, output->width, + output->height, 0); + } + + surface_advertise_cursor_formats(surface); +} + +static void surface_handle_output_commit_formats( + struct wlr_zext_screencopy_surface_v1 *surface, + struct wlr_output_event_commit *event) { + surface_advertise_buffer_formats(surface, event->buffer); surface->state = WLR_ZEXT_SCREENCOPY_SURFACE_V1_STATE_READY; } @@ -656,6 +687,26 @@ static void surface_handle_output_commit_ready( return; } + if (surface_check_cursor_formats(surface)) { + zext_screencopy_surface_v1_send_cursor_leave(surface->resource, + "default"); + zext_screencopy_surface_v1_send_reconfig(surface->resource); + surface_advertise_buffer_formats(surface, event->buffer); + // TODO: Reset staged buffers? + return; + } + + if (surface->have_cursor && !output->cursor_front_buffer) { + zext_screencopy_surface_v1_send_cursor_leave(surface->resource, + "default"); + surface->have_cursor = false; + } else if (!surface->have_cursor && output->cursor_front_buffer) { + zext_screencopy_surface_v1_send_cursor_enter(surface->resource, + "default"); + surface->have_cursor = true; + } + + if (!surface->current_buffer.resource) { return; } From 93b2b017da6baef48fa0649ba1d29174c75dedc3 Mon Sep 17 00:00:00 2001 From: Andri Yngvason Date: Sat, 5 Feb 2022 23:13:00 +0000 Subject: [PATCH 178/188] Continue work on cursor capturing and damage tracking --- include/wlr/types/wlr_zext_screencopy_v1.h | 3 + types/wlr_zext_screencopy_v1.c | 287 ++++++++++++++++----- 2 files changed, 226 insertions(+), 64 deletions(-) diff --git a/include/wlr/types/wlr_zext_screencopy_v1.h b/include/wlr/types/wlr_zext_screencopy_v1.h index c7734070c6..6f60407326 100644 --- a/include/wlr/types/wlr_zext_screencopy_v1.h +++ b/include/wlr/types/wlr_zext_screencopy_v1.h @@ -44,6 +44,7 @@ struct wlr_zext_screencopy_surface_v1 { enum wlr_zext_screencopy_surface_v1_state state; struct wlr_buffer *buffer; + struct wlr_surface *output_cursor_surface; uint32_t wl_shm_format; int wl_shm_stride; @@ -73,6 +74,8 @@ struct wlr_zext_screencopy_surface_v1 { struct wl_listener output_precommit; struct wl_listener output_commit; struct wl_listener output_destroy; + struct wl_listener output_cursor_surface_commit; + struct wl_listener output_cursor_surface_destroy; void *data; }; diff --git a/types/wlr_zext_screencopy_v1.c b/types/wlr_zext_screencopy_v1.c index 69fad066e9..902bfe984a 100644 --- a/types/wlr_zext_screencopy_v1.c +++ b/types/wlr_zext_screencopy_v1.c @@ -2,9 +2,11 @@ #include #include #include +#include #include #include #include +#include #include "util/signal.h" #include "render/wlr_renderer.h" @@ -13,6 +15,7 @@ #include #include #include +#include #include "screencopy-unstable-v1-protocol.h" @@ -50,6 +53,11 @@ static void surface_destroy(struct wlr_zext_screencopy_surface_v1 *surface) { wl_list_remove(&surface->output_commit.link); wl_list_remove(&surface->output_destroy.link); + if (surface->output_cursor_surface) { + wl_list_remove(&surface->output_cursor_surface_commit.link); + wl_list_remove(&surface->output_cursor_surface_destroy.link); + } + if (surface->staged_buffer.resource) { wl_list_remove(&surface->staged_buffer.destroy.link); } @@ -93,6 +101,11 @@ static void surface_handle_staged_buffer_destroy(struct wl_listener *listener, void *data) { struct wlr_zext_screencopy_surface_v1 *surface = wl_container_of(listener, surface, staged_buffer.destroy); + + if (!surface->staged_buffer.resource) { + return; + } + surface->staged_buffer.resource = NULL; wl_list_remove(&surface->staged_buffer.destroy.link); } @@ -101,6 +114,11 @@ static void surface_handle_committed_buffer_destroy(struct wl_listener *listener void *data) { struct wlr_zext_screencopy_surface_v1 *surface = wl_container_of(listener, surface, current_buffer.destroy); + + if (!surface->current_buffer.resource) { + return; + } + surface->current_buffer.resource = NULL; wl_list_remove(&surface->current_buffer.destroy.link); } @@ -109,6 +127,11 @@ static void surface_handle_staged_cursor_buffer_destroy(struct wl_listener *list void *data) { struct wlr_zext_screencopy_surface_v1 *surface = wl_container_of(listener, surface, staged_cursor_buffer.destroy); + + if (!surface->staged_cursor_buffer.resource) { + return; + } + surface->staged_cursor_buffer.resource = NULL; wl_list_remove(&surface->staged_cursor_buffer.destroy.link); } @@ -117,6 +140,11 @@ static void surface_handle_committed_cursor_buffer_destroy( struct wl_listener *listener, void *data) { struct wlr_zext_screencopy_surface_v1 *surface = wl_container_of(listener, surface, current_cursor_buffer.destroy); + + if (!surface->current_cursor_buffer.resource) { + return; + } + surface->current_cursor_buffer.resource = NULL; wl_list_remove(&surface->current_cursor_buffer.destroy.link); } @@ -191,9 +219,9 @@ static void surface_damage_cursor_buffer(struct wl_client *client, // TODO: Do something with "seat_name" - pixman_region32_union_rect(&surface->staged_buffer.damage, - &surface->staged_buffer.damage, 0, 0, - INT32_MAX, INT32_MAX); + pixman_region32_union_rect(&surface->staged_cursor_buffer.damage, + &surface->staged_cursor_buffer.damage, 0, 0, + UINT32_MAX, UINT32_MAX); } static void surface_commit(struct wl_client *client, @@ -249,9 +277,9 @@ static void surface_commit(struct wl_client *client, surface->options = options; - if (options & ZEXT_SCREENCOPY_SURFACE_V1_OPTIONS_IMMEDIATE) { +// if (options & ZEXT_SCREENCOPY_SURFACE_V1_OPTIONS_IMMEDIATE) { wlr_output_schedule_frame(output); - } +// } } static void surface_handle_destroy(struct wl_client *client, @@ -278,7 +306,8 @@ static void surface_handle_resource_destroy(struct wl_resource *resource) { static void surface_handle_output_destroy(struct wl_listener *listener, void *data) { - struct wlr_zext_screencopy_surface_v1 *surface = data; + struct wlr_zext_screencopy_surface_v1 *surface = + wl_container_of(listener, surface, output_destroy); surface->output = NULL; } @@ -338,6 +367,26 @@ static void surface_accumulate_frame_damage( } } +static void surface_accumulate_cursor_damage( + struct wlr_zext_screencopy_surface_v1 *surface) +{ + struct pixman_region32 *region = &surface->cursor_damage; + struct wlr_surface *cursor_surface = surface->output_cursor_surface; + struct wlr_surface_state *state = &cursor_surface->current; + + if (state->committed & WLR_SURFACE_STATE_SURFACE_DAMAGE) { + // If the compositor submitted damage, copy it over + pixman_region32_union(region, region, &state->surface_damage); + pixman_region32_intersect_rect(region, region, 0, 0, + state->width, state->height); + } else if (state->committed & WLR_SURFACE_STATE_BUFFER) { + // If the compositor did not submit damage but did submit a + // buffer damage everything + pixman_region32_union_rect(region, region, 0, 0, state->width, + state->height); + } +} + static void surface_handle_output_precommit_ready( struct wlr_zext_screencopy_surface_v1 *surface, struct wlr_output_event_precommit *event) { @@ -389,6 +438,76 @@ static bool surface_check_cursor_formats( surface->cursor_dmabuf_format != dmabuf_format; } +static bool surface_is_cursor_visible( + struct wlr_zext_screencopy_surface_v1 *surface) +{ + struct wlr_output *output = surface->output; + struct wlr_output_cursor *cursor = output->hardware_cursor; + + return output->cursor_front_buffer && cursor && cursor->enabled && + cursor->visible; +} + +static void surface_handle_output_cursor_surface_destroy( + struct wl_listener *listener, void *data) { + struct wlr_zext_screencopy_surface_v1 *surface = + wl_container_of(listener, surface, output_cursor_surface_destroy); + + if (!surface->output_cursor_surface) { + return; + } + + // The cursor surface is gone, so something must have taken its place: + pixman_region32_union_rect(&surface->cursor_damage, + &surface->cursor_damage, 0, 0, UINT32_MAX, UINT32_MAX); + + wl_list_remove(&surface->output_cursor_surface_commit.link); + wl_list_remove(&surface->output_cursor_surface_destroy.link); + surface->output_cursor_surface = NULL; +} + +static void surface_handle_output_cursor_surface_commit( + struct wl_listener *listener, void *data) { + struct wlr_zext_screencopy_surface_v1 *surface = + wl_container_of(listener, surface, output_cursor_surface_commit); + + if (!surface->output_cursor_surface) { + return; + } + + surface_accumulate_cursor_damage(surface); +} + +static void surface_init_output_cursor_surface( + struct wlr_zext_screencopy_surface_v1 *surface) { + if (surface->output_cursor_surface) { + return; + } + + struct wlr_output *output = surface_check_output(surface); + if (!output || !output->hardware_cursor || + !output->hardware_cursor->surface) { + return; + } + + struct wlr_surface *cursor_surface = output->hardware_cursor->surface; + + wl_list_init(&surface->output_cursor_surface_commit.link); + wl_list_init(&surface->output_cursor_surface_destroy.link); + + wl_signal_add(&cursor_surface->events.destroy, + &surface->output_cursor_surface_destroy); + surface->output_cursor_surface_destroy.notify = + surface_handle_output_cursor_surface_destroy; + + wl_signal_add(&cursor_surface->events.commit, + &surface->output_cursor_surface_commit); + surface->output_cursor_surface_commit.notify = + surface_handle_output_cursor_surface_commit; + + surface->output_cursor_surface = cursor_surface; +} + static void surface_advertise_cursor_formats( struct wlr_zext_screencopy_surface_v1 *surface) { struct wlr_output *output = surface_check_output(surface); @@ -431,13 +550,6 @@ static void surface_advertise_cursor_formats( buffer->width, buffer->height, 0); } - zext_screencopy_surface_v1_send_init_done(surface->resource); - - if (output->cursor_front_buffer) { - zext_screencopy_surface_v1_send_cursor_enter(surface->resource, - "default"); - surface->have_cursor = true; - } } static void surface_advertise_buffer_formats( @@ -473,28 +585,35 @@ static void surface_advertise_buffer_formats( } surface_advertise_cursor_formats(surface); + + zext_screencopy_surface_v1_send_init_done(surface->resource); + + if (surface_is_cursor_visible(surface)) { + zext_screencopy_surface_v1_send_cursor_enter(surface->resource, + "default"); + surface->have_cursor = true; + } } static void surface_handle_output_commit_formats( struct wlr_zext_screencopy_surface_v1 *surface, struct wlr_output_event_commit *event) { + surface_init_output_cursor_surface(surface); surface_advertise_buffer_formats(surface, event->buffer); surface->state = WLR_ZEXT_SCREENCOPY_SURFACE_V1_STATE_READY; } static void surface_send_cursor_info( struct wlr_zext_screencopy_surface_v1 *surface) { - struct wlr_output *output = surface->output; - struct wlr_output_cursor *cursor = output->hardware_cursor; - - if (!output->cursor_front_buffer || - !surface->current_cursor_buffer.resource) { + if (!surface->current_cursor_buffer.resource || + !surface_is_cursor_visible(surface)) { return; } - bool have_damage = true; - // TODO: -// bool have_damage = pixman_region32_not_empty(&surface->cursor_damage); + struct wlr_output *output = surface->output; + struct wlr_output_cursor *cursor = output->hardware_cursor; + + bool have_damage = pixman_region32_not_empty(&surface->cursor_damage); zext_screencopy_surface_v1_send_cursor_info(surface->resource, "default", have_damage, cursor->x, cursor->y, @@ -508,7 +627,8 @@ static void surface_send_transform(struct wlr_zext_screencopy_surface_v1 *surfac static bool surface_copy_wl_shm(struct wlr_zext_screencopy_surface_v1 *surface, struct wlr_buffer *dst_buffer, struct wlr_shm_attributes *attr, - struct wlr_buffer *src_buffer, uint32_t wl_shm_format) { + struct wlr_buffer *src_buffer, uint32_t wl_shm_format, + struct pixman_region32 *damage) { struct wlr_output *output = surface->output; struct wlr_renderer *renderer = output->renderer; @@ -582,7 +702,8 @@ static bool blit_dmabuf(struct wlr_renderer *renderer, static bool surface_copy_dmabuf(struct wlr_zext_screencopy_surface_v1 *surface, struct wlr_buffer *dst_buffer, struct wlr_dmabuf_attributes *attr, - struct wlr_buffer *src_buffer, uint32_t format) { + struct wlr_buffer *src_buffer, uint32_t format, + struct pixman_region32 *damage) { struct wlr_output *output = surface->output; struct wlr_renderer *renderer = output->renderer; @@ -595,13 +716,7 @@ static bool surface_copy_dmabuf(struct wlr_zext_screencopy_surface_v1 *surface, return false; // TODO: More fine grained damage regions - struct pixman_region32 damage; - - pixman_region32_init(&damage); - pixman_region32_union(&damage, &surface->frame_damage, - &surface->current_buffer.damage); - - pixman_box32_t *extents = pixman_region32_extents(&damage); + pixman_box32_t *extents = pixman_region32_extents(damage); struct wlr_box clip_box = { .x = extents->x1, .y = extents->y1, @@ -609,15 +724,18 @@ static bool surface_copy_dmabuf(struct wlr_zext_screencopy_surface_v1 *surface, .height = extents->y2 - extents->y1, }; - pixman_region32_fini(&damage); - return blit_dmabuf(renderer, dst_buffer, src_buffer, &clip_box); } static bool surface_copy(struct wlr_zext_screencopy_surface_v1 *surface, struct wlr_zext_screencopy_surface_v1_buffer *surface_buffer, struct wlr_buffer *src_buffer, uint32_t wl_shm_format, - uint32_t dmabuf_format) { + uint32_t dmabuf_format, struct pixman_region32 *damage) { + if (!pixman_region32_not_empty(damage)) { + // Nothing to do here... + return true; + } + struct wlr_buffer *dst_buffer = wlr_buffer_from_resource(surface_buffer->resource); if (!dst_buffer) { @@ -627,7 +745,7 @@ static bool surface_copy(struct wlr_zext_screencopy_surface_v1 *surface, struct wlr_shm_attributes shm_attr = { 0 }; if (wlr_buffer_get_shm(dst_buffer, &shm_attr)) { if (!surface_copy_wl_shm(surface, dst_buffer, &shm_attr, - src_buffer, wl_shm_format)) { + src_buffer, wl_shm_format, damage)) { goto failure; } } @@ -635,7 +753,7 @@ static bool surface_copy(struct wlr_zext_screencopy_surface_v1 *surface, struct wlr_dmabuf_attributes dmabuf_attr = { 0 }; if (wlr_buffer_get_dmabuf(dst_buffer, &dmabuf_attr)) { if (!surface_copy_dmabuf(surface, dst_buffer, &dmabuf_attr, - src_buffer, dmabuf_format)) { + src_buffer, dmabuf_format, damage)) { goto failure; } } @@ -650,17 +768,19 @@ static bool surface_copy(struct wlr_zext_screencopy_surface_v1 *surface, } static void surface_send_damage(struct wlr_zext_screencopy_surface_v1 *surface) { - // TODO: send fine-grained damage events - struct pixman_box32 *damage_box = - pixman_region32_extents(&surface->frame_damage); - - int damage_x = damage_box->x1; - int damage_y = damage_box->y1; - int damage_width = damage_box->x2 - damage_box->x1; - int damage_height = damage_box->y2 - damage_box->y1; - - zext_screencopy_surface_v1_send_damage(surface->resource, - damage_x, damage_y, damage_width, damage_height); + int n_rects = 0; + struct pixman_box32 *rects = + pixman_region32_rectangles(&surface->frame_damage, &n_rects); + + for (int i = 0; i < n_rects; ++i) { + int damage_x = rects[i].x1; + int damage_y = rects[i].y1; + int damage_width = rects[i].x2 - rects[i].x1; + int damage_height = rects[i].y2 - rects[i].y1; + + zext_screencopy_surface_v1_send_damage(surface->resource, + damage_x, damage_y, damage_width, damage_height); + } } static void surface_send_commit_time( @@ -682,48 +802,88 @@ static void surface_handle_output_commit_ready( return; } - // TODO: We might have cursor damage but no new main buffer... if (!(event->committed & WLR_OUTPUT_STATE_BUFFER)) { return; } + surface_init_output_cursor_surface(surface); + if (surface_check_cursor_formats(surface)) { + if (surface->staged_buffer.resource) { + wl_list_remove(&surface->staged_buffer.destroy.link); + } + + if (surface->current_buffer.resource) { + wl_list_remove(&surface->current_buffer.destroy.link); + } + + if (surface->staged_cursor_buffer.resource) { + wl_list_remove(&surface->staged_cursor_buffer.destroy.link); + } + + if (surface->current_cursor_buffer.resource) { + wl_list_remove(&surface->current_cursor_buffer.destroy.link); + } + + surface->current_buffer.resource = NULL; + surface->staged_buffer.resource = NULL; + surface->current_cursor_buffer.resource = NULL; + surface->staged_cursor_buffer.resource = NULL; + zext_screencopy_surface_v1_send_cursor_leave(surface->resource, "default"); zext_screencopy_surface_v1_send_reconfig(surface->resource); surface_advertise_buffer_formats(surface, event->buffer); - // TODO: Reset staged buffers? return; } - if (surface->have_cursor && !output->cursor_front_buffer) { + if (surface->have_cursor && !surface_is_cursor_visible(surface)) { zext_screencopy_surface_v1_send_cursor_leave(surface->resource, "default"); surface->have_cursor = false; - } else if (!surface->have_cursor && output->cursor_front_buffer) { + } else if (!surface->have_cursor && surface_is_cursor_visible(surface)) { zext_screencopy_surface_v1_send_cursor_enter(surface->resource, "default"); surface->have_cursor = true; } + if (surface->current_buffer.resource) { + struct pixman_region32 damage; + pixman_region32_init(&damage); + pixman_region32_union(&damage, &surface->frame_damage, + &surface->current_buffer.damage); + + bool ok = surface_copy(surface, &surface->current_buffer, + event->buffer, surface->wl_shm_format, + surface->dmabuf_format, &damage); + pixman_region32_fini(&damage); + if (!ok) { + // TODO: Raise some error + } - if (!surface->current_buffer.resource) { - return; + wl_list_remove(&surface->current_buffer.destroy.link); + surface->current_buffer.resource = NULL; } - if (!surface_copy(surface, &surface->current_buffer, event->buffer, - surface->wl_shm_format, surface->dmabuf_format)) { - return; - } + if (surface->current_cursor_buffer.resource && + surface_is_cursor_visible(surface)) { + struct pixman_region32 damage; + pixman_region32_init(&damage); + pixman_region32_union(&damage, &surface->cursor_damage, + &surface->current_cursor_buffer.damage); - if (surface->output->cursor_front_buffer && - surface->current_cursor_buffer.resource) { - if (!surface_copy(surface, &surface->current_cursor_buffer, + bool ok = surface_copy(surface, &surface->current_cursor_buffer, output->cursor_front_buffer, surface->cursor_wl_shm_format, - surface->cursor_dmabuf_format)) { - return; + surface->cursor_dmabuf_format, + &damage); + pixman_region32_fini(&damage); + if (!ok) { + // TODO: Raise some error } + + wl_list_remove(&surface->current_cursor_buffer.destroy.link); + surface->current_cursor_buffer.resource = NULL; } surface_send_transform(surface); @@ -733,11 +893,9 @@ static void surface_handle_output_commit_ready( zext_screencopy_surface_v1_send_ready(surface->resource); pixman_region32_clear(&surface->frame_damage); + pixman_region32_clear(&surface->cursor_damage); pixman_region32_clear(&surface->current_buffer.damage); pixman_region32_clear(&surface->current_cursor_buffer.damage); - - wl_list_remove(&surface->current_buffer.destroy.link); - surface->current_buffer.resource = NULL; } static void surface_handle_output_commit(struct wl_listener *listener, @@ -787,6 +945,7 @@ static void capture_output(struct wl_client *client, uint32_t version, wl_resource_set_implementation(surface->resource, &surface_impl, surface, surface_handle_resource_destroy); + wl_list_init(&surface->output_precommit.link); wl_list_init(&surface->output_commit.link); wl_list_init(&surface->output_destroy.link); From 08e92cd7a0d946285f08c23ffcf170504a30b01f Mon Sep 17 00:00:00 2001 From: Andri Yngvason Date: Sun, 6 Feb 2022 21:25:55 +0000 Subject: [PATCH 179/188] Cursor reporting seems to work now... --- types/wlr_zext_screencopy_v1.c | 53 ++++++++++++++++++++++++++++------ 1 file changed, 44 insertions(+), 9 deletions(-) diff --git a/types/wlr_zext_screencopy_v1.c b/types/wlr_zext_screencopy_v1.c index 902bfe984a..56d2cfb556 100644 --- a/types/wlr_zext_screencopy_v1.c +++ b/types/wlr_zext_screencopy_v1.c @@ -158,12 +158,15 @@ static void surface_attach_buffer(struct wl_client *client, return; } + assert(buffer_resource); + if (surface->staged_buffer.resource) { wl_list_remove(&surface->staged_buffer.destroy.link); } surface->staged_buffer.resource = buffer_resource; if (buffer_resource) { + wl_list_init(&surface->staged_buffer.destroy.link); wl_resource_add_destroy_listener(buffer_resource, &surface->staged_buffer.destroy); surface->staged_buffer.destroy.notify = @@ -221,7 +224,7 @@ static void surface_damage_cursor_buffer(struct wl_client *client, pixman_region32_union_rect(&surface->staged_cursor_buffer.damage, &surface->staged_cursor_buffer.damage, 0, 0, - UINT32_MAX, UINT32_MAX); + surface->cursor_width, surface->cursor_height); } static void surface_commit(struct wl_client *client, @@ -238,6 +241,10 @@ static void surface_commit(struct wl_client *client, } // Main buffer + if (surface->current_buffer.resource) { + wl_list_remove(&surface->current_buffer.destroy.link); + } + surface->current_buffer.resource = surface->staged_buffer.resource; surface->staged_buffer.resource = NULL; @@ -258,6 +265,10 @@ static void surface_commit(struct wl_client *client, output->height); // Cursor buffer + if (surface->current_cursor_buffer.resource) { + wl_list_remove(&surface->current_cursor_buffer.destroy.link); + } + surface->current_cursor_buffer.resource = surface->staged_cursor_buffer.resource; surface->staged_cursor_buffer.resource = NULL; @@ -379,12 +390,16 @@ static void surface_accumulate_cursor_damage( pixman_region32_union(region, region, &state->surface_damage); pixman_region32_intersect_rect(region, region, 0, 0, state->width, state->height); + + wlr_log(WLR_DEBUG, "Got cursor commit event with damage"); } else if (state->committed & WLR_SURFACE_STATE_BUFFER) { // If the compositor did not submit damage but did submit a // buffer damage everything pixman_region32_union_rect(region, region, 0, 0, state->width, state->height); + wlr_log(WLR_DEBUG, "Got cursor commit event with buffer"); } + } static void surface_handle_output_precommit_ready( @@ -427,7 +442,7 @@ static bool surface_check_cursor_formats( } struct wlr_buffer *buffer = output->cursor_front_buffer; - if (!buffer) { + if (!buffer || buffer->width == 0 || buffer->height == 0) { return false; } @@ -457,9 +472,12 @@ static void surface_handle_output_cursor_surface_destroy( return; } + wlr_log(WLR_DEBUG, "Lost a cursor surface"); + // The cursor surface is gone, so something must have taken its place: pixman_region32_union_rect(&surface->cursor_damage, - &surface->cursor_damage, 0, 0, UINT32_MAX, UINT32_MAX); + &surface->cursor_damage, 0, 0, surface->cursor_width, + surface->cursor_height); wl_list_remove(&surface->output_cursor_surface_commit.link); wl_list_remove(&surface->output_cursor_surface_destroy.link); @@ -471,6 +489,8 @@ static void surface_handle_output_cursor_surface_commit( struct wlr_zext_screencopy_surface_v1 *surface = wl_container_of(listener, surface, output_cursor_surface_commit); + wlr_log(WLR_DEBUG, "Got cursor commit event!"); + if (!surface->output_cursor_surface) { return; } @@ -506,6 +526,13 @@ static void surface_init_output_cursor_surface( surface_handle_output_cursor_surface_commit; surface->output_cursor_surface = cursor_surface; + + // We have a new cursor surface, so there's probably damage + pixman_region32_union_rect(&surface->cursor_damage, + &surface->cursor_damage, 0, 0, surface->cursor_width, + surface->cursor_height); + + wlr_log(WLR_DEBUG, "There's a new cursor surface"); } static void surface_advertise_cursor_formats( @@ -550,6 +577,9 @@ static void surface_advertise_cursor_formats( buffer->width, buffer->height, 0); } + pixman_region32_union_rect(&surface->staged_cursor_buffer.damage, + &surface->staged_cursor_buffer.damage, 0, 0, + surface->cursor_width, surface->cursor_height); } static void surface_advertise_buffer_formats( @@ -860,9 +890,6 @@ static void surface_handle_output_commit_ready( if (!ok) { // TODO: Raise some error } - - wl_list_remove(&surface->current_buffer.destroy.link); - surface->current_buffer.resource = NULL; } if (surface->current_cursor_buffer.resource && @@ -881,9 +908,6 @@ static void surface_handle_output_commit_ready( if (!ok) { // TODO: Raise some error } - - wl_list_remove(&surface->current_cursor_buffer.destroy.link); - surface->current_cursor_buffer.resource = NULL; } surface_send_transform(surface); @@ -896,6 +920,17 @@ static void surface_handle_output_commit_ready( pixman_region32_clear(&surface->cursor_damage); pixman_region32_clear(&surface->current_buffer.damage); pixman_region32_clear(&surface->current_cursor_buffer.damage); + + if (surface->current_buffer.resource) { + wl_list_remove(&surface->current_buffer.destroy.link); + } + + if (surface->current_cursor_buffer.resource) { + wl_list_remove(&surface->current_cursor_buffer.destroy.link); + } + + surface->current_buffer.resource = NULL; + surface->current_cursor_buffer.resource = NULL; } static void surface_handle_output_commit(struct wl_listener *listener, From 689b04c9f2356d0bc942b4c91f3fbb7255e0a748 Mon Sep 17 00:00:00 2001 From: Andri Yngvason Date: Sat, 12 Feb 2022 19:10:38 +0000 Subject: [PATCH 180/188] output: Add hardware cursor events --- include/wlr/types/wlr_output.h | 14 ++++++++++++++ types/output/cursor.c | 28 +++++++++++++++++++++++++++- types/output/output.c | 2 ++ 3 files changed, 43 insertions(+), 1 deletion(-) diff --git a/include/wlr/types/wlr_output.h b/include/wlr/types/wlr_output.h index 791023d2e0..99975f7b64 100644 --- a/include/wlr/types/wlr_output.h +++ b/include/wlr/types/wlr_output.h @@ -171,6 +171,8 @@ struct wlr_output { struct wl_signal enable; struct wl_signal mode; struct wl_signal description; + struct wl_signal set_cursor; + struct wl_signal move_cursor; struct wl_signal destroy; } events; @@ -250,6 +252,18 @@ struct wlr_output_event_bind { struct wl_resource *resource; }; +struct wlr_output_event_set_cursor { + struct wlr_output *output; + struct wlr_buffer *buffer; + int hotspot_x; + int hotspot_y; +}; + +struct wlr_output_event_move_cursor { + struct wlr_output* output; + int x, y; +}; + struct wlr_surface; /** diff --git a/types/output/cursor.c b/types/output/cursor.c index f3055c5c47..3ac65632fa 100644 --- a/types/output/cursor.c +++ b/types/output/cursor.c @@ -28,6 +28,14 @@ static bool output_set_hardware_cursor(struct wlr_output *output, output->cursor_front_buffer = wlr_buffer_lock(buffer); } + struct wlr_output_event_set_cursor event = { + .output = output, + .buffer = buffer, + .hotspot_x = hotspot_x, + .hotspot_y = hotspot_y, + }; + wlr_signal_emit_safe(&output->events.set_cursor, &event); + return true; } @@ -347,6 +355,13 @@ static bool output_cursor_attempt_hardware(struct wlr_output_cursor *cursor) { output->impl->move_cursor(cursor->output, (int)cursor->x, (int)cursor->y); + struct wlr_output_event_move_cursor event = { + .output = cursor->output, + .x = (int)cursor->x, + .y = (int)cursor->y, + }; + wlr_signal_emit_safe(&cursor->output->events.move_cursor, &event); + struct wlr_buffer *buffer = NULL; if (texture != NULL) { buffer = render_cursor_buffer(cursor); @@ -537,7 +552,18 @@ bool wlr_output_cursor_move(struct wlr_output_cursor *cursor, } assert(cursor->output->impl->move_cursor); - return cursor->output->impl->move_cursor(cursor->output, (int)x, (int)y); + if (!cursor->output->impl->move_cursor(cursor->output, (int)x, (int)y)) { + return false; + } + + struct wlr_output_event_move_cursor event = { + .output = cursor->output, + .x = (int)x, + .y = (int)y, + }; + wlr_signal_emit_safe(&cursor->output->events.move_cursor, &event); + + return true; } struct wlr_output_cursor *wlr_output_cursor_create(struct wlr_output *output) { diff --git a/types/output/output.c b/types/output/output.c index 0062013f74..72a0fad3bd 100644 --- a/types/output/output.c +++ b/types/output/output.c @@ -401,6 +401,8 @@ void wlr_output_init(struct wlr_output *output, struct wlr_backend *backend, wl_signal_init(&output->events.enable); wl_signal_init(&output->events.mode); wl_signal_init(&output->events.description); + wl_signal_init(&output->events.set_cursor); + wl_signal_init(&output->events.move_cursor); wl_signal_init(&output->events.destroy); pixman_region32_init(&output->pending.damage); From 3912693b07c0d9c5df17a69c977bbac785e05f6f Mon Sep 17 00:00:00 2001 From: Andri Yngvason Date: Sat, 12 Feb 2022 20:21:37 +0000 Subject: [PATCH 181/188] zext-screencopy: Simplify cursor capturing --- include/wlr/types/wlr_zext_screencopy_v1.h | 7 +- types/wlr_zext_screencopy_v1.c | 136 +++++---------------- 2 files changed, 35 insertions(+), 108 deletions(-) diff --git a/include/wlr/types/wlr_zext_screencopy_v1.h b/include/wlr/types/wlr_zext_screencopy_v1.h index 6f60407326..0e61bab6dc 100644 --- a/include/wlr/types/wlr_zext_screencopy_v1.h +++ b/include/wlr/types/wlr_zext_screencopy_v1.h @@ -44,7 +44,6 @@ struct wlr_zext_screencopy_surface_v1 { enum wlr_zext_screencopy_surface_v1_state state; struct wlr_buffer *buffer; - struct wlr_surface *output_cursor_surface; uint32_t wl_shm_format; int wl_shm_stride; @@ -67,15 +66,15 @@ struct wlr_zext_screencopy_surface_v1 { /* Accumulated damage for the surface */ struct pixman_region32 frame_damage; - struct pixman_region32 cursor_damage; + struct pixman_region32 cursor_damage; struct wlr_output *output; struct wl_listener output_precommit; struct wl_listener output_commit; struct wl_listener output_destroy; - struct wl_listener output_cursor_surface_commit; - struct wl_listener output_cursor_surface_destroy; + struct wl_listener output_set_cursor; + struct wl_listener output_move_cursor; void *data; }; diff --git a/types/wlr_zext_screencopy_v1.c b/types/wlr_zext_screencopy_v1.c index 56d2cfb556..d2132daa13 100644 --- a/types/wlr_zext_screencopy_v1.c +++ b/types/wlr_zext_screencopy_v1.c @@ -49,15 +49,12 @@ static void surface_destroy(struct wlr_zext_screencopy_surface_v1 *surface) { pixman_region32_fini(&surface->staged_cursor_buffer.damage); pixman_region32_fini(&surface->current_cursor_buffer.damage); + wl_list_remove(&surface->output_set_cursor.link); + wl_list_remove(&surface->output_move_cursor.link); wl_list_remove(&surface->output_precommit.link); wl_list_remove(&surface->output_commit.link); wl_list_remove(&surface->output_destroy.link); - if (surface->output_cursor_surface) { - wl_list_remove(&surface->output_cursor_surface_commit.link); - wl_list_remove(&surface->output_cursor_surface_destroy.link); - } - if (surface->staged_buffer.resource) { wl_list_remove(&surface->staged_buffer.destroy.link); } @@ -378,30 +375,6 @@ static void surface_accumulate_frame_damage( } } -static void surface_accumulate_cursor_damage( - struct wlr_zext_screencopy_surface_v1 *surface) -{ - struct pixman_region32 *region = &surface->cursor_damage; - struct wlr_surface *cursor_surface = surface->output_cursor_surface; - struct wlr_surface_state *state = &cursor_surface->current; - - if (state->committed & WLR_SURFACE_STATE_SURFACE_DAMAGE) { - // If the compositor submitted damage, copy it over - pixman_region32_union(region, region, &state->surface_damage); - pixman_region32_intersect_rect(region, region, 0, 0, - state->width, state->height); - - wlr_log(WLR_DEBUG, "Got cursor commit event with damage"); - } else if (state->committed & WLR_SURFACE_STATE_BUFFER) { - // If the compositor did not submit damage but did submit a - // buffer damage everything - pixman_region32_union_rect(region, region, 0, 0, state->width, - state->height); - wlr_log(WLR_DEBUG, "Got cursor commit event with buffer"); - } - -} - static void surface_handle_output_precommit_ready( struct wlr_zext_screencopy_surface_v1 *surface, struct wlr_output_event_precommit *event) { @@ -461,78 +434,8 @@ static bool surface_is_cursor_visible( return output->cursor_front_buffer && cursor && cursor->enabled && cursor->visible; -} - -static void surface_handle_output_cursor_surface_destroy( - struct wl_listener *listener, void *data) { - struct wlr_zext_screencopy_surface_v1 *surface = - wl_container_of(listener, surface, output_cursor_surface_destroy); - - if (!surface->output_cursor_surface) { - return; - } - - wlr_log(WLR_DEBUG, "Lost a cursor surface"); - - // The cursor surface is gone, so something must have taken its place: - pixman_region32_union_rect(&surface->cursor_damage, - &surface->cursor_damage, 0, 0, surface->cursor_width, - surface->cursor_height); - - wl_list_remove(&surface->output_cursor_surface_commit.link); - wl_list_remove(&surface->output_cursor_surface_destroy.link); - surface->output_cursor_surface = NULL; -} - -static void surface_handle_output_cursor_surface_commit( - struct wl_listener *listener, void *data) { - struct wlr_zext_screencopy_surface_v1 *surface = - wl_container_of(listener, surface, output_cursor_surface_commit); - - wlr_log(WLR_DEBUG, "Got cursor commit event!"); - - if (!surface->output_cursor_surface) { - return; - } - - surface_accumulate_cursor_damage(surface); -} - -static void surface_init_output_cursor_surface( - struct wlr_zext_screencopy_surface_v1 *surface) { - if (surface->output_cursor_surface) { - return; - } - - struct wlr_output *output = surface_check_output(surface); - if (!output || !output->hardware_cursor || - !output->hardware_cursor->surface) { - return; - } - struct wlr_surface *cursor_surface = output->hardware_cursor->surface; - - wl_list_init(&surface->output_cursor_surface_commit.link); - wl_list_init(&surface->output_cursor_surface_destroy.link); - - wl_signal_add(&cursor_surface->events.destroy, - &surface->output_cursor_surface_destroy); - surface->output_cursor_surface_destroy.notify = - surface_handle_output_cursor_surface_destroy; - - wl_signal_add(&cursor_surface->events.commit, - &surface->output_cursor_surface_commit); - surface->output_cursor_surface_commit.notify = - surface_handle_output_cursor_surface_commit; - - surface->output_cursor_surface = cursor_surface; - - // We have a new cursor surface, so there's probably damage - pixman_region32_union_rect(&surface->cursor_damage, - &surface->cursor_damage, 0, 0, surface->cursor_width, - surface->cursor_height); - - wlr_log(WLR_DEBUG, "There's a new cursor surface"); + return output->cursor_front_buffer; } static void surface_advertise_cursor_formats( @@ -577,7 +480,7 @@ static void surface_advertise_cursor_formats( buffer->width, buffer->height, 0); } - pixman_region32_union_rect(&surface->staged_cursor_buffer.damage, + pixman_region32_union_rect(&surface->cursor_damage, &surface->staged_cursor_buffer.damage, 0, 0, surface->cursor_width, surface->cursor_height); } @@ -628,7 +531,6 @@ static void surface_advertise_buffer_formats( static void surface_handle_output_commit_formats( struct wlr_zext_screencopy_surface_v1 *surface, struct wlr_output_event_commit *event) { - surface_init_output_cursor_surface(surface); surface_advertise_buffer_formats(surface, event->buffer); surface->state = WLR_ZEXT_SCREENCOPY_SURFACE_V1_STATE_READY; } @@ -836,8 +738,6 @@ static void surface_handle_output_commit_ready( return; } - surface_init_output_cursor_surface(surface); - if (surface_check_cursor_formats(surface)) { if (surface->staged_buffer.resource) { wl_list_remove(&surface->staged_buffer.destroy.link); @@ -954,6 +854,26 @@ static void surface_handle_output_commit(struct wl_listener *listener, } } +static void surface_handle_output_set_cursor(struct wl_listener *listener, + void *data) { + struct wlr_zext_screencopy_surface_v1 *surface = + wl_container_of(listener, surface, output_set_cursor); + + pixman_region32_union_rect(&surface->cursor_damage, + &surface->cursor_damage, 0, 0, + surface->cursor_width, surface->cursor_height); + + wlr_output_schedule_frame(surface->output); +} + +static void surface_handle_output_move_cursor(struct wl_listener *listener, + void *data) { + struct wlr_zext_screencopy_surface_v1 *surface = + wl_container_of(listener, surface, output_move_cursor); + + wlr_output_schedule_frame(surface->output); +} + static void capture_output(struct wl_client *client, uint32_t version, struct wlr_zext_screencopy_manager_v1 *manager, uint32_t surface_id, struct wlr_output *output) { @@ -998,6 +918,14 @@ static void capture_output(struct wl_client *client, uint32_t version, &surface->output_commit); surface->output_commit.notify = surface_handle_output_commit; + wl_signal_add(&surface->output->events.set_cursor, + &surface->output_set_cursor); + surface->output_set_cursor.notify = surface_handle_output_set_cursor; + + wl_signal_add(&surface->output->events.move_cursor, + &surface->output_move_cursor); + surface->output_move_cursor.notify = surface_handle_output_move_cursor; + pixman_region32_init(&surface->current_buffer.damage); pixman_region32_init(&surface->staged_buffer.damage); pixman_region32_init(&surface->current_cursor_buffer.damage); From 1967c12b380d684c72c59c237c58c2e1e2bc24f3 Mon Sep 17 00:00:00 2001 From: Andri Yngvason Date: Sun, 13 Feb 2022 18:42:20 +0000 Subject: [PATCH 182/188] ext-screencopy: Allow oversized cursor buffers --- examples/screencopy-dmabuf.c | 3 +- examples/screencopy.c | 4 +- protocol/screencopy-unstable-v1.xml | 30 +++---- types/wlr_zext_screencopy_v1.c | 118 +++++++++++----------------- 4 files changed, 67 insertions(+), 88 deletions(-) diff --git a/examples/screencopy-dmabuf.c b/examples/screencopy-dmabuf.c index 6b40c5879a..f9aa24188e 100644 --- a/examples/screencopy-dmabuf.c +++ b/examples/screencopy-dmabuf.c @@ -192,7 +192,8 @@ static void surface_handle_damage(void *data, static void surface_handle_cursor_info(void *data, struct zext_screencopy_surface_v1 *surface, const char *name, int damaged, int32_t pos_x, int32_t pos_y, - int32_t hotspot_x, int32_t hotspot_y) { + int32_t width, int32_t height, int32_t hotspot_x, + int32_t hotspot_y) { } static void surface_handle_transform(void *data, diff --git a/examples/screencopy.c b/examples/screencopy.c index 6841735b9f..f2bf2ecc00 100644 --- a/examples/screencopy.c +++ b/examples/screencopy.c @@ -194,8 +194,8 @@ static void surface_handle_damage(void *data, static void surface_handle_cursor_info(void *data, struct zext_screencopy_surface_v1 *surface, const char *name, - int has_damage, int32_t pos_x, int32_t pos_y, - int32_t hotspot_x, int32_t hotspot_y) { + int has_damage, int32_t pos_x, int32_t pos_y, int32_t width, + int32_t height, int32_t hotspot_x, int32_t hotspot_y) { } static void surface_handle_commit_time(void *data, diff --git a/protocol/screencopy-unstable-v1.xml b/protocol/screencopy-unstable-v1.xml index 6eec9adb6b..c38c0b818b 100644 --- a/protocol/screencopy-unstable-v1.xml +++ b/protocol/screencopy-unstable-v1.xml @@ -1,7 +1,7 @@ - Copyright © 2021 Andri Yngvason + Copyright © 2021-2022 Andri Yngvason Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -78,10 +78,11 @@ - - - - + + + + + @@ -124,9 +125,9 @@ - - - + + + @@ -135,13 +136,6 @@ - - - Signals that some buffer attributes have changed. This event will be - followed by buffer info events and "init_done". - - - Attach a buffer to the surface. @@ -170,6 +164,10 @@ Attach a named cursor buffer to the surface. + + The cursor buffer may exceed the dimensions specified in the + "cursor_buffer_info" event. The cursor image will be drawn in the top, + left corner of the buffer. @@ -267,6 +265,8 @@ + + diff --git a/types/wlr_zext_screencopy_v1.c b/types/wlr_zext_screencopy_v1.c index d2132daa13..4009a34cee 100644 --- a/types/wlr_zext_screencopy_v1.c +++ b/types/wlr_zext_screencopy_v1.c @@ -285,9 +285,9 @@ static void surface_commit(struct wl_client *client, surface->options = options; -// if (options & ZEXT_SCREENCOPY_SURFACE_V1_OPTIONS_IMMEDIATE) { + if (options & ZEXT_SCREENCOPY_SURFACE_V1_OPTIONS_IMMEDIATE) { wlr_output_schedule_frame(output); -// } + } } static void surface_handle_destroy(struct wl_client *client, @@ -407,25 +407,6 @@ static void surface_handle_output_precommit(struct wl_listener *listener, } } -static bool surface_check_cursor_formats( - struct wlr_zext_screencopy_surface_v1 *surface) { - struct wlr_output *output = surface_check_output(surface); - if (!output) { - return false; - } - - struct wlr_buffer *buffer = output->cursor_front_buffer; - if (!buffer || buffer->width == 0 || buffer->height == 0) { - return false; - } - - uint32_t dmabuf_format = get_dmabuf_format(buffer); - - return buffer->width != surface->cursor_width || - buffer->height != surface->cursor_height || - surface->cursor_dmabuf_format != dmabuf_format; -} - static bool surface_is_cursor_visible( struct wlr_zext_screencopy_surface_v1 *surface) { @@ -549,7 +530,8 @@ static void surface_send_cursor_info( zext_screencopy_surface_v1_send_cursor_info(surface->resource, "default", have_damage, cursor->x, cursor->y, - cursor->hotspot_x, cursor->hotspot_y); + cursor->width, cursor->height, cursor->hotspot_x, + cursor->hotspot_y); } static void surface_send_transform(struct wlr_zext_screencopy_surface_v1 *surface) { @@ -564,8 +546,8 @@ static bool surface_copy_wl_shm(struct wlr_zext_screencopy_surface_v1 *surface, struct wlr_output *output = surface->output; struct wlr_renderer *renderer = output->renderer; - if (dst_buffer->width != src_buffer->width || - dst_buffer->height != src_buffer->height) { + if (dst_buffer->width < src_buffer->width || + dst_buffer->height < src_buffer->height) { return false; } @@ -575,18 +557,29 @@ static bool surface_copy_wl_shm(struct wlr_zext_screencopy_surface_v1 *surface, return false; } - int32_t width = dst_buffer->width; - int32_t height = dst_buffer->height; - void *data = NULL; + int32_t width = src_buffer->width; + int32_t height = src_buffer->height; + uint8_t *dst_data = NULL; + uint8_t *data = NULL; uint32_t format = DRM_FORMAT_INVALID; + size_t dst_stride = 0; size_t stride = 0; if (!wlr_buffer_begin_data_ptr_access(dst_buffer, - WLR_BUFFER_DATA_PTR_ACCESS_WRITE, &data, &format, - &stride)) { + WLR_BUFFER_DATA_PTR_ACCESS_WRITE, (void**)&dst_data, + &format, &dst_stride)) { return false; } + bool use_scratch_buffer = dst_buffer->width != src_buffer->width; + if (use_scratch_buffer) { + stride = width * 4; // TODO: This assumes things + data = malloc(height * stride); + } else { + data = dst_data; + stride = dst_stride; + } + // TODO: Only copy damaged region uint32_t renderer_flags = 0; bool ok; @@ -596,6 +589,15 @@ static bool surface_copy_wl_shm(struct wlr_zext_screencopy_surface_v1 *surface, wlr_renderer_end(renderer); // TODO: if renderer_flags & WLR_RENDERER_READ_PIXELS_Y_INVERT: // add vertical flip to transform + + if (use_scratch_buffer) { + for (size_t y = 0; y < (size_t)height; ++y) { + memcpy(dst_data + y * dst_stride, data + y * stride, + stride); + } + free(data); + } + wlr_buffer_end_data_ptr_access(dst_buffer); return ok; @@ -612,13 +614,15 @@ static bool blit_dmabuf(struct wlr_renderer *renderer, float mat[9]; wlr_matrix_identity(mat); - wlr_matrix_scale(mat, dst_buffer->width, dst_buffer->height); + wlr_matrix_translate(mat, -1.0, 1.0); + wlr_matrix_scale(mat, src_buffer->width, src_buffer->height); if (!wlr_renderer_begin_with_buffer(renderer, dst_buffer)) { goto error_renderer_begin; } wlr_renderer_scissor(renderer, clip_box); + wlr_renderer_clear(renderer, (float[]){ 0.0, 0.0, 0.0, 0.0 }); wlr_render_texture_with_matrix(renderer, src_tex, mat, 1.0f); wlr_renderer_scissor(renderer, NULL); @@ -639,8 +643,8 @@ static bool surface_copy_dmabuf(struct wlr_zext_screencopy_surface_v1 *surface, struct wlr_output *output = surface->output; struct wlr_renderer *renderer = output->renderer; - if (dst_buffer->width != src_buffer->width || - dst_buffer->height != src_buffer->height) { + if (dst_buffer->width < src_buffer->width || + dst_buffer->height < src_buffer->height) { return false; } @@ -693,9 +697,13 @@ static bool surface_copy(struct wlr_zext_screencopy_surface_v1 *surface, return true; failure: - zext_screencopy_surface_v1_send_failed(surface->resource, - ZEXT_SCREENCOPY_SURFACE_V1_FAILURE_REASON_INVALID_BUFFER); - surface_destroy(surface); + if (surface->output && src_buffer == surface->output->cursor_front_buffer) { + zext_screencopy_surface_v1_send_failed(surface->resource, + ZEXT_SCREENCOPY_SURFACE_V1_FAILURE_REASON_INVALID_CURSOR_BUFFER); + } else { + zext_screencopy_surface_v1_send_failed(surface->resource, + ZEXT_SCREENCOPY_SURFACE_V1_FAILURE_REASON_INVALID_MAIN_BUFFER); + } return false; } @@ -738,35 +746,6 @@ static void surface_handle_output_commit_ready( return; } - if (surface_check_cursor_formats(surface)) { - if (surface->staged_buffer.resource) { - wl_list_remove(&surface->staged_buffer.destroy.link); - } - - if (surface->current_buffer.resource) { - wl_list_remove(&surface->current_buffer.destroy.link); - } - - if (surface->staged_cursor_buffer.resource) { - wl_list_remove(&surface->staged_cursor_buffer.destroy.link); - } - - if (surface->current_cursor_buffer.resource) { - wl_list_remove(&surface->current_cursor_buffer.destroy.link); - } - - surface->current_buffer.resource = NULL; - surface->staged_buffer.resource = NULL; - surface->current_cursor_buffer.resource = NULL; - surface->staged_cursor_buffer.resource = NULL; - - zext_screencopy_surface_v1_send_cursor_leave(surface->resource, - "default"); - zext_screencopy_surface_v1_send_reconfig(surface->resource); - surface_advertise_buffer_formats(surface, event->buffer); - return; - } - if (surface->have_cursor && !surface_is_cursor_visible(surface)) { zext_screencopy_surface_v1_send_cursor_leave(surface->resource, "default"); @@ -788,7 +767,7 @@ static void surface_handle_output_commit_ready( surface->dmabuf_format, &damage); pixman_region32_fini(&damage); if (!ok) { - // TODO: Raise some error + // TODO: Raise some error, and destroy surface } } @@ -800,13 +779,12 @@ static void surface_handle_output_commit_ready( &surface->current_cursor_buffer.damage); bool ok = surface_copy(surface, &surface->current_cursor_buffer, - output->cursor_front_buffer, - surface->cursor_wl_shm_format, - surface->cursor_dmabuf_format, - &damage); + output->cursor_front_buffer, + surface->cursor_wl_shm_format, + surface->cursor_dmabuf_format, &damage); pixman_region32_fini(&damage); if (!ok) { - // TODO: Raise some error + // TODO: Raise some error, and destroy surface } } From 731e94b876c0dec0449770ff81f04035d669eb55 Mon Sep 17 00:00:00 2001 From: Andri Yngvason Date: Sun, 13 Feb 2022 19:02:18 +0000 Subject: [PATCH 183/188] ext-screencopy: Fix some damage tracking issues --- types/wlr_zext_screencopy_v1.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/types/wlr_zext_screencopy_v1.c b/types/wlr_zext_screencopy_v1.c index 4009a34cee..3de39ab102 100644 --- a/types/wlr_zext_screencopy_v1.c +++ b/types/wlr_zext_screencopy_v1.c @@ -285,7 +285,9 @@ static void surface_commit(struct wl_client *client, surface->options = options; - if (options & ZEXT_SCREENCOPY_SURFACE_V1_OPTIONS_IMMEDIATE) { + if (options & ZEXT_SCREENCOPY_SURFACE_V1_OPTIONS_IMMEDIATE || + pixman_region32_not_empty(&surface->frame_damage) || + pixman_region32_not_empty(&surface->cursor_damage)) { wlr_output_schedule_frame(output); } } @@ -462,8 +464,8 @@ static void surface_advertise_cursor_formats( } pixman_region32_union_rect(&surface->cursor_damage, - &surface->staged_cursor_buffer.damage, 0, 0, - surface->cursor_width, surface->cursor_height); + &surface->cursor_damage, 0, 0, surface->cursor_width, + surface->cursor_height); } static void surface_advertise_buffer_formats( From 454216e9575886b7da0dcfcae8fd77f33888f4a1 Mon Sep 17 00:00:00 2001 From: Andri Yngvason Date: Sun, 13 Feb 2022 19:18:10 +0000 Subject: [PATCH 184/188] ext-screencopy: Clear the outside region on shm buffers --- types/wlr_zext_screencopy_v1.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/types/wlr_zext_screencopy_v1.c b/types/wlr_zext_screencopy_v1.c index 3de39ab102..10fc9ef589 100644 --- a/types/wlr_zext_screencopy_v1.c +++ b/types/wlr_zext_screencopy_v1.c @@ -596,8 +596,15 @@ static bool surface_copy_wl_shm(struct wlr_zext_screencopy_surface_v1 *surface, for (size_t y = 0; y < (size_t)height; ++y) { memcpy(dst_data + y * dst_stride, data + y * stride, stride); + memset(dst_data + y * dst_stride + stride, 0, + dst_stride - stride); } free(data); + + // Clear the rest of the destination buffer + // TODO: Only do this if the rest is marked damaged + memset(dst_data + height * dst_stride, 0, + dst_stride * (dst_buffer->height - height)); } wlr_buffer_end_data_ptr_access(dst_buffer); From 1ae99b1be57627f9a15520c942af9152d8f49006 Mon Sep 17 00:00:00 2001 From: Andri Yngvason Date: Mon, 14 Feb 2022 23:22:08 +0000 Subject: [PATCH 185/188] ext-screencopy: Don't run output commit handler if no buffers are committed --- include/wlr/types/wlr_zext_screencopy_v1.h | 8 +++++--- types/wlr_zext_screencopy_v1.c | 11 +++++++++-- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/include/wlr/types/wlr_zext_screencopy_v1.h b/include/wlr/types/wlr_zext_screencopy_v1.h index 0e61bab6dc..0640c15c04 100644 --- a/include/wlr/types/wlr_zext_screencopy_v1.h +++ b/include/wlr/types/wlr_zext_screencopy_v1.h @@ -53,9 +53,9 @@ struct wlr_zext_screencopy_surface_v1 { int cursor_wl_shm_stride; uint32_t cursor_dmabuf_format; - int cursor_width; - int cursor_height; - bool have_cursor; + int cursor_width; + int cursor_height; + bool have_cursor; uint32_t options; struct wlr_zext_screencopy_surface_v1_buffer staged_buffer; @@ -64,6 +64,8 @@ struct wlr_zext_screencopy_surface_v1 { struct wlr_zext_screencopy_surface_v1_buffer staged_cursor_buffer; struct wlr_zext_screencopy_surface_v1_buffer current_cursor_buffer; + bool committed; + /* Accumulated damage for the surface */ struct pixman_region32 frame_damage; struct pixman_region32 cursor_damage; diff --git a/types/wlr_zext_screencopy_v1.c b/types/wlr_zext_screencopy_v1.c index 10fc9ef589..63583d21aa 100644 --- a/types/wlr_zext_screencopy_v1.c +++ b/types/wlr_zext_screencopy_v1.c @@ -290,6 +290,8 @@ static void surface_commit(struct wl_client *client, pixman_region32_not_empty(&surface->cursor_damage)) { wlr_output_schedule_frame(output); } + + surface->committed = true; } static void surface_handle_destroy(struct wl_client *client, @@ -755,6 +757,10 @@ static void surface_handle_output_commit_ready( return; } + if (!surface->committed) { + return; + } + if (surface->have_cursor && !surface_is_cursor_visible(surface)) { zext_screencopy_surface_v1_send_cursor_leave(surface->resource, "default"); @@ -803,21 +809,22 @@ static void surface_handle_output_commit_ready( surface_send_commit_time(surface, event->when); zext_screencopy_surface_v1_send_ready(surface->resource); - pixman_region32_clear(&surface->frame_damage); - pixman_region32_clear(&surface->cursor_damage); pixman_region32_clear(&surface->current_buffer.damage); pixman_region32_clear(&surface->current_cursor_buffer.damage); if (surface->current_buffer.resource) { wl_list_remove(&surface->current_buffer.destroy.link); + pixman_region32_clear(&surface->frame_damage); } if (surface->current_cursor_buffer.resource) { wl_list_remove(&surface->current_cursor_buffer.destroy.link); + pixman_region32_clear(&surface->cursor_damage); } surface->current_buffer.resource = NULL; surface->current_cursor_buffer.resource = NULL; + surface->committed = false; } static void surface_handle_output_commit(struct wl_listener *listener, From cf51cde27fefb07522a5d7e4244a2f13cf41938a Mon Sep 17 00:00:00 2001 From: Andri Yngvason Date: Tue, 15 Feb 2022 20:41:46 +0000 Subject: [PATCH 186/188] ext-screencopy: Only copy damaged area into sh m buffers --- types/wlr_zext_screencopy_v1.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/types/wlr_zext_screencopy_v1.c b/types/wlr_zext_screencopy_v1.c index 63583d21aa..040e0a1847 100644 --- a/types/wlr_zext_screencopy_v1.c +++ b/types/wlr_zext_screencopy_v1.c @@ -575,6 +575,12 @@ static bool surface_copy_wl_shm(struct wlr_zext_screencopy_surface_v1 *surface, return false; } + // TODO: More fine grained damage regions + pixman_region32_intersect_rect(damage, damage, 0, 0, width, height); + pixman_box32_t *extents = pixman_region32_extents(damage); + int32_t y_offset = extents->y1; + int32_t damage_height = extents->y2 - extents->y1; + bool use_scratch_buffer = dst_buffer->width != src_buffer->width; if (use_scratch_buffer) { stride = width * 4; // TODO: This assumes things @@ -584,29 +590,31 @@ static bool surface_copy_wl_shm(struct wlr_zext_screencopy_surface_v1 *surface, stride = dst_stride; } - // TODO: Only copy damaged region uint32_t renderer_flags = 0; bool ok; ok = wlr_renderer_begin_with_buffer(renderer, src_buffer); ok = ok && wlr_renderer_read_pixels(renderer, format, &renderer_flags, - stride, width, height, 0, 0, 0, 0, data); + stride, width, damage_height, 0, y_offset, 0, 0, + (uint8_t*)data + stride * y_offset); wlr_renderer_end(renderer); // TODO: if renderer_flags & WLR_RENDERER_READ_PIXELS_Y_INVERT: // add vertical flip to transform if (use_scratch_buffer) { - for (size_t y = 0; y < (size_t)height; ++y) { - memcpy(dst_data + y * dst_stride, data + y * stride, - stride); - memset(dst_data + y * dst_stride + stride, 0, - dst_stride - stride); + memset(dst_data, 0, y_offset * dst_stride); + + for (size_t y = 0; y < (size_t)damage_height; ++y) { + memcpy(dst_data + (y + y_offset) * dst_stride, + data + (y + y_offset) * stride, stride); + memset(dst_data + (y + y_offset) * dst_stride + stride, + 0, dst_stride - stride); } free(data); // Clear the rest of the destination buffer // TODO: Only do this if the rest is marked damaged - memset(dst_data + height * dst_stride, 0, - dst_stride * (dst_buffer->height - height)); + memset(dst_data + damage_height * dst_stride, 0, + dst_stride * (dst_buffer->height - damage_height)); } wlr_buffer_end_data_ptr_access(dst_buffer); From 2fa0951564106610421e0bc919360a9e0d266348 Mon Sep 17 00:00:00 2001 From: Andri Yngvason Date: Wed, 16 Feb 2022 21:38:22 +0000 Subject: [PATCH 187/188] ext-screencopy: Implement baked cursors for dmabufs --- include/wlr/types/wlr_zext_screencopy_v1.h | 4 + types/wlr_zext_screencopy_v1.c | 101 ++++++++++++++++++--- 2 files changed, 93 insertions(+), 12 deletions(-) diff --git a/include/wlr/types/wlr_zext_screencopy_v1.h b/include/wlr/types/wlr_zext_screencopy_v1.h index 0640c15c04..15be122558 100644 --- a/include/wlr/types/wlr_zext_screencopy_v1.h +++ b/include/wlr/types/wlr_zext_screencopy_v1.h @@ -57,6 +57,10 @@ struct wlr_zext_screencopy_surface_v1 { int cursor_height; bool have_cursor; + struct { + int x, y, width, height; + } last_cursor; + uint32_t options; struct wlr_zext_screencopy_surface_v1_buffer staged_buffer; struct wlr_zext_screencopy_surface_v1_buffer current_buffer; diff --git a/types/wlr_zext_screencopy_v1.c b/types/wlr_zext_screencopy_v1.c index 040e0a1847..3c8a622144 100644 --- a/types/wlr_zext_screencopy_v1.c +++ b/types/wlr_zext_screencopy_v1.c @@ -622,31 +622,64 @@ static bool surface_copy_wl_shm(struct wlr_zext_screencopy_surface_v1 *surface, return ok; } -static bool blit_dmabuf(struct wlr_renderer *renderer, - struct wlr_buffer *dst_buffer, - struct wlr_buffer *src_buffer, struct wlr_box *clip_box) { +static bool blit_dmabuf(struct wlr_zext_screencopy_surface_v1 *surface, + struct wlr_buffer *dst_buffer, struct wlr_buffer *src_buffer, + struct wlr_buffer *cursor_buffer, struct wlr_box *clip_box) { + struct wlr_output *output = surface->output; + struct wlr_renderer *renderer = output->renderer; + struct wlr_texture *src_tex = wlr_texture_from_buffer(renderer, src_buffer); if (!src_tex) { return false; } - float mat[9]; - wlr_matrix_identity(mat); - wlr_matrix_translate(mat, -1.0, 1.0); - wlr_matrix_scale(mat, src_buffer->width, src_buffer->height); + struct wlr_texture *cursor_tex = NULL; + if (cursor_buffer) { + cursor_tex = wlr_texture_from_buffer(renderer, cursor_buffer); + if (!cursor_tex) { + return false; + } + } if (!wlr_renderer_begin_with_buffer(renderer, dst_buffer)) { goto error_renderer_begin; } + float main_matrix[9]; + wlr_matrix_identity(main_matrix); + wlr_matrix_translate(main_matrix, -1.0, 1.0); + wlr_matrix_scale(main_matrix, src_buffer->width, src_buffer->height); + wlr_renderer_scissor(renderer, clip_box); wlr_renderer_clear(renderer, (float[]){ 0.0, 0.0, 0.0, 0.0 }); - wlr_render_texture_with_matrix(renderer, src_tex, mat, 1.0f); + wlr_render_texture_with_matrix(renderer, src_tex, main_matrix, 1.0f); wlr_renderer_scissor(renderer, NULL); + if (cursor_buffer) { + // TODO: Transforms, scaling, and more + struct wlr_box cursor_box = { + .x = output->hardware_cursor->x, + .y = output->hardware_cursor->y, + .width = cursor_buffer->width, + .height = cursor_buffer->height, + }; + + float identity_matrix[9]; + wlr_matrix_identity(identity_matrix); + + float cursor_matrix[9]; + wlr_matrix_project_box(cursor_matrix, &cursor_box, + WL_OUTPUT_TRANSFORM_NORMAL, 0, identity_matrix); + + + wlr_render_texture_with_matrix(renderer, cursor_tex, + cursor_matrix, 1.0); + } + wlr_renderer_end(renderer); + wlr_texture_destroy(cursor_tex); wlr_texture_destroy(src_tex); return true; @@ -660,7 +693,6 @@ static bool surface_copy_dmabuf(struct wlr_zext_screencopy_surface_v1 *surface, struct wlr_buffer *src_buffer, uint32_t format, struct pixman_region32 *damage) { struct wlr_output *output = surface->output; - struct wlr_renderer *renderer = output->renderer; if (dst_buffer->width < src_buffer->width || dst_buffer->height < src_buffer->height) { @@ -679,7 +711,16 @@ static bool surface_copy_dmabuf(struct wlr_zext_screencopy_surface_v1 *surface, .height = extents->y2 - extents->y1, }; - return blit_dmabuf(renderer, dst_buffer, src_buffer, &clip_box); + struct wlr_buffer *cursor_buffer = + surface->options & ZEXT_SCREENCOPY_SURFACE_V1_OPTIONS_RENDER_CURSORS ? + output->cursor_front_buffer : NULL; + + if (src_buffer == cursor_buffer) { + cursor_buffer = NULL; + } + + return blit_dmabuf(surface, dst_buffer, src_buffer, cursor_buffer, + &clip_box); } static bool surface_copy(struct wlr_zext_screencopy_surface_v1 *surface, @@ -819,15 +860,15 @@ static void surface_handle_output_commit_ready( pixman_region32_clear(&surface->current_buffer.damage); pixman_region32_clear(&surface->current_cursor_buffer.damage); + pixman_region32_clear(&surface->frame_damage); + pixman_region32_clear(&surface->cursor_damage); if (surface->current_buffer.resource) { wl_list_remove(&surface->current_buffer.destroy.link); - pixman_region32_clear(&surface->frame_damage); } if (surface->current_cursor_buffer.resource) { wl_list_remove(&surface->current_cursor_buffer.destroy.link); - pixman_region32_clear(&surface->cursor_damage); } surface->current_buffer.resource = NULL; @@ -856,6 +897,21 @@ static void surface_handle_output_commit(struct wl_listener *listener, } } +static void surface_save_cursor_coords( + struct wlr_zext_screencopy_surface_v1 *surface) +{ + struct wlr_output *output = surface->output; + struct wlr_output_cursor *cursor = output->hardware_cursor; + if (!cursor) { + return; + } + + surface->last_cursor.x = cursor->x; + surface->last_cursor.y = cursor->y; + surface->last_cursor.width = cursor->width; + surface->last_cursor.height = cursor->height; +} + static void surface_handle_output_set_cursor(struct wl_listener *listener, void *data) { struct wlr_zext_screencopy_surface_v1 *surface = @@ -866,12 +922,33 @@ static void surface_handle_output_set_cursor(struct wl_listener *listener, surface->cursor_width, surface->cursor_height); wlr_output_schedule_frame(surface->output); + + surface_save_cursor_coords(surface); } static void surface_handle_output_move_cursor(struct wl_listener *listener, void *data) { struct wlr_zext_screencopy_surface_v1 *surface = wl_container_of(listener, surface, output_move_cursor); + struct wlr_output *output = surface->output; + struct wlr_output_cursor *cursor = output->hardware_cursor; + + // TODO: These options might need to be set for the surface rather than + // per surface commit + if ((surface->options & ZEXT_SCREENCOPY_SURFACE_V1_OPTIONS_RENDER_CURSORS) + && cursor) { + struct pixman_region32 *region = &surface->frame_damage; + pixman_region32_union_rect(region, region, cursor->x, cursor->y, + cursor->width, cursor->height); + pixman_region32_union_rect(region, region, + surface->last_cursor.x, surface->last_cursor.y, + surface->last_cursor.width, + surface->last_cursor.height); + pixman_region32_intersect_rect(region, region, 0, 0, + output->width, output->height); + } + + surface_save_cursor_coords(surface); wlr_output_schedule_frame(surface->output); } From 1f57e451260479ce275eb889ac766d829816cea2 Mon Sep 17 00:00:00 2001 From: Andri Yngvason Date: Wed, 16 Feb 2022 22:21:17 +0000 Subject: [PATCH 188/188] ext-screencopy: Implement baked cursors for wl-shm --- types/wlr_zext_screencopy_v1.c | 67 ++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/types/wlr_zext_screencopy_v1.c b/types/wlr_zext_screencopy_v1.c index 3c8a622144..08c9c527f5 100644 --- a/types/wlr_zext_screencopy_v1.c +++ b/types/wlr_zext_screencopy_v1.c @@ -11,6 +11,7 @@ #include "util/signal.h" #include "render/wlr_renderer.h" #include "render/swapchain.h" +#include "render/pixman.h" #include #include @@ -543,6 +544,66 @@ static void surface_send_transform(struct wlr_zext_screencopy_surface_v1 *surfac zext_screencopy_surface_v1_send_transform(surface->resource, transform); } +// TODO: Handle output transforms and such? +static bool surface_composite_cursor_buffer( + struct wlr_zext_screencopy_surface_v1 *surface, + struct wlr_buffer *buffer, void *data, uint32_t drm_format, + size_t stride) { + struct wlr_output *output = surface->output; + struct wlr_renderer *renderer = output->renderer; + struct wlr_output_cursor *cursor = output->hardware_cursor; + struct wlr_buffer *cursor_buffer = output->cursor_front_buffer; + + pixman_format_code_t dst_format = get_pixman_format_from_drm(drm_format); + assert(dst_format); + + pixman_image_t *dst_image = + pixman_image_create_bits_no_clear(dst_format, buffer->width, + buffer->height, data, stride); + if (!dst_image) + return false; + + uint32_t cursor_drm_format = + get_buffer_preferred_read_format(cursor_buffer, renderer); + pixman_format_code_t cursor_format = + get_pixman_format_from_drm(cursor_drm_format); + assert(cursor_format); + + size_t cursor_stride = cursor_buffer->width * 4; + + void *scratch_buffer = malloc(cursor_buffer->height * cursor_stride); + assert(scratch_buffer); + + pixman_image_t *src_image = + pixman_image_create_bits(cursor_format, cursor_buffer->width, + cursor_buffer->height, scratch_buffer, + cursor_stride); + + uint32_t renderer_flags = 0; + bool ok; + ok = wlr_renderer_begin_with_buffer(renderer, cursor_buffer); + ok = ok && wlr_renderer_read_pixels(renderer, cursor_drm_format, + &renderer_flags, cursor_stride, cursor_buffer->width, + cursor_buffer->height, 0, 0, 0, 0, scratch_buffer); + wlr_renderer_end(renderer); + if (!ok) { + goto fail; + } + + pixman_image_composite32(PIXMAN_OP_OVER, src_image, NULL, dst_image, + 0, 0, // src x,y + 0, 0, // mask x,y + cursor->x, cursor->y, // dst x,y + cursor->width, cursor->height); + +fail: + pixman_image_unref(src_image); + free(scratch_buffer); + pixman_image_unref(dst_image); + + return ok; +} + static bool surface_copy_wl_shm(struct wlr_zext_screencopy_surface_v1 *surface, struct wlr_buffer *dst_buffer, struct wlr_shm_attributes *attr, struct wlr_buffer *src_buffer, uint32_t wl_shm_format, @@ -617,6 +678,12 @@ static bool surface_copy_wl_shm(struct wlr_zext_screencopy_surface_v1 *surface, dst_stride * (dst_buffer->height - damage_height)); } + if (surface->options & ZEXT_SCREENCOPY_SURFACE_V1_OPTIONS_RENDER_CURSORS + && src_buffer != output->cursor_front_buffer) { + surface_composite_cursor_buffer(surface, dst_buffer, dst_data, + format, dst_stride); + } + wlr_buffer_end_data_ptr_access(dst_buffer); return ok;