From 6b7b64ec1eed0eb2d02c3de69f4352c21ed3c288 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Sat, 14 Apr 2018 15:47:51 -0400 Subject: [PATCH 01/14] Generalize xdg-shell popups and add to layer-shell --- include/wlr/types/wlr_layer_shell.h | 2 + include/wlr/types/wlr_xdg_shell.h | 11 ++++- types/wlr_layer_shell.c | 18 +++++++- types/wlr_xdg_shell.c | 66 +++++++++++++---------------- 4 files changed, 57 insertions(+), 40 deletions(-) diff --git a/include/wlr/types/wlr_layer_shell.h b/include/wlr/types/wlr_layer_shell.h index 79b3a4ea..4b3e4e1c 100644 --- a/include/wlr/types/wlr_layer_shell.h +++ b/include/wlr/types/wlr_layer_shell.h @@ -59,6 +59,7 @@ struct wlr_layer_surface { struct wlr_output *output; struct wl_resource *resource; struct wlr_layer_shell *shell; + struct wl_list popups; // wlr_xdg_popup::link const char *namespace; enum zwlr_layer_shell_v1_layer layer; @@ -81,6 +82,7 @@ struct wlr_layer_surface { struct wl_signal destroy; struct wl_signal map; struct wl_signal unmap; + struct wl_signal new_popup; } events; void *data; diff --git a/include/wlr/types/wlr_xdg_shell.h b/include/wlr/types/wlr_xdg_shell.h index 5046339a..cf96df4a 100644 --- a/include/wlr/types/wlr_xdg_shell.h +++ b/include/wlr/types/wlr_xdg_shell.h @@ -32,15 +32,18 @@ struct wlr_xdg_client { struct wl_event_source *ping_timer; }; +struct wlr_xdg_positioner; + struct wlr_xdg_popup { struct wlr_xdg_surface *base; struct wl_list link; struct wl_resource *resource; bool committed; - struct wlr_xdg_surface *parent; + struct wlr_surface *parent; struct wlr_seat *seat; + struct wlr_xdg_positioner *positioner; // Position of the popup relative to the upper left corner of the window // geometry of the parent surface struct wlr_box geometry; @@ -178,6 +181,11 @@ struct wlr_xdg_toplevel_show_window_menu_event { struct wlr_xdg_shell *wlr_xdg_shell_create(struct wl_display *display); void wlr_xdg_shell_destroy(struct wlr_xdg_shell *xdg_shell); +struct wlr_xdg_surface *wlr_xdg_surface_from_resource( + struct wl_resource *resource); + +struct wlr_box wlr_xdg_popup_get_geometry(struct wlr_xdg_popup *popup); + /** * Send a ping to the surface. If the surface does not respond in a reasonable * amount of time, the ping_timeout event will be emitted. @@ -226,6 +234,7 @@ void wlr_xdg_surface_send_close(struct wlr_xdg_surface *surface); /** * Compute the popup position in its parent's surface-local coordinate system. + * This aborts if called for popups whose parent is not an xdg_surface. */ void wlr_xdg_surface_popup_get_position(struct wlr_xdg_surface *surface, double *popup_sx, double *popup_sy); diff --git a/types/wlr_layer_shell.c b/types/wlr_layer_shell.c index a567b8bc..7e603118 100644 --- a/types/wlr_layer_shell.c +++ b/types/wlr_layer_shell.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include "util/signal.h" #include "wlr-layer-shell-unstable-v1-protocol.h" @@ -131,8 +132,19 @@ static void layer_surface_handle_set_keyboard_interactivity( } static void layer_surface_handle_get_popup(struct wl_client *client, - struct wl_resource *resource, struct wl_resource *popup) { - // TODO + struct wl_resource *layer_resource, + struct wl_resource *popup_resource) { + struct wlr_layer_surface *parent = + layer_surface_from_resource(layer_resource); + struct wlr_xdg_surface *popup_surface = + wlr_xdg_surface_from_resource(popup_resource); + + assert(popup_surface->role == WLR_XDG_SURFACE_ROLE_POPUP); + struct wlr_xdg_popup *popup = popup_surface->popup; + popup->parent = parent->surface; + popup->geometry = wlr_xdg_popup_get_geometry(popup); + wl_list_insert(&parent->popups, &popup->link); + wlr_signal_emit_safe(&parent->events.new_popup, popup); } static const struct zwlr_layer_surface_v1_interface layer_surface_implementation = { @@ -343,6 +355,7 @@ static void layer_shell_handle_get_layer_surface(struct wl_client *wl_client, } wl_list_init(&surface->configure_list); + wl_list_init(&surface->popups); wl_signal_init(&surface->events.destroy); wl_signal_add(&surface->surface->events.destroy, @@ -350,6 +363,7 @@ static void layer_shell_handle_get_layer_surface(struct wl_client *wl_client, surface->surface_destroy_listener.notify = handle_wlr_surface_destroyed; wl_signal_init(&surface->events.map); wl_signal_init(&surface->events.unmap); + wl_signal_init(&surface->events.new_popup); wlr_surface_set_role_committed(surface->surface, handle_wlr_surface_committed, surface); diff --git a/types/wlr_xdg_shell.c b/types/wlr_xdg_shell.c index 8d424f3b..080d57a4 100644 --- a/types/wlr_xdg_shell.c +++ b/types/wlr_xdg_shell.c @@ -51,16 +51,6 @@ static void resource_handle_destroy(struct wl_client *client, wl_resource_destroy(resource); } -static struct wlr_xdg_surface *xdg_popup_grab_get_topmost( - struct wlr_xdg_popup_grab *grab) { - struct wlr_xdg_popup *popup; - wl_list_for_each(popup, &grab->popups, grab_link) { - return popup->base; - } - - return NULL; -} - static void xdg_pointer_grab_end(struct wlr_seat_pointer_grab *grab) { struct wlr_xdg_popup_grab *popup_grab = grab->data; @@ -447,9 +437,9 @@ static void xdg_shell_handle_create_positioner(struct wl_client *wl_client, positioner, xdg_positioner_destroy); } -static struct wlr_box xdg_positioner_get_geometry( - struct wlr_xdg_positioner *positioner, - struct wlr_xdg_surface *surface, struct wlr_xdg_surface *parent) { +struct wlr_box wlr_xdg_popup_get_geometry(struct wlr_xdg_popup *popup) { + assert(popup && popup->positioner); + struct wlr_xdg_positioner *positioner = popup->positioner; struct wlr_box geometry = { .x = positioner->offset.x, .y = positioner->offset.y, @@ -563,12 +553,7 @@ static void xdg_popup_handle_grab(struct wl_client *client, xdg_shell_popup_grab_from_seat(surface->client->shell, seat_client->seat); - struct wlr_xdg_surface *topmost = xdg_popup_grab_get_topmost(popup_grab); - bool parent_is_toplevel = - surface->popup->parent->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL; - - if ((topmost == NULL && !parent_is_toplevel) || - (topmost != NULL && topmost != surface->popup->parent)) { + if (!wl_list_empty(&surface->popups)) { wl_resource_post_error(surface->client->resource, XDG_WM_BASE_ERROR_NOT_THE_TOPMOST_POPUP, "xdg_popup was not created on the topmost popup"); @@ -616,8 +601,12 @@ static void xdg_popup_resource_destroy(struct wl_resource *resource) { static const struct xdg_surface_interface xdg_surface_implementation; -static struct wlr_xdg_surface *xdg_surface_from_resource( +struct wlr_xdg_surface *wlr_xdg_surface_from_resource( struct wl_resource *resource) { + // TODO: Double check that all of the callers can deal with NULL + if (!resource) { + return NULL; + } assert(wl_resource_instance_of(resource, &xdg_surface_interface, &xdg_surface_implementation)); return wl_resource_get_user_data(resource); @@ -628,9 +617,9 @@ static void xdg_surface_handle_get_popup(struct wl_client *client, struct wl_resource *parent_resource, struct wl_resource *positioner_resource) { struct wlr_xdg_surface *surface = - xdg_surface_from_resource(resource); + wlr_xdg_surface_from_resource(resource); struct wlr_xdg_surface *parent = - xdg_surface_from_resource(parent_resource); + wlr_xdg_surface_from_resource(parent_resource); struct wlr_xdg_positioner *positioner = xdg_positioner_from_resource(positioner_resource); @@ -663,16 +652,18 @@ static void xdg_surface_handle_get_popup(struct wl_client *client, surface->role = WLR_XDG_SURFACE_ROLE_POPUP; surface->popup->base = surface; - surface->popup->parent = parent; - surface->popup->geometry = - xdg_positioner_get_geometry(positioner, surface, parent); - wl_list_insert(&parent->popups, &surface->popup->link); + surface->popup->positioner = positioner; wl_resource_set_implementation(surface->popup->resource, &xdg_popup_implementation, surface, xdg_popup_resource_destroy); - wlr_signal_emit_safe(&parent->events.new_popup, surface->popup); + if (parent) { + surface->popup->parent = parent->surface; + surface->popup->geometry = wlr_xdg_popup_get_geometry(surface->popup); + wl_list_insert(&parent->popups, &surface->popup->link); + wlr_signal_emit_safe(&parent->events.new_popup, surface->popup); + } } @@ -913,7 +904,7 @@ static const struct xdg_toplevel_interface xdg_toplevel_implementation = { static void xdg_surface_resource_destroy(struct wl_resource *resource) { struct wlr_xdg_surface *surface = - xdg_surface_from_resource(resource); + wlr_xdg_surface_from_resource(resource); if (surface != NULL) { xdg_surface_destroy(surface); } @@ -929,7 +920,7 @@ static void xdg_toplevel_resource_destroy(struct wl_resource *resource) { static void xdg_surface_handle_get_toplevel(struct wl_client *client, struct wl_resource *resource, uint32_t id) { - struct wlr_xdg_surface *surface = xdg_surface_from_resource(resource); + struct wlr_xdg_surface *surface = wlr_xdg_surface_from_resource(resource); if (wlr_surface_set_role(surface->surface, wlr_desktop_xdg_toplevel_role, resource, XDG_WM_BASE_ERROR_ROLE)) { @@ -984,7 +975,7 @@ static void wlr_xdg_toplevel_ack_configure( static void xdg_surface_handle_ack_configure(struct wl_client *client, struct wl_resource *resource, uint32_t serial) { - struct wlr_xdg_surface *surface = xdg_surface_from_resource(resource); + struct wlr_xdg_surface *surface = wlr_xdg_surface_from_resource(resource); if (surface->role == WLR_XDG_SURFACE_ROLE_NONE) { wl_resource_post_error(surface->resource, @@ -1032,7 +1023,7 @@ static void xdg_surface_handle_ack_configure(struct wl_client *client, static void xdg_surface_handle_set_window_geometry(struct wl_client *client, struct wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) { - struct wlr_xdg_surface *surface = xdg_surface_from_resource(resource); + struct wlr_xdg_surface *surface = wlr_xdg_surface_from_resource(resource); if (surface->role == WLR_XDG_SURFACE_ROLE_NONE) { wl_resource_post_error(surface->resource, @@ -1050,7 +1041,7 @@ static void xdg_surface_handle_set_window_geometry(struct wl_client *client, static void xdg_surface_handle_destroy(struct wl_client *client, struct wl_resource *resource) { - struct wlr_xdg_surface *surface = xdg_surface_from_resource(resource); + struct wlr_xdg_surface *surface = wlr_xdg_surface_from_resource(resource); if (surface->role != WLR_XDG_SURFACE_ROLE_NONE) { wlr_log(L_ERROR, "Tried to destroy an xdg_surface before its role " @@ -1627,11 +1618,12 @@ void wlr_xdg_surface_send_close(struct wlr_xdg_surface *surface) { void wlr_xdg_surface_popup_get_position(struct wlr_xdg_surface *surface, double *popup_sx, double *popup_sy) { assert(surface->role == WLR_XDG_SURFACE_ROLE_POPUP); - struct wlr_xdg_surface *parent = surface->popup->parent; - *popup_sx = parent->geometry.x + surface->popup->geometry.x - - surface->geometry.x; - *popup_sy = parent->geometry.y + surface->popup->geometry.y - - surface->geometry.y; + struct wlr_xdg_popup *popup = surface->popup; + assert(strcmp(popup->parent->role, wlr_desktop_xdg_toplevel_role) == 0 + || strcmp(popup->parent->role, wlr_desktop_xdg_popup_role) == 0); + struct wlr_xdg_surface *parent = popup->parent->role_data; + *popup_sx = parent->geometry.x + popup->geometry.x - surface->geometry.x; + *popup_sy = parent->geometry.y + popup->geometry.y - surface->geometry.y; } struct wlr_surface *wlr_xdg_surface_surface_at( From d3cdb00208f119e1e2a2fc1f1733e6eff6d47da5 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Sat, 14 Apr 2018 16:50:05 -0400 Subject: [PATCH 02/14] Add (shitty) support for popups to layer example --- examples/layer-shell.c | 101 +++++++++++++++++++++++++++--- include/wlr/types/wlr_xdg_shell.h | 2 + types/wlr_layer_shell.c | 2 +- types/wlr_xdg_shell.c | 15 +++-- 4 files changed, 106 insertions(+), 14 deletions(-) diff --git a/examples/layer-shell.c b/examples/layer-shell.c index 245d762f..eee90b73 100644 --- a/examples/layer-shell.c +++ b/examples/layer-shell.c @@ -1,4 +1,9 @@ #define _POSIX_C_SOURCE 199309L +#ifdef __linux__ +#include +#elif __FreeBSD__ +#include +#endif #include #include #include @@ -13,15 +18,19 @@ #include #include #include "wlr-layer-shell-unstable-v1-client-protocol.h" +#include "xdg-shell-client-protocol.h" + +static struct wl_display *display; +static struct wl_compositor *compositor; +static struct wl_seat *seat; +static struct wl_shm *shm; +static struct wl_pointer *pointer; +static struct wl_keyboard *keyboard; +static struct xdg_wm_base *xdg_wm_base; +static struct zwlr_layer_shell_v1 *layer_shell; -static struct wl_compositor *compositor = NULL; -static struct wl_seat *seat = NULL; -static struct wl_shm *shm = NULL; -static struct wl_pointer *pointer = NULL; -static struct wl_keyboard *keyboard = NULL; -static struct zwlr_layer_shell_v1 *layer_shell = NULL; struct zwlr_layer_surface_v1 *layer_surface; -static struct wl_output *wl_output = NULL; +static struct wl_output *wl_output; struct wl_surface *wl_surface; struct wlr_egl egl; @@ -30,6 +39,9 @@ struct wlr_egl_surface *egl_surface; struct wl_callback *frame_callback; static uint32_t output = UINT32_MAX; +struct xdg_surface *popup_surface; +struct xdg_popup *popup; + static uint32_t layer = ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND; static uint32_t anchor = 0; static uint32_t width = 256, height = 256; @@ -121,6 +133,71 @@ static void draw(void) { demo.last_frame = ts; } +static void xdg_surface_handle_configure(void *data, + struct xdg_surface *xdg_surface, uint32_t serial) { + xdg_surface_ack_configure(xdg_surface, serial); + // Whatever +} + +static const struct xdg_surface_listener xdg_surface_listener = { + .configure = xdg_surface_handle_configure, +}; + +static void xdg_popup_configure(void *data, struct xdg_popup *xdg_popup, + int32_t x, int32_t y, int32_t width, int32_t height) { + // Meh. +} + +static void xdg_popup_done(void *data, struct xdg_popup *xdg_popup) { + // We leak the surface, but who cares + xdg_popup_destroy(popup); + popup = NULL; +} + +static const struct xdg_popup_listener xdg_popup_listener = { + .configure = xdg_popup_configure, + .popup_done = xdg_popup_done, +}; + +static void create_popup() { + if (popup) { + return; + } + struct wl_surface *surface = wl_compositor_create_surface(compositor); + assert(xdg_wm_base && surface); + struct xdg_surface *xdg_surface = xdg_wm_base_get_xdg_surface( + xdg_wm_base, surface); + struct xdg_positioner *xdg_positioner = xdg_wm_base_create_positioner( + xdg_wm_base); + assert(xdg_surface && xdg_positioner); + + xdg_positioner_set_size(xdg_positioner, 256, 256); + xdg_positioner_set_anchor_rect(xdg_positioner, 0, 0, width, height); + + popup = xdg_surface_get_popup(xdg_surface, NULL, xdg_positioner); + assert(popup); + + zwlr_layer_surface_v1_get_popup(layer_surface, popup); + + xdg_surface_add_listener(xdg_surface, &xdg_surface_listener, NULL); + xdg_popup_add_listener(popup, &xdg_popup_listener, NULL); + + wl_surface_commit(surface); + wl_display_roundtrip(display); + + struct wl_egl_window *egl_window; + struct wlr_egl_surface *egl_surface; + egl_window = wl_egl_window_create(surface, 256, 256); + assert(egl_window); + egl_surface = wlr_egl_create_surface(&egl, egl_window); + assert(egl_surface); + + eglMakeCurrent(egl.display, egl_surface, egl_surface, egl.context); + glClearColor(0.5f, 0.5f, 0.5f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + eglSwapBuffers(egl.display, egl_surface); +} + static void layer_surface_configure(void *data, struct zwlr_layer_surface_v1 *surface, uint32_t serial, uint32_t w, uint32_t h) { @@ -172,6 +249,9 @@ static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t state) { if (state == WL_POINTER_BUTTON_STATE_PRESSED) { buttons++; + if (button == BTN_RIGHT) { + create_popup(); + } } else { buttons--; } @@ -277,7 +357,7 @@ const struct wl_seat_listener seat_listener = { static void handle_global(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { - if (strcmp(interface, "wl_compositor") == 0) { + if (strcmp(interface, wl_compositor_interface.name) == 0) { compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 1); } else if (strcmp(interface, wl_shm_interface.name) == 0) { @@ -299,6 +379,9 @@ static void handle_global(void *data, struct wl_registry *registry, } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) { layer_shell = wl_registry_bind( registry, name, &zwlr_layer_shell_v1_interface, 1); + } else if (strcmp(interface, xdg_wm_base_interface.name) == 0) { + xdg_wm_base = wl_registry_bind( + registry, name, &xdg_wm_base_interface, 1); } } @@ -407,7 +490,7 @@ int main(int argc, char **argv) { } } - struct wl_display *display = wl_display_connect(NULL); + display = wl_display_connect(NULL); if (display == NULL) { fprintf(stderr, "Failed to create display\n"); return 1; diff --git a/include/wlr/types/wlr_xdg_shell.h b/include/wlr/types/wlr_xdg_shell.h index cf96df4a..b05dcac1 100644 --- a/include/wlr/types/wlr_xdg_shell.h +++ b/include/wlr/types/wlr_xdg_shell.h @@ -183,6 +183,8 @@ void wlr_xdg_shell_destroy(struct wlr_xdg_shell *xdg_shell); struct wlr_xdg_surface *wlr_xdg_surface_from_resource( struct wl_resource *resource); +struct wlr_xdg_surface *wlr_xdg_surface_from_popup_resource( + struct wl_resource *resource); struct wlr_box wlr_xdg_popup_get_geometry(struct wlr_xdg_popup *popup); diff --git a/types/wlr_layer_shell.c b/types/wlr_layer_shell.c index 7e603118..29e44827 100644 --- a/types/wlr_layer_shell.c +++ b/types/wlr_layer_shell.c @@ -137,7 +137,7 @@ static void layer_surface_handle_get_popup(struct wl_client *client, struct wlr_layer_surface *parent = layer_surface_from_resource(layer_resource); struct wlr_xdg_surface *popup_surface = - wlr_xdg_surface_from_resource(popup_resource); + wlr_xdg_surface_from_popup_resource(popup_resource); assert(popup_surface->role == WLR_XDG_SURFACE_ROLE_POPUP); struct wlr_xdg_popup *popup = popup_surface->popup; diff --git a/types/wlr_xdg_shell.c b/types/wlr_xdg_shell.c index 080d57a4..66dac02a 100644 --- a/types/wlr_xdg_shell.c +++ b/types/wlr_xdg_shell.c @@ -527,7 +527,7 @@ struct wlr_box wlr_xdg_popup_get_geometry(struct wlr_xdg_popup *popup) { static const struct xdg_popup_interface xdg_popup_implementation; -static struct wlr_xdg_surface *xdg_surface_from_xdg_popup_resource( +struct wlr_xdg_surface *wlr_xdg_surface_from_popup_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &xdg_popup_interface, &xdg_popup_implementation)); @@ -538,7 +538,7 @@ static void xdg_popup_handle_grab(struct wl_client *client, struct wl_resource *resource, struct wl_resource *seat_resource, uint32_t serial) { struct wlr_xdg_surface *surface = - xdg_surface_from_xdg_popup_resource(resource); + wlr_xdg_surface_from_popup_resource(resource); struct wlr_seat_client *seat_client = wlr_seat_client_from_resource(seat_resource); @@ -574,7 +574,7 @@ static void xdg_popup_handle_grab(struct wl_client *client, static void xdg_popup_handle_destroy(struct wl_client *client, struct wl_resource *resource) { struct wlr_xdg_surface *surface = - xdg_surface_from_xdg_popup_resource(resource); + wlr_xdg_surface_from_popup_resource(resource); if (!wl_list_empty(&surface->popups)) { wl_resource_post_error(surface->client->resource, @@ -593,7 +593,7 @@ static const struct xdg_popup_interface xdg_popup_implementation = { static void xdg_popup_resource_destroy(struct wl_resource *resource) { struct wlr_xdg_surface *surface = - xdg_surface_from_xdg_popup_resource(resource); + wlr_xdg_surface_from_popup_resource(resource); if (surface != NULL) { xdg_popup_destroy(surface); } @@ -1281,6 +1281,13 @@ static void wlr_xdg_surface_popup_committed( struct wlr_xdg_surface *surface) { assert(surface->role == WLR_XDG_SURFACE_ROLE_POPUP); + if (!surface->popup->parent) { + wl_resource_post_error(surface->resource, + XDG_SURFACE_ERROR_NOT_CONSTRUCTED, + "xdg_popup has no parent"); + return; + } + if (!surface->popup->committed) { wlr_xdg_surface_schedule_configure(surface); surface->popup->committed = true; From 278aa846195ceb8ea85c181aba21e0befbcce5de Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Sat, 14 Apr 2018 20:36:01 -0400 Subject: [PATCH 03/14] Basic layer popup rendering --- include/wlr/types/wlr_layer_shell.h | 4 +++ rootston/output.c | 2 ++ types/wlr_layer_shell.c | 46 +++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+) diff --git a/include/wlr/types/wlr_layer_shell.h b/include/wlr/types/wlr_layer_shell.h index 4b3e4e1c..6040478d 100644 --- a/include/wlr/types/wlr_layer_shell.h +++ b/include/wlr/types/wlr_layer_shell.h @@ -109,4 +109,8 @@ bool wlr_surface_is_layer_surface(struct wlr_surface *surface); struct wlr_layer_surface *wlr_layer_surface_from_wlr_surface( struct wlr_surface *surface); +/* Calls the iterator function for each sub-surface and popup of this surface */ +void wlr_layer_surface_for_each_surface(struct wlr_layer_surface *surface, + wlr_surface_iterator_func_t iterator, void *user_data); + #endif diff --git a/rootston/output.c b/rootston/output.c index 791b2819..48b20466 100644 --- a/rootston/output.c +++ b/rootston/output.c @@ -366,6 +366,8 @@ static void render_layer(struct roots_output *output, roots_surface->geo.x + output_layout_box->x, roots_surface->geo.y + output_layout_box->y, 0, &data->layout, render_surface, data); + + wlr_layer_surface_for_each_surface(layer, render_surface, data); } } diff --git a/types/wlr_layer_shell.c b/types/wlr_layer_shell.c index 29e44827..9121b1d2 100644 --- a/types/wlr_layer_shell.c +++ b/types/wlr_layer_shell.c @@ -452,3 +452,49 @@ void wlr_layer_shell_destroy(struct wlr_layer_shell *layer_shell) { wl_global_destroy(layer_shell->wl_global); free(layer_shell); } + +struct layer_surface_iterator_data { + wlr_surface_iterator_func_t user_iterator; + void *user_data; + int x, y; +}; + +static void layer_surface_iterator(struct wlr_surface *surface, + int sx, int sy, void *data) { + struct layer_surface_iterator_data *iter_data = data; + iter_data->user_iterator(surface, iter_data->x + sx, iter_data->y + sy, + iter_data->user_data); +} + +static void layer_surface_for_each_surface(struct wlr_layer_surface *surface, + int x, int y, wlr_surface_iterator_func_t iterator, void *user_data) { + struct layer_surface_iterator_data data = { + .user_iterator = iterator, + .user_data = user_data, + .x = x, .y = y, + }; + wlr_surface_for_each_surface(surface->surface, + layer_surface_iterator, &data); + + struct wlr_xdg_popup *popup_state; + wl_list_for_each(popup_state, &surface->popups, link) { + struct wlr_xdg_surface *popup = popup_state->base; + if (!popup->configured) { + continue; + } + + double popup_sx, popup_sy; + popup_sx = popup->geometry.x; + popup_sy = popup->geometry.y; + + iterator(popup->surface, data.x + popup_sx, + data.y + popup_sy, user_data); + + wlr_xdg_surface_for_each_surface(popup, layer_surface_iterator, &data); + } +} + +void wlr_layer_surface_for_each_surface(struct wlr_layer_surface *surface, + wlr_surface_iterator_func_t iterator, void *user_data) { + layer_surface_for_each_surface(surface, 0, 0, iterator, user_data); +} From 2e3d901ac56f962d2ac9aab8b86c01b388c593f5 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Sat, 14 Apr 2018 21:17:40 -0400 Subject: [PATCH 04/14] Forward-port xdg-shell-v6 positioner improvements --- examples/layer-shell.c | 17 ++-- include/wlr/types/wlr_xdg_shell.h | 25 +++++- types/wlr_layer_shell.c | 2 +- types/wlr_xdg_shell.c | 137 ++++++++++-------------------- types/wlr_xdg_shell_v6.c | 2 - 5 files changed, 79 insertions(+), 104 deletions(-) diff --git a/examples/layer-shell.c b/examples/layer-shell.c index eee90b73..ac6e8a9e 100644 --- a/examples/layer-shell.c +++ b/examples/layer-shell.c @@ -165,14 +165,19 @@ static void create_popup() { } struct wl_surface *surface = wl_compositor_create_surface(compositor); assert(xdg_wm_base && surface); - struct xdg_surface *xdg_surface = xdg_wm_base_get_xdg_surface( - xdg_wm_base, surface); - struct xdg_positioner *xdg_positioner = xdg_wm_base_create_positioner( - xdg_wm_base); + struct xdg_surface *xdg_surface = + xdg_wm_base_get_xdg_surface(xdg_wm_base, surface); + struct xdg_positioner *xdg_positioner = + xdg_wm_base_create_positioner(xdg_wm_base); assert(xdg_surface && xdg_positioner); xdg_positioner_set_size(xdg_positioner, 256, 256); - xdg_positioner_set_anchor_rect(xdg_positioner, 0, 0, width, height); + xdg_positioner_set_offset(xdg_positioner, 0, 0); + xdg_positioner_set_anchor_rect(xdg_positioner, cur_x, cur_y, 1, 1); + xdg_positioner_set_anchor(xdg_positioner, XDG_POSITIONER_ANCHOR_TOP_LEFT); + xdg_positioner_set_gravity(xdg_positioner, XDG_POSITIONER_GRAVITY_TOP_LEFT); + xdg_positioner_set_constraint_adjustment(xdg_positioner, + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_NONE); popup = xdg_surface_get_popup(xdg_surface, NULL, xdg_positioner); assert(popup); @@ -185,6 +190,8 @@ static void create_popup() { wl_surface_commit(surface); wl_display_roundtrip(display); + xdg_positioner_destroy(xdg_positioner); + struct wl_egl_window *egl_window; struct wlr_egl_surface *egl_surface; egl_window = wl_egl_window_create(surface, 256, 256); diff --git a/include/wlr/types/wlr_xdg_shell.h b/include/wlr/types/wlr_xdg_shell.h index b05dcac1..809d6390 100644 --- a/include/wlr/types/wlr_xdg_shell.h +++ b/include/wlr/types/wlr_xdg_shell.h @@ -1,9 +1,9 @@ #ifndef WLR_TYPES_WLR_XDG_SHELL_H #define WLR_TYPES_WLR_XDG_SHELL_H - #include #include #include +#include "xdg-shell-protocol.h" struct wlr_xdg_shell { struct wl_global *wl_global; @@ -32,7 +32,22 @@ struct wlr_xdg_client { struct wl_event_source *ping_timer; }; -struct wlr_xdg_positioner; +struct wlr_xdg_positioner { + struct wl_resource *resource; + + struct wlr_box anchor_rect; + enum xdg_positioner_anchor anchor; + enum xdg_positioner_gravity gravity; + enum xdg_positioner_constraint_adjustment constraint_adjustment; + + struct { + int32_t width, height; + } size; + + struct { + int32_t x, y; + } offset; +}; struct wlr_xdg_popup { struct wlr_xdg_surface *base; @@ -43,11 +58,12 @@ struct wlr_xdg_popup { struct wlr_surface *parent; struct wlr_seat *seat; - struct wlr_xdg_positioner *positioner; // Position of the popup relative to the upper left corner of the window // geometry of the parent surface struct wlr_box geometry; + struct wlr_xdg_positioner positioner; + struct wl_list grab_link; // wlr_xdg_popup_grab::popups }; @@ -186,7 +202,8 @@ struct wlr_xdg_surface *wlr_xdg_surface_from_resource( struct wlr_xdg_surface *wlr_xdg_surface_from_popup_resource( struct wl_resource *resource); -struct wlr_box wlr_xdg_popup_get_geometry(struct wlr_xdg_popup *popup); +struct wlr_box wlr_xdg_positioner_get_geometry( + struct wlr_xdg_positioner *positioner); /** * Send a ping to the surface. If the surface does not respond in a reasonable diff --git a/types/wlr_layer_shell.c b/types/wlr_layer_shell.c index 9121b1d2..5c709713 100644 --- a/types/wlr_layer_shell.c +++ b/types/wlr_layer_shell.c @@ -142,7 +142,7 @@ static void layer_surface_handle_get_popup(struct wl_client *client, assert(popup_surface->role == WLR_XDG_SURFACE_ROLE_POPUP); struct wlr_xdg_popup *popup = popup_surface->popup; popup->parent = parent->surface; - popup->geometry = wlr_xdg_popup_get_geometry(popup); + popup->geometry = wlr_xdg_positioner_get_geometry(&popup->positioner); wl_list_insert(&parent->popups, &popup->link); wlr_signal_emit_safe(&parent->events.new_popup, popup); } diff --git a/types/wlr_xdg_shell.c b/types/wlr_xdg_shell.c index 66dac02a..22d404a7 100644 --- a/types/wlr_xdg_shell.c +++ b/types/wlr_xdg_shell.c @@ -28,24 +28,11 @@ struct wlr_xdg_surface *wlr_xdg_surface_from_wlr_surface( return (struct wlr_xdg_surface *)surface->role_data; } -struct wlr_xdg_positioner { +struct wlr_xdg_positioner_resource { struct wl_resource *resource; - - struct wlr_box anchor_rect; - enum xdg_positioner_anchor anchor; - enum xdg_positioner_gravity gravity; - enum xdg_positioner_constraint_adjustment constraint_adjustment; - - struct { - int32_t width, height; - } size; - - struct { - int32_t x, y; - } offset; + struct wlr_xdg_positioner attrs; }; - static void resource_handle_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); @@ -301,7 +288,7 @@ static void xdg_surface_destroy(struct wlr_xdg_surface *surface) { static const struct xdg_positioner_interface xdg_positioner_implementation; -static struct wlr_xdg_positioner *xdg_positioner_from_resource( +static struct wlr_xdg_positioner_resource *xdg_positioner_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &xdg_positioner_interface, &xdg_positioner_implementation)); @@ -309,14 +296,14 @@ static struct wlr_xdg_positioner *xdg_positioner_from_resource( } static void xdg_positioner_destroy(struct wl_resource *resource) { - struct wlr_xdg_positioner *positioner = + struct wlr_xdg_positioner_resource *positioner = xdg_positioner_from_resource(resource); free(positioner); } static void xdg_positioner_handle_set_size(struct wl_client *client, struct wl_resource *resource, int32_t width, int32_t height) { - struct wlr_xdg_positioner *positioner = + struct wlr_xdg_positioner_resource *positioner = xdg_positioner_from_resource(resource); if (width < 1 || height < 1) { @@ -326,14 +313,14 @@ static void xdg_positioner_handle_set_size(struct wl_client *client, return; } - positioner->size.width = width; - positioner->size.height = height; + positioner->attrs.size.width = width; + positioner->attrs.size.height = height; } static void xdg_positioner_handle_set_anchor_rect(struct wl_client *client, struct wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) { - struct wlr_xdg_positioner *positioner = + struct wlr_xdg_positioner_resource *positioner = xdg_positioner_from_resource(resource); if (width < 0 || height < 0) { @@ -343,15 +330,15 @@ static void xdg_positioner_handle_set_anchor_rect(struct wl_client *client, return; } - positioner->anchor_rect.x = x; - positioner->anchor_rect.y = y; - positioner->anchor_rect.width = width; - positioner->anchor_rect.height = height; + positioner->attrs.anchor_rect.x = x; + positioner->attrs.anchor_rect.y = y; + positioner->attrs.anchor_rect.width = width; + positioner->attrs.anchor_rect.height = height; } static void xdg_positioner_handle_set_anchor(struct wl_client *client, struct wl_resource *resource, uint32_t anchor) { - struct wlr_xdg_positioner *positioner = + struct wlr_xdg_positioner_resource *positioner = xdg_positioner_from_resource(resource); if (anchor > XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT) { @@ -361,12 +348,12 @@ static void xdg_positioner_handle_set_anchor(struct wl_client *client, return; } - positioner->anchor = anchor; + positioner->attrs.anchor = anchor; } static void xdg_positioner_handle_set_gravity(struct wl_client *client, struct wl_resource *resource, uint32_t gravity) { - struct wlr_xdg_positioner *positioner = + struct wlr_xdg_positioner_resource *positioner = xdg_positioner_from_resource(resource); if (gravity > XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT) { @@ -376,25 +363,25 @@ static void xdg_positioner_handle_set_gravity(struct wl_client *client, return; } - positioner->gravity = gravity; + positioner->attrs.gravity = gravity; } static void xdg_positioner_handle_set_constraint_adjustment( struct wl_client *client, struct wl_resource *resource, uint32_t constraint_adjustment) { - struct wlr_xdg_positioner *positioner = + struct wlr_xdg_positioner_resource *positioner = xdg_positioner_from_resource(resource); - positioner->constraint_adjustment = constraint_adjustment; + positioner->attrs.constraint_adjustment = constraint_adjustment; } static void xdg_positioner_handle_set_offset(struct wl_client *client, struct wl_resource *resource, int32_t x, int32_t y) { - struct wlr_xdg_positioner *positioner = + struct wlr_xdg_positioner_resource *positioner = xdg_positioner_from_resource(resource); - positioner->offset.x = x; - positioner->offset.y = y; + positioner->attrs.offset.x = x; + positioner->attrs.offset.y = y; } static const struct xdg_positioner_interface @@ -412,16 +399,12 @@ static const struct xdg_positioner_interface static void xdg_shell_handle_create_positioner(struct wl_client *wl_client, struct wl_resource *resource, uint32_t id) { struct wlr_xdg_positioner *positioner = - calloc(1, sizeof(struct wlr_xdg_positioner)); + calloc(1, sizeof(struct wlr_xdg_positioner_resource)); if (positioner == NULL) { wl_client_post_no_memory(wl_client); return; } - /* set widths to detect improper usages of get_popup */ - positioner->size.width = -1; - positioner->anchor_rect.width = -1; - positioner->resource = wl_resource_create(wl_client, &xdg_positioner_interface, wl_resource_get_version(resource), @@ -437,9 +420,8 @@ static void xdg_shell_handle_create_positioner(struct wl_client *wl_client, positioner, xdg_positioner_destroy); } -struct wlr_box wlr_xdg_popup_get_geometry(struct wlr_xdg_popup *popup) { - assert(popup && popup->positioner); - struct wlr_xdg_positioner *positioner = popup->positioner; +struct wlr_box wlr_xdg_positioner_get_geometry( + struct wlr_xdg_positioner *positioner) { struct wlr_box geometry = { .x = positioner->offset.x, .y = positioner->offset.y, @@ -447,71 +429,39 @@ struct wlr_box wlr_xdg_popup_get_geometry(struct wlr_xdg_popup *popup) { .height = positioner->size.height, }; - switch (positioner->anchor) { - case XDG_POSITIONER_ANCHOR_TOP: - case XDG_POSITIONER_ANCHOR_TOP_LEFT: - case XDG_POSITIONER_ANCHOR_TOP_RIGHT: + if (positioner->anchor & XDG_POSITIONER_ANCHOR_TOP) { geometry.y += positioner->anchor_rect.y; - break; - case XDG_POSITIONER_ANCHOR_BOTTOM: - case XDG_POSITIONER_ANCHOR_BOTTOM_LEFT: - case XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT: + } else if (positioner->anchor & XDG_POSITIONER_ANCHOR_BOTTOM) { geometry.y += positioner->anchor_rect.y + positioner->anchor_rect.height; - break; - default: + } else { geometry.y += positioner->anchor_rect.y + positioner->anchor_rect.height / 2; - break; } - switch (positioner->anchor) { - case XDG_POSITIONER_ANCHOR_LEFT: - case XDG_POSITIONER_ANCHOR_TOP_LEFT: - case XDG_POSITIONER_ANCHOR_BOTTOM_LEFT: + if (positioner->anchor & XDG_POSITIONER_ANCHOR_LEFT) { geometry.x += positioner->anchor_rect.x; - break; - case XDG_POSITIONER_ANCHOR_RIGHT: - case XDG_POSITIONER_ANCHOR_TOP_RIGHT: - case XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT: + } else if (positioner->anchor & XDG_POSITIONER_ANCHOR_RIGHT) { geometry.x += positioner->anchor_rect.x + positioner->anchor_rect.width; - break; - default: + } else { geometry.x += positioner->anchor_rect.x + positioner->anchor_rect.width / 2; - break; } - switch (positioner->gravity) { - case XDG_POSITIONER_GRAVITY_TOP: - case XDG_POSITIONER_GRAVITY_TOP_LEFT: - case XDG_POSITIONER_GRAVITY_TOP_RIGHT: + if (positioner->gravity & XDG_POSITIONER_GRAVITY_TOP) { geometry.y -= geometry.height; - break; - case XDG_POSITIONER_GRAVITY_BOTTOM: - case XDG_POSITIONER_GRAVITY_BOTTOM_LEFT: - case XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT: + } else if (positioner->gravity & XDG_POSITIONER_GRAVITY_BOTTOM) { geometry.y = geometry.y; - break; - default: + } else { geometry.y -= geometry.height / 2; - break; } - switch (positioner->gravity) { - case XDG_POSITIONER_GRAVITY_LEFT: - case XDG_POSITIONER_GRAVITY_TOP_LEFT: - case XDG_POSITIONER_GRAVITY_BOTTOM_LEFT: + if (positioner->gravity & XDG_POSITIONER_GRAVITY_LEFT) { geometry.x -= geometry.width; - break; - case XDG_POSITIONER_GRAVITY_RIGHT: - case XDG_POSITIONER_GRAVITY_TOP_RIGHT: - case XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT: + } else if (positioner->gravity & XDG_POSITIONER_GRAVITY_RIGHT) { geometry.x = geometry.x; - break; - default: + } else { geometry.x -= geometry.width / 2; - break; } if (positioner->constraint_adjustment == @@ -519,8 +469,6 @@ struct wlr_box wlr_xdg_popup_get_geometry(struct wlr_xdg_popup *popup) { return geometry; } - // TODO: add compositor policy configuration and the code here - return geometry; } @@ -620,10 +568,11 @@ static void xdg_surface_handle_get_popup(struct wl_client *client, wlr_xdg_surface_from_resource(resource); struct wlr_xdg_surface *parent = wlr_xdg_surface_from_resource(parent_resource); - struct wlr_xdg_positioner *positioner = + struct wlr_xdg_positioner_resource *positioner = xdg_positioner_from_resource(positioner_resource); - if (positioner->size.width == -1 || positioner->anchor_rect.width == -1) { + if (positioner->attrs.size.width == 0 || + positioner->attrs.anchor_rect.width == 0) { wl_resource_post_error(resource, XDG_WM_BASE_ERROR_INVALID_POSITIONER, "positioner object is not complete"); @@ -652,7 +601,10 @@ static void xdg_surface_handle_get_popup(struct wl_client *client, surface->role = WLR_XDG_SURFACE_ROLE_POPUP; surface->popup->base = surface; - surface->popup->positioner = positioner; + + // positioner properties + memcpy(&surface->popup->positioner, &positioner->attrs, + sizeof(struct wlr_xdg_positioner)); wl_resource_set_implementation(surface->popup->resource, &xdg_popup_implementation, surface, @@ -660,7 +612,8 @@ static void xdg_surface_handle_get_popup(struct wl_client *client, if (parent) { surface->popup->parent = parent->surface; - surface->popup->geometry = wlr_xdg_popup_get_geometry(surface->popup); + surface->popup->geometry = + wlr_xdg_positioner_get_geometry(&positioner->attrs); wl_list_insert(&parent->popups, &surface->popup->link); wlr_signal_emit_safe(&parent->events.new_popup, surface->popup); } diff --git a/types/wlr_xdg_shell_v6.c b/types/wlr_xdg_shell_v6.c index 22b387f1..2d0fbe29 100644 --- a/types/wlr_xdg_shell_v6.c +++ b/types/wlr_xdg_shell_v6.c @@ -486,8 +486,6 @@ struct wlr_box wlr_xdg_positioner_v6_get_geometry( return geometry; } - // TODO: add compositor policy configuration and the code here - return geometry; } From 0a0627f5d03afdaef2251e2442a3ff3285cd8356 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Sat, 14 Apr 2018 21:34:50 -0400 Subject: [PATCH 05/14] Finish forward-porting @acrisci's positioner work --- examples/layer-shell.c | 5 +- include/wlr/types/wlr_xdg_shell.h | 40 +++++ rootston/xdg_shell.c | 56 +++++++ types/wlr_xdg_shell.c | 262 ++++++++++++++++++++++++++++++ 4 files changed, 360 insertions(+), 3 deletions(-) diff --git a/examples/layer-shell.c b/examples/layer-shell.c index ac6e8a9e..56fa28f7 100644 --- a/examples/layer-shell.c +++ b/examples/layer-shell.c @@ -145,7 +145,8 @@ static const struct xdg_surface_listener xdg_surface_listener = { static void xdg_popup_configure(void *data, struct xdg_popup *xdg_popup, int32_t x, int32_t y, int32_t width, int32_t height) { - // Meh. + wlr_log(L_DEBUG, "Popup configured %dx%d@%d,%d", + width, height, x, y); } static void xdg_popup_done(void *data, struct xdg_popup *xdg_popup) { @@ -176,8 +177,6 @@ static void create_popup() { xdg_positioner_set_anchor_rect(xdg_positioner, cur_x, cur_y, 1, 1); xdg_positioner_set_anchor(xdg_positioner, XDG_POSITIONER_ANCHOR_TOP_LEFT); xdg_positioner_set_gravity(xdg_positioner, XDG_POSITIONER_GRAVITY_TOP_LEFT); - xdg_positioner_set_constraint_adjustment(xdg_positioner, - XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_NONE); popup = xdg_surface_get_popup(xdg_surface, NULL, xdg_positioner); assert(popup); diff --git a/include/wlr/types/wlr_xdg_shell.h b/include/wlr/types/wlr_xdg_shell.h index 809d6390..ead4613a 100644 --- a/include/wlr/types/wlr_xdg_shell.h +++ b/include/wlr/types/wlr_xdg_shell.h @@ -258,6 +258,46 @@ void wlr_xdg_surface_send_close(struct wlr_xdg_surface *surface); void wlr_xdg_surface_popup_get_position(struct wlr_xdg_surface *surface, double *popup_sx, double *popup_sy); +/** + * Get the geometry for this positioner based on the anchor rect, gravity, and + * size of this positioner. + */ +struct wlr_box wlr_xdg_positioner_get_geometry( + struct wlr_xdg_positioner *positioner); + +/** + * Get the anchor point for this popup in the toplevel parent's coordinate system. + */ +void wlr_xdg_popup_get_anchor_point(struct wlr_xdg_popup *popup, + int *toplevel_sx, int *toplevel_sy); + +/** + * Convert the given coordinates in the popup coordinate system to the toplevel + * surface coordinate system. + */ +void wlr_xdg_popup_get_toplevel_coords(struct wlr_xdg_popup *popup, + int popup_sx, int popup_sy, int *toplevel_sx, int *toplevel_sy); + +/** + * Set the geometry of this popup to unconstrain it according to its + * xdg-positioner rules. The box should be in the popup's root toplevel parent + * surface coordinate system. + */ +void wlr_xdg_popup_unconstrain_from_box(struct wlr_xdg_popup *popup, + struct wlr_box *toplevel_sx_box); + +/** + Invert the right/left anchor and gravity for this positioner. This can be + used to "flip" the positioner around the anchor rect in the x direction. + */ +void wlr_positioner_invert_x(struct wlr_xdg_positioner *positioner); + +/** + Invert the top/bottom anchor and gravity for this positioner. This can be + used to "flip" the positioner around the anchor rect in the y direction. + */ +void wlr_positioner_invert_y(struct wlr_xdg_positioner *positioner); + /** * Find a surface within this xdg-surface tree at the given surface-local * coordinates. Returns the surface and coordinates in the leaf surface diff --git a/rootston/xdg_shell.c b/rootston/xdg_shell.c index 927bd018..bd670a87 100644 --- a/rootston/xdg_shell.c +++ b/rootston/xdg_shell.c @@ -48,6 +48,59 @@ static void popup_handle_new_popup(struct wl_listener *listener, void *data) { popup_create(popup->view_child.view, wlr_popup); } +static void popup_unconstrain(struct roots_xdg_popup *popup) { + // get the output of the popup's positioner anchor point and convert it to + // the toplevel parent's coordinate system and then pass it to + // wlr_xdg_popup_v6_unconstrain_from_box + + // TODO: unconstrain popups for rotated windows + if (popup->view_child.view->rotation != 0.0) { + return; + } + + struct roots_view *view = popup->view_child.view; + struct wlr_output_layout *layout = view->desktop->layout; + struct wlr_xdg_popup *wlr_popup = popup->wlr_popup; + + int anchor_lx, anchor_ly; + wlr_xdg_popup_get_anchor_point(wlr_popup, &anchor_lx, &anchor_ly); + + int popup_lx, popup_ly; + wlr_xdg_popup_get_toplevel_coords(wlr_popup, wlr_popup->geometry.x, + wlr_popup->geometry.y, &popup_lx, &popup_ly); + popup_lx += view->x; + popup_ly += view->y; + + anchor_lx += popup_lx; + anchor_ly += popup_ly; + + double dest_x = 0, dest_y = 0; + wlr_output_layout_closest_point(layout, NULL, anchor_lx, anchor_ly, + &dest_x, &dest_y); + + struct wlr_output *output = + wlr_output_layout_output_at(layout, dest_x, dest_y); + + if (output == NULL) { + return; + } + + int width = 0, height = 0; + wlr_output_effective_resolution(output, &width, &height); + + // the output box expressed in the coordinate system of the toplevel parent + // of the popup + struct wlr_box output_toplevel_sx_box = { + .x = output->lx - view->x, + .y = output->ly - view->y, + .width = width, + .height = height + }; + + wlr_xdg_popup_unconstrain_from_box( + popup->wlr_popup, &output_toplevel_sx_box); +} + static struct roots_xdg_popup *popup_create(struct roots_view *view, struct wlr_xdg_popup *wlr_popup) { struct roots_xdg_popup *popup = @@ -66,6 +119,9 @@ static struct roots_xdg_popup *popup_create(struct roots_view *view, wl_signal_add(&wlr_popup->base->events.unmap, &popup->unmap); popup->new_popup.notify = popup_handle_new_popup; wl_signal_add(&wlr_popup->base->events.new_popup, &popup->new_popup); + + popup_unconstrain(popup); + return popup; } diff --git a/types/wlr_xdg_shell.c b/types/wlr_xdg_shell.c index 22d404a7..78b18563 100644 --- a/types/wlr_xdg_shell.c +++ b/types/wlr_xdg_shell.c @@ -1608,6 +1608,268 @@ struct wlr_surface *wlr_xdg_surface_surface_at( return wlr_surface_surface_at(surface->surface, sx, sy, sub_x, sub_y); } +void wlr_xdg_popup_get_anchor_point(struct wlr_xdg_popup *popup, + int *root_sx, int *root_sy) { + struct wlr_box rect = popup->positioner.anchor_rect; + enum xdg_positioner_anchor anchor = popup->positioner.anchor; + int sx = 0, sy = 0; + + if (anchor == XDG_POSITIONER_ANCHOR_NONE) { + sx = (rect.x + rect.width) / 2; + sy = (rect.y + rect.height) / 2; + } else if (anchor == XDG_POSITIONER_ANCHOR_TOP) { + sx = (rect.x + rect.width) / 2; + sy = rect.y; + } else if (anchor == XDG_POSITIONER_ANCHOR_BOTTOM) { + sx = (rect.x + rect.width) / 2; + sy = rect.y + rect.height; + } else if (anchor == XDG_POSITIONER_ANCHOR_LEFT) { + sx = rect.x; + sy = (rect.y + rect.height) / 2; + } else if (anchor == XDG_POSITIONER_ANCHOR_RIGHT) { + sx = rect.x + rect.width; + sy = (rect.y + rect.height) / 2; + } else if (anchor == (XDG_POSITIONER_ANCHOR_TOP | + XDG_POSITIONER_ANCHOR_LEFT)) { + sx = rect.x; + sy = rect.y; + } else if (anchor == (XDG_POSITIONER_ANCHOR_TOP | + XDG_POSITIONER_ANCHOR_RIGHT)) { + sx = rect.x + rect.width; + sy = rect.y; + } else if (anchor == (XDG_POSITIONER_ANCHOR_BOTTOM | + XDG_POSITIONER_ANCHOR_LEFT)) { + sx = rect.x; + sy = rect.y + rect.height; + } else if (anchor == (XDG_POSITIONER_ANCHOR_BOTTOM | + XDG_POSITIONER_ANCHOR_RIGHT)) { + sx = rect.x + rect.width; + sy = rect.y + rect.height; + } + + *root_sx = sx; + *root_sy = sy; +} + +void wlr_xdg_popup_get_toplevel_coords(struct wlr_xdg_popup *popup, + int popup_sx, int popup_sy, int *toplevel_sx, int *toplevel_sy) { + assert(strcmp(popup->parent->role, wlr_desktop_xdg_toplevel_role) == 0 + || strcmp(popup->parent->role, wlr_desktop_xdg_popup_role) == 0); + struct wlr_xdg_surface *parent = popup->parent->role_data; + while (parent != NULL && parent->role == WLR_XDG_SURFACE_ROLE_POPUP) { + popup_sx += parent->popup->geometry.x; + popup_sy += parent->popup->geometry.y; + parent = parent->popup->parent->role_data; + } + + assert(parent); + + *toplevel_sx = popup_sx + parent->geometry.x; + *toplevel_sy = popup_sy + parent->geometry.y; + +} + +static void wlr_xdg_popup_box_constraints(struct wlr_xdg_popup *popup, + struct wlr_box *toplevel_sx_box, int *offset_x, int *offset_y) { + int popup_width = popup->geometry.width; + int popup_height = popup->geometry.height; + int anchor_sx = 0, anchor_sy = 0; + wlr_xdg_popup_get_anchor_point(popup, &anchor_sx, &anchor_sy); + int popup_sx = 0, popup_sy = 0; + wlr_xdg_popup_get_toplevel_coords(popup, popup->geometry.x, + popup->geometry.y, &popup_sx, &popup_sy); + *offset_x = 0, *offset_y = 0; + + if (popup_sx < toplevel_sx_box->x) { + *offset_x = toplevel_sx_box->x - popup_sx; + } else if (popup_sx + popup_width > + toplevel_sx_box->x + toplevel_sx_box->width) { + *offset_x = toplevel_sx_box->x + toplevel_sx_box->width - + (popup_sx + popup_width); + } + + if (popup_sy < toplevel_sx_box->y) { + *offset_y = toplevel_sx_box->y - popup_sy; + } else if (popup_sy + popup_height > + toplevel_sx_box->y + toplevel_sx_box->height) { + *offset_y = toplevel_sx_box->y + toplevel_sx_box->height - + (popup_sy + popup_height); + } +} + +static bool wlr_xdg_popup_unconstrain_flip(struct wlr_xdg_popup *popup, + struct wlr_box *toplevel_sx_box) { + int offset_x = 0, offset_y = 0; + wlr_xdg_popup_box_constraints(popup, toplevel_sx_box, + &offset_x, &offset_y); + + if (!offset_x && !offset_y) { + return true; + } + + bool flip_x = offset_x && + (popup->positioner.constraint_adjustment & + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_X); + + bool flip_y = offset_y && + (popup->positioner.constraint_adjustment & + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y); + + if (flip_x) { + wlr_positioner_invert_x(&popup->positioner); + } + if (flip_y) { + wlr_positioner_invert_y(&popup->positioner); + } + + popup->geometry = + wlr_xdg_positioner_get_geometry(&popup->positioner); + + wlr_xdg_popup_box_constraints(popup, toplevel_sx_box, + &offset_x, &offset_y); + + if (!offset_x && !offset_y) { + // no longer constrained + return true; + } + + // revert the positioner back if it didn't fix it and go to the next part + if (flip_x) { + wlr_positioner_invert_x(&popup->positioner); + } + if (flip_y) { + wlr_positioner_invert_y(&popup->positioner); + } + + popup->geometry = + wlr_xdg_positioner_get_geometry(&popup->positioner); + + return false; +} + +static bool wlr_xdg_popup_unconstrain_slide(struct wlr_xdg_popup *popup, + struct wlr_box *toplevel_sx_box) { + int offset_x = 0, offset_y = 0; + wlr_xdg_popup_box_constraints(popup, toplevel_sx_box, + &offset_x, &offset_y); + + if (!offset_x && !offset_y) { + return true; + } + + bool slide_x = offset_x && + (popup->positioner.constraint_adjustment & + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X); + + bool slide_y = offset_x && + (popup->positioner.constraint_adjustment & + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y); + + if (slide_x) { + popup->geometry.x += offset_x; + } + + if (slide_y) { + popup->geometry.y += offset_y; + } + + int toplevel_x = 0, toplevel_y = 0; + wlr_xdg_popup_get_toplevel_coords(popup, popup->geometry.x, + popup->geometry.y, &toplevel_x, &toplevel_y); + + if (slide_x && toplevel_x < toplevel_sx_box->x) { + popup->geometry.x += toplevel_sx_box->x - toplevel_x; + } + if (slide_y && toplevel_y < toplevel_sx_box->y) { + popup->geometry.y += toplevel_sx_box->y - toplevel_y; + } + + wlr_xdg_popup_box_constraints(popup, toplevel_sx_box, + &offset_x, &offset_y); + + return !offset_x && !offset_y; +} + +static bool wlr_xdg_popup_unconstrain_resize(struct wlr_xdg_popup *popup, + struct wlr_box *toplevel_sx_box) { + int offset_x, offset_y; + wlr_xdg_popup_box_constraints(popup, toplevel_sx_box, + &offset_x, &offset_y); + + if (!offset_x && !offset_y) { + return true; + } + + bool resize_x = offset_x && + (popup->positioner.constraint_adjustment & + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_X); + + bool resize_y = offset_x && + (popup->positioner.constraint_adjustment & + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_Y); + + if (resize_x) { + popup->geometry.width -= offset_x; + } + if (resize_y) { + popup->geometry.height -= offset_y; + } + + wlr_xdg_popup_box_constraints(popup, toplevel_sx_box, + &offset_y, &offset_y); + + return !offset_x && !offset_y; +} + +void wlr_xdg_popup_unconstrain_from_box(struct wlr_xdg_popup *popup, + struct wlr_box *toplevel_sx_box) { + if (wlr_xdg_popup_unconstrain_flip(popup, toplevel_sx_box)) { + return; + } + if (wlr_xdg_popup_unconstrain_slide(popup, toplevel_sx_box)) { + return; + } + if (wlr_xdg_popup_unconstrain_resize(popup, toplevel_sx_box)) { + return; + } +} + +void wlr_positioner_invert_x(struct wlr_xdg_positioner *positioner) { + if (positioner->anchor & XDG_POSITIONER_ANCHOR_LEFT) { + positioner->anchor &= ~XDG_POSITIONER_ANCHOR_LEFT; + positioner->anchor |= XDG_POSITIONER_ANCHOR_RIGHT; + } else if (positioner->anchor & XDG_POSITIONER_ANCHOR_RIGHT) { + positioner->anchor &= ~XDG_POSITIONER_ANCHOR_RIGHT; + positioner->anchor |= XDG_POSITIONER_ANCHOR_LEFT; + } + + if (positioner->gravity & XDG_POSITIONER_GRAVITY_RIGHT) { + positioner->gravity &= ~XDG_POSITIONER_GRAVITY_RIGHT; + positioner->gravity |= XDG_POSITIONER_GRAVITY_LEFT; + } else if (positioner->gravity & XDG_POSITIONER_GRAVITY_LEFT) { + positioner->gravity &= ~XDG_POSITIONER_GRAVITY_LEFT; + positioner->gravity |= XDG_POSITIONER_GRAVITY_RIGHT; + } +} + +void wlr_positioner_invert_y( + struct wlr_xdg_positioner *positioner) { + if (positioner->anchor & XDG_POSITIONER_ANCHOR_TOP) { + positioner->anchor &= ~XDG_POSITIONER_ANCHOR_TOP; + positioner->anchor |= XDG_POSITIONER_ANCHOR_BOTTOM; + } else if (positioner->anchor & XDG_POSITIONER_ANCHOR_BOTTOM) { + positioner->anchor &= ~XDG_POSITIONER_ANCHOR_BOTTOM; + positioner->anchor |= XDG_POSITIONER_ANCHOR_TOP; + } + + if (positioner->gravity & XDG_POSITIONER_GRAVITY_TOP) { + positioner->gravity &= ~XDG_POSITIONER_GRAVITY_TOP; + positioner->gravity |= XDG_POSITIONER_GRAVITY_BOTTOM; + } else if (positioner->gravity & XDG_POSITIONER_GRAVITY_BOTTOM) { + positioner->gravity &= ~XDG_POSITIONER_GRAVITY_BOTTOM; + positioner->gravity |= XDG_POSITIONER_GRAVITY_TOP; + } +} struct xdg_surface_iterator_data { wlr_surface_iterator_func_t user_iterator; From 52baf3dd8acee6e288ae1623860b93b116242367 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Sat, 14 Apr 2018 21:56:19 -0400 Subject: [PATCH 06/14] Move get_geometry call back to xdg-shell --- types/wlr_layer_shell.c | 1 - types/wlr_xdg_shell.c | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/types/wlr_layer_shell.c b/types/wlr_layer_shell.c index 5c709713..9a18bebd 100644 --- a/types/wlr_layer_shell.c +++ b/types/wlr_layer_shell.c @@ -142,7 +142,6 @@ static void layer_surface_handle_get_popup(struct wl_client *client, assert(popup_surface->role == WLR_XDG_SURFACE_ROLE_POPUP); struct wlr_xdg_popup *popup = popup_surface->popup; popup->parent = parent->surface; - popup->geometry = wlr_xdg_positioner_get_geometry(&popup->positioner); wl_list_insert(&parent->popups, &popup->link); wlr_signal_emit_safe(&parent->events.new_popup, popup); } diff --git a/types/wlr_xdg_shell.c b/types/wlr_xdg_shell.c index 78b18563..4c8c9983 100644 --- a/types/wlr_xdg_shell.c +++ b/types/wlr_xdg_shell.c @@ -605,6 +605,8 @@ static void xdg_surface_handle_get_popup(struct wl_client *client, // positioner properties memcpy(&surface->popup->positioner, &positioner->attrs, sizeof(struct wlr_xdg_positioner)); + surface->popup->geometry = + wlr_xdg_positioner_get_geometry(&positioner->attrs); wl_resource_set_implementation(surface->popup->resource, &xdg_popup_implementation, surface, @@ -612,8 +614,6 @@ static void xdg_surface_handle_get_popup(struct wl_client *client, if (parent) { surface->popup->parent = parent->surface; - surface->popup->geometry = - wlr_xdg_positioner_get_geometry(&positioner->attrs); wl_list_insert(&parent->popups, &surface->popup->link); wlr_signal_emit_safe(&parent->events.new_popup, surface->popup); } From 941f88ce2367164bb78769ad832d8a748d7e9aca Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Sat, 14 Apr 2018 22:21:05 -0400 Subject: [PATCH 07/14] Fix popup positioning & double popups --- examples/layer-shell.c | 3 +-- types/wlr_layer_shell.c | 37 +++++++++++++++++++++++++++++++------ 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/examples/layer-shell.c b/examples/layer-shell.c index 56fa28f7..1824c50c 100644 --- a/examples/layer-shell.c +++ b/examples/layer-shell.c @@ -175,8 +175,7 @@ static void create_popup() { xdg_positioner_set_size(xdg_positioner, 256, 256); xdg_positioner_set_offset(xdg_positioner, 0, 0); xdg_positioner_set_anchor_rect(xdg_positioner, cur_x, cur_y, 1, 1); - xdg_positioner_set_anchor(xdg_positioner, XDG_POSITIONER_ANCHOR_TOP_LEFT); - xdg_positioner_set_gravity(xdg_positioner, XDG_POSITIONER_GRAVITY_TOP_LEFT); + xdg_positioner_set_anchor(xdg_positioner, XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT); popup = xdg_surface_get_popup(xdg_surface, NULL, xdg_positioner); assert(popup); diff --git a/types/wlr_layer_shell.c b/types/wlr_layer_shell.c index 9a18bebd..c61556bf 100644 --- a/types/wlr_layer_shell.c +++ b/types/wlr_layer_shell.c @@ -465,6 +465,33 @@ static void layer_surface_iterator(struct wlr_surface *surface, iter_data->user_data); } +static void xdg_surface_for_each_surface(struct wlr_xdg_surface *surface, + int x, int y, wlr_surface_iterator_func_t iterator, void *user_data) { + struct layer_surface_iterator_data data = { + .user_iterator = iterator, + .user_data = user_data, + .x = x, .y = y, + }; + wlr_surface_for_each_surface( + surface->surface, layer_surface_iterator, &data); + + struct wlr_xdg_popup *popup_state; + wl_list_for_each(popup_state, &surface->popups, link) { + struct wlr_xdg_surface *popup = popup_state->base; + if (!popup->configured) { + continue; + } + + double popup_sx, popup_sy; + wlr_xdg_surface_popup_get_position(popup, &popup_sx, &popup_sy); + + xdg_surface_for_each_surface(popup, + x + popup_sx, + y + popup_sy, + iterator, user_data); + } +} + static void layer_surface_for_each_surface(struct wlr_layer_surface *surface, int x, int y, wlr_surface_iterator_func_t iterator, void *user_data) { struct layer_surface_iterator_data data = { @@ -483,13 +510,11 @@ static void layer_surface_for_each_surface(struct wlr_layer_surface *surface, } double popup_sx, popup_sy; - popup_sx = popup->geometry.x; - popup_sy = popup->geometry.y; + popup_sx = popup->popup->geometry.x - popup->geometry.x; + popup_sy = popup->popup->geometry.y - popup->geometry.y; - iterator(popup->surface, data.x + popup_sx, - data.y + popup_sy, user_data); - - wlr_xdg_surface_for_each_surface(popup, layer_surface_iterator, &data); + xdg_surface_for_each_surface(popup, + popup_sx, popup_sy, iterator, user_data); } } From d1e82a8ede200862453e50cb77bf7cd6e1e73d33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20G=C3=BCnther?= Date: Thu, 19 Apr 2018 12:26:18 +0200 Subject: [PATCH 08/14] examples: Drop unused variable --- examples/layer-shell.c | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/layer-shell.c b/examples/layer-shell.c index 1824c50c..718fbe0a 100644 --- a/examples/layer-shell.c +++ b/examples/layer-shell.c @@ -39,7 +39,6 @@ struct wlr_egl_surface *egl_surface; struct wl_callback *frame_callback; static uint32_t output = UINT32_MAX; -struct xdg_surface *popup_surface; struct xdg_popup *popup; static uint32_t layer = ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND; From d4cb33c9fccc219c7d66e634618fe07c6daaf18d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20G=C3=BCnther?= Date: Thu, 19 Apr 2018 15:30:39 +0200 Subject: [PATCH 09/14] rootston: Let layer_surface_at look at popups This allows them to receive input as well. --- rootston/desktop.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/rootston/desktop.c b/rootston/desktop.c index 0949b5db..6ac665bc 100644 --- a/rootston/desktop.c +++ b/rootston/desktop.c @@ -641,11 +641,24 @@ static struct wlr_surface *layer_surface_at(struct roots_output *output, struct wl_list *layer, double ox, double oy, double *sx, double *sy) { struct roots_layer_surface *roots_surface; wl_list_for_each_reverse(roots_surface, layer, link) { - struct wlr_surface *wlr_surface = - roots_surface->layer_surface->surface; - double _sx = ox - roots_surface->geo.x; - double _sy = oy - roots_surface->geo.y; - // TODO: Test popups/subsurfaces + struct wlr_surface *wlr_surface; + double _sx, _sy; + struct wlr_xdg_popup *popup; + wl_list_for_each(popup, &roots_surface->layer_surface->popups, link) { + wlr_surface = popup->base->surface; + _sx = ox - roots_surface->geo.x - popup->geometry.x; + _sy = oy - roots_surface->geo.y - popup->geometry.y; + if (wlr_surface_point_accepts_input(wlr_surface, _sx, _sy)) { + *sx = _sx; + *sy = _sy; + return wlr_surface; + } + // TODO: popups can have popups + } + // TODO: Test subsurfaces + wlr_surface = roots_surface->layer_surface->surface; + _sx = ox - roots_surface->geo.x; + _sy = oy - roots_surface->geo.y; if (wlr_surface_point_accepts_input(wlr_surface, _sx, _sy)) { *sx = _sx; *sy = _sy; From ad22e023103fc737188cac0bb468eedbc4d3982e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20G=C3=BCnther?= Date: Thu, 19 Apr 2018 18:25:19 +0200 Subject: [PATCH 10/14] rootston: Damage layer-shell popups --- include/rootston/layers.h | 10 +++++ rootston/layer_shell.c | 78 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+) diff --git a/include/rootston/layers.h b/include/rootston/layers.h index 0e5164bb..9eab53ca 100644 --- a/include/rootston/layers.h +++ b/include/rootston/layers.h @@ -14,11 +14,21 @@ struct roots_layer_surface { struct wl_listener unmap; struct wl_listener surface_commit; struct wl_listener output_destroy; + struct wl_listener new_popup; bool configured; struct wlr_box geo; }; +struct roots_layer_popup { + struct roots_layer_surface *parent; + struct wlr_xdg_popup *wlr_popup; + struct wl_listener map; + struct wl_listener unmap; + struct wl_listener destroy; + struct wl_listener commit; +}; + struct roots_output; void arrange_layers(struct roots_output *output); diff --git a/rootston/layer_shell.c b/rootston/layer_shell.c index b6f7ba4c..071467e0 100644 --- a/rootston/layer_shell.c +++ b/rootston/layer_shell.c @@ -292,6 +292,82 @@ static void handle_unmap(struct wl_listener *listener, void *data) { unmap(layer->layer_surface); } +static void popup_handle_map(struct wl_listener *listener, void *data) { + struct roots_layer_popup *popup = wl_container_of(listener, popup, map); + struct roots_layer_surface *layer = popup->parent; + struct wlr_output *wlr_output = layer->layer_surface->output; + struct roots_output *output = wlr_output->data; + struct wlr_box geom; + memcpy(&geom, &popup->wlr_popup->geometry, sizeof(struct wlr_box)); + geom.x += layer->geo.x; + geom.y += layer->geo.y; + wlr_output_damage_add_box(output->damage, &geom); +} + +static void popup_handle_unmap(struct wl_listener *listener, void *data) { + struct roots_layer_popup *popup = wl_container_of(listener, popup, unmap); + struct roots_layer_surface *layer = popup->parent; + struct wlr_output *wlr_output = layer->layer_surface->output; + struct roots_output *output = wlr_output->data; + struct wlr_box geom; + memcpy(&geom, &popup->wlr_popup->geometry, sizeof(struct wlr_box)); + geom.x += layer->geo.x; + geom.y += layer->geo.y; + wlr_output_damage_add_box(output->damage, &geom); +} + +static void popup_handle_commit(struct wl_listener *listener, void *data) { + struct roots_layer_popup *popup = wl_container_of(listener, popup, commit); + struct roots_layer_surface *layer = popup->parent; + struct wlr_output *wlr_output = layer->layer_surface->output; + struct roots_output *output = wlr_output->data; + struct wlr_box geom; + memcpy(&geom, &popup->wlr_popup->geometry, sizeof(struct wlr_box)); + geom.x += layer->geo.x; + geom.y += layer->geo.y; + wlr_output_damage_add_box(output->damage, &geom); +} + +static void popup_handle_destroy(struct wl_listener *listener, void *data) { + struct roots_layer_popup *popup = + wl_container_of(listener, popup, destroy); + + wl_list_remove(&popup->map.link); + wl_list_remove(&popup->unmap.link); + wl_list_remove(&popup->destroy.link); + wl_list_remove(&popup->commit.link); + free(popup); +} + +static struct roots_layer_popup *popup_create(struct roots_layer_surface *parent, + struct wlr_xdg_popup *wlr_popup) { + struct roots_layer_popup *popup = + calloc(1, sizeof(struct roots_layer_popup)); + if (popup == NULL) { + return NULL; + } + popup->wlr_popup = wlr_popup; + popup->parent = parent; + popup->map.notify = popup_handle_map; + wl_signal_add(&wlr_popup->base->events.map, &popup->map); + popup->unmap.notify = popup_handle_unmap; + wl_signal_add(&wlr_popup->base->events.unmap, &popup->unmap); + popup->destroy.notify = popup_handle_destroy; + wl_signal_add(&wlr_popup->base->events.destroy, &popup->destroy); + popup->commit.notify = popup_handle_commit; + wl_signal_add(&wlr_popup->base->surface->events.commit, &popup->commit); + /* TODO: popups can have popups, see xdg_shell::popup_create */ + + return popup; +} + +static void handle_new_popup(struct wl_listener *listener, void *data) { + struct roots_layer_surface *roots_layer_surface = + wl_container_of(listener, roots_layer_surface, new_popup); + struct wlr_xdg_popup *wlr_popup = data; + popup_create(roots_layer_surface, wlr_popup); +} + void handle_layer_shell_surface(struct wl_listener *listener, void *data) { struct wlr_layer_surface *layer_surface = data; struct roots_desktop *desktop = @@ -338,6 +414,8 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { wl_signal_add(&layer_surface->events.map, &roots_surface->map); roots_surface->unmap.notify = handle_unmap; wl_signal_add(&layer_surface->events.unmap, &roots_surface->unmap); + roots_surface->new_popup.notify = handle_new_popup; + wl_signal_add(&layer_surface->events.new_popup, &roots_surface->new_popup); // TODO: Listen for subsurfaces roots_surface->layer_surface = layer_surface; From 421652a450eca9910aec0491760a0ce7ff69ee24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20G=C3=BCnther?= Date: Fri, 20 Apr 2018 15:05:48 +0200 Subject: [PATCH 11/14] examples: Animate popup in layer-shell --- examples/layer-shell.c | 74 ++++++++++++++++++++++++++++++++---------- 1 file changed, 57 insertions(+), 17 deletions(-) diff --git a/examples/layer-shell.c b/examples/layer-shell.c index 718fbe0a..115b72cb 100644 --- a/examples/layer-shell.c +++ b/examples/layer-shell.c @@ -40,6 +40,12 @@ struct wl_callback *frame_callback; static uint32_t output = UINT32_MAX; struct xdg_popup *popup; +struct wl_surface *popup_wl_surface; +struct wl_egl_window *popup_egl_window; +static uint32_t popup_width = 256, popup_height = 256; +struct wlr_egl_surface *popup_egl_surface; +struct wl_callback *popup_frame_callback; +float popup_alpha = 1.0; static uint32_t layer = ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND; static uint32_t anchor = 0; @@ -64,6 +70,7 @@ static struct { } demo; static void draw(void); +static void draw_popup(void); static void surface_frame_callback( void *data, struct wl_callback *cb, uint32_t time) { @@ -76,9 +83,19 @@ static struct wl_callback_listener frame_listener = { .done = surface_frame_callback }; +static void popup_surface_frame_callback( + void *data, struct wl_callback *cb, uint32_t time) { + wl_callback_destroy(cb); + popup_frame_callback = NULL; + draw_popup(); +} + +static struct wl_callback_listener popup_frame_listener = { + .done = popup_surface_frame_callback +}; + static void draw(void) { eglMakeCurrent(egl.display, egl_surface, egl_surface, egl.context); - struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); @@ -132,10 +149,28 @@ static void draw(void) { demo.last_frame = ts; } +static void draw_popup() { + static float alpha_mod = -0.01; + + eglMakeCurrent(egl.display, popup_egl_surface, popup_egl_surface, egl.context); + glViewport(0, 0, popup_width, popup_height); + glClearColor(0.5f, 0.5f, 0.5f, popup_alpha); + popup_alpha += alpha_mod; + if (popup_alpha < 0.01 || popup_alpha >= 1.0f) { + alpha_mod *= -1.0; + } + glClear(GL_COLOR_BUFFER_BIT); + + popup_frame_callback = wl_surface_frame(popup_wl_surface); + assert(popup_frame_callback); + wl_callback_add_listener(popup_frame_callback, &popup_frame_listener, NULL); + eglSwapBuffers(egl.display, popup_egl_surface); + wl_surface_commit(popup_wl_surface); +} + static void xdg_surface_handle_configure(void *data, struct xdg_surface *xdg_surface, uint32_t serial) { xdg_surface_ack_configure(xdg_surface, serial); - // Whatever } static const struct xdg_surface_listener xdg_surface_listener = { @@ -146,10 +181,17 @@ static void xdg_popup_configure(void *data, struct xdg_popup *xdg_popup, int32_t x, int32_t y, int32_t width, int32_t height) { wlr_log(L_DEBUG, "Popup configured %dx%d@%d,%d", width, height, x, y); + popup_width = width; + popup_height = height; + if (popup_egl_window) { + wl_egl_window_resize(popup_egl_window, width, height, 0, 0); + } } static void xdg_popup_done(void *data, struct xdg_popup *xdg_popup) { - // We leak the surface, but who cares + eglDestroySurface(egl.display, popup_egl_surface); + wl_egl_window_destroy(popup_egl_window); + wl_surface_destroy(popup_wl_surface); xdg_popup_destroy(popup); popup = NULL; } @@ -171,7 +213,7 @@ static void create_popup() { xdg_wm_base_create_positioner(xdg_wm_base); assert(xdg_surface && xdg_positioner); - xdg_positioner_set_size(xdg_positioner, 256, 256); + xdg_positioner_set_size(xdg_positioner, popup_width, popup_height); xdg_positioner_set_offset(xdg_positioner, 0, 0); xdg_positioner_set_anchor_rect(xdg_positioner, cur_x, cur_y, 1, 1); xdg_positioner_set_anchor(xdg_positioner, XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT); @@ -189,17 +231,12 @@ static void create_popup() { xdg_positioner_destroy(xdg_positioner); - struct wl_egl_window *egl_window; - struct wlr_egl_surface *egl_surface; - egl_window = wl_egl_window_create(surface, 256, 256); - assert(egl_window); - egl_surface = wlr_egl_create_surface(&egl, egl_window); - assert(egl_surface); - - eglMakeCurrent(egl.display, egl_surface, egl_surface, egl.context); - glClearColor(0.5f, 0.5f, 0.5f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT); - eglSwapBuffers(egl.display, egl_surface); + popup_wl_surface = surface; + popup_egl_window = wl_egl_window_create(surface, popup_width, popup_height); + assert(popup_egl_window); + popup_egl_surface = wlr_egl_create_surface(&egl, popup_egl_window); + assert(popup_egl_surface); + draw_popup(); } static void layer_surface_configure(void *data, @@ -252,12 +289,15 @@ static void wl_pointer_motion(void *data, struct wl_pointer *wl_pointer, static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t state) { if (state == WL_POINTER_BUTTON_STATE_PRESSED) { - buttons++; if (button == BTN_RIGHT) { create_popup(); + } else { + buttons++; } } else { - buttons--; + if (button != BTN_RIGHT) { + buttons--; + } } } From 57cc4c319d1d7002dc281e623c6716e619e21fdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20G=C3=BCnther?= Date: Fri, 20 Apr 2018 17:48:50 +0200 Subject: [PATCH 12/14] rootston: Send frame_done for popups too Thanks @emersion --- rootston/output.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/rootston/output.c b/rootston/output.c index 48b20466..225b7213 100644 --- a/rootston/output.c +++ b/rootston/output.c @@ -379,6 +379,10 @@ static void layers_send_done( wl_list_for_each(roots_surface, &output->layers[i], link) { struct wlr_layer_surface *layer = roots_surface->layer_surface; wlr_surface_send_frame_done(layer->surface, when); + struct wlr_xdg_popup *popup; + wl_list_for_each(popup, &roots_surface->layer_surface->popups, link) { + wlr_surface_send_frame_done(popup->base->surface, when); + } } } } From 5209c797026cc5c97d2abc796b70d43ceaaac519 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20G=C3=BCnther?= Date: Mon, 23 Apr 2018 09:53:46 +0200 Subject: [PATCH 13/14] examples: Handle input entering popup in layer-shell Change the cursor when entering the popup and make mouse buttons change the red component of the square. This makes sure we can handle input correctly. --- examples/layer-shell.c | 61 ++++++++++++++++++++++++++++++------------ 1 file changed, 44 insertions(+), 17 deletions(-) diff --git a/examples/layer-shell.c b/examples/layer-shell.c index 115b72cb..721fd69e 100644 --- a/examples/layer-shell.c +++ b/examples/layer-shell.c @@ -45,7 +45,7 @@ struct wl_egl_window *popup_egl_window; static uint32_t popup_width = 256, popup_height = 256; struct wlr_egl_surface *popup_egl_surface; struct wl_callback *popup_frame_callback; -float popup_alpha = 1.0; +float popup_alpha = 1.0, popup_red = 0.5f; static uint32_t layer = ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND; static uint32_t anchor = 0; @@ -59,9 +59,9 @@ static double frame = 0; static int cur_x = -1, cur_y = -1; static int buttons = 0; -struct wl_cursor_theme *cursor_theme; struct wl_cursor_image *cursor_image; -struct wl_surface *cursor_surface; +struct wl_cursor_image *popup_cursor_image; +struct wl_surface *cursor_surface, *input_surface; static struct { struct timespec last_frame; @@ -154,7 +154,7 @@ static void draw_popup() { eglMakeCurrent(egl.display, popup_egl_surface, popup_egl_surface, egl.context); glViewport(0, 0, popup_width, popup_height); - glClearColor(0.5f, 0.5f, 0.5f, popup_alpha); + glClearColor(popup_red, 0.5f, 0.5f, popup_alpha); popup_alpha += alpha_mod; if (popup_alpha < 0.01 || popup_alpha >= 1.0f) { alpha_mod *= -1.0; @@ -267,11 +267,20 @@ struct zwlr_layer_surface_v1_listener layer_surface_listener = { static void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface, wl_fixed_t surface_x, wl_fixed_t surface_y) { + struct wl_cursor_image *image; + if (surface == popup_wl_surface) { + image = popup_cursor_image; + } else { + image = cursor_image; + } wl_surface_attach(cursor_surface, - wl_cursor_image_get_buffer(cursor_image), 0, 0); - wl_pointer_set_cursor(wl_pointer, serial, cursor_surface, - cursor_image->hotspot_x, cursor_image->hotspot_y); + wl_cursor_image_get_buffer(image), 0, 0); + wl_surface_damage(cursor_surface, 1, 0, + image->width, image->height); wl_surface_commit(cursor_surface); + wl_pointer_set_cursor(wl_pointer, serial, cursor_surface, + image->hotspot_x, image->hotspot_y); + input_surface = surface; } static void wl_pointer_leave(void *data, struct wl_pointer *wl_pointer, @@ -288,16 +297,28 @@ static void wl_pointer_motion(void *data, struct wl_pointer *wl_pointer, static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t state) { - if (state == WL_POINTER_BUTTON_STATE_PRESSED) { - if (button == BTN_RIGHT) { - create_popup(); + if (input_surface == wl_surface) { + if (state == WL_POINTER_BUTTON_STATE_PRESSED) { + if (button == BTN_RIGHT) { + create_popup(); + } else { + buttons++; + } } else { - buttons++; + if (button != BTN_RIGHT) { + buttons--; + } + } + } else if (input_surface == popup_wl_surface) { + if (state == WL_POINTER_BUTTON_STATE_PRESSED) { + if (button == BTN_LEFT && popup_red <= 0.9f) { + popup_red += 0.1; + } else if (button == BTN_RIGHT && popup_red >= 0.1f) { + popup_red -= 0.1; + } } } else { - if (button != BTN_RIGHT) { - buttons--; - } + assert(false && "Unknown surface"); } } @@ -557,12 +578,18 @@ int main(int argc, char **argv) { return 1; } - cursor_theme = wl_cursor_theme_load(NULL, 16, shm); + struct wl_cursor_theme *cursor_theme = + wl_cursor_theme_load(NULL, 16, shm); assert(cursor_theme); - struct wl_cursor *cursor; - cursor = wl_cursor_theme_get_cursor(cursor_theme, "crosshair"); + struct wl_cursor *cursor = + wl_cursor_theme_get_cursor(cursor_theme, "crosshair"); assert(cursor); cursor_image = cursor->images[0]; + + cursor = wl_cursor_theme_get_cursor(cursor_theme, "tcross"); + assert(cursor); + popup_cursor_image = cursor->images[0]; + cursor_surface = wl_compositor_create_surface(compositor); assert(cursor_surface); From 32e043f996d00428731e9d97f8e45180574681b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20G=C3=BCnther?= Date: Mon, 23 Apr 2018 09:53:46 +0200 Subject: [PATCH 14/14] examples: Allow to close the popup So we can the xdg_popup_destroy path. --- examples/layer-shell.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/examples/layer-shell.c b/examples/layer-shell.c index 721fd69e..0e1d61ca 100644 --- a/examples/layer-shell.c +++ b/examples/layer-shell.c @@ -188,12 +188,20 @@ static void xdg_popup_configure(void *data, struct xdg_popup *xdg_popup, } } -static void xdg_popup_done(void *data, struct xdg_popup *xdg_popup) { +static void popup_destroy() +{ eglDestroySurface(egl.display, popup_egl_surface); wl_egl_window_destroy(popup_egl_window); - wl_surface_destroy(popup_wl_surface); xdg_popup_destroy(popup); + wl_surface_destroy(popup_wl_surface); + popup_wl_surface = NULL; popup = NULL; + popup_egl_window = NULL; +} + +static void xdg_popup_done(void *data, struct xdg_popup *xdg_popup) { + wlr_log(L_DEBUG, "Popup done"); + popup_destroy(); } static const struct xdg_popup_listener xdg_popup_listener = { @@ -300,7 +308,11 @@ static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer, if (input_surface == wl_surface) { if (state == WL_POINTER_BUTTON_STATE_PRESSED) { if (button == BTN_RIGHT) { - create_popup(); + if (popup_wl_surface) { + popup_destroy(); + } else { + create_popup(); + } } else { buttons++; }