]> git.mdlowis.com Git - proto/labwc.git/commitdiff
cursor: Factor out cursor_update_common() and fix some glitches
authorJohn Lindgren <john@jlindgren.net>
Mon, 12 Sep 2022 17:14:18 +0000 (13:14 -0400)
committerJohn Lindgren <john@jlindgren.net>
Tue, 13 Sep 2022 17:29:36 +0000 (13:29 -0400)
Fix a couple of glitches seen when exiting interactive move/resize:

 - Cursor briefly set to left_ptr rather than the correct cursor image
 - Cursor not updated if the view being moved/resized is destroyed

Also make sure to exit interactive mode if the view is going fullscreen
(labwc gets very confused otherwise).

Code changes in detail:

 - Factor out set_server_cursor() which will set the correct cursor
   image for non-client areas (either XCURSOR_DEFAULT or one of the
   resize cursors).
 - Unify the logic from cursor_rebase() and process_cursor_motion by
   factoring out cursor_update_common().  This corrects some logic
   discrepancies between the two, which should be a good thing(TM).
 - Remove the extra cursor_set(XCURSOR_DEFAULT) from interactive_end()
   and instead rely on cursor_update_focus() to do the right thing.
 - Simplify cursor_button() by just calling interactive_end() when we
   want to exit interactive mode.
 - Call cursor_update_focus() from view_destroy() if the view had mouse
   focus or was being interactively moved/resized.

v2: Eliminate force_reenter parameters and figure out automatically
    when we need to re-enter the surface.
v3: Rename wlseat -> wlr_seat.
v4: Simplify client/server cursor logic.

include/labwc.h
src/cursor.c
src/desktop.c
src/interactive.c
src/view.c
src/xwayland-unmanaged.c

index 0466e9a103105329319498406f294ac1bfbff792..2de216f93181fc8dba5aa7ce808f6ef846922427 100644 (file)
@@ -80,6 +80,12 @@ struct seat {
        struct wlr_keyboard_group *keyboard_group;
 
        bool cursor_requires_fallback;
+       /*
+        * Name of most recent server-side cursor image.  Set by
+        * cursor_set().  Cleared when a client surface is entered
+        * (in that case the client is expected to set a cursor image).
+        */
+       char *cursor_set_by_server;
        struct wlr_cursor *cursor;
        struct wlr_xcursor_manager *xcursor_manager;
 
@@ -533,13 +539,12 @@ void cursor_set(struct seat *seat, const char *cursor_name);
 /**
  * cursor_update_focus - update cursor focus, may update the cursor icon
  * @server - server
- * @force_reenter - re-enter a surface if it already owns the cursor focus
  *
  * This can be used to give the mouse focus to the surface under the cursor
  * or to force an update of the cursor icon by sending an exit and enter
- * event to an already focused surface when @force_reenter is true.
+ * event to an already focused surface.
  */
-void cursor_update_focus(struct server *server, bool force_reenter);
+void cursor_update_focus(struct server *server);
 
 void cursor_init(struct seat *seat);
 void cursor_finish(struct seat *seat);
index d535ba05ede8ae41b94d0a810e546f4a1fbbf8f2..cf7d01ba2059c73bdf066d8686e01d925e6d8dfd 100644 (file)
 #include "common/scene-helpers.h"
 #include "common/zfree.h"
 
-/* Used to prevent setting the same cursor image twice */
-static char *last_cursor_image = NULL;
-
-
 static const struct cursor_alias {
        const char *name, *alias;
 } cursor_aliases[] = {
@@ -49,52 +45,6 @@ is_surface(enum ssd_part_type view_area)
                ;
 }
 
-/*
- * cursor_rebase() for internal use: reuses node, surface, sx and sy
- * For a public variant use cursor_update_focus()
- */
-static void
-cursor_rebase(struct seat *seat, struct wlr_scene_node *node,
-               struct wlr_surface *surface, double sx, double sy, uint32_t time_msec,
-               bool force)
-{
-       if (seat->pressed.surface && surface != seat->pressed.surface) {
-               /* Don't leave surface when a button was pressed over another surface */
-               return;
-       }
-
-       if (seat->server->input_mode != LAB_INPUT_STATE_PASSTHROUGH) {
-               /* Prevent resetting focus / cursor image when moving or resizing */
-               return;
-       }
-
-       struct wlr_surface *focused_surface =
-               seat->seat->pointer_state.focused_surface;
-
-       if (surface) {
-               if (!force && surface == focused_surface) {
-                       /*
-                        * Usually we prevent re-entering an already focused surface
-                        * because it sends useless leave and enter events.
-                        *
-                        * They may also seriously confuse clients if sent between
-                        * connected events like a double click (#258) or fast scroll
-                        * events caused by a touchpad (#225).
-                        *
-                        * If we just want to update the cursor image though
-                        * the @force argument may be used to allow re-entering.
-                        */
-                       return;
-               }
-               wlr_seat_pointer_notify_clear_focus(seat->seat);
-               wlr_seat_pointer_notify_enter(seat->seat, surface, sx, sy);
-               wlr_seat_pointer_notify_motion(seat->seat, time_msec, sx, sy);
-       } else if (focused_surface) {
-               cursor_set(seat, XCURSOR_DEFAULT);
-               wlr_seat_pointer_notify_clear_focus(seat->seat);
-       }
-}
-
 static void
 request_cursor_notify(struct wl_listener *listener, void *data)
 {
@@ -127,10 +77,6 @@ request_cursor_notify(struct wl_listener *listener, void *data)
 
                wlr_cursor_set_surface(seat->cursor, event->surface,
                        event->hotspot_x, event->hotspot_y);
-
-               if (last_cursor_image) {
-                       zfree(last_cursor_image);
-               }
        }
 }
 
@@ -250,16 +196,26 @@ cursor_set(struct seat *seat, const char *cursor_name)
        }
 
        /* Prevent setting the same cursor image twice */
-       if (last_cursor_image) {
-               if (!strcmp(last_cursor_image, cursor_name)) {
-                       return;
-               }
-               free(last_cursor_image);
+       if (seat->cursor_set_by_server && !strcmp(cursor_name,
+                       seat->cursor_set_by_server)) {
+               return;
        }
-       last_cursor_image = strdup(cursor_name);
 
        wlr_xcursor_manager_set_cursor_image(
                seat->xcursor_manager, cursor_name, seat->cursor);
+       zfree(seat->cursor_set_by_server);
+       seat->cursor_set_by_server = strdup(cursor_name);
+}
+
+static void
+set_server_cursor(struct seat *seat, enum ssd_part_type view_area)
+{
+       uint32_t resize_edges = ssd_resize_edges(view_area);
+       if (resize_edges) {
+               cursor_set(seat, wlr_xcursor_get_resize_name(resize_edges));
+       } else {
+               cursor_set(seat, XCURSOR_DEFAULT);
+       }
 }
 
 bool
@@ -307,11 +263,86 @@ process_cursor_motion_out_of_surface(struct server *server, uint32_t time)
        wlr_seat_pointer_notify_motion(server->seat.seat, time, sx, sy);
 }
 
+/*
+ * Common logic shared by cursor_update_focus() and process_cursor_motion()
+ */
 static void
-process_cursor_motion(struct server *server, uint32_t time)
+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)
 {
-       static bool cursor_name_set_by_server;
+       struct seat *seat = &server->seat;
+       struct wlr_seat *wlr_seat = seat->seat;
 
+       ssd_update_button_hover(node, &server->ssd_hover_state);
+
+       if (server->input_mode != LAB_INPUT_STATE_PASSTHROUGH) {
+               /*
+                * Prevent updating focus/cursor image during
+                * interactive move/resize
+                */
+               return;
+       }
+
+       /* TODO: verify drag_icon logic */
+       if (seat->pressed.surface && surface != seat->pressed.surface
+                       && !seat->drag_icon) {
+               if (cursor_has_moved) {
+                       /*
+                        * Button has been pressed while over another
+                        * surface and is still held down.  Just send
+                        * the motion events to the focused surface so
+                        * we can keep scrolling or selecting text even
+                        * if the cursor moves outside of the surface.
+                        */
+                       process_cursor_motion_out_of_surface(server, time_msec);
+               }
+               return;
+       }
+
+       if (surface && !input_inhibit_blocks_surface(seat, surface->resource)) {
+               /*
+                * Cursor is over an input-enabled client surface.  The
+                * cursor image will be set by request_cursor_notify()
+                * in response to the enter event.
+                */
+               if (surface != wlr_seat->pointer_state.focused_surface
+                               || seat->cursor_set_by_server) {
+                       /*
+                        * Enter the surface if necessary.  Usually we
+                        * prevent re-entering an already focused
+                        * surface, because the extra leave and enter
+                        * events can confuse clients (e.g. break
+                        * double-click detection).
+                        *
+                        * We do however send a leave/enter event pair
+                        * if a server-side cursor was set and we need
+                        * to trigger a cursor image update.
+                        */
+                       wlr_seat_pointer_notify_clear_focus(wlr_seat);
+                       wlr_seat_pointer_notify_enter(wlr_seat, surface,
+                               sx, sy);
+                       zfree(seat->cursor_set_by_server);
+               }
+               if (cursor_has_moved) {
+                       wlr_seat_pointer_notify_motion(wlr_seat, time_msec,
+                               sx, sy);
+               }
+       } else {
+               /*
+                * Cursor is over a server (labwc) surface.  Clear focus
+                * from the focused client (if any, no-op otherwise) and
+                * set the cursor image ourselves.
+                */
+               wlr_seat_pointer_notify_clear_focus(wlr_seat);
+               set_server_cursor(seat, view_area);
+       }
+}
+
+static void
+process_cursor_motion(struct server *server, uint32_t time)
+{
        /* If the mode is non-passthrough, delegate to those functions. */
        if (server->input_mode == LAB_INPUT_STATE_MOVE) {
                process_cursor_move(server, time);
@@ -323,7 +354,6 @@ process_cursor_motion(struct server *server, uint32_t time)
 
        /* Otherwise, find view under the pointer and send the event along */
        double sx, sy;
-       struct wlr_seat *wlr_seat = server->seat.seat;
        struct wlr_scene_node *node;
        enum ssd_part_type view_area = LAB_SSD_NONE;
        struct view *view = desktop_node_and_view_at(server,
@@ -335,29 +365,9 @@ process_cursor_motion(struct server *server, uint32_t time)
                surface = lab_wlr_surface_from_node(node);
        }
 
-       /* resize handles */
-       uint32_t resize_edges = ssd_resize_edges(view_area);
-
-       /* Set cursor */
-       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) {
-               /* 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) {
                menu_process_cursor_motion(node);
+               set_server_cursor(&server->seat, view_area);
                return;
        }
 
@@ -373,6 +383,7 @@ process_cursor_motion(struct server *server, uint32_t time)
                if (mousebind->mouse_event == MOUSE_ACTION_DRAG
                                && mousebind->pressed_in_context) {
                        /* Find closest resize edges in case action is Resize */
+                       uint32_t resize_edges = ssd_resize_edges(view_area);
                        if (view && !resize_edges) {
                                resize_edges |= server->seat.cursor->x
                                        < view->x + view->w / 2 ? WLR_EDGE_LEFT
@@ -387,50 +398,8 @@ process_cursor_motion(struct server *server, uint32_t time)
                }
        }
 
-       /* TODO: ssd_hover_state should likely be located in server->seat */
-       ssd_update_button_hover(node, &server->ssd_hover_state);
-
-       if (server->seat.pressed.surface &&
-                       server->seat.pressed.surface != surface &&
-                       !server->seat.drag_icon) {
-               /*
-                * Button has been pressed while over another surface
-                * and is still held down. Just send the adjusted motion
-                * events to the focused surface so we can keep scrolling
-                * or selecting text even if the cursor moves outside of
-                * the surface.
-                */
-               process_cursor_motion_out_of_surface(server, time);
-       } else if (surface && !input_inhibit_blocks_surface(
-                       &server->seat, surface->resource)) {
-               bool focus_changed =
-                       wlr_seat->pointer_state.focused_surface != surface;
-               /*
-                * "Enter" the surface if necessary. This lets the client know
-                * that the cursor has entered one of its surfaces.
-                *
-                * Note that this gives the surface "pointer focus", which is
-                * distinct from keyboard focus. You get pointer focus by moving
-                * the pointer over a window.
-                */
-               wlr_seat_pointer_notify_enter(wlr_seat, surface, sx, sy);
-               if (!focus_changed || server->seat.drag_icon) {
-                       /*
-                        * The enter event contains coordinates, so we only need
-                        * to notify on motion if the focus did not change.
-                        */
-                       wlr_seat_pointer_notify_motion(wlr_seat, time, sx, sy);
-               }
-       } else {
-               /*
-                * Clear pointer focus so future button events and such are not
-                * sent to the last client to have the cursor over it.
-                *
-                * Except if we started pressing a button on the last client and
-                * are not currently in drag and drop mode.
-                */
-               wlr_seat_pointer_clear_focus(wlr_seat);
-       }
+       cursor_update_common(server, node, surface, sx, sy, view_area, time,
+               /*cursor_has_moved*/ true);
 }
 
 static uint32_t
@@ -440,7 +409,7 @@ msec(const struct timespec *t)
 }
 
 void
-cursor_update_focus(struct server *server, bool force_reenter)
+cursor_update_focus(struct server *server)
 {
        double sx, sy;
        struct wlr_scene_node *node = NULL;
@@ -458,10 +427,9 @@ cursor_update_focus(struct server *server, bool force_reenter)
                surface = lab_wlr_surface_from_node(node);
        }
 
-       ssd_update_button_hover(node, &seat->server->ssd_hover_state);
-
        /* Focus surface under cursor if it isn't already focused */
-       cursor_rebase(seat, node, surface, sx, sy, msec(&now), force_reenter);
+       cursor_update_common(server, node, surface, sx, sy, view_area,
+               msec(&now), /*cursor_has_moved*/ false);
 }
 
 void
@@ -830,27 +798,15 @@ cursor_button(struct wl_listener *listener, void *data)
                if (server->input_mode == LAB_INPUT_STATE_MENU) {
                        if (close_menu) {
                                menu_close_root(server);
-                               cursor_rebase(&server->seat, node, surface, sx, sy,
-                                       event->time_msec, false);
+                               cursor_update_common(server, node, surface, sx, sy,
+                                       view_area, event->time_msec, false);
                                close_menu = false;
                        }
                        return;
                }
                if (server->input_mode != LAB_INPUT_STATE_PASSTHROUGH) {
-                       /* Exit interactive move/resize/menu mode. */
-                       if (server->grabbed_view == view) {
-                               interactive_end(view);
-                       } else {
-                               server->input_mode = LAB_INPUT_STATE_PASSTHROUGH;
-                               server->grabbed_view = NULL;
-                       }
-
-                       /*
-                        * Focus surface under cursor and force updating the
-                        * cursor icon
-                        */
-                       cursor_rebase(&server->seat, node, surface, sx, sy,
-                               event->time_msec, true);
+                       /* Exit interactive move/resize mode */
+                       interactive_end(server->grabbed_view);
                        return;
                }
                goto mousebindings;
@@ -923,7 +879,7 @@ cursor_axis(struct wl_listener *listener, void *data)
        wlr_idle_notify_activity(seat->wlr_idle, seat->seat);
 
        /* Make sure we are sending the events to the surface under the cursor */
-       cursor_update_focus(seat->server, false);
+       cursor_update_focus(seat->server);
 
        /* Notify the client with pointer focus of the axis event. */
        wlr_seat_pointer_notify_axis(seat->seat, event->time_msec,
index 20e73ff98bc57c75c07a10143a812c57de64b9ea..e6d09080d3cba2754683e7c049105ddd1236db73 100644 (file)
@@ -65,7 +65,7 @@ desktop_move_to_front(struct view *view)
 #if HAVE_XWAYLAND
        move_xwayland_sub_views_to_front(view);
 #endif
-       cursor_update_focus(view->server, false);
+       cursor_update_focus(view->server);
 }
 
 static void
index c56a17d7d8418d7c30dd86c47a47e0a896e90cfc..02ee9ee66f93cd11d3cf9363fa5e7387379f3acd 100644 (file)
@@ -134,14 +134,7 @@ interactive_end(struct view *view)
                                view_snap_to_edge(view, "down");
                        }
                }
-               /*
-                * First set the cursor image in case we moved / resized via SSD.
-                * Then allow an application to set its own image in case there
-                * is a surface below the cursor (e.g. moved / resized via 'Alt'
-                * modifier). If there is no surface below the cursor the second
-                * call is a no-op.
-                */
-               cursor_set(&view->server->seat, XCURSOR_DEFAULT);
-               cursor_update_focus(view->server, true);
+               /* Update focus/cursor image */
+               cursor_update_focus(view->server);
        }
 }
index 088d3647a864a42fcf106efb8ba46edff81b2d37..0ee9c31753cfd353351e5ebc841e1913d6623303 100644 (file)
@@ -149,7 +149,7 @@ view_moved(struct view *view)
        wlr_scene_node_set_position(&view->scene_tree->node, view->x, view->y);
        view_discover_output(view);
        ssd_update_geometry(view);
-       cursor_update_focus(view->server, false);
+       cursor_update_focus(view->server);
 }
 
 /* N.B. Use view_move() if not resizing. */
@@ -533,6 +533,7 @@ view_set_fullscreen(struct view *view, bool fullscreen,
                wlr_output = view_wlr_output(view);
        }
        if (fullscreen) {
+               interactive_end(view);
                if (!view->maximized && !view->tiled) {
                        view_store_natural_geometry(view);
                }
@@ -801,6 +802,7 @@ void
 view_destroy(struct view *view)
 {
        struct server *server = view->server;
+       bool need_cursor_update = false;
 
        if (view->toplevel_handle) {
                wlr_foreign_toplevel_handle_v1_destroy(view->toplevel_handle);
@@ -810,6 +812,7 @@ view_destroy(struct view *view)
                /* Application got killed while moving around */
                server->input_mode = LAB_INPUT_STATE_PASSTHROUGH;
                server->grabbed_view = NULL;
+               need_cursor_update = true;
        }
 
        if (server->seat.pressed.view == view) {
@@ -819,6 +822,7 @@ view_destroy(struct view *view)
 
        if (server->focused_view == view) {
                server->focused_view = NULL;
+               need_cursor_update = true;
        }
 
        osd_on_view_destroy(view);
@@ -850,4 +854,8 @@ view_destroy(struct view *view)
        /* Remove view from server->views */
        wl_list_remove(&view->link);
        free(view);
+
+       if (need_cursor_update) {
+               cursor_update_focus(server);
+       }
 }
index db6022d6d1db3ad3ee255ca79e5e571dad659a9b..b09b6196c46bc115f5c67dc00b9796c423a65b6d 100644 (file)
@@ -11,7 +11,7 @@ unmanaged_handle_request_configure(struct wl_listener *listener, void *data)
        wlr_xwayland_surface_configure(xsurface, ev->x, ev->y, ev->width, ev->height);
        if (unmanaged->node) {
                wlr_scene_node_set_position(unmanaged->node, ev->x, ev->y);
-               cursor_update_focus(unmanaged->server, false);
+               cursor_update_focus(unmanaged->server);
        }
 }
 
@@ -23,7 +23,7 @@ unmanaged_handle_set_geometry(struct wl_listener *listener, void *data)
        struct wlr_xwayland_surface *xsurface = unmanaged->xwayland_surface;
        if (unmanaged->node) {
                wlr_scene_node_set_position(unmanaged->node, xsurface->x, xsurface->y);
-               cursor_update_focus(unmanaged->server, false);
+               cursor_update_focus(unmanaged->server);
        }
 }
 
@@ -51,7 +51,7 @@ unmanaged_handle_map(struct wl_listener *listener, void *data)
                        unmanaged->server->unmanaged_tree,
                        xsurface->surface)->buffer->node;
        wlr_scene_node_set_position(unmanaged->node, xsurface->x, xsurface->y);
-       cursor_update_focus(unmanaged->server, false);
+       cursor_update_focus(unmanaged->server);
 }
 
 static void
@@ -105,7 +105,7 @@ unmanaged_handle_unmap(struct wl_listener *listener, void *data)
                seat_reset_pressed(seat);
        }
        unmanaged->node = NULL;
-       cursor_update_focus(unmanaged->server, false);
+       cursor_update_focus(unmanaged->server);
 
        if (seat->seat->keyboard_state.focused_surface == xsurface->surface) {
                focus_next_surface(unmanaged->server, xsurface);