Handle 'lost' unmanaged xsurfaces + improve cursor handling

This commit is contained in:
Consolatis 2022-02-22 07:57:17 +01:00 committed by Johan Malm
parent 5862b65f89
commit a2523081e2
6 changed files with 135 additions and 76 deletions

View file

@ -143,9 +143,6 @@ struct server {
struct seat seat; struct seat seat;
struct wlr_scene *scene; struct wlr_scene *scene;
/* Tree for all non-layer xdg/xwayland-shell surfaces */
struct wlr_scene_tree *view_tree;
/* cursor interactive */ /* cursor interactive */
enum input_mode input_mode; enum input_mode input_mode;
struct view *grabbed_view; struct view *grabbed_view;
@ -157,8 +154,16 @@ struct server {
struct view *ssd_focused_view; struct view *ssd_focused_view;
struct ssd_hover_state ssd_hover_state; 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; 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_list outputs;
struct wl_listener new_output; 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) * 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 * Behavior if node points to a surface:
* view_area will be LAB_SSD_CLIENT * - 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, struct view *desktop_node_and_view_at(struct server *server, double lx,
double ly, struct wlr_scene_node **scene_node, double *sx, double *sy, double ly, struct wlr_scene_node **scene_node, double *sx, double *sy,

View file

@ -45,6 +45,7 @@ enum ssd_part_type {
LAB_SSD_MENU, LAB_SSD_MENU,
LAB_SSD_OSD, LAB_SSD_OSD,
LAB_SSD_LAYER_SURFACE, LAB_SSD_LAYER_SURFACE,
LAB_SSD_UNMANAGED,
LAB_SSD_END_MARKER LAB_SSD_END_MARKER
}; };

View file

@ -12,6 +12,17 @@
#include "ssd.h" #include "ssd.h"
#include "config/mousebind.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 void
cursor_rebase(struct seat *seat, uint32_t time_msec) 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, desktop_node_and_view_at(seat->server, seat->cursor->x,
seat->cursor->y, &node, &sx, &sy, &view_area); 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; 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); &sx, &sy, &view_area);
struct wlr_surface *surface = NULL; 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; 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); uint32_t resize_edges = ssd_resize_edges(view_area);
/* Set cursor */ /* Set cursor */
if (!view) { if (view_area == LAB_SSD_ROOT || view_area == LAB_SSD_MENU) {
/* root, etc. */
cursor_set(&server->seat, XCURSOR_DEFAULT); cursor_set(&server->seat, XCURSOR_DEFAULT);
} else { } else if (resize_edges) {
if (resize_edges) { cursor_name_set_by_server = true;
cursor_name_set_by_server = true; cursor_set(&server->seat,
cursor_set(&server->seat, wlr_xcursor_get_resize_name(resize_edges));
wlr_xcursor_get_resize_name(resize_edges)); } else if (ssd_part_contains(LAB_SSD_PART_TITLEBAR, view_area)) {
} else if (ssd_part_contains(LAB_SSD_PART_TITLEBAR, view_area)) { /* title and buttons */
/* title and buttons */ cursor_set(&server->seat, XCURSOR_DEFAULT);
cursor_set(&server->seat, XCURSOR_DEFAULT); cursor_name_set_by_server = true;
cursor_name_set_by_server = true; } else if (cursor_name_set_by_server) {
} else if (cursor_name_set_by_server) { /* xdg/xwindow window content */
/* window content */ /* layershell or unmanaged */
cursor_set(&server->seat, XCURSOR_DEFAULT); cursor_set(&server->seat, XCURSOR_DEFAULT);
cursor_name_set_by_server = false; cursor_name_set_by_server = false;
}
} }
if (view_area == LAB_SSD_MENU) { if (view_area == LAB_SSD_MENU) {
@ -496,6 +505,13 @@ handle_release_mousebinding(struct view *view, struct server *server,
break; break;
} }
continue; 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: default:
continue; continue;
} }
@ -565,6 +581,9 @@ handle_press_mousebinding(struct view *view, struct server *server,
* counted as a DOUBLECLICK. * counted as a DOUBLECLICK.
*/ */
if (!double_click) { 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; mousebind->pressed_in_context = true;
} }
continue; continue;
@ -601,7 +620,15 @@ cursor_button(struct wl_listener *listener, void *data)
double sx, sy; double sx, sy;
struct wlr_scene_node *node; struct wlr_scene_node *node;
enum ssd_part_type view_area = LAB_SSD_NONE; 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 */ /* bindings to the Frame context swallow mouse events if activated */
bool triggered_frame_binding = false; bool triggered_frame_binding = false;
@ -611,7 +638,7 @@ cursor_button(struct wl_listener *listener, void *data)
&sx, &sy, &view_area); &sx, &sy, &view_area);
struct wlr_surface *surface = NULL; 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; surface = wlr_scene_surface_from_node(node)->surface;
} }
@ -622,9 +649,17 @@ cursor_button(struct wl_listener *listener, void *data)
/* handle _release_ */ /* handle _release_ */
if (event->state == WLR_BUTTON_RELEASED) { if (event->state == WLR_BUTTON_RELEASED) {
if (server->input_mode == LAB_INPUT_STATE_MENU) { 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; return;
} }
damage_all_outputs(server);
if (server->input_mode != LAB_INPUT_STATE_PASSTHROUGH) { if (server->input_mode != LAB_INPUT_STATE_PASSTHROUGH) {
/* Exit interactive move/resize/menu mode. */ /* Exit interactive move/resize/menu mode. */
if (server->grabbed_view == view) { if (server->grabbed_view == view) {
@ -634,63 +669,41 @@ cursor_button(struct wl_listener *listener, void *data)
server->grabbed_view = NULL; server->grabbed_view = NULL;
} }
cursor_rebase(&server->seat, event->time_msec); cursor_rebase(&server->seat, event->time_msec);
} return;
/* Handle _release_ on root window */
if (!view) {
handle_release_mousebinding(NULL, server, event->button,
modifiers, LAB_SSD_ROOT, 0);
} }
goto mousebindings; goto mousebindings;
} }
/* Handle _press */
if (server->input_mode == LAB_INPUT_STATE_MENU) { if (server->input_mode == LAB_INPUT_STATE_MENU) {
if (!server->menu_current) { if (view_area != LAB_SSD_MENU) {
wlr_log(WLR_ERROR, /* We close the menu on release so we don't leak a stray release */
"on mouse button input_mode STATE_MENU but no current menu"); close_menu = true;
} else if (view_area != LAB_SSD_MENU) { } else if (menu_call_actions(server->menu_current, node)) {
menu_close(server->menu_current); /* Action was successfull, may fail if item contains a submenu */
server->menu_current = NULL; close_menu = true;
} else if (!menu_call_actions(server->menu_current, node)) {
/* Action was not successfull, maybe this menu has a submenu */
return;
} }
/* 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; return;
} }
/* Handle _press_ on a layer surface */ /* Handle _press_ on a layer surface */
if (view_area == LAB_SSD_LAYER_SURFACE && surface) { if (view_area == LAB_SSD_LAYER_SURFACE) {
if (!wlr_surface_is_layer_surface(surface)) {
return;
}
struct wlr_layer_surface_v1 *layer = struct wlr_layer_surface_v1 *layer =
wlr_layer_surface_v1_from_wlr_surface(surface); wlr_layer_surface_v1_from_wlr_surface(surface);
if (layer->current.keyboard_interactive) { if (layer->current.keyboard_interactive) {
seat_set_focus_layer(&server->seat, layer); 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) {
if (!view) { /* Determine closest resize edges in case action is Resize */
handle_press_mousebinding(NULL, server, resize_edges = ssd_resize_edges(view_area);
event->button, modifiers, LAB_SSD_ROOT, 0); if (!resize_edges) {
return; 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
/* Determine closest resize edges in case action is Resize */ ? WLR_EDGE_TOP : WLR_EDGE_BOTTOM;
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: mousebindings:
@ -703,7 +716,7 @@ mousebindings:
server, event->button, modifiers, server, event->button, modifiers,
view_area, resize_edges); view_area, resize_edges);
} }
if (!triggered_frame_binding) { if (surface && !triggered_frame_binding) {
/* Notify client with pointer focus of button press */ /* Notify client with pointer focus of button press */
wlr_seat_pointer_notify_button(seat->seat, event->time_msec, wlr_seat_pointer_notify_button(seat->seat, event->time_msec,
event->button, event->state); event->button, event->state);

View file

@ -266,7 +266,7 @@ desktop_node_and_view_at(struct server *server, double lx, double ly,
*scene_node = node; *scene_node = node;
if (!node) { if (!node) {
*view_area = LAB_SSD_NONE; *view_area = LAB_SSD_ROOT;
return NULL; return NULL;
} }
if (node->type == WLR_SCENE_NODE_SURFACE) { 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; *view_area = LAB_SSD_LAYER_SURFACE;
return NULL; 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 *osd = &server->osd_tree->node;
struct wlr_scene_node *menu = &server->menu_tree->node; struct wlr_scene_node *menu = &server->menu_tree->node;

View file

@ -224,8 +224,11 @@ server_init(struct server *server)
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
server->view_tree = wlr_scene_tree_create(&server->scene->node); 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->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); wlr_scene_attach_output_layout(server->scene, server->output_layout);
/* /*
@ -406,6 +409,8 @@ server_start(struct server *server)
void void
server_finish(struct server *server) server_finish(struct server *server)
{ {
/* TODO: clean up various scene_tree nodes */
#if HAVE_XWAYLAND #if HAVE_XWAYLAND
wlr_xwayland_destroy(server->xwayland); wlr_xwayland_destroy(server->xwayland);
#endif #endif

View file

@ -32,7 +32,7 @@ parent_view(struct server *server, struct wlr_xwayland_surface *surface)
} }
struct view *view; struct view *view;
wl_list_for_each(view, &server->views, link) { wl_list_for_each(view, &server->views, link) {
if (view->surface == s->surface) { if (view->xwayland_surface == s) {
return view; return view;
} }
} }
@ -59,11 +59,20 @@ unmanaged_handle_map(struct wl_listener *listener, void *data)
seat_focus_surface(&unmanaged->server->seat, xsurface->surface); 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 view *view = parent_view(unmanaged->server, xsurface);
struct wlr_scene_node *node = wlr_scene_subsurface_tree_create( if (!view || !view->scene_tree) {
view->scene_node, xsurface->surface); parent = &view->server->unmanaged_tree->node;
wlr_scene_node_set_position(node, unmanaged->lx - view->x, } else {
unmanaged->ly - view->y); 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 static void