diff --git a/src/shell.c b/src/shell.c index 6ad312b3..12130149 100644 --- a/src/shell.c +++ b/src/shell.c @@ -109,6 +109,7 @@ struct desktop_shell { unsigned int num; struct weston_animation animation; + struct wl_list anim_sticky_list; int anim_dir; uint32_t anim_timestamp; double anim_current; @@ -449,6 +450,34 @@ restore_focus_state(struct desktop_shell *shell, struct workspace *ws) } } +static void +replace_focus_state(struct desktop_shell *shell, struct workspace *ws, + struct weston_seat *seat) +{ + struct focus_state *state; + struct wl_surface *surface; + + wl_list_for_each(state, &ws->focus_list, link) { + if (state->seat == seat) { + surface = seat->seat.keyboard->focus; + state->keyboard_focus = + (struct weston_surface *) surface; + return; + } + } +} + +static void +drop_focus_state(struct desktop_shell *shell, struct workspace *ws, + struct weston_surface *surface) +{ + struct focus_state *state; + + wl_list_for_each(state, &ws->focus_list, link) + if (state->keyboard_focus == surface) + state->keyboard_focus = NULL; +} + static void workspace_destroy(struct workspace *ws) { @@ -591,8 +620,6 @@ reverse_workspace_change_animation(struct desktop_shell *shell, shell->workspaces.anim_dir = -1 * shell->workspaces.anim_dir; shell->workspaces.anim_timestamp = 0; - restore_focus_state(shell, to); - weston_compositor_schedule_repaint(shell->compositor); } @@ -604,8 +631,10 @@ workspace_deactivate_transforms(struct workspace *ws) wl_list_for_each(surface, &ws->layer.surface_list, layer_link) { shsurf = get_shell_surface(surface); - wl_list_remove(&shsurf->workspace_transform.link); - wl_list_init(&shsurf->workspace_transform.link); + if (!wl_list_empty(&shsurf->workspace_transform.link)) { + wl_list_remove(&shsurf->workspace_transform.link); + wl_list_init(&shsurf->workspace_transform.link); + } shsurf->surface->geometry.dirty = 1; } } @@ -704,7 +733,7 @@ animate_workspace_change(struct desktop_shell *shell, wl_list_insert(&output->animation_list, &shell->workspaces.animation.link); - wl_list_insert(&from->layer.link, &to->layer.link); + wl_list_insert(from->layer.link.prev, &to->layer.link); workspace_translate_in(to, 0); @@ -713,6 +742,15 @@ animate_workspace_change(struct desktop_shell *shell, weston_compositor_schedule_repaint(shell->compositor); } +static void +update_workspace(struct desktop_shell *shell, unsigned int index, + struct workspace *from, struct workspace *to) +{ + shell->workspaces.current = index; + wl_list_insert(&from->layer.link, &to->layer.link); + wl_list_remove(&from->layer.link); +} + static void change_workspace(struct desktop_shell *shell, unsigned int index) { @@ -731,6 +769,7 @@ change_workspace(struct desktop_shell *shell, unsigned int index) if (shell->workspaces.anim_from == to && shell->workspaces.anim_to == from) { + restore_focus_state(shell, to); reverse_workspace_change_animation(shell, index, from, to); return; } @@ -740,17 +779,79 @@ change_workspace(struct desktop_shell *shell, unsigned int index) shell->workspaces.anim_from, shell->workspaces.anim_to); - if (workspace_is_empty(to) && workspace_is_empty(from)) { - shell->workspaces.current = index; - wl_list_insert(&from->layer.link, &to->layer.link); - wl_list_remove(&from->layer.link); + restore_focus_state(shell, to); - restore_focus_state(shell, to); - } + if (workspace_is_empty(to) && workspace_is_empty(from)) + update_workspace(shell, index, from, to); else animate_workspace_change(shell, index, from, to); } +static bool +workspace_has_only(struct workspace *ws, struct weston_surface *surface) +{ + struct wl_list *list = &ws->layer.surface_list; + struct wl_list *e; + + if (wl_list_empty(list)) + return false; + + e = list->next; + + if (e->next != list) + return false; + + return container_of(e, struct weston_surface, layer_link) == surface; +} + +static void +take_surface_to_workspace_by_seat(struct desktop_shell *shell, + struct wl_seat *seat, + unsigned int index) +{ + struct weston_surface *surface = + (struct weston_surface *) seat->keyboard->focus; + struct shell_surface *shsurf; + struct workspace *from; + struct workspace *to; + + if (surface == NULL || + index == shell->workspaces.current) + return; + + from = get_current_workspace(shell); + to = get_workspace(shell, index); + + wl_list_remove(&surface->layer_link); + wl_list_insert(&to->layer.surface_list, &surface->layer_link); + + replace_focus_state(shell, to, (struct weston_seat *) seat); + drop_focus_state(shell, from, surface); + + if (shell->workspaces.anim_from == to && + shell->workspaces.anim_to == from) { + reverse_workspace_change_animation(shell, index, from, to); + return; + } + + if (shell->workspaces.anim_to != NULL) + finish_workspace_change_animation(shell, + shell->workspaces.anim_from, + shell->workspaces.anim_to); + + if (workspace_is_empty(from) && + workspace_has_only(to, surface)) + update_workspace(shell, index, from, to); + else { + shsurf = get_shell_surface(surface); + if (wl_list_empty(&shsurf->workspace_transform.link)) + wl_list_insert(&shell->workspaces.anim_sticky_list, + &shsurf->workspace_transform.link); + + animate_workspace_change(shell, index, from, to); + } +} + static void noop_grab_focus(struct wl_pointer_grab *grab, struct wl_surface *surface, wl_fixed_t x, wl_fixed_t y) @@ -3345,6 +3446,37 @@ workspace_f_binding(struct wl_seat *seat, uint32_t time, change_workspace(shell, new_index); } +static void +workspace_move_surface_up_binding(struct wl_seat *seat, uint32_t time, + uint32_t key, void *data) +{ + struct desktop_shell *shell = data; + unsigned int new_index = shell->workspaces.current; + + if (shell->locked) + return; + + if (new_index != 0) + new_index--; + + take_surface_to_workspace_by_seat(shell, seat, new_index); +} + +static void +workspace_move_surface_down_binding(struct wl_seat *seat, uint32_t time, + uint32_t key, void *data) +{ + struct desktop_shell *shell = data; + unsigned int new_index = shell->workspaces.current; + + if (shell->locked) + return; + + if (new_index < shell->workspaces.num - 1) + new_index++; + + take_surface_to_workspace_by_seat(shell, seat, new_index); +} static void shell_destroy(struct wl_listener *listener, void *data) @@ -3421,6 +3553,12 @@ shell_add_bindings(struct weston_compositor *ec, struct desktop_shell *shell) workspace_up_binding, shell); weston_compositor_add_key_binding(ec, KEY_DOWN, mod, workspace_down_binding, shell); + weston_compositor_add_key_binding(ec, KEY_UP, mod | MODIFIER_SHIFT, + workspace_move_surface_up_binding, + shell); + weston_compositor_add_key_binding(ec, KEY_DOWN, mod | MODIFIER_SHIFT, + workspace_move_surface_down_binding, + shell); /* Add bindings for mod+F[1-6] for workspace 1 to 6. */ if (shell->workspaces.num > 1) { @@ -3494,6 +3632,7 @@ shell_init(struct weston_compositor *ec) } activate_workspace(shell, 0); + wl_list_init(&shell->workspaces.anim_sticky_list); wl_list_init(&shell->workspaces.animation.link); shell->workspaces.animation.frame = animate_workspace_change_frame;