shell: Store focus state in workspaces
When moving moving back to a workspace or resuming a locked desktop the keyboard focus state information was lost. By pushing the state to the workspace when navigating away from a workspace, or locking a desktop, we can restore it when navigating back, or resuming. Signed-off-by: Jonas Ådahl <jadahl@gmail.com>
This commit is contained in:
parent
62fcd0417e
commit
04769744e1
159
src/shell.c
159
src/shell.c
@ -48,8 +48,19 @@ enum animation_type {
|
|||||||
ANIMATION_FADE
|
ANIMATION_FADE
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct focus_state {
|
||||||
|
struct weston_seat *seat;
|
||||||
|
struct weston_surface *keyboard_focus;
|
||||||
|
struct wl_list link;
|
||||||
|
struct wl_listener seat_destroy_listener;
|
||||||
|
struct wl_listener surface_destroy_listener;
|
||||||
|
};
|
||||||
|
|
||||||
struct workspace {
|
struct workspace {
|
||||||
struct weston_layer layer;
|
struct weston_layer layer;
|
||||||
|
|
||||||
|
struct wl_list focus_list;
|
||||||
|
struct wl_listener seat_destroyed_listener;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct desktop_shell {
|
struct desktop_shell {
|
||||||
@ -176,6 +187,8 @@ struct shell_surface {
|
|||||||
|
|
||||||
struct weston_transform workspace_transform;
|
struct weston_transform workspace_transform;
|
||||||
|
|
||||||
|
struct focus_state *focus_state;
|
||||||
|
|
||||||
struct weston_output *fullscreen_output;
|
struct weston_output *fullscreen_output;
|
||||||
struct weston_output *output;
|
struct weston_output *output;
|
||||||
struct wl_list link;
|
struct wl_list link;
|
||||||
@ -329,12 +342,127 @@ shell_configuration(struct desktop_shell *shell)
|
|||||||
shell->workspaces.num = num_workspaces > 0 ? num_workspaces : 1;
|
shell->workspaces.num = num_workspaces > 0 ? num_workspaces : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
focus_state_destroy(struct focus_state *state)
|
||||||
|
{
|
||||||
|
wl_list_remove(&state->seat_destroy_listener.link);
|
||||||
|
wl_list_remove(&state->surface_destroy_listener.link);
|
||||||
|
free(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
focus_state_seat_destroy(struct wl_listener *listener, void *data)
|
||||||
|
{
|
||||||
|
struct focus_state *state = container_of(listener,
|
||||||
|
struct focus_state,
|
||||||
|
seat_destroy_listener);
|
||||||
|
|
||||||
|
wl_list_remove(&state->link);
|
||||||
|
focus_state_destroy(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
focus_state_surface_destroy(struct wl_listener *listener, void *data)
|
||||||
|
{
|
||||||
|
struct focus_state *state = container_of(listener,
|
||||||
|
struct focus_state,
|
||||||
|
seat_destroy_listener);
|
||||||
|
|
||||||
|
wl_list_remove(&state->link);
|
||||||
|
focus_state_destroy(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct focus_state *
|
||||||
|
focus_state_create(struct weston_seat *seat)
|
||||||
|
{
|
||||||
|
struct wl_keyboard *keyboard = seat->seat.keyboard;
|
||||||
|
struct focus_state *state;
|
||||||
|
struct wl_surface *surface;
|
||||||
|
struct shell_surface *shsurf;
|
||||||
|
|
||||||
|
state = malloc(sizeof *state);
|
||||||
|
if (state == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
surface = keyboard->focus;
|
||||||
|
shsurf = get_shell_surface((struct weston_surface *)keyboard->focus);
|
||||||
|
shsurf->focus_state = state;
|
||||||
|
|
||||||
|
state->seat = seat;
|
||||||
|
state->keyboard_focus = shsurf->surface;
|
||||||
|
wl_list_init(&state->link);
|
||||||
|
|
||||||
|
state->seat_destroy_listener.notify = focus_state_seat_destroy;
|
||||||
|
state->surface_destroy_listener.notify = focus_state_surface_destroy;
|
||||||
|
wl_signal_add(&seat->seat.destroy_signal,
|
||||||
|
&state->seat_destroy_listener);
|
||||||
|
wl_signal_add(&surface->resource.destroy_signal,
|
||||||
|
&state->surface_destroy_listener);
|
||||||
|
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
pop_focus_state(struct desktop_shell *shell, struct workspace *ws)
|
||||||
|
{
|
||||||
|
struct focus_state *state, *next;
|
||||||
|
|
||||||
|
wl_list_for_each_safe(state, next, &ws->focus_list, link) {
|
||||||
|
if (state->keyboard_focus)
|
||||||
|
wl_keyboard_set_focus(state->seat->seat.keyboard,
|
||||||
|
&state->keyboard_focus->surface);
|
||||||
|
|
||||||
|
focus_state_destroy(state);
|
||||||
|
}
|
||||||
|
wl_list_init(&ws->focus_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
push_focus_state(struct desktop_shell *shell, struct workspace *ws)
|
||||||
|
{
|
||||||
|
struct weston_seat *seat;
|
||||||
|
struct focus_state *state;
|
||||||
|
struct wl_keyboard *keyboard;
|
||||||
|
|
||||||
|
wl_list_for_each(seat, &shell->compositor->seat_list, link) {
|
||||||
|
keyboard = seat->seat.keyboard;
|
||||||
|
if (keyboard && keyboard->focus) {
|
||||||
|
state = focus_state_create(seat);
|
||||||
|
if (state == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
wl_list_insert(&ws->focus_list, &state->link);
|
||||||
|
|
||||||
|
wl_keyboard_set_focus(seat->seat.keyboard, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
workspace_destroy(struct workspace *ws)
|
workspace_destroy(struct workspace *ws)
|
||||||
{
|
{
|
||||||
|
struct focus_state *state, *next;
|
||||||
|
|
||||||
|
wl_list_for_each_safe(state, next, &ws->focus_list, link)
|
||||||
|
focus_state_destroy(state);
|
||||||
|
|
||||||
free(ws);
|
free(ws);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
seat_destroyed(struct wl_listener *listener, void *data)
|
||||||
|
{
|
||||||
|
struct weston_seat *seat = data;
|
||||||
|
struct focus_state *state, *next;
|
||||||
|
struct workspace *ws = container_of(listener,
|
||||||
|
struct workspace,
|
||||||
|
seat_destroyed_listener);
|
||||||
|
|
||||||
|
wl_list_for_each_safe(state, next, &ws->focus_list, link)
|
||||||
|
if (state->seat == seat)
|
||||||
|
wl_list_remove(&state->link);
|
||||||
|
}
|
||||||
|
|
||||||
static struct workspace *
|
static struct workspace *
|
||||||
workspace_create(void)
|
workspace_create(void)
|
||||||
{
|
{
|
||||||
@ -344,6 +472,10 @@ workspace_create(void)
|
|||||||
|
|
||||||
weston_layer_init(&ws->layer, NULL);
|
weston_layer_init(&ws->layer, NULL);
|
||||||
|
|
||||||
|
wl_list_init(&ws->focus_list);
|
||||||
|
wl_list_init(&ws->seat_destroyed_listener.link);
|
||||||
|
ws->seat_destroyed_listener.notify = seat_destroyed;
|
||||||
|
|
||||||
return ws;
|
return ws;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -457,6 +589,9 @@ reverse_workspace_change_animation(struct desktop_shell *shell,
|
|||||||
shell->workspaces.anim_dir = -1 * shell->workspaces.anim_dir;
|
shell->workspaces.anim_dir = -1 * shell->workspaces.anim_dir;
|
||||||
shell->workspaces.anim_timestamp = 0;
|
shell->workspaces.anim_timestamp = 0;
|
||||||
|
|
||||||
|
push_focus_state(shell, from);
|
||||||
|
pop_focus_state(shell, to);
|
||||||
|
|
||||||
workspace_damage_all_surfaces(from);
|
workspace_damage_all_surfaces(from);
|
||||||
workspace_damage_all_surfaces(to);
|
workspace_damage_all_surfaces(to);
|
||||||
}
|
}
|
||||||
@ -540,9 +675,8 @@ animate_workspace_change_frame(struct weston_animation *animation,
|
|||||||
workspace_damage_all_surfaces(from);
|
workspace_damage_all_surfaces(from);
|
||||||
workspace_damage_all_surfaces(to);
|
workspace_damage_all_surfaces(to);
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
finish_workspace_change_animation(shell, from, to);
|
finish_workspace_change_animation(shell, from, to);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -577,6 +711,9 @@ animate_workspace_change(struct desktop_shell *shell,
|
|||||||
|
|
||||||
workspace_translate_in(to, 0);
|
workspace_translate_in(to, 0);
|
||||||
|
|
||||||
|
push_focus_state(shell, from);
|
||||||
|
pop_focus_state(shell, to);
|
||||||
|
|
||||||
workspace_damage_all_surfaces(from);
|
workspace_damage_all_surfaces(from);
|
||||||
workspace_damage_all_surfaces(to);
|
workspace_damage_all_surfaces(to);
|
||||||
}
|
}
|
||||||
@ -586,7 +723,6 @@ change_workspace(struct desktop_shell *shell, unsigned int index)
|
|||||||
{
|
{
|
||||||
struct workspace *from;
|
struct workspace *from;
|
||||||
struct workspace *to;
|
struct workspace *to;
|
||||||
struct weston_seat *seat;
|
|
||||||
|
|
||||||
if (index == shell->workspaces.current)
|
if (index == shell->workspaces.current)
|
||||||
return;
|
return;
|
||||||
@ -595,11 +731,6 @@ change_workspace(struct desktop_shell *shell, unsigned int index)
|
|||||||
if (!wl_list_empty(&shell->fullscreen_layer.surface_list))
|
if (!wl_list_empty(&shell->fullscreen_layer.surface_list))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* Clear keyboard focus so that no hidden surfaces will keep it. */
|
|
||||||
wl_list_for_each(seat, &shell->compositor->seat_list, link)
|
|
||||||
if (seat->seat.keyboard)
|
|
||||||
wl_keyboard_set_focus(seat->seat.keyboard, NULL);
|
|
||||||
|
|
||||||
from = get_current_workspace(shell);
|
from = get_current_workspace(shell);
|
||||||
to = get_workspace(shell, index);
|
to = get_workspace(shell, index);
|
||||||
|
|
||||||
@ -618,6 +749,9 @@ change_workspace(struct desktop_shell *shell, unsigned int index)
|
|||||||
shell->workspaces.current = index;
|
shell->workspaces.current = index;
|
||||||
wl_list_insert(&from->layer.link, &to->layer.link);
|
wl_list_insert(&from->layer.link, &to->layer.link);
|
||||||
wl_list_remove(&from->layer.link);
|
wl_list_remove(&from->layer.link);
|
||||||
|
|
||||||
|
push_focus_state(shell, from);
|
||||||
|
pop_focus_state(shell, to);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
animate_workspace_change(shell, index, from, to);
|
animate_workspace_change(shell, index, from, to);
|
||||||
@ -1862,6 +1996,8 @@ resume_desktop(struct desktop_shell *shell)
|
|||||||
wl_list_insert(&shell->fullscreen_layer.link,
|
wl_list_insert(&shell->fullscreen_layer.link,
|
||||||
&shell->panel_layer.link);
|
&shell->panel_layer.link);
|
||||||
|
|
||||||
|
pop_focus_state(shell, get_current_workspace(shell));
|
||||||
|
|
||||||
shell->locked = false;
|
shell->locked = false;
|
||||||
shell->compositor->idle_time = shell->compositor->option_idle_time;
|
shell->compositor->idle_time = shell->compositor->option_idle_time;
|
||||||
weston_compositor_wake(shell->compositor);
|
weston_compositor_wake(shell->compositor);
|
||||||
@ -2339,7 +2475,6 @@ lock(struct wl_listener *listener, void *data)
|
|||||||
{
|
{
|
||||||
struct desktop_shell *shell =
|
struct desktop_shell *shell =
|
||||||
container_of(listener, struct desktop_shell, lock_listener);
|
container_of(listener, struct desktop_shell, lock_listener);
|
||||||
struct weston_seat *seat;
|
|
||||||
struct shell_surface *shsurf;
|
struct shell_surface *shsurf;
|
||||||
struct weston_output *output;
|
struct weston_output *output;
|
||||||
|
|
||||||
@ -2376,10 +2511,8 @@ lock(struct wl_listener *listener, void *data)
|
|||||||
/* reset pointer foci */
|
/* reset pointer foci */
|
||||||
weston_compositor_schedule_repaint(shell->compositor);
|
weston_compositor_schedule_repaint(shell->compositor);
|
||||||
|
|
||||||
/* reset keyboard foci */
|
/* stash keyboard foci in current workspace */
|
||||||
wl_list_for_each(seat, &shell->compositor->seat_list, link) {
|
push_focus_state(shell, get_current_workspace(shell));
|
||||||
wl_keyboard_set_focus(seat->seat.keyboard, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* TODO: disable bindings that should not work while locked. */
|
/* TODO: disable bindings that should not work while locked. */
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user