diff --git a/include/wlr/types/wlr_wl_shell.h b/include/wlr/types/wlr_wl_shell.h index 0db99989..4e814817 100644 --- a/include/wlr/types/wlr_wl_shell.h +++ b/include/wlr/types/wlr_wl_shell.h @@ -3,11 +3,13 @@ #include #include +#include struct wlr_wl_shell { struct wl_global *wl_global; struct wl_list wl_resources; struct wl_list surfaces; + struct wl_list popup_grabs; uint32_t ping_timeout; struct { @@ -18,17 +20,25 @@ struct wlr_wl_shell { }; struct wlr_wl_shell_surface_transient_state { - struct wlr_wl_shell_surface *parent; int32_t x; int32_t y; enum wl_shell_surface_transient flags; }; struct wlr_wl_shell_surface_popup_state { - struct wlr_seat_handle *seat_handle; + struct wlr_seat *seat; uint32_t serial; }; +// each seat gets a popup grab +struct wlr_wl_shell_popup_grab { + struct wl_client *client; + struct wlr_seat_pointer_grab pointer_grab; + struct wlr_seat *seat; + struct wl_list popups; + struct wl_list link; // wlr_wl_shell::popup_grabs +}; + enum wlr_wl_shell_surface_state { WLR_WL_SHELL_SURFACE_STATE_NONE, WLR_WL_SHELL_SURFACE_STATE_TOPLEVEL, @@ -41,7 +51,8 @@ struct wlr_wl_shell_surface { struct wl_client *client; struct wl_resource *resource; struct wlr_surface *surface; - struct wl_list link; + bool configured; + struct wl_list link; // wlr_wl_shell::surfaces uint32_t ping_serial; struct wl_event_source *ping_timer; @@ -49,11 +60,18 @@ struct wlr_wl_shell_surface { enum wlr_wl_shell_surface_state state; struct wlr_wl_shell_surface_transient_state *transient_state; struct wlr_wl_shell_surface_popup_state *popup_state; + struct wl_list grab_link; // wlr_wl_shell_popup_grab::popups char *title; char *class; struct wl_listener surface_destroy_listener; + struct wl_listener surface_commit_listener; + + struct wlr_wl_shell_surface *parent; + struct wl_list popup_link; + struct wl_list popups; + bool popup_mapped; struct { struct wl_signal destroy; @@ -101,12 +119,35 @@ struct wlr_wl_shell_surface_set_maximized_event { struct wlr_output *output; }; +/** + * Create a wl_shell for this display. + */ struct wlr_wl_shell *wlr_wl_shell_create(struct wl_display *display); + +/** + * Destroy this surface. + */ void wlr_wl_shell_destroy(struct wlr_wl_shell *wlr_wl_shell); +/** + * Send a ping to the surface. If the surface does not respond with a pong + * within a reasonable amount of time, the ping timeout event will be emitted. + */ void wlr_wl_shell_surface_ping(struct wlr_wl_shell_surface *surface); + +/** + * Request that the surface configure itself to be the given size. + */ void wlr_wl_shell_surface_configure(struct wlr_wl_shell_surface *surface, enum wl_shell_surface_resize edges, int32_t width, int32_t height); -void wlr_wl_shell_surface_popup_done(struct wlr_wl_shell_surface *surface); + +/** + * Find a popup within this surface at the surface-local coordinates. Returns + * the popup and coordinates in the topmost surface coordinate system or NULL if + * no popup is found at that location. + */ +struct wlr_wl_shell_surface *wlr_wl_shell_surface_popup_at( + struct wlr_wl_shell_surface *surface, double sx, double sy, + double *popup_sx, double *popup_sy); #endif diff --git a/rootston/desktop.c b/rootston/desktop.c index bef0fad4..d5cac575 100644 --- a/rootston/desktop.c +++ b/rootston/desktop.c @@ -103,6 +103,12 @@ struct roots_view *view_at(struct roots_desktop *desktop, double lx, double ly, for (int i = desktop->views->length - 1; i >= 0; --i) { struct roots_view *view = desktop->views->items[i]; + if (view->type == ROOTS_WL_SHELL_VIEW && + view->wl_shell_surface->state == + WLR_WL_SHELL_SURFACE_STATE_POPUP) { + continue; + } + double view_sx = lx - view->x; double view_sy = ly - view->y; @@ -138,6 +144,21 @@ struct roots_view *view_at(struct roots_desktop *desktop, double lx, double ly, } } + if (view->type == ROOTS_WL_SHELL_VIEW) { + // TODO: test if this works with rotated views + double popup_sx, popup_sy; + struct wlr_wl_shell_surface *popup = + wlr_wl_shell_surface_popup_at(view->wl_shell_surface, + view_sx, view_sy, &popup_sx, &popup_sy); + + if (popup) { + *sx = view_sx - popup_sx; + *sy = view_sy - popup_sy; + *surface = popup->surface; + return view; + } + } + double sub_x, sub_y; struct wlr_subsurface *subsurface = wlr_surface_subsurface_at(view->wlr_surface, diff --git a/rootston/output.c b/rootston/output.c index d1b2c7de..2c061739 100644 --- a/rootston/output.c +++ b/rootston/output.c @@ -97,13 +97,39 @@ static void render_xdg_v6_popups(struct wlr_xdg_surface_v6 *surface, } } +static void render_wl_shell_surface(struct wlr_wl_shell_surface *surface, struct roots_desktop *desktop, + struct wlr_output *wlr_output, struct timespec *when, double lx, + double ly, float rotation, bool is_child) { + if (is_child || surface->state != WLR_WL_SHELL_SURFACE_STATE_POPUP) { + render_surface(surface->surface, desktop, wlr_output, when, + lx, ly, rotation); + struct wlr_wl_shell_surface *popup; + wl_list_for_each(popup, &surface->popups, popup_link) { + render_wl_shell_surface(popup, desktop, wlr_output, when, + lx + popup->transient_state->x, + ly + popup->transient_state->y, + rotation, true); + } + } +} + static void render_view(struct roots_view *view, struct roots_desktop *desktop, struct wlr_output *wlr_output, struct timespec *when) { - render_surface(view->wlr_surface, desktop, wlr_output, when, - view->x, view->y, view->rotation); - if (view->type == ROOTS_XDG_SHELL_V6_VIEW) { + switch (view->type) { + case ROOTS_XDG_SHELL_V6_VIEW: + render_surface(view->wlr_surface, desktop, wlr_output, when, + view->x, view->y, view->rotation); render_xdg_v6_popups(view->xdg_surface_v6, desktop, wlr_output, when, view->x, view->y, view->rotation); + break; + case ROOTS_WL_SHELL_VIEW: + render_wl_shell_surface(view->wl_shell_surface, desktop, wlr_output, + when, view->x, view->y, view->rotation, false); + break; + case ROOTS_XWAYLAND_VIEW: + render_surface(view->wlr_surface, desktop, wlr_output, when, + view->x, view->y, view->rotation); + break; } } diff --git a/rootston/wl_shell.c b/rootston/wl_shell.c index 009a8c06..34f53c7a 100644 --- a/rootston/wl_shell.c +++ b/rootston/wl_shell.c @@ -73,6 +73,14 @@ static void handle_destroy(struct wl_listener *listener, void *data) { free(roots_surface); } +static int shell_surface_compare_equals(const void *item, const void *cmp_to) { + const struct roots_view *view = item; + if (view->type == ROOTS_WL_SHELL_VIEW && view->wl_shell_surface == cmp_to) { + return 0; + } + return -1; +} + void handle_wl_shell_surface(struct wl_listener *listener, void *data) { struct roots_desktop *desktop = wl_container_of(listener, desktop, wl_shell_surface); @@ -107,7 +115,20 @@ void handle_wl_shell_surface(struct wl_listener *listener, void *data) { struct roots_view *view = calloc(1, sizeof(struct roots_view)); view->type = ROOTS_WL_SHELL_VIEW; - view->x = view->y = 200; + + if (surface->state == WLR_WL_SHELL_SURFACE_STATE_TRANSIENT) { + // we need to map it relative to the parent + int i = + list_seq_find(desktop->views, + shell_surface_compare_equals, surface->parent); + if (i != -1) { + struct roots_view *parent = desktop->views->items[i]; + view->x = parent->x + surface->transient_state->x; + view->y = parent->y + surface->transient_state->y; + } + } else { + view->x = view->y = 200; + } view->wl_shell_surface = surface; view->roots_wl_shell_surface = roots_surface; view->wlr_surface = surface->surface; diff --git a/types/wlr_seat.c b/types/wlr_seat.c index d58b2129..ef81655c 100644 --- a/types/wlr_seat.c +++ b/types/wlr_seat.c @@ -514,8 +514,9 @@ void wlr_seat_pointer_start_grab(struct wlr_seat *wlr_seat, void wlr_seat_pointer_end_grab(struct wlr_seat *wlr_seat) { struct wlr_seat_pointer_grab *grab = wlr_seat->pointer_state.grab; - wlr_seat->pointer_state.grab = wlr_seat->pointer_state.default_grab; - wl_signal_emit(&wlr_seat->events.pointer_grab_end, grab); + if (grab != wlr_seat->pointer_state.default_grab) { + wlr_seat->pointer_state.grab = wlr_seat->pointer_state.default_grab; + } } void wlr_seat_pointer_notify_enter(struct wlr_seat *wlr_seat, diff --git a/types/wlr_wl_shell.c b/types/wlr_wl_shell.c index e1507e2f..022f865a 100644 --- a/types/wlr_wl_shell.c +++ b/types/wlr_wl_shell.c @@ -8,10 +8,94 @@ #include #include #include +#include -static const char *wlr_wl_shell_surface_role = "wl_shell_surface"; +static const char *wlr_wl_shell_surface_role = "wl-shell-surface"; -static void shell_surface_pong(struct wl_client *client, +static void shell_pointer_grab_end(struct wlr_seat_pointer_grab *grab) { + struct wlr_wl_shell_popup_grab *popup_grab = grab->data; + + struct wlr_wl_shell_surface *popup, *tmp = NULL; + wl_list_for_each_safe(popup, tmp, &popup_grab->popups, grab_link) { + if (popup->popup_mapped) { + wl_shell_surface_send_popup_done(popup->resource); + popup->popup_mapped = false; + } + } + + + if (grab->seat->pointer_state.grab == grab) { + wlr_seat_pointer_end_grab(grab->seat); + } +} + +static void shell_pointer_grab_maybe_end(struct wlr_seat_pointer_grab *grab) { + struct wlr_wl_shell_popup_grab *popup_grab = grab->data; + + if (grab->seat->pointer_state.grab != grab) { + return; + } + + bool has_mapped = false; + + struct wlr_wl_shell_surface *popup, *tmp = NULL; + wl_list_for_each_safe(popup, tmp, &popup_grab->popups, grab_link) { + if (popup->popup_mapped) { + has_mapped = true; + break; + } + } + + if (!has_mapped) { + shell_pointer_grab_end(grab); + } +} + +static void shell_pointer_grab_enter(struct wlr_seat_pointer_grab *grab, + struct wlr_surface *surface, double sx, double sy) { + struct wlr_wl_shell_popup_grab *popup_grab = grab->data; + if (wl_resource_get_client(surface->resource) == popup_grab->client) { + wlr_seat_pointer_enter(grab->seat, surface, sx, sy); + } else { + wlr_seat_pointer_clear_focus(grab->seat); + } +} + +static void shell_pointer_grab_motion(struct wlr_seat_pointer_grab *grab, + uint32_t time, double sx, double sy) { + wlr_seat_pointer_send_motion(grab->seat, time, sx, sy); +} + +static uint32_t shell_pointer_grab_button(struct wlr_seat_pointer_grab *grab, + uint32_t time, uint32_t button, uint32_t state) { + uint32_t serial = + wlr_seat_pointer_send_button(grab->seat, time, button, state); + if (serial) { + return serial; + } else { + shell_pointer_grab_end(grab); + return 0; + } +} + +static void shell_pointer_grab_cancel(struct wlr_seat_pointer_grab *grab) { + shell_pointer_grab_end(grab); +} + +static void shell_pointer_grab_axis(struct wlr_seat_pointer_grab *grab, + uint32_t time, enum wlr_axis_orientation orientation, double value) { + wlr_seat_pointer_send_axis(grab->seat, time, orientation, value); +} + +static const struct wlr_pointer_grab_interface shell_pointer_grab_impl = { + .enter = shell_pointer_grab_enter, + .motion = shell_pointer_grab_motion, + .button = shell_pointer_grab_button, + .cancel = shell_pointer_grab_cancel, + .axis = shell_pointer_grab_axis, +}; + +static void shell_surface_protocol_pong(struct wl_client *client, struct wl_resource *resource, uint32_t serial) { wlr_log(L_DEBUG, "got shell surface pong"); struct wlr_wl_shell_surface *surface = wl_resource_get_user_data(resource); @@ -23,7 +107,7 @@ static void shell_surface_pong(struct wl_client *client, surface->ping_serial = 0; } -static void shell_surface_move(struct wl_client *client, +static void shell_surface_protocol_move(struct wl_client *client, struct wl_resource *resource, struct wl_resource *seat_resource, uint32_t serial) { wlr_log(L_DEBUG, "got shell surface move"); @@ -47,7 +131,50 @@ static void shell_surface_move(struct wl_client *client, free(event); } -static void shell_surface_resize(struct wl_client *client, +static struct wlr_wl_shell_popup_grab *shell_popup_grab_from_seat( + struct wlr_wl_shell *shell, struct wlr_seat *seat) { + struct wlr_wl_shell_popup_grab *shell_grab; + wl_list_for_each(shell_grab, &shell->popup_grabs, link) { + if (shell_grab->seat == seat) { + return shell_grab; + } + } + + shell_grab = calloc(1, sizeof(struct wlr_wl_shell_popup_grab)); + if (!shell_grab) { + return NULL; + } + + shell_grab->pointer_grab.data = shell_grab; + shell_grab->pointer_grab.interface = &shell_pointer_grab_impl; + + wl_list_init(&shell_grab->popups); + + wl_list_insert(&shell->popup_grabs, &shell_grab->link); + shell_grab->seat = seat; + + return shell_grab; +} + +static void shell_surface_destroy_popup_state( + struct wlr_wl_shell_surface *surface) { + if (surface->popup_state) { + wl_list_remove(&surface->grab_link); + struct wlr_wl_shell_popup_grab *grab = + shell_popup_grab_from_seat(surface->shell, + surface->popup_state->seat); + if (wl_list_empty(&grab->popups)) { + if (grab->seat->pointer_state.grab == &grab->pointer_grab) { + wlr_seat_pointer_end_grab(grab->seat); + } + } + free(surface->popup_state); + surface->popup_state = NULL; + } +} + + +static void shell_surface_protocol_resize(struct wl_client *client, struct wl_resource *resource, struct wl_resource *seat_resource, uint32_t serial, enum wl_shell_surface_resize edges) { wlr_log(L_DEBUG, "got shell surface resize"); @@ -79,13 +206,13 @@ static void shell_surface_set_state(struct wlr_wl_shell_surface *surface, surface->state = state; free(surface->transient_state); surface->transient_state = transient_state; - free(surface->popup_state); + shell_surface_destroy_popup_state(surface); surface->popup_state = popup_state; wl_signal_emit(&surface->events.set_state, surface); } -static void shell_surface_set_toplevel(struct wl_client *client, +static void shell_surface_protocol_set_toplevel(struct wl_client *client, struct wl_resource *resource) { wlr_log(L_DEBUG, "got shell surface toplevel"); struct wlr_wl_shell_surface *surface = wl_resource_get_user_data(resource); @@ -93,22 +220,56 @@ static void shell_surface_set_toplevel(struct wl_client *client, NULL); } -static void shell_surface_set_transient(struct wl_client *client, +static void shell_surface_popup_set_parent(struct wlr_wl_shell_surface *surface, + struct wlr_wl_shell_surface *parent) { + assert(surface && surface->state == WLR_WL_SHELL_SURFACE_STATE_POPUP); + if (surface->parent == parent) { + return; + } + surface->parent = parent; + if (parent) { + wl_list_remove(&surface->popup_link); + wl_list_insert(&parent->popups, &surface->popup_link); + } +} + +static struct wlr_wl_shell_surface *shell_find_shell_surface( + struct wlr_wl_shell *shell, struct wlr_surface *surface) { + if (surface) { + struct wlr_wl_shell_surface *wl_surface; + wl_list_for_each(wl_surface, &shell->surfaces, link) { + if (wl_surface->surface == surface) { + return wl_surface; + } + } + } + return NULL; +} + +static void shell_surface_protocol_set_transient(struct wl_client *client, struct wl_resource *resource, struct wl_resource *parent_resource, int32_t x, int32_t y, enum wl_shell_surface_transient flags) { wlr_log(L_DEBUG, "got shell surface transient"); struct wlr_wl_shell_surface *surface = wl_resource_get_user_data(resource); - struct wlr_wl_shell_surface *parent = + struct wlr_surface *parent = wl_resource_get_user_data(parent_resource); // TODO: check if parent_resource == NULL? + struct wlr_wl_shell_surface *wl_parent = + shell_find_shell_surface(surface->shell, parent); + + if (!wl_parent) { + return; + } + struct wlr_wl_shell_surface_transient_state *transient_state = calloc(1, sizeof(struct wlr_wl_shell_surface_transient_state)); if (transient_state == NULL) { wl_client_post_no_memory(client); return; } - transient_state->parent = parent; + + surface->parent = wl_parent; transient_state->x = x; transient_state->y = y; transient_state->flags = flags; @@ -117,7 +278,7 @@ static void shell_surface_set_transient(struct wl_client *client, transient_state, NULL); } -static void shell_surface_set_fullscreen(struct wl_client *client, +static void shell_surface_protocol_set_fullscreen(struct wl_client *client, struct wl_resource *resource, enum wl_shell_surface_fullscreen_method method, uint32_t framerate, struct wl_resource *output_resource) { @@ -149,18 +310,35 @@ static void shell_surface_set_fullscreen(struct wl_client *client, free(event); } -static void shell_surface_set_popup(struct wl_client *client, + +static void shell_surface_protocol_set_popup(struct wl_client *client, struct wl_resource *resource, struct wl_resource *seat_resource, uint32_t serial, struct wl_resource *parent_resource, int32_t x, int32_t y, enum wl_shell_surface_transient flags) { - // TODO: do a pointer grab - wlr_log(L_DEBUG, "got shell surface popup"); struct wlr_wl_shell_surface *surface = wl_resource_get_user_data(resource); struct wlr_seat_handle *seat_handle = wl_resource_get_user_data(seat_resource); - struct wlr_wl_shell_surface *parent = + struct wlr_surface *parent = wl_resource_get_user_data(parent_resource); - // TODO: check if parent_resource == NULL? + struct wlr_wl_shell_popup_grab *grab = + shell_popup_grab_from_seat(surface->shell, seat_handle->wlr_seat); + if (!grab) { + wl_client_post_no_memory(client); + return; + } + + struct wlr_wl_shell_surface *wl_parent = + shell_find_shell_surface(surface->shell, parent); + + if (surface->state == WLR_WL_SHELL_SURFACE_STATE_POPUP) { + surface->transient_state->x = x; + surface->transient_state->y = y; + shell_surface_popup_set_parent(surface, wl_parent); + grab->client = surface->client; + surface->popup_mapped = true; + wlr_seat_pointer_start_grab(seat_handle->wlr_seat, &grab->pointer_grab); + return; + } struct wlr_wl_shell_surface_transient_state *transient_state = calloc(1, sizeof(struct wlr_wl_shell_surface_transient_state)); @@ -168,7 +346,6 @@ static void shell_surface_set_popup(struct wl_client *client, wl_client_post_no_memory(client); return; } - transient_state->parent = parent; transient_state->x = x; transient_state->y = y; transient_state->flags = flags; @@ -180,14 +357,20 @@ static void shell_surface_set_popup(struct wl_client *client, wl_client_post_no_memory(client); return; } - popup_state->seat_handle = seat_handle; + popup_state->seat = seat_handle->wlr_seat; popup_state->serial = serial; shell_surface_set_state(surface, WLR_WL_SHELL_SURFACE_STATE_POPUP, transient_state, popup_state); + + shell_surface_popup_set_parent(surface, wl_parent); + grab->client = surface->client; + wl_list_insert(&grab->popups, &surface->grab_link); + surface->popup_mapped = true; + wlr_seat_pointer_start_grab(seat_handle->wlr_seat, &grab->pointer_grab); } -static void shell_surface_set_maximized(struct wl_client *client, +static void shell_surface_protocol_set_maximized(struct wl_client *client, struct wl_resource *resource, struct wl_resource *output_resource) { wlr_log(L_DEBUG, "got shell surface maximized"); struct wlr_wl_shell_surface *surface = wl_resource_get_user_data(resource); @@ -215,7 +398,7 @@ static void shell_surface_set_maximized(struct wl_client *client, free(event); } -static void shell_surface_set_title(struct wl_client *client, +static void shell_surface_protocol_set_title(struct wl_client *client, struct wl_resource *resource, const char *title) { wlr_log(L_DEBUG, "new shell surface title: %s", title); struct wlr_wl_shell_surface *surface = wl_resource_get_user_data(resource); @@ -231,7 +414,7 @@ static void shell_surface_set_title(struct wl_client *client, wl_signal_emit(&surface->events.set_title, surface); } -static void shell_surface_set_class(struct wl_client *client, +static void shell_surface_protocol_set_class(struct wl_client *client, struct wl_resource *resource, const char *class) { wlr_log(L_DEBUG, "new shell surface class: %s", class); struct wlr_wl_shell_surface *surface = wl_resource_get_user_data(resource); @@ -247,36 +430,44 @@ static void shell_surface_set_class(struct wl_client *client, wl_signal_emit(&surface->events.set_class, surface); } -struct wl_shell_surface_interface shell_surface_interface = { - .pong = shell_surface_pong, - .move = shell_surface_move, - .resize = shell_surface_resize, - .set_toplevel = shell_surface_set_toplevel, - .set_transient = shell_surface_set_transient, - .set_fullscreen = shell_surface_set_fullscreen, - .set_popup = shell_surface_set_popup, - .set_maximized = shell_surface_set_maximized, - .set_title = shell_surface_set_title, - .set_class = shell_surface_set_class, +static const struct wl_shell_surface_interface shell_surface_impl = { + .pong = shell_surface_protocol_pong, + .move = shell_surface_protocol_move, + .resize = shell_surface_protocol_resize, + .set_toplevel = shell_surface_protocol_set_toplevel, + .set_transient = shell_surface_protocol_set_transient, + .set_fullscreen = shell_surface_protocol_set_fullscreen, + .set_popup = shell_surface_protocol_set_popup, + .set_maximized = shell_surface_protocol_set_maximized, + .set_title = shell_surface_protocol_set_title, + .set_class = shell_surface_protocol_set_class, }; -static void wl_shell_surface_destroy(struct wlr_wl_shell_surface *surface) { +static void shell_surface_destroy(struct wlr_wl_shell_surface *surface) { wl_signal_emit(&surface->events.destroy, surface); + shell_surface_destroy_popup_state(surface); wl_resource_set_user_data(surface->resource, NULL); + + struct wlr_wl_shell_surface *child; + wl_list_for_each(child, &surface->popups, popup_link) { + shell_surface_popup_set_parent(child, NULL); + } + wl_list_remove(&surface->popup_link); + wl_list_remove(&surface->link); wl_list_remove(&surface->surface_destroy_listener.link); + wl_list_remove(&surface->surface_commit_listener.link); wl_event_source_remove(surface->ping_timer); free(surface->transient_state); - free(surface->popup_state); free(surface->title); free(surface->class); free(surface); } -static void wl_shell_surface_resource_destroy(struct wl_resource *resource) { +static void shell_surface_resource_destroy(struct wl_resource *resource) { struct wlr_wl_shell_surface *surface = wl_resource_get_user_data(resource); if (surface != NULL) { - wl_shell_surface_destroy(surface); + shell_surface_destroy(surface); } } @@ -284,10 +475,31 @@ static void handle_wlr_surface_destroyed(struct wl_listener *listener, void *data) { struct wlr_wl_shell_surface *surface = wl_container_of(listener, surface, surface_destroy_listener); - wl_shell_surface_destroy(surface); + shell_surface_destroy(surface); +} +static void handle_wlr_surface_committed(struct wl_listener *listener, + void *data) { + struct wlr_wl_shell_surface *surface = + wl_container_of(listener, surface, surface_commit_listener); + if (!surface->configured && + surface->surface->texture->valid && + surface->state != WLR_WL_SHELL_SURFACE_STATE_NONE) { + surface->configured = true; + wl_signal_emit(&surface->shell->events.new_surface, surface); + } + + if (surface->popup_mapped && + surface->state == WLR_WL_SHELL_SURFACE_STATE_POPUP && + !surface->surface->texture->valid) { + surface->popup_mapped = false; + struct wlr_wl_shell_popup_grab *grab = + shell_popup_grab_from_seat(surface->shell, + surface->popup_state->seat); + shell_pointer_grab_maybe_end(&grab->pointer_grab); + } } -static int wlr_wl_shell_surface_ping_timeout(void *user_data) { +static int shell_surface_ping_timeout(void *user_data) { struct wlr_wl_shell_surface *surface = user_data; wl_signal_emit(&surface->events.ping_timeout, surface); @@ -295,7 +507,7 @@ static int wlr_wl_shell_surface_ping_timeout(void *user_data) { return 1; } -static void wl_shell_get_shell_surface(struct wl_client *client, +static void shell_protocol_get_shell_surface(struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *surface_resource) { struct wlr_surface *surface = wl_resource_get_user_data(surface_resource); @@ -311,6 +523,9 @@ static void wl_shell_get_shell_surface(struct wl_client *client, wl_client_post_no_memory(client); return; } + wl_list_init(&wl_surface->grab_link); + wl_list_init(&wl_surface->popup_link); + wl_list_init(&wl_surface->popups); wl_surface->shell = wl_shell; wl_surface->client = client; @@ -321,8 +536,8 @@ static void wl_shell_get_shell_surface(struct wl_client *client, wlr_log(L_DEBUG, "new wl_shell %p (res %p)", wl_surface, wl_surface->resource); wl_resource_set_implementation(wl_surface->resource, - &shell_surface_interface, wl_surface, - wl_shell_surface_resource_destroy); + &shell_surface_impl, wl_surface, + shell_surface_resource_destroy); wl_signal_init(&wl_surface->events.destroy); wl_signal_init(&wl_surface->events.ping_timeout); @@ -338,27 +553,30 @@ static void wl_shell_get_shell_surface(struct wl_client *client, &wl_surface->surface_destroy_listener); wl_surface->surface_destroy_listener.notify = handle_wlr_surface_destroyed; + wl_signal_add(&wl_surface->surface->events.commit, + &wl_surface->surface_commit_listener); + wl_surface->surface_commit_listener.notify = handle_wlr_surface_committed; + struct wl_display *display = wl_client_get_display(client); struct wl_event_loop *loop = wl_display_get_event_loop(display); wl_surface->ping_timer = wl_event_loop_add_timer(loop, - wlr_wl_shell_surface_ping_timeout, wl_surface); + shell_surface_ping_timeout, wl_surface); if (wl_surface->ping_timer == NULL) { wl_client_post_no_memory(client); } wl_list_insert(&wl_shell->surfaces, &wl_surface->link); - wl_signal_emit(&wl_shell->events.new_surface, wl_surface); } -static struct wl_shell_interface wl_shell_impl = { - .get_shell_surface = wl_shell_get_shell_surface +static struct wl_shell_interface shell_impl = { + .get_shell_surface = shell_protocol_get_shell_surface }; -static void wl_shell_destroy(struct wl_resource *resource) { +static void shell_destroy(struct wl_resource *resource) { wl_list_remove(wl_resource_get_link(resource)); } -static void wl_shell_bind(struct wl_client *wl_client, void *_wl_shell, +static void shell_bind(struct wl_client *wl_client, void *_wl_shell, uint32_t version, uint32_t id) { struct wlr_wl_shell *wl_shell = _wl_shell; assert(wl_client && wl_shell); @@ -370,8 +588,8 @@ static void wl_shell_bind(struct wl_client *wl_client, void *_wl_shell, } struct wl_resource *wl_resource = wl_resource_create(wl_client, &wl_shell_interface, version, id); - wl_resource_set_implementation(wl_resource, &wl_shell_impl, wl_shell, - wl_shell_destroy); + wl_resource_set_implementation(wl_resource, &shell_impl, wl_shell, + shell_destroy); wl_list_insert(&wl_shell->wl_resources, wl_resource_get_link(wl_resource)); } @@ -382,7 +600,7 @@ struct wlr_wl_shell *wlr_wl_shell_create(struct wl_display *display) { } wl_shell->ping_timeout = 10000; struct wl_global *wl_global = wl_global_create(display, &wl_shell_interface, - 1, wl_shell, wl_shell_bind); + 1, wl_shell, shell_bind); if (!wl_global) { free(wl_shell); return NULL; @@ -390,6 +608,7 @@ struct wlr_wl_shell *wlr_wl_shell_create(struct wl_display *display) { wl_shell->wl_global = wl_global; wl_list_init(&wl_shell->wl_resources); wl_list_init(&wl_shell->surfaces); + wl_list_init(&wl_shell->popup_grabs); wl_signal_init(&wl_shell->events.new_surface); return wl_shell; } @@ -427,6 +646,42 @@ void wlr_wl_shell_surface_configure(struct wlr_wl_shell_surface *surface, wl_shell_surface_send_configure(surface->resource, edges, width, height); } -void wlr_wl_shell_surface_popup_done(struct wlr_wl_shell_surface *surface) { - wl_shell_surface_send_popup_done(surface->resource); +struct wlr_wl_shell_surface *wlr_wl_shell_surface_popup_at( + struct wlr_wl_shell_surface *surface, double sx, double sy, + double *popup_sx, double *popup_sy) { + struct wlr_wl_shell_surface *popup; + wl_list_for_each(popup, &surface->popups, popup_link) { + if (!popup->popup_mapped) { + continue; + } + double _popup_sx = popup->transient_state->x; + double _popup_sy = popup->transient_state->y; + int popup_width = + popup->surface->current->buffer_width; + int popup_height = + popup->surface->current->buffer_height; + + struct wlr_wl_shell_surface *_popup = + wlr_wl_shell_surface_popup_at(popup, + popup->transient_state->x, + popup->transient_state->y, + popup_sx, popup_sy); + if (_popup) { + *popup_sx = sx + _popup_sx; + *popup_sy = sy + _popup_sy; + return _popup; + } + + if ((sx > _popup_sx && sx < _popup_sx + popup_width) && + (sy > _popup_sy && sy < _popup_sy + popup_height)) { + if (pixman_region32_contains_point(&popup->surface->current->input, + sx - _popup_sx, sy - _popup_sy, NULL)) { + *popup_sx = _popup_sx; + *popup_sy = _popup_sy; + return popup; + } + } + } + + return NULL; }