From: Emilio Pozuelo Monfort <emilio.pozu...@collabora.co.uk> --- desktop-shell/shell.c | 251 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 251 insertions(+)
diff --git a/desktop-shell/shell.c b/desktop-shell/shell.c index 96e31a1..3d0836d 100644 --- a/desktop-shell/shell.c +++ b/desktop-shell/shell.c @@ -3964,6 +3964,254 @@ terminate_binding(struct weston_seat *seat, uint32_t time, uint32_t key, wl_display_terminate(compositor->wl_display); } +struct alt_tab { + struct desktop_shell *shell; + struct weston_keyboard_grab grab; + + struct wl_list *current; + + struct wl_list preview_list; +}; + +struct alt_tab_preview { + struct alt_tab *alt_tab; + + struct weston_view *view; + struct weston_transform transform; + + struct wl_listener listener; + + struct wl_list link; +}; + +static void +alt_tab_next(struct alt_tab *alt_tab) +{ + alt_tab->current = alt_tab->current->next; + + /* Make sure we're not pointing to the list header e.g. after + * cycling through the whole list. */ + if (alt_tab->current->next == alt_tab->preview_list.next) + alt_tab->current = alt_tab->current->next; +} + +static void +alt_tab_destroy(struct alt_tab *alt_tab) +{ + struct alt_tab_preview *preview, *next; + struct weston_keyboard *keyboard = alt_tab->grab.keyboard; + + if (alt_tab->current && alt_tab->current != &alt_tab->preview_list) { + preview = wl_container_of(alt_tab->current, preview, link); + + activate(alt_tab->shell, preview->view->surface, + (struct weston_seat *) keyboard->seat); + } + + wl_list_for_each_safe(preview, next, &alt_tab->preview_list, link) { + wl_list_remove(&preview->link); + wl_list_remove(&preview->listener.link); + weston_view_destroy(preview->view); + free(preview); + } + + weston_keyboard_end_grab(keyboard); + if (keyboard->input_method_resource) + keyboard->grab = &keyboard->input_method_grab; + + free(alt_tab); +} + +static void +alt_tab_handle_surface_destroy(struct wl_listener *listener, void *data) +{ + struct alt_tab_preview *preview = + container_of(listener, struct alt_tab_preview, listener); + struct alt_tab *alt_tab = preview->alt_tab; + int advance = 0; + + /* If the preview that we're removing is the currently selected one, + * we want to move to the next one. So we move to ->prev and then + * call _next() after removing the preview. */ + if (alt_tab->current == &preview->link) { + alt_tab->current = alt_tab->current->prev; + advance = 1; + } + + wl_list_remove(&preview->listener.link); + wl_list_remove(&preview->link); + + free(preview); + + if (advance) + alt_tab_next(alt_tab); + + /* If the last preview goes away, stop the alt-tab */ + if (wl_list_empty(alt_tab->current)) + alt_tab_destroy(alt_tab); +} + +static void +alt_tab_key(struct weston_keyboard_grab *grab, + uint32_t time, uint32_t key, uint32_t state_w) +{ + struct alt_tab *alt_tab = container_of(grab, struct alt_tab, grab); + enum wl_keyboard_key_state state = state_w; + + if (key == KEY_TAB && state == WL_KEYBOARD_KEY_STATE_PRESSED) + alt_tab_next(alt_tab); +} + +static void +alt_tab_modifier(struct weston_keyboard_grab *grab, uint32_t serial, + uint32_t mods_depressed, uint32_t mods_latched, + uint32_t mods_locked, uint32_t group) +{ + struct alt_tab *alt_tab = container_of(grab, struct alt_tab, grab); + struct weston_seat *seat = (struct weston_seat *) grab->keyboard->seat; + + if ((seat->modifier_state & MODIFIER_ALT) == 0) + alt_tab_destroy(alt_tab); +} + +static void +alt_tab_cancel(struct weston_keyboard_grab *grab) +{ + struct alt_tab *alt_tab = container_of(grab, struct alt_tab, grab); + + alt_tab_destroy(alt_tab); +} + +static const struct weston_keyboard_grab_interface alt_tab_grab = { + alt_tab_key, + alt_tab_modifier, + alt_tab_cancel, +}; + +static int +view_for_alt_tab(struct weston_view *view) +{ + struct shell_surface *shsurf = get_shell_surface(view->surface); + if (!shsurf) + return 0; + + if (shsurf->parent) + return 0; + + if (view != get_default_view(view->surface)) + return 0; + + return 1; +} + +static void +alt_tab_binding(struct weston_seat *seat, uint32_t time, uint32_t key, + void *data) +{ + struct alt_tab *alt_tab; + struct desktop_shell *shell = data; + struct weston_output *output = get_default_output(shell->compositor); + struct workspace *ws; + struct weston_view *view; + int num_surfaces = 0; + int x, y, side, margin; + + wl_list_for_each(view, &shell->compositor->view_list, link) { + + if (view_for_alt_tab(view)) + num_surfaces++; + } + + if (!num_surfaces) + return; + + alt_tab = malloc(sizeof *alt_tab); + if (!alt_tab) + return; + + alt_tab->shell = shell; + wl_list_init(&alt_tab->preview_list); + alt_tab->current = &alt_tab->preview_list; + + restore_all_output_modes(shell->compositor); + lower_fullscreen_layer(alt_tab->shell); + + alt_tab->grab.interface = &alt_tab_grab; + weston_keyboard_start_grab(seat->keyboard, &alt_tab->grab); + weston_keyboard_set_focus(seat->keyboard, NULL); + + ws = get_current_workspace(shell); + + /* FIXME: add some visual candy e.g. prelight the selected view + * and/or add a black surrounding background à la gnome-shell */ + + /* Determine the size for each preview */ + side = output->width / num_surfaces; + if (side > 200) + side = 200; + margin = side / 4; + side -= margin; + + x = margin; + y = (output->height - side) / 2; + + /* Create a view for each surface */ + wl_list_for_each(view, &shell->compositor->view_list, link) { + struct alt_tab_preview *preview; + struct weston_view *v; + float scale; + + if (!view_for_alt_tab(view)) + continue; + + preview = malloc(sizeof *preview); + if (!preview) { + alt_tab_destroy(alt_tab); + return; + } + + preview->alt_tab = alt_tab; + + preview->view = v = weston_view_create(view->surface); + v->output = view->output; + + wl_list_remove(&v->layer_link); + wl_list_insert(&ws->layer.view_list, &v->layer_link); + weston_view_damage_below(v); + weston_surface_damage(v->surface); + + weston_view_set_position(v, x, y); + + preview->listener.notify = alt_tab_handle_surface_destroy; + wl_signal_add(&v->destroy_signal, &preview->listener); + + if (view->surface->width > view->surface->height) + scale = side / (float) view->surface->width; + else + scale = side / (float) view->surface->height; + + wl_list_insert(&v->geometry.transformation_list, + &preview->transform.link); + weston_matrix_init(&preview->transform.matrix); + weston_matrix_scale(&preview->transform.matrix, + scale, scale, 1.0f); + + weston_view_geometry_dirty(v); + weston_compositor_schedule_repaint(v->surface->compositor); + + /* Insert at the end of the list */ + wl_list_insert(alt_tab->preview_list.prev, &preview->link); + + x += side + margin; + } + + /* Start at the second preview so a simple <alt>tab changes window. + * We set `current' to the first preview and not the second because + * we're going to receive a key press callback for the initial + * <alt>tab which will make `current' point to the second element. */ + alt_tab_next(alt_tab); +} + static void rotate_grab_motion(struct weston_pointer_grab *grab, uint32_t time, wl_fixed_t x, wl_fixed_t y) @@ -5443,6 +5691,9 @@ shell_add_bindings(struct weston_compositor *ec, struct desktop_shell *shell) weston_compositor_add_key_binding(ec, KEY_BACKSPACE, MODIFIER_CTRL | MODIFIER_ALT, terminate_binding, ec); + weston_compositor_add_key_binding(ec, KEY_TAB, + MODIFIER_ALT, + alt_tab_binding, shell); weston_compositor_add_button_binding(ec, BTN_LEFT, 0, click_to_activate_binding, shell); -- 1.8.5.2 _______________________________________________ wayland-devel mailing list wayland-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/wayland-devel