diff --git a/include/labwc.h b/include/labwc.h index d7f5cbde..78280b24 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -167,6 +167,7 @@ struct seat { struct wl_client *active_client_while_inhibited; struct wl_list inputs; struct wl_listener new_input; + struct wl_listener focus_change; struct wl_listener cursor_motion; struct wl_listener cursor_motion_absolute; @@ -247,11 +248,8 @@ struct server { /* SSD state */ /* - * Currently focused view (cached value). This pointer is used - * primarily to track which view is being drawn with "active" - * SSD coloring. We consider it a bug if this gets out of sync - * with the actual keyboard focus (according to wlroots). See - * also desktop_focused_view(). + * Currently focused view. Updated with each "focus change" + * event. This view is drawn with "active" SSD coloring. */ struct view *focused_view; struct ssd_hover_state *ssd_hover_state; @@ -395,14 +393,6 @@ enum lab_cycle_dir { */ struct view *desktop_cycle_view(struct server *server, struct view *start_view, enum lab_cycle_dir dir); -/** - * desktop_focused_view - return the view that is currently focused - * (determined on-the-fly from the wlroots keyboard focus). The return - * value should ideally match server->focused_view (we consider it a - * bug otherwise) but is guaranteed to be up-to-date even if - * server->focused_view gets out of sync. - */ -struct view *desktop_focused_view(struct server *server); void desktop_focus_topmost_mapped_view(struct server *server); void keyboard_cancel_keybind_repeat(struct keyboard *keyboard); diff --git a/include/view.h b/include/view.h index 7ff69015..d5eb5838 100644 --- a/include/view.h +++ b/include/view.h @@ -186,6 +186,12 @@ enum lab_view_criteria { LAB_VIEW_CRITERIA_NO_SKIP_WINDOW_SWITCHER = 1 << 2, }; +/** + * view_from_wlr_surface() - returns the view associated with a + * wlr_surface, or NULL if the surface has no associated view. + */ +struct view *view_from_wlr_surface(struct wlr_surface *surface); + /** * for_each_view() - iterate over all views which match criteria * @view: Iterator. @@ -257,8 +263,7 @@ bool view_isfocusable(struct view *view); bool view_inhibits_keybinds(struct view *view); void view_toggle_keybinds(struct view *view); -void view_focus(struct view *view); -void view_defocus(struct view *view); +void view_set_activated(struct view *view, bool activated); void view_set_output(struct view *view, struct output *output); void view_close(struct view *view); diff --git a/src/action.c b/src/action.c index cf8b4558..bdecf68d 100644 --- a/src/action.c +++ b/src/action.c @@ -503,7 +503,7 @@ view_for_action(struct view *activator, struct server *server, return ctx.view; } default: - return desktop_focused_view(server); + return server->focused_view; } } diff --git a/src/cursor.c b/src/cursor.c index 16d9e824..773cd76a 100644 --- a/src/cursor.c +++ b/src/cursor.c @@ -602,7 +602,7 @@ create_constraint(struct wl_listener *listener, void *data) constraint->destroy.notify = destroy_constraint; wl_signal_add(&wlr_constraint->events.destroy, &constraint->destroy); - struct view *view = desktop_focused_view(server); + struct view *view = server->focused_view; if (view && view->surface == wlr_constraint->surface) { constrain_cursor(server, wlr_constraint); } diff --git a/src/desktop.c b/src/desktop.c index cbaa0de6..4d2deba1 100644 --- a/src/desktop.c +++ b/src/desktop.c @@ -74,7 +74,10 @@ desktop_focus_view(struct view *view, bool raise) workspaces_switch_to(view->workspace, /*update_focus*/ false); } - view_focus(view); + struct seat *seat = &server->seat; + if (view->surface != seat->seat->keyboard_state.focused_surface) { + seat_focus_surface(seat, view->surface); + } if (raise) { view_move_to_front(view); @@ -147,7 +150,7 @@ desktop_cycle_view(struct server *server, struct view *start_view, if (!start_view) { start_view = first_view(server); - if (!start_view || start_view != desktop_focused_view(server)) { + if (!start_view || start_view != server->focused_view) { return start_view; /* may be NULL */ } } @@ -206,32 +209,6 @@ desktop_topmost_mapped_view(struct server *server) return NULL; } -struct view * -desktop_focused_view(struct server *server) -{ - struct seat *seat = &server->seat; - struct wlr_surface *focused_surface = - seat->seat->keyboard_state.focused_surface; - struct view *focused_view = NULL; - - if (focused_surface) { - struct view *view; - wl_list_for_each(view, &server->views, link) { - if (view->surface == focused_surface) { - focused_view = view; - break; - } - } - } - - /* warn so we can identify cases where this occurs */ - if (focused_view != server->focused_view) { - wlr_log(WLR_ERROR, "server->focused_view is out of sync"); - } - - return focused_view; -} - void desktop_focus_topmost_mapped_view(struct server *server) { @@ -242,12 +219,8 @@ desktop_focus_topmost_mapped_view(struct server *server) /* * Defocus previous focused surface/view if no longer * focusable (e.g. unmapped or on a different workspace). - * Note than a non-view surface may have been focused. */ seat_focus_surface(&server->seat, NULL); - if (server->focused_view) { - view_defocus(server->focused_view); - } } } diff --git a/src/keyboard.c b/src/keyboard.c index 3f954c7c..e738afa3 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -94,7 +94,7 @@ handle_keybinding(struct server *server, uint32_t modifiers, xkb_keysym_t sym, x continue; } if (server->seat.nr_inhibited_keybind_views - && view_inhibits_keybinds(desktop_focused_view(server)) + && view_inhibits_keybinds(server->focused_view) && !actions_contain_toggle_keybinds(&keybind->actions)) { continue; } diff --git a/src/seat.c b/src/seat.c index a38858a8..c16f86dc 100644 --- a/src/seat.c +++ b/src/seat.c @@ -11,6 +11,7 @@ #include "common/mem.h" #include "key-state.h" #include "labwc.h" +#include "view.h" static void input_device_destroy(struct wl_listener *listener, void *data) @@ -335,6 +336,26 @@ new_virtual_keyboard(struct wl_listener *listener, void *data) seat_add_device(seat, input); } +static void +focus_change_notify(struct wl_listener *listener, void *data) +{ + struct seat *seat = wl_container_of(listener, seat, focus_change); + struct wlr_seat_keyboard_focus_change_event *event = data; + struct server *server = seat->server; + struct view *view = event->new_surface ? + view_from_wlr_surface(event->new_surface) : NULL; + + if (view != server->focused_view) { + if (server->focused_view) { + view_set_activated(server->focused_view, false); + } + if (view) { + view_set_activated(view, true); + } + server->focused_view = view; + } +} + void seat_init(struct server *server) { @@ -353,6 +374,10 @@ seat_init(struct server *server) seat->new_input.notify = new_input_notify; wl_signal_add(&server->backend->events.new_input, &seat->new_input); + seat->focus_change.notify = focus_change_notify; + wl_signal_add(&seat->seat->keyboard_state.events.focus_change, + &seat->focus_change); + seat->virtual_pointer = wlr_virtual_pointer_manager_v1_create( server->wl_display); wl_signal_add(&seat->virtual_pointer->events.new_virtual_pointer, @@ -382,6 +407,7 @@ seat_finish(struct server *server) { struct seat *seat = &server->seat; wl_list_remove(&seat->new_input.link); + wl_list_remove(&seat->focus_change.link); struct input *input, *next; wl_list_for_each_safe(input, next, &seat->inputs, link) { diff --git a/src/view.c b/src/view.c index ed88739a..23b345fe 100644 --- a/src/view.c +++ b/src/view.c @@ -2,6 +2,7 @@ #include #include #include +#include #include "common/mem.h" #include "common/scene-helpers.h" #include "labwc.h" @@ -19,6 +20,34 @@ #define LAB_FALLBACK_WIDTH 640 #define LAB_FALLBACK_HEIGHT 480 +struct view * +view_from_wlr_surface(struct wlr_surface *surface) +{ + assert(surface); + /* + * TODO: + * - find a way to get rid of xdg/xwayland-specific stuff + * - look up root/toplevel surface if passed a subsurface? + */ + if (wlr_surface_is_xdg_surface(surface)) { + struct wlr_xdg_surface *xdg_surface = + wlr_xdg_surface_from_wlr_surface(surface); + if (xdg_surface) { + return xdg_surface->data; + } + } +#if HAVE_XWAYLAND + if (wlr_surface_is_xwayland_surface(surface)) { + struct wlr_xwayland_surface *xsurface = + wlr_xwayland_surface_from_wlr_surface(surface); + if (xsurface) { + return xsurface->data; + } + } +#endif + return NULL; +} + static bool matches_criteria(struct view *view, enum lab_view_criteria criteria) { @@ -174,9 +203,10 @@ view_discover_output(struct view *view) view->current.y + view->current.height / 2); } -static void -_view_set_activated(struct view *view, bool activated) +void +view_set_activated(struct view *view, bool activated) { + assert(view); ssd_set_active(view->ssd, activated); if (view->impl->set_activated) { view->impl->set_activated(view, activated); @@ -187,52 +217,6 @@ _view_set_activated(struct view *view, bool activated) } } -/* - * Give the view keyboard focus and mark it active. This function should - * only be called by desktop_focus_view(), which contains additional - * checks to make sure it's okay to give focus. - */ -void -view_focus(struct view *view) -{ - assert(view); - /* Update seat focus */ - struct seat *seat = &view->server->seat; - if (view->surface != seat->seat->keyboard_state.focused_surface) { - seat_focus_surface(seat, view->surface); - } - /* Update active view */ - if (view != view->server->focused_view) { - if (view->server->focused_view) { - _view_set_activated(view->server->focused_view, false); - } - _view_set_activated(view, true); - view->server->focused_view = view; - } -} - -/* - * Take keyboard focus from the view and mark it inactive. It's rarely - * necessary to call this function directly; usually it's better to - * focus a different view instead by calling something like - * desktop_focus_topmost_mapped_view(). - */ -void -view_defocus(struct view *view) -{ - assert(view); - /* Update seat focus */ - struct seat *seat = &view->server->seat; - if (view->surface == seat->seat->keyboard_state.focused_surface) { - seat_focus_surface(seat, NULL); - } - /* Update active view */ - if (view == view->server->focused_view) { - _view_set_activated(view, false); - view->server->focused_view = NULL; - } -} - void view_set_output(struct view *view, struct output *output) { @@ -408,7 +392,6 @@ _minimize(struct view *view, bool minimized) view->minimized = minimized; if (minimized) { view->impl->unmap(view, /* client_request */ false); - view_defocus(view); } else { view->impl->map(view); } diff --git a/src/workspaces.c b/src/workspaces.c index 48be68f3..19606ffa 100644 --- a/src/workspaces.c +++ b/src/workspaces.c @@ -287,7 +287,7 @@ workspaces_switch_to(struct workspace *target, bool update_focus) * Only refocus if the focus is not already on an always-on-top view. */ if (update_focus) { - struct view *view = desktop_focused_view(server); + struct view *view = server->focused_view; if (!view || !view_is_always_on_top(view)) { desktop_focus_topmost_mapped_view(server); }