diff --git a/examples/layer-shell.c b/examples/layer-shell.c index 245d762f..0e1d61ca 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,14 @@ struct wlr_egl_surface *egl_surface; struct wl_callback *frame_callback; static uint32_t output = UINT32_MAX; +struct xdg_popup *popup; +struct wl_surface *popup_wl_surface; +struct wl_egl_window *popup_egl_window; +static uint32_t popup_width = 256, popup_height = 256; +struct wlr_egl_surface *popup_egl_surface; +struct wl_callback *popup_frame_callback; +float popup_alpha = 1.0, popup_red = 0.5f; + static uint32_t layer = ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND; static uint32_t anchor = 0; static uint32_t width = 256, height = 256; @@ -42,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; @@ -53,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) { @@ -65,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); @@ -121,6 +149,104 @@ 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(popup_red, 0.5f, 0.5f, popup_alpha); + popup_alpha += alpha_mod; + if (popup_alpha < 0.01 || popup_alpha >= 1.0f) { + alpha_mod *= -1.0; + } + glClear(GL_COLOR_BUFFER_BIT); + + popup_frame_callback = wl_surface_frame(popup_wl_surface); + assert(popup_frame_callback); + wl_callback_add_listener(popup_frame_callback, &popup_frame_listener, NULL); + eglSwapBuffers(egl.display, popup_egl_surface); + wl_surface_commit(popup_wl_surface); +} + +static void xdg_surface_handle_configure(void *data, + struct xdg_surface *xdg_surface, uint32_t serial) { + xdg_surface_ack_configure(xdg_surface, serial); +} + +static const struct xdg_surface_listener xdg_surface_listener = { + .configure = xdg_surface_handle_configure, +}; + +static void xdg_popup_configure(void *data, struct xdg_popup *xdg_popup, + int32_t x, int32_t y, int32_t width, int32_t height) { + wlr_log(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 popup_destroy() +{ + eglDestroySurface(egl.display, popup_egl_surface); + wl_egl_window_destroy(popup_egl_window); + xdg_popup_destroy(popup); + wl_surface_destroy(popup_wl_surface); + popup_wl_surface = NULL; + popup = NULL; + popup_egl_window = NULL; +} + +static void xdg_popup_done(void *data, struct xdg_popup *xdg_popup) { + wlr_log(L_DEBUG, "Popup done"); + popup_destroy(); +} + +static const struct xdg_popup_listener xdg_popup_listener = { + .configure = xdg_popup_configure, + .popup_done = xdg_popup_done, +}; + +static void create_popup() { + if (popup) { + return; + } + struct wl_surface *surface = wl_compositor_create_surface(compositor); + assert(xdg_wm_base && surface); + struct xdg_surface *xdg_surface = + xdg_wm_base_get_xdg_surface(xdg_wm_base, surface); + struct xdg_positioner *xdg_positioner = + xdg_wm_base_create_positioner(xdg_wm_base); + assert(xdg_surface && xdg_positioner); + + xdg_positioner_set_size(xdg_positioner, popup_width, popup_height); + xdg_positioner_set_offset(xdg_positioner, 0, 0); + xdg_positioner_set_anchor_rect(xdg_positioner, cur_x, cur_y, 1, 1); + xdg_positioner_set_anchor(xdg_positioner, XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT); + + popup = xdg_surface_get_popup(xdg_surface, NULL, xdg_positioner); + assert(popup); + + zwlr_layer_surface_v1_get_popup(layer_surface, popup); + + xdg_surface_add_listener(xdg_surface, &xdg_surface_listener, NULL); + xdg_popup_add_listener(popup, &xdg_popup_listener, NULL); + + wl_surface_commit(surface); + wl_display_roundtrip(display); + + xdg_positioner_destroy(xdg_positioner); + + popup_wl_surface = surface; + popup_egl_window = wl_egl_window_create(surface, popup_width, popup_height); + assert(popup_egl_window); + popup_egl_surface = wlr_egl_create_surface(&egl, popup_egl_window); + assert(popup_egl_surface); + draw_popup(); +} + static void layer_surface_configure(void *data, struct zwlr_layer_surface_v1 *surface, uint32_t serial, uint32_t w, uint32_t h) { @@ -149,11 +275,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, @@ -170,10 +305,32 @@ 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 (input_surface == wl_surface) { + if (state == WL_POINTER_BUTTON_STATE_PRESSED) { + if (button == BTN_RIGHT) { + if (popup_wl_surface) { + popup_destroy(); + } else { + create_popup(); + } + } else { + buttons++; + } + } else { + if (button != BTN_RIGHT) { + buttons--; + } + } + } else if (input_surface == popup_wl_surface) { + if (state == WL_POINTER_BUTTON_STATE_PRESSED) { + if (button == BTN_LEFT && popup_red <= 0.9f) { + popup_red += 0.1; + } else if (button == BTN_RIGHT && popup_red >= 0.1f) { + popup_red -= 0.1; + } + } } else { - buttons--; + assert(false && "Unknown surface"); } } @@ -277,7 +434,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 +456,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 +567,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; @@ -430,12 +590,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); 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/include/wlr/types/wlr_layer_shell.h b/include/wlr/types/wlr_layer_shell.h index 79b3a4ea..6040478d 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; @@ -107,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/include/wlr/types/wlr_xdg_shell.h b/include/wlr/types/wlr_xdg_shell.h index 5046339a..ead4613a 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,19 +32,38 @@ struct wlr_xdg_client { struct wl_event_source *ping_timer; }; +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; struct wl_list link; struct wl_resource *resource; bool committed; - struct wlr_xdg_surface *parent; + struct wlr_surface *parent; struct wlr_seat *seat; // 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 }; @@ -178,6 +197,14 @@ 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_xdg_surface *wlr_xdg_surface_from_popup_resource( + struct wl_resource *resource); + +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 * amount of time, the ping_timeout event will be emitted. @@ -226,10 +253,51 @@ 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); +/** + * 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/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; 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; diff --git a/rootston/output.c b/rootston/output.c index 791b2819..225b7213 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); } } @@ -377,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); + } } } } 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_layer_shell.c b/types/wlr_layer_shell.c index a567b8bc..c61556bf 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,18 @@ 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_popup_resource(popup_resource); + + assert(popup_surface->role == WLR_XDG_SURFACE_ROLE_POPUP); + struct wlr_xdg_popup *popup = popup_surface->popup; + popup->parent = parent->surface; + 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 +354,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 +362,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); @@ -438,3 +451,74 @@ 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 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 = { + .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->popup->geometry.x - popup->geometry.x; + popup_sy = popup->popup->geometry.y - popup->geometry.y; + + xdg_surface_for_each_surface(popup, + popup_sx, popup_sy, iterator, user_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); +} diff --git a/types/wlr_xdg_shell.c b/types/wlr_xdg_shell.c index 8d424f3b..4c8c9983 100644 --- a/types/wlr_xdg_shell.c +++ b/types/wlr_xdg_shell.c @@ -28,39 +28,16 @@ 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); } -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; @@ -311,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)); @@ -319,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) { @@ -336,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) { @@ -353,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) { @@ -371,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) { @@ -386,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 @@ -422,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), @@ -447,9 +420,8 @@ 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_positioner_get_geometry( + struct wlr_xdg_positioner *positioner) { struct wlr_box geometry = { .x = positioner->offset.x, .y = positioner->offset.y, @@ -457,71 +429,39 @@ static struct wlr_box xdg_positioner_get_geometry( .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 == @@ -529,15 +469,13 @@ static struct wlr_box xdg_positioner_get_geometry( return geometry; } - // TODO: add compositor policy configuration and the code here - return geometry; } 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)); @@ -548,7 +486,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); @@ -563,12 +501,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"); @@ -589,7 +522,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, @@ -608,7 +541,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); } @@ -616,8 +549,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,13 +565,14 @@ 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); - struct wlr_xdg_positioner *positioner = + wlr_xdg_surface_from_resource(parent_resource); + 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"); @@ -663,16 +601,22 @@ 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; + + // positioner properties + memcpy(&surface->popup->positioner, &positioner->attrs, + sizeof(struct wlr_xdg_positioner)); surface->popup->geometry = - xdg_positioner_get_geometry(positioner, surface, parent); - wl_list_insert(&parent->popups, &surface->popup->link); + wlr_xdg_positioner_get_geometry(&positioner->attrs); 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; + wl_list_insert(&parent->popups, &surface->popup->link); + wlr_signal_emit_safe(&parent->events.new_popup, surface->popup); + } } @@ -913,7 +857,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 +873,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 +928,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 +976,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 +994,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 " @@ -1290,6 +1234,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; @@ -1627,11 +1578,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( @@ -1656,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; 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; }