diff --git a/src/shell.c b/src/shell.c index cdd4e125..7a85ff1c 100644 --- a/src/shell.c +++ b/src/shell.c @@ -48,8 +48,19 @@ enum animation_type { 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 weston_layer layer; + + struct wl_list focus_list; + struct wl_listener seat_destroyed_listener; }; struct desktop_shell { @@ -176,6 +187,8 @@ struct shell_surface { struct weston_transform workspace_transform; + struct focus_state *focus_state; + struct weston_output *fullscreen_output; struct weston_output *output; struct wl_list link; @@ -329,12 +342,127 @@ shell_configuration(struct desktop_shell *shell) 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 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); } +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 * workspace_create(void) { @@ -344,6 +472,10 @@ workspace_create(void) 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; } @@ -457,6 +589,9 @@ reverse_workspace_change_animation(struct desktop_shell *shell, shell->workspaces.anim_dir = -1 * shell->workspaces.anim_dir; shell->workspaces.anim_timestamp = 0; + push_focus_state(shell, from); + pop_focus_state(shell, to); + workspace_damage_all_surfaces(from); 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(to); } - else { + else finish_workspace_change_animation(shell, from, to); - } } static void @@ -577,6 +711,9 @@ animate_workspace_change(struct desktop_shell *shell, workspace_translate_in(to, 0); + push_focus_state(shell, from); + pop_focus_state(shell, to); + workspace_damage_all_surfaces(from); workspace_damage_all_surfaces(to); } @@ -586,7 +723,6 @@ change_workspace(struct desktop_shell *shell, unsigned int index) { struct workspace *from; struct workspace *to; - struct weston_seat *seat; if (index == shell->workspaces.current) return; @@ -595,11 +731,6 @@ change_workspace(struct desktop_shell *shell, unsigned int index) if (!wl_list_empty(&shell->fullscreen_layer.surface_list)) 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); to = get_workspace(shell, index); @@ -618,6 +749,9 @@ change_workspace(struct desktop_shell *shell, unsigned int index) shell->workspaces.current = index; wl_list_insert(&from->layer.link, &to->layer.link); wl_list_remove(&from->layer.link); + + push_focus_state(shell, from); + pop_focus_state(shell, to); } else animate_workspace_change(shell, index, from, to); @@ -1862,6 +1996,8 @@ resume_desktop(struct desktop_shell *shell) wl_list_insert(&shell->fullscreen_layer.link, &shell->panel_layer.link); + pop_focus_state(shell, get_current_workspace(shell)); + shell->locked = false; shell->compositor->idle_time = shell->compositor->option_idle_time; weston_compositor_wake(shell->compositor); @@ -2339,7 +2475,6 @@ lock(struct wl_listener *listener, void *data) { struct desktop_shell *shell = container_of(listener, struct desktop_shell, lock_listener); - struct weston_seat *seat; struct shell_surface *shsurf; struct weston_output *output; @@ -2376,10 +2511,8 @@ lock(struct wl_listener *listener, void *data) /* reset pointer foci */ weston_compositor_schedule_repaint(shell->compositor); - /* reset keyboard foci */ - wl_list_for_each(seat, &shell->compositor->seat_list, link) { - wl_keyboard_set_focus(seat->seat.keyboard, NULL); - } + /* stash keyboard foci in current workspace */ + push_focus_state(shell, get_current_workspace(shell)); /* TODO: disable bindings that should not work while locked. */