From a2523081e2886bbbb969ea4f5c1e8a8b8f39bfa9 Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Tue, 22 Feb 2022 07:57:17 +0100 Subject: [PATCH] Handle 'lost' unmanaged xsurfaces + improve cursor handling --- include/labwc.h | 39 +++++++++-- include/ssd.h | 1 + src/cursor.c | 137 +++++++++++++++++++++------------------ src/desktop.c | 8 ++- src/server.c | 7 +- src/xwayland-unmanaged.c | 19 ++++-- 6 files changed, 135 insertions(+), 76 deletions(-) diff --git a/include/labwc.h b/include/labwc.h index c54b1f23..bee3607f 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -143,9 +143,6 @@ struct server { struct seat seat; struct wlr_scene *scene; - /* Tree for all non-layer xdg/xwayland-shell surfaces */ - struct wlr_scene_tree *view_tree; - /* cursor interactive */ enum input_mode input_mode; struct view *grabbed_view; @@ -157,8 +154,16 @@ struct server { struct view *ssd_focused_view; struct ssd_hover_state ssd_hover_state; - struct wlr_scene_tree *osd_tree; + /* Tree for all non-layer xdg/xwayland-shell surfaces */ + struct wlr_scene_tree *view_tree; +#if HAVE_XWAYLAND + /* Tree for unmanaged xsurfaces without initialized view (usually popups) */ + struct wlr_scene_tree *unmanaged_tree; +#endif + /* Tree for built in menu */ struct wlr_scene_tree *menu_tree; + /* Tree for built in OSD / app switcher */ + struct wlr_scene_tree *osd_tree; struct wl_list outputs; struct wl_listener new_output; @@ -415,9 +420,29 @@ bool isfocusable(struct view *view); /** * desktop_node_and_view_at - find view and scene_node at (lx, ly) - * Note: If node points to layer-surface, view_area will be set - * to LAB_SSD_LAYER_SURFACE, if view points to another surface - * view_area will be LAB_SSD_CLIENT + * + * Behavior if node points to a surface: + * - If surface is a layer-surface, *view_area will be + * set to LAB_SSD_LAYER_SURFACE and view will be NULL. + * + * - If surface is a 'lost' unmanaged xsurface (one + * with a never-mapped parent view), *view_area will + * be set to LAB_SSD_UNMANAGED and view will be NULL. + * + * 'Lost' unmanaged xsurfaces are usually caused by + * X11 applications opening popups without setting + * the main window as parent. Example: VLC submenus. + * + * - Any other surface will cause *view_area be set to + * LAB_SSD_CLIENT and return the attached view. + * + * Behavior if node points to internal elements: + * - *view_area will be set to the appropiate enum value + * and view will be NULL if the node is not part of the SSD. + * + * If no node is found for the given layout coordinates, + * *view_area will be set to LAB_SSD_ROOT and view will be NULL. + * */ struct view *desktop_node_and_view_at(struct server *server, double lx, double ly, struct wlr_scene_node **scene_node, double *sx, double *sy, diff --git a/include/ssd.h b/include/ssd.h index 0f4473eb..c547b784 100644 --- a/include/ssd.h +++ b/include/ssd.h @@ -45,6 +45,7 @@ enum ssd_part_type { LAB_SSD_MENU, LAB_SSD_OSD, LAB_SSD_LAYER_SURFACE, + LAB_SSD_UNMANAGED, LAB_SSD_END_MARKER }; diff --git a/src/cursor.c b/src/cursor.c index 4751916e..e55dcf78 100644 --- a/src/cursor.c +++ b/src/cursor.c @@ -12,6 +12,17 @@ #include "ssd.h" #include "config/mousebind.h" +static bool +is_surface(enum ssd_part_type view_area) +{ + return view_area == LAB_SSD_CLIENT + || view_area == LAB_SSD_LAYER_SURFACE +#if HAVE_XWAYLAND + || view_area == LAB_SSD_UNMANAGED +#endif + ; +} + void cursor_rebase(struct seat *seat, uint32_t time_msec) { @@ -22,7 +33,7 @@ cursor_rebase(struct seat *seat, uint32_t time_msec) desktop_node_and_view_at(seat->server, seat->cursor->x, seat->cursor->y, &node, &sx, &sy, &view_area); - if (view_area == LAB_SSD_CLIENT || view_area == LAB_SSD_LAYER_SURFACE) { + if (is_surface(view_area)) { surface = wlr_scene_surface_from_node(node)->surface; } @@ -198,7 +209,7 @@ process_cursor_motion(struct server *server, uint32_t time) &sx, &sy, &view_area); struct wlr_surface *surface = NULL; - if (view_area == LAB_SSD_CLIENT || view_area == LAB_SSD_LAYER_SURFACE) { + if (is_surface(view_area)) { surface = wlr_scene_surface_from_node(node)->surface; } @@ -206,23 +217,21 @@ process_cursor_motion(struct server *server, uint32_t time) uint32_t resize_edges = ssd_resize_edges(view_area); /* Set cursor */ - if (!view) { - /* root, etc. */ + if (view_area == LAB_SSD_ROOT || view_area == LAB_SSD_MENU) { cursor_set(&server->seat, XCURSOR_DEFAULT); - } else { - if (resize_edges) { - cursor_name_set_by_server = true; - cursor_set(&server->seat, - wlr_xcursor_get_resize_name(resize_edges)); - } else if (ssd_part_contains(LAB_SSD_PART_TITLEBAR, view_area)) { - /* title and buttons */ - cursor_set(&server->seat, XCURSOR_DEFAULT); - cursor_name_set_by_server = true; - } else if (cursor_name_set_by_server) { - /* window content */ - cursor_set(&server->seat, XCURSOR_DEFAULT); - cursor_name_set_by_server = false; - } + } else if (resize_edges) { + cursor_name_set_by_server = true; + cursor_set(&server->seat, + wlr_xcursor_get_resize_name(resize_edges)); + } else if (ssd_part_contains(LAB_SSD_PART_TITLEBAR, view_area)) { + /* title and buttons */ + cursor_set(&server->seat, XCURSOR_DEFAULT); + cursor_name_set_by_server = true; + } else if (cursor_name_set_by_server) { + /* xdg/xwindow window content */ + /* layershell or unmanaged */ + cursor_set(&server->seat, XCURSOR_DEFAULT); + cursor_name_set_by_server = false; } if (view_area == LAB_SSD_MENU) { @@ -496,6 +505,13 @@ handle_release_mousebinding(struct view *view, struct server *server, break; } continue; + case MOUSE_ACTION_DRAG: + if (mousebind->pressed_in_context) { + /* Swallow the release event as well as the press one */ + activated_any = true; + activated_any_frame |= mousebind->context == LAB_SSD_FRAME; + } + continue; default: continue; } @@ -565,6 +581,9 @@ handle_press_mousebinding(struct view *view, struct server *server, * counted as a DOUBLECLICK. */ if (!double_click) { + /* Swallow the press event as well as the release one */ + activated_any = true; + activated_any_frame |= mousebind->context == LAB_SSD_FRAME; mousebind->pressed_in_context = true; } continue; @@ -601,7 +620,15 @@ cursor_button(struct wl_listener *listener, void *data) double sx, sy; struct wlr_scene_node *node; enum ssd_part_type view_area = LAB_SSD_NONE; - uint32_t resize_edges; + uint32_t resize_edges = 0; + + /** + * Used in WLR_BUTTON_RELEASED, set on WLR_BUTTON_PRESSED + * + * Automatically initialized with 0 / false and + * checkpatch.pl complains when done manually. + */ + static bool close_menu; /* bindings to the Frame context swallow mouse events if activated */ bool triggered_frame_binding = false; @@ -611,7 +638,7 @@ cursor_button(struct wl_listener *listener, void *data) &sx, &sy, &view_area); struct wlr_surface *surface = NULL; - if (view_area == LAB_SSD_CLIENT || view_area == LAB_SSD_LAYER_SURFACE) { + if (is_surface(view_area)) { surface = wlr_scene_surface_from_node(node)->surface; } @@ -622,9 +649,17 @@ cursor_button(struct wl_listener *listener, void *data) /* handle _release_ */ if (event->state == WLR_BUTTON_RELEASED) { if (server->input_mode == LAB_INPUT_STATE_MENU) { + if (close_menu) { + if (server->menu_current) { + menu_close(server->menu_current); + server->menu_current = NULL; + } + server->input_mode = LAB_INPUT_STATE_PASSTHROUGH; + cursor_rebase(&server->seat, event->time_msec); + close_menu = false; + } return; } - damage_all_outputs(server); if (server->input_mode != LAB_INPUT_STATE_PASSTHROUGH) { /* Exit interactive move/resize/menu mode. */ if (server->grabbed_view == view) { @@ -634,63 +669,41 @@ cursor_button(struct wl_listener *listener, void *data) server->grabbed_view = NULL; } cursor_rebase(&server->seat, event->time_msec); - } - - /* Handle _release_ on root window */ - if (!view) { - handle_release_mousebinding(NULL, server, event->button, - modifiers, LAB_SSD_ROOT, 0); + return; } goto mousebindings; } + /* Handle _press */ if (server->input_mode == LAB_INPUT_STATE_MENU) { - if (!server->menu_current) { - wlr_log(WLR_ERROR, - "on mouse button input_mode STATE_MENU but no current menu"); - } else if (view_area != LAB_SSD_MENU) { - menu_close(server->menu_current); - server->menu_current = NULL; - } else if (!menu_call_actions(server->menu_current, node)) { - /* Action was not successfull, maybe this menu has a submenu */ - return; + if (view_area != LAB_SSD_MENU) { + /* We close the menu on release so we don't leak a stray release */ + close_menu = true; + } else if (menu_call_actions(server->menu_current, node)) { + /* Action was successfull, may fail if item contains a submenu */ + close_menu = true; } - /* TODO: following causes stray release */ - /* Maybe add LAB_INPUT_STATE_IGNORE_MOUSE_RELEASE ? */ - server->input_mode = LAB_INPUT_STATE_PASSTHROUGH; - cursor_rebase(&server->seat, event->time_msec); return; } /* Handle _press_ on a layer surface */ - if (view_area == LAB_SSD_LAYER_SURFACE && surface) { - if (!wlr_surface_is_layer_surface(surface)) { - return; - } + if (view_area == LAB_SSD_LAYER_SURFACE) { struct wlr_layer_surface_v1 *layer = wlr_layer_surface_v1_from_wlr_surface(surface); if (layer->current.keyboard_interactive) { seat_set_focus_layer(&server->seat, layer); } - wlr_seat_pointer_notify_button(seat->seat, event->time_msec, - event->button, event->state); - return; } - /* Handle _press_ on root window */ - if (!view) { - handle_press_mousebinding(NULL, server, - event->button, modifiers, LAB_SSD_ROOT, 0); - return; - } - - /* Determine closest resize edges in case action is Resize */ - resize_edges = ssd_resize_edges(view_area); - if (!resize_edges) { - resize_edges |= server->seat.cursor->x < view->x + view->w / 2 - ? WLR_EDGE_LEFT : WLR_EDGE_RIGHT; - resize_edges |= server->seat.cursor->y < view->y + view->h / 2 - ? WLR_EDGE_TOP : WLR_EDGE_BOTTOM; + if (view) { + /* Determine closest resize edges in case action is Resize */ + resize_edges = ssd_resize_edges(view_area); + if (!resize_edges) { + resize_edges |= server->seat.cursor->x < view->x + view->w / 2 + ? WLR_EDGE_LEFT : WLR_EDGE_RIGHT; + resize_edges |= server->seat.cursor->y < view->y + view->h / 2 + ? WLR_EDGE_TOP : WLR_EDGE_BOTTOM; + } } mousebindings: @@ -703,7 +716,7 @@ mousebindings: server, event->button, modifiers, view_area, resize_edges); } - if (!triggered_frame_binding) { + if (surface && !triggered_frame_binding) { /* Notify client with pointer focus of button press */ wlr_seat_pointer_notify_button(seat->seat, event->time_msec, event->button, event->state); diff --git a/src/desktop.c b/src/desktop.c index 51a39210..e829c701 100644 --- a/src/desktop.c +++ b/src/desktop.c @@ -266,7 +266,7 @@ desktop_node_and_view_at(struct server *server, double lx, double ly, *scene_node = node; if (!node) { - *view_area = LAB_SSD_NONE; + *view_area = LAB_SSD_ROOT; return NULL; } if (node->type == WLR_SCENE_NODE_SURFACE) { @@ -276,6 +276,12 @@ desktop_node_and_view_at(struct server *server, double lx, double ly, *view_area = LAB_SSD_LAYER_SURFACE; return NULL; } +#if HAVE_XWAYLAND + if (node->parent == &server->unmanaged_tree->node) { + *view_area = LAB_SSD_UNMANAGED; + return NULL; + } +#endif } struct wlr_scene_node *osd = &server->osd_tree->node; struct wlr_scene_node *menu = &server->menu_tree->node; diff --git a/src/server.c b/src/server.c index 9e279aa6..f29971d4 100644 --- a/src/server.c +++ b/src/server.c @@ -224,8 +224,11 @@ server_init(struct server *server) exit(EXIT_FAILURE); } server->view_tree = wlr_scene_tree_create(&server->scene->node); - server->osd_tree = wlr_scene_tree_create(&server->scene->node); +#if HAVE_XWAYLAND + server->unmanaged_tree = wlr_scene_tree_create(&server->scene->node); +#endif server->menu_tree = wlr_scene_tree_create(&server->scene->node); + server->osd_tree = wlr_scene_tree_create(&server->scene->node); wlr_scene_attach_output_layout(server->scene, server->output_layout); /* @@ -406,6 +409,8 @@ server_start(struct server *server) void server_finish(struct server *server) { + +/* TODO: clean up various scene_tree nodes */ #if HAVE_XWAYLAND wlr_xwayland_destroy(server->xwayland); #endif diff --git a/src/xwayland-unmanaged.c b/src/xwayland-unmanaged.c index a80b9e6e..03387320 100644 --- a/src/xwayland-unmanaged.c +++ b/src/xwayland-unmanaged.c @@ -32,7 +32,7 @@ parent_view(struct server *server, struct wlr_xwayland_surface *surface) } struct view *view; wl_list_for_each(view, &server->views, link) { - if (view->surface == s->surface) { + if (view->xwayland_surface == s) { return view; } } @@ -59,11 +59,20 @@ unmanaged_handle_map(struct wl_listener *listener, void *data) seat_focus_surface(&unmanaged->server->seat, xsurface->surface); } + int lx = unmanaged->lx; + int ly = unmanaged->ly; + struct wlr_scene_node *parent, *node; struct view *view = parent_view(unmanaged->server, xsurface); - struct wlr_scene_node *node = wlr_scene_subsurface_tree_create( - view->scene_node, xsurface->surface); - wlr_scene_node_set_position(node, unmanaged->lx - view->x, - unmanaged->ly - view->y); + if (!view || !view->scene_tree) { + parent = &view->server->unmanaged_tree->node; + } else { + lx -= view->x; + ly -= view->y; + parent = &view->scene_tree->node; + } + /* node will be destroyed automatically once surface is destroyed */ + node = &wlr_scene_surface_create(parent, xsurface->surface)->node; + wlr_scene_node_set_position(node, lx, ly); } static void -- 2.52.0