diff --git a/src/libwayland-shim.c b/src/libwayland-shim.c index f3aa39f..33a4a20 100644 --- a/src/libwayland-shim.c +++ b/src/libwayland-shim.c @@ -17,7 +17,7 @@ struct wrapped_proxy { struct request_hook { const char* interface_name; - uint32_t opcode; + int opcode; libwayland_shim_request_handler_func_t handler; void* data; }; @@ -62,7 +62,7 @@ static void libwayland_shim_init() { void libwayland_shim_install_request_hook( struct wl_interface const* interface, - uint32_t opcode, + int opcode, libwayland_shim_request_handler_func_t handler, void* data ) { @@ -282,7 +282,9 @@ struct wl_proxy* wl_proxy_marshal_array_flags( } else { const char* interface_name = proxy->object.interface->name; for (int i = 0; i < request_hook_count; i++) { - if (strcmp(request_hooks[i].interface_name, interface_name) == 0 && request_hooks[i].opcode == opcode) { + if (strcmp(request_hooks[i].interface_name, interface_name) == 0 && + (request_hooks[i].opcode < 0 || request_hooks[i].opcode == (int)opcode) + ) { struct wl_proxy* ret_proxy = NULL; if (request_hooks[i].handler( request_hooks[i].data, diff --git a/src/libwayland-shim.h b/src/libwayland-shim.h index 01c3c52..f277fe6 100644 --- a/src/libwayland-shim.h +++ b/src/libwayland-shim.h @@ -25,10 +25,10 @@ typedef void (*libwayland_shim_destroy_handler_func_t)(void* data, struct wl_pro bool libwayland_shim_has_initialized(); // The handler will be called and can optionally handle all matching requests. There is no way to uninstall a hook, and -// installing too many hooks may hurt performance. +// installing too many hooks may hurt performance. If opcode < 0 all requests are matched for that interface. void libwayland_shim_install_request_hook( struct wl_interface const* interface, - uint32_t opcode, + int opcode, libwayland_shim_request_handler_func_t handler, void* data ); diff --git a/src/lock-surface.c b/src/lock-surface.c index 432ad98..69cb4a4 100644 --- a/src/lock-surface.c +++ b/src/lock-surface.c @@ -142,4 +142,5 @@ void lock_surface_install_hook(lock_surface_hook_callback_t callback) { xdg_wm_base_get_xdg_surface_hook, callback ); + stubbed_surface_install_positioner_hook(); } diff --git a/src/registry.c b/src/registry.c index 3a7a385..cb85bb8 100644 --- a/src/registry.c +++ b/src/registry.c @@ -15,6 +15,9 @@ struct zwlr_layer_shell_v1* layer_shell_global = NULL; bool session_lock_requested = false; struct ext_session_lock_manager_v1* session_lock_global = NULL; +bool subcompositor_requested = false; +struct wl_subcompositor* subcompositor_global = NULL; + static void wl_registry_handle_global( void* _data, struct wl_registry* registry, @@ -25,7 +28,15 @@ static void wl_registry_handle_global( (void)_data; // pull out needed globals - if (layer_shell_requested && strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) { + if (subcompositor_requested && strcmp(interface, wl_subcompositor_interface.name) == 0) { + uint32_t supported_version = wl_subcompositor_interface.version; + subcompositor_global = wl_registry_bind( + registry, + id, + &wl_subcompositor_interface, + supported_version < version ? supported_version : version + ); + } else if (layer_shell_requested && strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) { uint32_t supported_version = zwlr_layer_shell_v1_interface.version; layer_shell_global = wl_registry_bind( registry, @@ -88,6 +99,7 @@ struct zwlr_layer_shell_v1* get_layer_shell_global_from_display(struct wl_displa struct ext_session_lock_manager_v1* get_session_lock_global_from_display(struct wl_display* display) { if (!session_lock_requested) { session_lock_requested = true; + subcompositor_requested = true; // If session lock is in use the subcompositor will likely be needed bind_globals(display); if (!session_lock_global) { fprintf(stderr, "it appears your Wayland compositor does not support the Session Lock protocol\n"); @@ -95,3 +107,11 @@ struct ext_session_lock_manager_v1* get_session_lock_global_from_display(struct } return session_lock_global; } + +struct wl_subcompositor* get_subcompositor_global_from_display(struct wl_display* display) { + if (!subcompositor_requested) { + subcompositor_requested = true; + bind_globals(display); + } + return subcompositor_global; +} diff --git a/src/registry.h b/src/registry.h index d860a6d..743d8e5 100644 --- a/src/registry.h +++ b/src/registry.h @@ -6,3 +6,4 @@ struct wl_display; struct zwlr_layer_shell_v1* get_layer_shell_global_from_display(struct wl_display* display); struct ext_session_lock_manager_v1* get_session_lock_global_from_display(struct wl_display* display); +struct wl_subcompositor* get_subcompositor_global_from_display(struct wl_display* display); diff --git a/src/stubbed-surface.c b/src/stubbed-surface.c index 33731c5..34a67fd 100644 --- a/src/stubbed-surface.c +++ b/src/stubbed-surface.c @@ -1,21 +1,51 @@ #include "stubbed-surface.h" #include "libwayland-shim.h" #include "xdg-surface-server.h" +#include "layer-surface.h" +#include "registry.h" #include #include #include #include +#include +#include + +struct geom_point_t { + int x, y; +}; + +struct geom_rect_t { + struct geom_point_t top_left; + struct geom_size_t size; +}; struct stubbed_surface_t { struct xdg_surface_server_t super; + struct geom_rect_t configure; + struct wl_subsurface* subsurface; }; +static struct { + void* wayland_object; + struct geom_size_t size; + struct geom_rect_t anchor_rect; + enum xdg_positioner_anchor anchor; + enum xdg_positioner_gravity gravity; + uint32_t constraint_adjustment; + struct geom_point_t offset; +} current_positioner; + static void stubbed_configure_callback(void *data, struct wl_callback *callback, uint32_t callback_data) { (void)callback, (void)callback_data; struct stubbed_surface_t* self = data; - xdg_surface_server_send_configure(&self->super, 0, 0, 100, 100, 0); + xdg_surface_server_send_configure( + &self->super, + self->configure.top_left.x, self->configure.top_left.y, + self->configure.size.width, self->configure.size.height, + 0 + ); } static const struct wl_callback_listener stubbed_configure_callback_listener = { stubbed_configure_callback }; @@ -24,7 +54,8 @@ static void stubbed_role_created(struct xdg_surface_server_t* super) { struct stubbed_surface_t* self = (void*)super; if (self->super.xdg_popup) { - fprintf(stderr, "stubbed popup not displayed\n"); + self->configure.top_left = current_positioner.anchor_rect.top_left; + self->configure.size = current_positioner.size; } else if (self->super.xdg_toplevel) { fprintf(stderr, "stubbed toplevel not displayed\n"); } @@ -41,6 +72,34 @@ static void stubbed_role_created(struct xdg_surface_server_t* super) { struct wl_callback* callback = wl_display_sync(wrapped_display); wl_callback_add_listener(callback, &stubbed_configure_callback_listener, self); wl_proxy_wrapper_destroy(wrapped_display); + + struct xdg_surface_server_t* parent_surface = get_xdg_surface_server_from_xdg_surface(super->popup_parent); + if (parent_surface) { + struct wl_subcompositor* subcompositor = get_subcompositor_global_from_display(display); + assert(subcompositor); + self->subsurface = wl_subcompositor_get_subsurface( + subcompositor, + super->wl_surface, + parent_surface->wl_surface + ); + wl_subsurface_set_desync(self->subsurface); + wl_subsurface_set_position(self->subsurface, self->configure.top_left.x, self->configure.top_left.y); + // Trigger parent commit to make subsurface appear + xdg_surface_server_send_configure( + parent_surface, + parent_surface->last_configure.x, parent_surface->last_configure.y, + parent_surface->last_configure.width, parent_surface->last_configure.height, + 0 + ); + } +} + +static void stubbed_role_destroyed(struct xdg_surface_server_t* super) { + struct stubbed_surface_t* self = (void*)super; + + if (self->subsurface) { + wl_subsurface_destroy(self->subsurface); + } } void stubbed_surface_destroyed(struct xdg_surface_server_t* super) { @@ -52,5 +111,67 @@ struct wl_proxy* stubbed_surface_init(struct xdg_wm_base* creating_object, struc struct stubbed_surface_t* self = calloc(1, sizeof(struct stubbed_surface_t)); self->super.popup_created = stubbed_role_created; self->super.toplevel_created = stubbed_role_created; + self->super.popup_destroyed = stubbed_role_destroyed; + self->super.toplevel_destroyed = stubbed_role_destroyed; return xdg_surface_server_get_xdg_surface(&self->super, creating_object, surface); } + +static bool xdg_positioner_hook( + void* data, + struct wl_proxy* proxy, + uint32_t opcode, + const struct wl_interface* create_interface, + uint32_t create_version, + uint32_t flags, + union wl_argument* args, + struct wl_proxy** ret_proxy +) { + (void)data, (void)opcode, (void)create_interface, (void)create_version, (void)flags, (void)ret_proxy; + if (opcode == XDG_POSITIONER_DESTROY) return false; + + if (current_positioner.wayland_object != proxy) { + memset(¤t_positioner, 0, sizeof(current_positioner)); + current_positioner.wayland_object = proxy; + } + + switch (opcode) { + case XDG_POSITIONER_SET_SIZE: + current_positioner.size = (struct geom_size_t){args[0].i, args[1].i}; + break; + + case XDG_POSITIONER_SET_ANCHOR_RECT: + current_positioner.anchor_rect.top_left = (struct geom_point_t){args[0].i, args[1].i}; + current_positioner.anchor_rect.size = (struct geom_size_t){args[2].i, args[3].i}; + break; + + case XDG_POSITIONER_SET_ANCHOR: + current_positioner.anchor = args[0].u; + break; + + case XDG_POSITIONER_SET_GRAVITY: + current_positioner.gravity = args[0].u; + break; + + case XDG_POSITIONER_SET_CONSTRAINT_ADJUSTMENT: + current_positioner.constraint_adjustment = args[0].u; + break; + + case XDG_POSITIONER_SET_OFFSET: + current_positioner.offset = (struct geom_point_t){args[0].i, args[1].i}; + break; + } + return false; +} + +void stubbed_surface_install_positioner_hook() { + static bool installed = false; + if (!installed) { + installed = true; + libwayland_shim_install_request_hook( + &xdg_positioner_interface, + -1, + xdg_positioner_hook, + NULL + ); + } +} diff --git a/src/stubbed-surface.h b/src/stubbed-surface.h index 291d7e7..451a776 100644 --- a/src/stubbed-surface.h +++ b/src/stubbed-surface.h @@ -4,3 +4,4 @@ #include struct wl_proxy* stubbed_surface_init(struct xdg_wm_base* creating_object, struct wl_surface* surface); +void stubbed_surface_install_positioner_hook(); diff --git a/src/xdg-surface-server.c b/src/xdg-surface-server.c index f1f387d..3fc9523 100644 --- a/src/xdg-surface-server.c +++ b/src/xdg-surface-server.c @@ -87,6 +87,7 @@ static bool xdg_surface_handle_request( self ); self->xdg_popup = (struct xdg_popup*)*ret_proxy; + self->popup_parent = (struct xdg_surface*)args[1].o; if (self->popup_created) self->popup_created(self); return true; @@ -139,6 +140,11 @@ void xdg_surface_server_send_configure( int width, int height, uint32_t serial ) { + self->last_configure.x = x; + self->last_configure.y = y; + self->last_configure.width = width; + self->last_configure.height = height; + if (self->xdg_toplevel) { struct wl_array states; wl_array_init(&states); diff --git a/src/xdg-surface-server.h b/src/xdg-surface-server.h index 60081ab..367e804 100644 --- a/src/xdg-surface-server.h +++ b/src/xdg-surface-server.h @@ -15,8 +15,12 @@ struct xdg_surface_server_t { struct wl_surface* wl_surface; struct xdg_surface* xdg_surface; + struct xdg_surface* popup_parent; struct xdg_popup* xdg_popup; struct xdg_toplevel* xdg_toplevel; + struct { + int x, y, width, height; + } last_configure; }; // If the xdg_surface is managed by a surface server, this returns the surface server