From b8c3fdaef9631afec6be17957ee7dae693b56a18 Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Tue, 13 Sep 2022 10:55:59 -0400 Subject: [PATCH 1/3] seat: Listen for destroy signal of pressed.surface --- include/labwc.h | 3 +++ src/cursor.c | 4 +--- src/layers.c | 3 --- src/seat.c | 34 +++++++++++++++++++++++++++++++--- src/view.c | 5 ----- src/xwayland-unmanaged.c | 3 --- 6 files changed, 35 insertions(+), 17 deletions(-) diff --git a/include/labwc.h b/include/labwc.h index 2de216f9..c63a1818 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -152,6 +152,7 @@ struct seat { struct wl_listener destroy_drag; struct wl_listener constraint_commit; struct wl_listener idle_inhibitor_create; + struct wl_listener pressed_surface_destroy; }; struct lab_data_buffer; @@ -561,6 +562,8 @@ void seat_finish(struct server *server); void seat_reconfigure(struct server *server); void seat_focus_surface(struct seat *seat, struct wlr_surface *surface); void seat_set_focus_layer(struct seat *seat, struct wlr_layer_surface_v1 *layer); +void seat_set_pressed(struct seat *seat, struct view *view, + struct wlr_scene_node *node, struct wlr_surface *surface); void seat_reset_pressed(struct seat *seat); void interactive_begin(struct view *view, enum input_mode mode, diff --git a/src/cursor.c b/src/cursor.c index cf7d01ba..27f8cfea 100644 --- a/src/cursor.c +++ b/src/cursor.c @@ -814,9 +814,7 @@ cursor_button(struct wl_listener *listener, void *data) /* Handle _press */ if (surface) { - seat->pressed.view = view; - seat->pressed.node = node; - seat->pressed.surface = surface; + seat_set_pressed(seat, view, node, surface); } if (server->input_mode == LAB_INPUT_STATE_MENU) { diff --git a/src/layers.c b/src/layers.c index a6e2e4bd..674deaa9 100644 --- a/src/layers.c +++ b/src/layers.c @@ -145,9 +145,6 @@ unmap(struct lab_layer_surface *layer) if (seat->focused_layer == layer->scene_layer_surface->layer_surface) { seat_set_focus_layer(seat, NULL); } - if (seat->pressed.surface == layer->scene_layer_surface->layer_surface->surface) { - seat_reset_pressed(seat); - } } static void diff --git a/src/seat.c b/src/seat.c index 56edaef3..e7dabff9 100644 --- a/src/seat.c +++ b/src/seat.c @@ -370,10 +370,38 @@ seat_set_focus_layer(struct seat *seat, struct wlr_layer_surface_v1 *layer) } } +static void +pressed_surface_destroy(struct wl_listener *listener, void *data) +{ + struct wlr_surface *surface = data; + struct seat *seat = wl_container_of(listener, seat, + pressed_surface_destroy); + + assert(surface == seat->pressed.surface); + seat_reset_pressed(seat); +} + +void +seat_set_pressed(struct seat *seat, struct view *view, + struct wlr_scene_node *node, struct wlr_surface *surface) +{ + assert(surface); + seat_reset_pressed(seat); + + seat->pressed.view = view; + seat->pressed.node = node; + seat->pressed.surface = surface; + seat->pressed_surface_destroy.notify = pressed_surface_destroy; + wl_signal_add(&surface->events.destroy, &seat->pressed_surface_destroy); +} + void seat_reset_pressed(struct seat *seat) { - seat->pressed.view = NULL; - seat->pressed.node = NULL; - seat->pressed.surface = NULL; + if (seat->pressed.surface) { + seat->pressed.view = NULL; + seat->pressed.node = NULL; + seat->pressed.surface = NULL; + wl_list_remove(&seat->pressed_surface_destroy.link); + } } diff --git a/src/view.c b/src/view.c index 0ee9c317..2976a087 100644 --- a/src/view.c +++ b/src/view.c @@ -815,11 +815,6 @@ view_destroy(struct view *view) need_cursor_update = true; } - if (server->seat.pressed.view == view) { - /* Mouse was pressed on surface and is still pressed */ - seat_reset_pressed(&server->seat); - } - if (server->focused_view == view) { server->focused_view = NULL; need_cursor_update = true; diff --git a/src/xwayland-unmanaged.c b/src/xwayland-unmanaged.c index b09b6196..44608485 100644 --- a/src/xwayland-unmanaged.c +++ b/src/xwayland-unmanaged.c @@ -101,9 +101,6 @@ unmanaged_handle_unmap(struct wl_listener *listener, void *data) * Mark the node as gone so a racing configure event * won't try to reposition the node while unmapped. */ - if (unmanaged->node && seat->pressed.node == unmanaged->node) { - seat_reset_pressed(seat); - } unmanaged->node = NULL; cursor_update_focus(unmanaged->server); From a8fbe1aac207cf7efe8c2f464de3c83c107818dd Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Tue, 13 Sep 2022 12:51:23 -0400 Subject: [PATCH 2/3] xdg-popup: Check for NULL from wlr_xdg_surface_from_wlr_surface() Also eliminate struct view_child and replace it with a simple (struct view *)parent_view field. --- src/xdg-popup.c | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/src/xdg-popup.c b/src/xdg-popup.c index 3c92d609..36873b6c 100644 --- a/src/xdg-popup.c +++ b/src/xdg-popup.c @@ -10,13 +10,8 @@ #include "labwc.h" #include "node.h" -struct view_child { - struct wlr_surface *surface; - struct view *parent; -}; - struct xdg_popup { - struct view_child view_child; + struct view *parent_view; struct wlr_xdg_popup *wlr_popup; struct wl_listener destroy; @@ -58,20 +53,27 @@ popup_handle_new_xdg_popup(struct wl_listener *listener, void *data) { struct xdg_popup *popup = wl_container_of(listener, popup, new_popup); struct wlr_xdg_popup *wlr_popup = data; - xdg_popup_create(popup->view_child.parent, wlr_popup); + xdg_popup_create(popup->parent_view, wlr_popup); } void xdg_popup_create(struct view *view, struct wlr_xdg_popup *wlr_popup) { + struct wlr_xdg_surface *parent = + wlr_surface_is_xdg_surface(wlr_popup->parent) ? + wlr_xdg_surface_from_wlr_surface(wlr_popup->parent) : NULL; + if (!parent) { + wlr_log(WLR_ERROR, "parent is not a valid XDG surface"); + return; + } + struct xdg_popup *popup = calloc(1, sizeof(struct xdg_popup)); if (!popup) { return; } + popup->parent_view = view; popup->wlr_popup = wlr_popup; - popup->view_child.parent = view; - popup->view_child.surface = wlr_popup->base->surface; popup->destroy.notify = handle_xdg_popup_destroy; wl_signal_add(&wlr_popup->base->events.destroy, &popup->destroy); @@ -85,12 +87,6 @@ xdg_popup_create(struct view *view, struct wlr_xdg_popup *wlr_popup) * this, we always set the user data field of xdg_surfaces to the * corresponding scene node. */ - if (!wlr_surface_is_xdg_surface(wlr_popup->parent)) { - wlr_log(WLR_ERROR, "xdg_surface is not xdg"); - return; - } - struct wlr_xdg_surface *parent = - wlr_xdg_surface_from_wlr_surface(wlr_popup->parent); struct wlr_scene_tree *parent_tree = parent->surface->data; wlr_popup->base->surface->data = wlr_scene_xdg_surface_create(parent_tree, wlr_popup->base); From e18f7a32ba072fa7763b1c0742000e93f366d6ab Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Sat, 10 Sep 2022 01:57:39 -0400 Subject: [PATCH 3/3] cursor: Allow leave/enter events within the same XDG toplevel Attempting to open a GTK3 menu and activate a menu item in it, using a single mouse motion (press-move-release), was broken due to GTK apparently expecting to receive leave/enter events when the cursor enters the menu (XDG popup). To fix the issue, allow leave/enter events when the cursor is moved between an XDG toplevel and popups of the same. v2: - Use (struct view *) as proxy for toplevel in comparisons - Update seat->pressed.surface when entering/leaving popups v3: - Go back to using get_toplevel() rather than (struct view *) --- include/labwc.h | 4 ++- src/cursor.c | 75 +++++++++++++++++++++++++++++++++++++++++-------- src/seat.c | 5 +++- 3 files changed, 70 insertions(+), 14 deletions(-) diff --git a/include/labwc.h b/include/labwc.h index c63a1818..ac90a521 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -115,6 +115,7 @@ struct seat { struct view *view; struct wlr_scene_node *node; struct wlr_surface *surface; + struct wlr_surface *toplevel; } pressed; struct wl_client *active_client_while_inhibited; @@ -563,7 +564,8 @@ void seat_reconfigure(struct server *server); void seat_focus_surface(struct seat *seat, struct wlr_surface *surface); void seat_set_focus_layer(struct seat *seat, struct wlr_layer_surface_v1 *layer); void seat_set_pressed(struct seat *seat, struct view *view, - struct wlr_scene_node *node, struct wlr_surface *surface); + struct wlr_scene_node *node, struct wlr_surface *surface, + struct wlr_surface *toplevel); void seat_reset_pressed(struct seat *seat); void interactive_begin(struct view *view, enum input_mode mode, diff --git a/src/cursor.c b/src/cursor.c index 27f8cfea..8b08b978 100644 --- a/src/cursor.c +++ b/src/cursor.c @@ -45,6 +45,32 @@ is_surface(enum ssd_part_type view_area) ; } +static struct wlr_surface * +get_toplevel(struct wlr_surface *surface) +{ + while (surface && wlr_surface_is_xdg_surface(surface)) { + struct wlr_xdg_surface *xdg_surface = + wlr_xdg_surface_from_wlr_surface(surface); + if (!xdg_surface) { + return NULL; + } + + switch (xdg_surface->role) { + case WLR_XDG_SURFACE_ROLE_NONE: + return NULL; + case WLR_XDG_SURFACE_ROLE_TOPLEVEL: + return surface; + case WLR_XDG_SURFACE_ROLE_POPUP: + surface = xdg_surface->popup->parent; + continue; + } + } + if (surface && wlr_surface_is_layer_surface(surface)) { + return surface; + } + return NULL; +} + static void request_cursor_notify(struct wl_listener *listener, void *data) { @@ -227,6 +253,28 @@ input_inhibit_blocks_surface(struct seat *seat, struct wl_resource *resource) && inhibiting_client != wl_resource_get_client(resource); } +static bool +update_pressed_surface(struct seat *seat, struct view *view, + struct wlr_scene_node *node, struct wlr_surface *surface) +{ + /* + * In most cases, we don't want to leave one surface and enter + * another while a button is pressed. However, GTK/Wayland + * menus (implemented as XDG popups) do need the leave/enter + * events to work properly. To cover this case, we allow + * leave/enter events between XDG popups and their toplevel. + */ + if (seat->pressed.surface && surface != seat->pressed.surface) { + struct wlr_surface *toplevel = get_toplevel(surface); + if (toplevel && toplevel == seat->pressed.toplevel) { + seat_set_pressed(seat, view, node, + surface, toplevel); + return true; + } + } + return false; +} + static void process_cursor_motion_out_of_surface(struct server *server, uint32_t time) { @@ -267,10 +315,10 @@ process_cursor_motion_out_of_surface(struct server *server, uint32_t time) * Common logic shared by cursor_update_focus() and process_cursor_motion() */ static void -cursor_update_common(struct server *server, struct wlr_scene_node *node, - struct wlr_surface *surface, double sx, double sy, - enum ssd_part_type view_area, uint32_t time_msec, - bool cursor_has_moved) +cursor_update_common(struct server *server, struct view *view, + struct wlr_scene_node *node, struct wlr_surface *surface, + double sx, double sy, enum ssd_part_type view_area, + uint32_t time_msec, bool cursor_has_moved) { struct seat *seat = &server->seat; struct wlr_seat *wlr_seat = seat->seat; @@ -287,6 +335,7 @@ cursor_update_common(struct server *server, struct wlr_scene_node *node, /* TODO: verify drag_icon logic */ if (seat->pressed.surface && surface != seat->pressed.surface + && !update_pressed_surface(seat, view, node, surface) && !seat->drag_icon) { if (cursor_has_moved) { /* @@ -398,8 +447,8 @@ process_cursor_motion(struct server *server, uint32_t time) } } - cursor_update_common(server, node, surface, sx, sy, view_area, time, - /*cursor_has_moved*/ true); + cursor_update_common(server, view, node, surface, sx, sy, view_area, + time, /*cursor_has_moved*/ true); } static uint32_t @@ -420,15 +469,15 @@ cursor_update_focus(struct server *server) clock_gettime(CLOCK_MONOTONIC, &now); struct seat *seat = &server->seat; - desktop_node_and_view_at(seat->server, seat->cursor->x, - seat->cursor->y, &node, &sx, &sy, &view_area); + struct view *view = desktop_node_and_view_at(seat->server, + seat->cursor->x, seat->cursor->y, &node, &sx, &sy, &view_area); if (is_surface(view_area)) { surface = lab_wlr_surface_from_node(node); } /* Focus surface under cursor if it isn't already focused */ - cursor_update_common(server, node, surface, sx, sy, view_area, + cursor_update_common(server, view, node, surface, sx, sy, view_area, msec(&now), /*cursor_has_moved*/ false); } @@ -798,8 +847,9 @@ cursor_button(struct wl_listener *listener, void *data) if (server->input_mode == LAB_INPUT_STATE_MENU) { if (close_menu) { menu_close_root(server); - cursor_update_common(server, node, surface, sx, sy, - view_area, event->time_msec, false); + cursor_update_common(server, view, node, + surface, sx, sy, view_area, + event->time_msec, false); close_menu = false; } return; @@ -814,7 +864,8 @@ cursor_button(struct wl_listener *listener, void *data) /* Handle _press */ if (surface) { - seat_set_pressed(seat, view, node, surface); + seat_set_pressed(seat, view, node, surface, + get_toplevel(surface)); } if (server->input_mode == LAB_INPUT_STATE_MENU) { diff --git a/src/seat.c b/src/seat.c index e7dabff9..16aaa256 100644 --- a/src/seat.c +++ b/src/seat.c @@ -383,7 +383,8 @@ pressed_surface_destroy(struct wl_listener *listener, void *data) void seat_set_pressed(struct seat *seat, struct view *view, - struct wlr_scene_node *node, struct wlr_surface *surface) + struct wlr_scene_node *node, struct wlr_surface *surface, + struct wlr_surface *toplevel) { assert(surface); seat_reset_pressed(seat); @@ -391,6 +392,7 @@ seat_set_pressed(struct seat *seat, struct view *view, seat->pressed.view = view; seat->pressed.node = node; seat->pressed.surface = surface; + seat->pressed.toplevel = toplevel; seat->pressed_surface_destroy.notify = pressed_surface_destroy; wl_signal_add(&surface->events.destroy, &seat->pressed_surface_destroy); } @@ -402,6 +404,7 @@ seat_reset_pressed(struct seat *seat) seat->pressed.view = NULL; seat->pressed.node = NULL; seat->pressed.surface = NULL; + seat->pressed.toplevel = NULL; wl_list_remove(&seat->pressed_surface_destroy.link); } }