mirror of
				https://github.com/labwc/labwc.git
				synced 2025-11-03 09:01:51 -05:00 
			
		
		
		
	Clear keyboard/pointer focus on Move/Resize, window switcher and menu
The previous revert fixed the problem of stuck modifier keys with
keybinds in Blender, but made Firefox show its menu bar with Alt-*
keybinds. This is fundamentally inevitable due to the limitation of
wayland protocol, but at least for the default Alt-Tab keybind for
window switcher, we can mitigate this problem by clearing the keyboard
focus when the window switcher is activated. This is what KWin does, and
we decided to follow that.
So in this commit, keyboard and pointer focus are temporarily cleared
while Move/Resize, window switcher and menu interactions and restored
after them. We slightly deviate from KWin as KWin doesn't clear the
keyboard focus while Move/Resize, but it solves our existing problem
that Firefox shows its menu bar after dragging it with default Alt-Drag
mousebind, and this is what Mutter does.
We considered other solutions, but they don't work well:
1. Send wl_keyboard.{leave,enter} every time keybinds/mousebinds are
   triggered. This solves the Firefox's menu bar problem, but that
   sounds like a workaround and sending unnecessary events every time is
   not desirable.
2. Send release events for both modifiers and keys even when they are
   bound to keybinds. This is what Mutter is doing, but it looks like an
   implementation issue and violates wayland protocol.
			
			
This commit is contained in:
		
							parent
							
								
									bd7a533dd6
								
							
						
					
					
						commit
						bad788ccdd
					
				
					 9 changed files with 139 additions and 54 deletions
				
			
		| 
						 | 
				
			
			@ -810,7 +810,9 @@ start_window_cycling(struct server *server, enum lab_cycle_dir direction)
 | 
			
		|||
		shift_is_pressed(server);
 | 
			
		||||
	server->osd_state.cycle_view = desktop_cycle_view(server,
 | 
			
		||||
		server->osd_state.cycle_view, direction);
 | 
			
		||||
	server->input_mode = LAB_INPUT_STATE_WINDOW_SWITCHER;
 | 
			
		||||
 | 
			
		||||
	seat_focus_override_begin(&server->seat,
 | 
			
		||||
		LAB_INPUT_STATE_WINDOW_SWITCHER, LAB_CURSOR_DEFAULT);
 | 
			
		||||
	osd_update(server);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -496,7 +496,8 @@ cursor_update_common(struct server *server, struct cursor_context *ctx,
 | 
			
		|||
	if (server->input_mode != LAB_INPUT_STATE_PASSTHROUGH) {
 | 
			
		||||
		/*
 | 
			
		||||
		 * Prevent updating focus/cursor image during
 | 
			
		||||
		 * interactive move/resize
 | 
			
		||||
		 * interactive move/resize, window switcher and
 | 
			
		||||
		 * menu interaction.
 | 
			
		||||
		 */
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -524,28 +525,9 @@ cursor_update_common(struct server *server, struct cursor_context *ctx,
 | 
			
		|||
		 * cursor image will be set by request_cursor_notify()
 | 
			
		||||
		 * in response to the enter event.
 | 
			
		||||
		 */
 | 
			
		||||
		bool has_focus = (ctx->surface ==
 | 
			
		||||
			wlr_seat->pointer_state.focused_surface);
 | 
			
		||||
 | 
			
		||||
		if (!has_focus || seat->server_cursor != LAB_CURSOR_CLIENT) {
 | 
			
		||||
			/*
 | 
			
		||||
			 * 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.
 | 
			
		||||
			 */
 | 
			
		||||
			if (has_focus) {
 | 
			
		||||
				wlr_seat_pointer_notify_clear_focus(wlr_seat);
 | 
			
		||||
			}
 | 
			
		||||
			wlr_seat_pointer_notify_enter(wlr_seat, ctx->surface,
 | 
			
		||||
				ctx->sx, ctx->sy);
 | 
			
		||||
			seat->server_cursor = LAB_CURSOR_CLIENT;
 | 
			
		||||
		}
 | 
			
		||||
		wlr_seat_pointer_notify_enter(wlr_seat, ctx->surface,
 | 
			
		||||
			ctx->sx, ctx->sy);
 | 
			
		||||
		seat->server_cursor = LAB_CURSOR_CLIENT;
 | 
			
		||||
		if (cursor_has_moved) {
 | 
			
		||||
			*sx = ctx->sx;
 | 
			
		||||
			*sy = ctx->sy;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -69,15 +69,18 @@ keyboard_any_modifiers_pressed(struct wlr_keyboard *keyboard)
 | 
			
		|||
static void
 | 
			
		||||
end_cycling(struct server *server)
 | 
			
		||||
{
 | 
			
		||||
	osd_preview_restore(server);
 | 
			
		||||
	if (server->osd_state.cycle_view) {
 | 
			
		||||
		desktop_focus_view(server->osd_state.cycle_view,
 | 
			
		||||
			/*raise*/ true);
 | 
			
		||||
	should_cancel_cycling_on_next_key_release = false;
 | 
			
		||||
 | 
			
		||||
	if (server->input_mode != LAB_INPUT_STATE_WINDOW_SWITCHER) {
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* osd_finish() additionally resets cycle_view to NULL */
 | 
			
		||||
	struct view *cycle_view = server->osd_state.cycle_view;
 | 
			
		||||
	osd_preview_restore(server);
 | 
			
		||||
	/* FIXME: osd_finish() transiently sets focus to the old surface */
 | 
			
		||||
	osd_finish(server);
 | 
			
		||||
	should_cancel_cycling_on_next_key_release = false;
 | 
			
		||||
	/* Note that server->osd_state.cycle_view is cleared at this point */
 | 
			
		||||
	desktop_focus_view(cycle_view, /*raise*/ true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct wlr_seat_client *
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -73,6 +73,8 @@ interactive_begin(struct view *view, enum input_mode mode, uint32_t edges)
 | 
			
		|||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	enum lab_cursors cursor_shape = LAB_CURSOR_DEFAULT;
 | 
			
		||||
 | 
			
		||||
	switch (mode) {
 | 
			
		||||
	case LAB_INPUT_STATE_MOVE:
 | 
			
		||||
		if (view->fullscreen) {
 | 
			
		||||
| 
						 | 
				
			
			@ -96,7 +98,7 @@ interactive_begin(struct view *view, enum input_mode mode, uint32_t edges)
 | 
			
		|||
		struct wlr_keyboard *keyboard = &seat->keyboard_group->keyboard;
 | 
			
		||||
		seat->region_prevent_snap = keyboard_any_modifiers_pressed(keyboard);
 | 
			
		||||
 | 
			
		||||
		cursor_set(seat, LAB_CURSOR_GRAB);
 | 
			
		||||
		cursor_shape = LAB_CURSOR_GRAB;
 | 
			
		||||
		break;
 | 
			
		||||
	case LAB_INPUT_STATE_RESIZE:
 | 
			
		||||
		if (view->shaded || view->fullscreen ||
 | 
			
		||||
| 
						 | 
				
			
			@ -121,14 +123,13 @@ interactive_begin(struct view *view, enum input_mode mode, uint32_t edges)
 | 
			
		|||
		 */
 | 
			
		||||
		view_set_untiled(view);
 | 
			
		||||
		view_restore_to(view, view->pending);
 | 
			
		||||
		cursor_set(seat, cursor_get_from_edge(edges));
 | 
			
		||||
		cursor_shape = cursor_get_from_edge(edges);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		/* Should not be reached */
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	server->input_mode = mode;
 | 
			
		||||
	server->grabbed_view = view;
 | 
			
		||||
	/* Remember view and cursor positions at start of move/resize */
 | 
			
		||||
	server->grab_x = seat->cursor->x;
 | 
			
		||||
| 
						 | 
				
			
			@ -136,6 +137,8 @@ interactive_begin(struct view *view, enum input_mode mode, uint32_t edges)
 | 
			
		|||
	server->grab_box = view->current;
 | 
			
		||||
	server->resize_edges = edges;
 | 
			
		||||
 | 
			
		||||
	seat_focus_override_begin(seat, mode, cursor_shape);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Un-tile maximized/tiled view immediately if <unSnapThreshold> is
 | 
			
		||||
	 * zero. Otherwise, un-tile it later in cursor motion handler.
 | 
			
		||||
| 
						 | 
				
			
			@ -281,9 +284,8 @@ interactive_cancel(struct view *view)
 | 
			
		|||
 | 
			
		||||
	resize_indicator_hide(view);
 | 
			
		||||
 | 
			
		||||
	view->server->input_mode = LAB_INPUT_STATE_PASSTHROUGH;
 | 
			
		||||
	view->server->grabbed_view = NULL;
 | 
			
		||||
 | 
			
		||||
	/* Update focus/cursor image */
 | 
			
		||||
	cursor_update_focus(view->server);
 | 
			
		||||
	/* Restore keyboard/pointer focus */
 | 
			
		||||
	seat_focus_override_end(&view->server->seat);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1336,8 +1336,9 @@ menu_open_root(struct menu *menu, int x, int y)
 | 
			
		|||
	menu_configure(menu, (struct wlr_box){.x = x, .y = y});
 | 
			
		||||
	wlr_scene_node_set_enabled(&menu->scene_tree->node, true);
 | 
			
		||||
	menu->server->menu_current = menu;
 | 
			
		||||
	menu->server->input_mode = LAB_INPUT_STATE_MENU;
 | 
			
		||||
	selected_item = NULL;
 | 
			
		||||
	seat_focus_override_begin(&menu->server->seat,
 | 
			
		||||
		LAB_INPUT_STATE_MENU, LAB_CURSOR_DEFAULT);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
| 
						 | 
				
			
			@ -1628,8 +1629,7 @@ menu_execute_item(struct menuitem *item)
 | 
			
		|||
	 */
 | 
			
		||||
	struct server *server = item->parent->server;
 | 
			
		||||
	menu_close(server->menu_current);
 | 
			
		||||
	server->input_mode = LAB_INPUT_STATE_PASSTHROUGH;
 | 
			
		||||
	cursor_update_focus(server);
 | 
			
		||||
	seat_focus_override_end(&server->seat);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * We call the actions after closing the menu so that virtual keyboard
 | 
			
		||||
| 
						 | 
				
			
			@ -1739,7 +1739,7 @@ menu_close_root(struct server *server)
 | 
			
		|||
		server->menu_current = NULL;
 | 
			
		||||
		destroy_pipemenus(server);
 | 
			
		||||
	}
 | 
			
		||||
	server->input_mode = LAB_INPUT_STATE_PASSTHROUGH;
 | 
			
		||||
	seat_focus_override_end(&server->seat);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -105,7 +105,8 @@ osd_on_view_destroy(struct view *view)
 | 
			
		|||
void
 | 
			
		||||
osd_finish(struct server *server)
 | 
			
		||||
{
 | 
			
		||||
	server->input_mode = LAB_INPUT_STATE_PASSTHROUGH;
 | 
			
		||||
	seat_focus_override_end(&server->seat);
 | 
			
		||||
 | 
			
		||||
	server->osd_state.preview_node = NULL;
 | 
			
		||||
	server->osd_state.preview_anchor = NULL;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										83
									
								
								src/seat.c
									
										
									
									
									
								
							
							
						
						
									
										83
									
								
								src/seat.c
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -507,6 +507,14 @@ focus_change_notify(struct wl_listener *listener, void *data)
 | 
			
		|||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * We clear the keyboard focus at the beginning of Move/Resize, window
 | 
			
		||||
	 * switcher and opening menus, but don't want to deactivate the view.
 | 
			
		||||
	 */
 | 
			
		||||
	if (server->input_mode != LAB_INPUT_STATE_PASSTHROUGH) {
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (view != server->active_view) {
 | 
			
		||||
		if (server->active_view) {
 | 
			
		||||
			view_set_activated(server->active_view, false);
 | 
			
		||||
| 
						 | 
				
			
			@ -656,8 +664,16 @@ seat_reconfigure(struct server *server)
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
seat_focus(struct seat *seat, struct wlr_surface *surface, bool is_lock_surface)
 | 
			
		||||
seat_focus(struct seat *seat, struct wlr_surface *surface,
 | 
			
		||||
		bool replace_exclusive_layer, bool is_lock_surface)
 | 
			
		||||
{
 | 
			
		||||
	/* Respect layer-shell exclusive keyboard-interactivity. */
 | 
			
		||||
	if (seat->focused_layer && seat->focused_layer->current.keyboard_interactive
 | 
			
		||||
			== ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE
 | 
			
		||||
				&& !replace_exclusive_layer) {
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Respect session lock. This check is critical, DO NOT REMOVE.
 | 
			
		||||
	 * It should also come before the !surface condition, or the
 | 
			
		||||
| 
						 | 
				
			
			@ -708,18 +724,20 @@ seat_focus(struct seat *seat, struct wlr_surface *surface, bool is_lock_surface)
 | 
			
		|||
void
 | 
			
		||||
seat_focus_surface(struct seat *seat, struct wlr_surface *surface)
 | 
			
		||||
{
 | 
			
		||||
	/* Respect layer-shell exclusive keyboard-interactivity. */
 | 
			
		||||
	if (seat->focused_layer && seat->focused_layer->current.keyboard_interactive
 | 
			
		||||
			== ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE) {
 | 
			
		||||
	/* Don't update focus while window switcher, Move/Resize and menu interaction */
 | 
			
		||||
	if (seat->server->osd_state.cycle_view || seat->server->input_mode
 | 
			
		||||
			!= LAB_INPUT_STATE_PASSTHROUGH) {
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	seat_focus(seat, surface, /*is_lock_surface*/ false);
 | 
			
		||||
	seat_focus(seat, surface, /*replace_exclusive_layer*/ false,
 | 
			
		||||
		/*is_lock_surface*/ false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
seat_focus_lock_surface(struct seat *seat, struct wlr_surface *surface)
 | 
			
		||||
{
 | 
			
		||||
	seat_focus(seat, surface, /*is_lock_surface*/ true);
 | 
			
		||||
	seat_focus(seat, surface, /*replace_exclusive_layer*/ true,
 | 
			
		||||
		/*is_lock_surface*/ true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
| 
						 | 
				
			
			@ -730,7 +748,8 @@ seat_set_focus_layer(struct seat *seat, struct wlr_layer_surface_v1 *layer)
 | 
			
		|||
		desktop_focus_topmost_view(seat->server);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	seat_focus(seat, layer->surface, /*is_lock_surface*/ false);
 | 
			
		||||
	seat_focus(seat, layer->surface, /*replace_exclusive_layer*/ true,
 | 
			
		||||
		/*is_lock_surface*/ false);
 | 
			
		||||
	seat->focused_layer = layer;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -794,3 +813,53 @@ seat_output_layout_changed(struct seat *seat)
 | 
			
		|||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
handle_focus_override_surface_destroy(struct wl_listener *listener, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct seat *seat = wl_container_of(listener, seat,
 | 
			
		||||
		focus_override.surface_destroy);
 | 
			
		||||
	wl_list_remove(&seat->focus_override.surface_destroy.link);
 | 
			
		||||
	seat->focus_override.surface = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
seat_focus_override_begin(struct seat *seat, enum input_mode input_mode,
 | 
			
		||||
	enum lab_cursors cursor_shape)
 | 
			
		||||
{
 | 
			
		||||
	assert(!seat->focus_override.surface);
 | 
			
		||||
	assert(seat->server->input_mode == LAB_INPUT_STATE_PASSTHROUGH);
 | 
			
		||||
 | 
			
		||||
	seat->server->input_mode = input_mode;
 | 
			
		||||
 | 
			
		||||
	seat->focus_override.surface = seat->seat->keyboard_state.focused_surface;
 | 
			
		||||
	if (seat->focus_override.surface) {
 | 
			
		||||
		seat->focus_override.surface_destroy.notify =
 | 
			
		||||
			handle_focus_override_surface_destroy;
 | 
			
		||||
		wl_signal_add(&seat->focus_override.surface->events.destroy,
 | 
			
		||||
			&seat->focus_override.surface_destroy);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	seat_focus(seat, NULL, /*replace_exclusive_layer*/ false,
 | 
			
		||||
		/*is_lock_surface*/ false);
 | 
			
		||||
	wlr_seat_pointer_clear_focus(seat->seat);
 | 
			
		||||
	cursor_set(seat, cursor_shape);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
seat_focus_override_end(struct seat *seat)
 | 
			
		||||
{
 | 
			
		||||
	seat->server->input_mode = LAB_INPUT_STATE_PASSTHROUGH;
 | 
			
		||||
 | 
			
		||||
	if (seat->focus_override.surface) {
 | 
			
		||||
		if (!seat->seat->keyboard_state.focused_surface) {
 | 
			
		||||
			seat_focus(seat, seat->focus_override.surface,
 | 
			
		||||
				/*replace_exclusive_layer*/ false,
 | 
			
		||||
				/*is_lock_surface*/ false);
 | 
			
		||||
		}
 | 
			
		||||
		wl_list_remove(&seat->focus_override.surface_destroy.link);
 | 
			
		||||
		seat->focus_override.surface = NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cursor_update_focus(seat->server);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2531,9 +2531,7 @@ view_destroy(struct view *view)
 | 
			
		|||
 | 
			
		||||
	if (server->grabbed_view == view) {
 | 
			
		||||
		/* Application got killed while moving around */
 | 
			
		||||
		server->input_mode = LAB_INPUT_STATE_PASSTHROUGH;
 | 
			
		||||
		server->grabbed_view = NULL;
 | 
			
		||||
		overlay_hide(&server->seat);
 | 
			
		||||
		interactive_cancel(view);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (server->active_view == view) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue