double x, y;
} smooth_scroll_offset;
+ /*
+ * The surface whose keyboard focus is temporarily cleared with
+ * seat_focus_override_begin() and restored with
+ * seat_focus_override_end().
+ */
+ struct {
+ struct wlr_surface *surface;
+ struct wl_listener surface_destroy;
+ } focus_override;
+
struct wlr_pointer_constraint_v1 *current_constraint;
/* In support for ToggleKeybinds */
* 'active_view' is generally the view with keyboard-focus, updated with
* each "focus change". This view is drawn with "active" SSD coloring.
*
- * The exception is when a layer-shell client takes keyboard-focus in
- * which case the currently active view stays active. This is important
- * for foreign-toplevel protocol.
+ * The exceptions are:
+ * - when a layer-shell client takes keyboard-focus in which case the
+ * currently active view stays active
+ * - when keyboard focus is temporarily cleared for server-side
+ * interactions like Move/Resize, window switcher and menus.
+ *
+ * Note that active_view is synced with foreign-toplevel clients.
*/
struct view *active_view;
/*
void seat_reset_pressed(struct seat *seat);
void seat_output_layout_changed(struct seat *seat);
+/*
+ * Temporarily clear the pointer/keyboard focus from the client at the
+ * beginning of interactive move/resize, window switcher or menu interactions.
+ * The focus is kept cleared until seat_focus_override_end() is called or
+ * layer-shell/session-lock surfaces are mapped.
+ */
+void seat_focus_override_begin(struct seat *seat, enum input_mode input_mode,
+ enum lab_cursors cursor_shape);
+/*
+ * Restore the pointer/keyboard focus which was cleared in
+ * seat_focus_override_begin().
+ */
+void seat_focus_override_end(struct seat *seat);
+
/**
* interactive_anchor_to_cursor() - repositions the geometry to remain
* underneath the cursor when its size changes during interactive move.
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);
}
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;
}
* 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;
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 *
return;
}
+ enum lab_cursors cursor_shape = LAB_CURSOR_DEFAULT;
+
switch (mode) {
case LAB_INPUT_STATE_MOVE:
if (view->fullscreen) {
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 ||
*/
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;
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.
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);
}
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
*/
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
server->menu_current = NULL;
destroy_pipemenus(server);
}
- server->input_mode = LAB_INPUT_STATE_PASSTHROUGH;
+ seat_focus_override_end(&server->seat);
}
void
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;
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);
}
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
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
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;
}
}
}
}
+
+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);
+}
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) {