]> git.mdlowis.com Git - proto/labwc.git/commitdiff
Handle 'lost' unmanaged xsurfaces + improve cursor handling
authorConsolatis <35009135+Consolatis@users.noreply.github.com>
Tue, 22 Feb 2022 06:57:17 +0000 (07:57 +0100)
committerJohan Malm <jgm323@gmail.com>
Wed, 23 Feb 2022 21:47:01 +0000 (21:47 +0000)
include/labwc.h
include/ssd.h
src/cursor.c
src/desktop.c
src/server.c
src/xwayland-unmanaged.c

index c54b1f239e69ea529db7cfc70168365460b83cbb..bee3607f9fe3c1ab1be3e27f9d71b1d43a53f833 100644 (file)
@@ -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,
index 0f4473eb6c892eea0c9b16003082e2a88fcec536..c547b78476facbb67e44d805a1c92863c66b9303 100644 (file)
@@ -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
 };
 
index 4751916e489726db0e150f21cf73502b646ead90..e55dcf784f3bba67abf4ef1ea26c0c246701f609 100644 (file)
 #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);
index 51a3921032ed576e382338c86c04013abf5361ab..e829c7014c699dd8fd99d5136de20a104d27366d 100644 (file)
@@ -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;
index 9e279aa63ddd3d25c180ab4ffefafdf95f3847b9..f29971d48b406377716228d5b7b565f39fa0f943 100644 (file)
@@ -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
index a80b9e6e19280c4d6ddb6c448d48c3372bf2694f..033873207b45fff2385210bc25a496312d9ae845 100644 (file)
@@ -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