mirror of
https://gitlab.freedesktop.org/wayland/weston
synced 2024-10-15 14:31:42 +00:00
shell: Implement alt-tab switcher
Signed-off-by: Emilio Pozuelo Monfort <emilio.pozuelo@collabora.co.uk>
This commit is contained in:
parent
aa7a4761f6
commit
03251b6a7a
248
src/shell.c
248
src/shell.c
|
@ -3343,6 +3343,251 @@ terminate_binding(struct weston_seat *seat, uint32_t time, uint32_t key,
|
|||
wl_display_terminate(compositor->wl_display);
|
||||
}
|
||||
|
||||
static void
|
||||
lower_fullscreen_layer(struct desktop_shell *shell);
|
||||
|
||||
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)
|
||||
{
|
||||
if (!get_shell_surface(view->surface))
|
||||
return 0;
|
||||
|
||||
if (get_shell_surface_type(view->surface) == SHELL_SURFACE_TRANSIENT)
|
||||
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;
|
||||
weston_view_restack(v, &ws->layer.view_list);
|
||||
weston_view_configure(v, x, y, view->geometry.width, view->geometry.height);
|
||||
|
||||
preview->listener.notify = alt_tab_handle_surface_destroy;
|
||||
wl_signal_add(&v->destroy_signal, &preview->listener);
|
||||
|
||||
if (view->geometry.width > view->geometry.height)
|
||||
scale = side / (float) view->geometry.width;
|
||||
else
|
||||
scale = side / (float) view->geometry.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)
|
||||
|
@ -5606,6 +5851,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);
|
||||
|
|
Loading…
Reference in a new issue