From 83ab5055fd36bd0f8a0106257e45d8ed303636d8 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Sat, 1 Jan 2022 14:04:53 +0300 Subject: [PATCH 01/18] 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 3f22ee3d6..bb3c7ff7a 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 02/18] 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 2f9adf71e..418aab94b 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 03/18] 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 04391c741..756f6f30d 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 000000000..f9c887bb7 --- /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 faa0a9a13..722abd10c 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 1694ac94a..8aa93a4ac 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 7ad684543..65549627d 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 000000000..f5a624865 --- /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 d5d495714..fc4a509c3 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 04/18] 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 59168eaa3..ca1a55ac0 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 fc4a509c3..1b49a21b8 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 05/18] 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 b8c7d02d2..000000000 --- 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 8aa93a4ac..476bffe82 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 65549627d..4b4b34f99 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 f5a624865..45d0349d2 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 1b49a21b8..000000000 --- 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 06/18] 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 756f6f30d..0146a8ab5 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 f9c887bb7..d8e57ed83 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 ca1a55ac0..4a6801e8e 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 07/18] 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 5a9d4c926..bc4bb7a86 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 bd2003f53..aa542996d 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 79e435a00..370fad837 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 0d91a517d..b05b1a30f 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 e0b370adf..7dfccbf9b 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 631b7dd3d..41f3918b0 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 4a6801e8e..9612dc085 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 c9ee0b221..5bac69a65 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 cf42e82a7..8d1a6fce6 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 6cd84ec0a..ceaf80e72 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 1952dcb88..9865d9309 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 efbf65032..f3055c5c4 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 85590ab63..0062013f7 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 bb3c7ff7a..8384db8fb 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 a0c06b6bf..9353fef5c 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 57dbfe2f5..9ed5fb345 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 2fa911ee0..ce4ca54e2 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 195928a3b..99c50cf88 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 020cba57c..58094f575 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 bf2a91593..1d3c08fd4 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 51efc1e84..9e5b9658a 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 4b4b34f99..96122c1c5 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 49805ef9f..9bf811a44 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 59636e4c7..7e9f85539 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 5acf740e5..61a66dc1f 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 7ccd31ee0..c9390e979 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 fe4e64b04..247d350d8 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 d7a2123e9..983f2186e 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 ae9bfeb90..5138e469f 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 d608b778a..e90daf72e 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 6b09a764f..b8229dfc4 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 36b7fe95f..a6facbdbe 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 39ba624aa..a80793939 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 45d0349d2..8a9e6207a 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 f5267b359..65a8ebd6f 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 f07c18feb..097af8576 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 208ada28c..02ba9e07f 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 313bfc0a7..f0a010d9c 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 08/18] 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 509b1d2c1..8b2c66cc2 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 0146a8ab5..071610b8f 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 96122c1c5..6c4eb61b3 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 983f2186e..b2c82955f 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 8a9e6207a..397baf8c2 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 c45baabb9..d54c058e0 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 f0a010d9c..0c1992690 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 09/18] 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 722abd10c..6f628836c 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 10/18] 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 c9390e979..559927eb1 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 11/18] 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 6ea9d47fd..d9030b2ea 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 9bf811a44..a700a115b 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 12/18] 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 aa542996d..a8ac66844 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 13/18] 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 a8ac66844..cf1372e72 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 14/18] 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 cf1372e72..c3ce05893 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 15/18] 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 | 38 ++++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/render/vulkan/vulkan.c b/render/vulkan/vulkan.c index 4932ec4da..7e9e93962 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) { + 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; + } + + 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; } - VkPhysicalDeviceDrmPropertiesEXT drm_props = {0}; - drm_props.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRM_PROPERTIES_EXT; - - VkPhysicalDeviceProperties2 props = {0}; - props.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; - props.pNext = &drm_props; - - vkGetPhysicalDeviceProperties2(phdev, &props); - 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 16/18] 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 f392d812a..9dcb30d7d 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 17/18] 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 397baf8c2..e9d53f537 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 18/18] 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 6c4eb61b3..e7b922a5f 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 e9d53f537..8ee5a9496 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,