diff --git a/include/labwc.h b/include/labwc.h index 2747aa9e..70199998 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -79,6 +79,18 @@ struct seat { /* if set, views cannot receive focus */ struct wlr_layer_surface_v1 *focused_layer; + /** + * active_view will usually be NULL and is only set on button press + * while the mouse is over a view surface and reset to NULL on button + * release. + * It is used to send cursor motion events to a surface even though + * the cursor has left the surface in the meantime. + * + * This allows to keep dragging a scrollbar or selecting text even + * when moving outside of the window. + */ + struct view *active_view; + struct wl_client *active_client_while_inhibited; struct wl_list inputs; struct wl_listener new_input; diff --git a/src/cursor.c b/src/cursor.c index 03117baf..d03fee04 100644 --- a/src/cursor.c +++ b/src/cursor.c @@ -213,6 +213,15 @@ process_cursor_motion(struct server *server, uint32_t time) server->seat.cursor->x, server->seat.cursor->y); damage_all_outputs(server); return; + } else if (server->seat.active_view && !server->seat.drag_icon) { + /* Button has been pressed while over a view surface */ + struct view *view = server->seat.active_view; + double sx = server->seat.cursor->x - view->x; + double sy = server->seat.cursor->y - view->y; + sx = sx < 0 ? 0 : (sx > view->w ? view->w : sx); + sy = sy < 0 ? 0 : (sy > view->h ? view->h : sy); + wlr_seat_pointer_notify_motion(server->seat.seat, time, sx, sy); + return; } /* Otherwise, find view under the pointer and send the event along */ @@ -321,6 +330,7 @@ start_drag(struct wl_listener *listener, void *data) { struct seat *seat = wl_container_of(listener, seat, start_drag); struct wlr_drag *wlr_drag = data; + seat->active_view = NULL; seat->drag_icon = wlr_drag->icon; wl_signal_add(&seat->drag_icon->events.destroy, &seat->destroy_drag); } @@ -627,6 +637,8 @@ cursor_button(struct wl_listener *listener, void *data) /* handle _release_ */ if (event->state == WLR_BUTTON_RELEASED) { + server->seat.active_view = NULL; + if (server->input_mode == LAB_INPUT_STATE_MENU) { return; } @@ -658,6 +670,11 @@ cursor_button(struct wl_listener *listener, void *data) goto mousebindings; } + /* Handle _press */ + if (view_area == LAB_SSD_CLIENT) { + server->seat.active_view = view; + } + if (server->input_mode == LAB_INPUT_STATE_MENU) { if (server->rootmenu->visible) { menu_action_selected(server, server->rootmenu); diff --git a/src/xdg.c b/src/xdg.c index 02b202e3..b66341fc 100644 --- a/src/xdg.c +++ b/src/xdg.c @@ -96,6 +96,9 @@ handle_destroy(struct wl_listener *listener, void *data) wlr_foreign_toplevel_handle_v1_destroy(view->toplevel_handle); } interactive_end(view); + if (view->server->seat.active_view == view) { + view->server->seat.active_view = NULL; + } wl_list_remove(&view->link); wl_list_remove(&view->destroy.link); ssd_destroy(view); diff --git a/src/xwayland.c b/src/xwayland.c index a8194562..23a4cbc2 100644 --- a/src/xwayland.c +++ b/src/xwayland.c @@ -82,6 +82,9 @@ handle_destroy(struct wl_listener *listener, void *data) wlr_foreign_toplevel_handle_v1_destroy(view->toplevel_handle); } interactive_end(view); + if (view->server->seat.active_view == view) { + view->server->seat.active_view = NULL; + } view->xwayland_surface = NULL; wl_list_remove(&view->link); wl_list_remove(&view->map.link);