From 0061105b29f1af3eb6007e39a56347978303e4dc Mon Sep 17 00:00:00 2001 From: Kevin Lange Date: Fri, 9 May 2014 23:03:47 -0700 Subject: [PATCH] Make the alt-tab slick and operate as expected --- userspace/gui/compositor/compositor.c | 25 ++++-- userspace/gui/compositor/yutani_int.h | 2 + userspace/gui/core/panel.c | 121 ++++++++++++++++++++++---- userspace/lib/yutani.c | 10 +++ 4 files changed, 134 insertions(+), 24 deletions(-) diff --git a/userspace/gui/compositor/compositor.c b/userspace/gui/compositor/compositor.c index 84d0bf92..6256336b 100644 --- a/userspace/gui/compositor/compositor.c +++ b/userspace/gui/compositor/compositor.c @@ -559,7 +559,7 @@ static int yutani_blit_window(yutani_globals_t * yg, yutani_server_window_t * wi if (frame >= yutani_animation_lengths[window->anim_mode]) { /* XXX handle animation-end things like cleanup of closing windows */ if (window->anim_mode == YUTANI_EFFECT_FADE_OUT) { - window_actually_close(yg, window); + list_insert(yg->windows_to_remove, window); goto draw_finish; } window->anim_mode = 0; @@ -599,11 +599,6 @@ draw_window: cairo_paint(cr); } -draw_finish: - - /* Clean up */ - cairo_surface_destroy(surf); - /* Paint select buffer */ cairo_set_operator(cs, CAIRO_OPERATOR_SOURCE); cairo_set_source_rgb(cs, @@ -615,6 +610,12 @@ draw_finish: cairo_set_antialias(cs, CAIRO_ANTIALIAS_NONE); cairo_fill(cs); +draw_finish: + + /* Clean up */ + cairo_surface_destroy(surf); + + /* Restore context stack */ cairo_restore(cr); cairo_restore(cs); @@ -728,6 +729,8 @@ static void redraw_windows(yutani_globals_t * yg) { yutani_set_clip(yg); + yg->windows_to_remove = list_create(); + /* * In theory, we should restrict this to windows within the clip region, * but calculating that may be more trouble than it's worth; @@ -762,6 +765,15 @@ static void redraw_windows(yutani_globals_t * yg) { cairo_set_source_surface(yg->real_ctx, yg->framebuffer_surface, 0, 0); cairo_paint(yg->real_ctx); + while (yg->windows_to_remove->head) { + node_t * node = list_pop(yg->windows_to_remove); + + window_actually_close(yg, node->value); + + free(node); + } + free(yg->windows_to_remove); + } /* Restore the cairo contexts to reset clip regions */ @@ -1004,6 +1016,7 @@ static void handle_key_event(yutani_globals_t * yg, struct yutani_msg_key_event } uint32_t key_code = ((ke->event.modifiers << 24) | (ke->event.keycode)); + fprintf(stderr, "[yutani-server] Checking binding table for 0x%x\n", key_code); if (hashmap_has(yg->key_binds, (void*)key_code)) { struct key_bind * bind = hashmap_get(yg->key_binds, (void*)key_code); diff --git a/userspace/gui/compositor/yutani_int.h b/userspace/gui/compositor/yutani_int.h index 71a4f1f0..efdf31b0 100644 --- a/userspace/gui/compositor/yutani_int.h +++ b/userspace/gui/compositor/yutani_int.h @@ -125,6 +125,8 @@ typedef struct { hashmap_t * key_binds; + list_t * windows_to_remove; + } yutani_globals_t; struct key_bind { diff --git a/userspace/gui/core/panel.c b/userspace/gui/core/panel.c index fa22b1e1..95d8aefc 100644 --- a/userspace/gui/core/panel.c +++ b/userspace/gui/core/panel.c @@ -36,6 +36,8 @@ static gfx_context_t * ctx; static yutani_t * yctx; static yutani_window_t * panel; +static gfx_context_t * actx; +static yutani_window_t * alttab; static list_t * window_list = NULL; static volatile int lock = 0; static volatile int drawlock = 0; @@ -51,6 +53,11 @@ static int height; static sprite_t * sprite_panel; static sprite_t * sprite_logout; +#define ALTTAB_WIDTH 250 +#define ALTTAB_HEIGHT 70 +#define ALTTAB_BACKGROUND premultiply(rgba(0,0,0,150)) +#define ALTTAB_OFFSET 10 + static int center_x(int x) { return (width - x) / 2; } @@ -59,16 +66,18 @@ static int center_y(int y) { return (height - y) / 2; } +static int center_x_a(int x) { + return (ALTTAB_WIDTH - x) / 2; +} + +static int center_y_a(int y) { + return (ALTTAB_HEIGHT - y) / 2; +} + static void redraw(void); static volatile int _continue = 1; -/* XXX Stores some quick access information about the window list */ -static int icon_lefts[20] = {0}; -static int icon_wids[20] = {0}; -static int focused_app = -1; -static int active_window = -1; - struct window_ad { yutani_wid_t wid; uint32_t flags; @@ -77,6 +86,19 @@ struct window_ad { char * strings; }; +/* XXX Stores some quick access information about the window list */ +static int icon_lefts[20] = {0}; +static int icon_wids[20] = {0}; +static int focused_app = -1; + +static int wids_by_z[20] = {0}; +static int active_window = -1; +static int was_tabbing = 0; +static int new_focused = -1; +static struct window_ad * ads_by_z[20] = {NULL}; + +static sprite_t * icon_get(char * name); + /* Handle SIGINT by telling other threads (clock) to shut down */ static void sig_int(int sig) { printf("Received shutdown signal in panel!\n"); @@ -138,6 +160,33 @@ static void launch_application(char * app) { } } +static void redraw_alttab(void) { + /* Draw the background, right now just a dark semi-transparent box */ + draw_fill(actx, ALTTAB_BACKGROUND); + + if (ads_by_z[new_focused]) { + struct window_ad * ad = ads_by_z[new_focused]; + + sprite_t * icon = icon_get(ad->icon); + + /* Draw it, scaled if necessary */ + if (icon->width == 24) { + draw_sprite(actx, icon, center_x_a(24), ALTTAB_OFFSET); + } else { + draw_sprite_scaled(actx, icon, center_x_a(24), ALTTAB_OFFSET, 24, 24); + } + + set_font_face(FONT_SANS_SERIF_BOLD); + set_font_size(14); + int t = draw_string_width(ad->name); + + draw_string(actx, center_x_a(t), 24+ALTTAB_OFFSET+16, rgb(255,255,255), ad->name); + } + + flip(actx); + yutani_flip(yctx, alttab); +} + static void handle_key_event(struct yutani_msg_key_event * ke) { if ((ke->event.modifiers & KEY_MOD_LEFT_CTRL) && (ke->event.modifiers & KEY_MOD_LEFT_ALT) && @@ -147,28 +196,58 @@ static void handle_key_event(struct yutani_msg_key_event * ke) { launch_application("terminal"); } + if ((was_tabbing) && (ke->event.keycode == 0) && + (ke->event.modifiers == 0) && (ke->event.action == KEY_ACTION_UP)) { + + fprintf(stderr, "[panel] Stopping focus new_focused = %d\n", new_focused); + + yutani_focus_window(yctx, wids_by_z[new_focused]); + was_tabbing = 0; + new_focused = -1; + + free(actx->backbuffer); + free(actx); + + yutani_close(yctx, alttab); + + return; + } + if ((ke->event.modifiers & KEY_MOD_LEFT_ALT) && (ke->event.keycode == '\t') && (ke->event.action == KEY_ACTION_DOWN)) { - int direction = (ke->event.modifiers & KEY_MOD_LEFT_SHIFT) ? -1 : 1; + int direction = (ke->event.modifiers & KEY_MOD_LEFT_SHIFT) ? 1 : -1; + + if (was_tabbing) { + new_focused = new_focused + direction; + } else { + new_focused = active_window + direction; + /* Create tab window */ + alttab = yutani_window_create(yctx, ALTTAB_WIDTH, ALTTAB_HEIGHT); + + /* And move it to the top layer */ + yutani_window_move(yctx, alttab, center_x(ALTTAB_WIDTH), center_y(ALTTAB_HEIGHT)); + + /* Initialize graphics context against the window */ + actx = init_graphics_yutani_double_buffer(alttab); + } - int new_focused = active_window + direction; if (new_focused < 0) { new_focused = 0; for (int i = 0; i < 18; i++) { - if (icon_wids[i+1] == 0) { + if (wids_by_z[i+1] == 0) { new_focused = i; break; } } - } - if (icon_wids[new_focused] == 0) { + } else if (wids_by_z[new_focused] == 0) { new_focused = 0; } - yutani_focus_window(yctx, icon_wids[new_focused]); + was_tabbing = 1; + redraw_alttab(); } } @@ -346,6 +425,7 @@ static void update_window_list(void) { list_t * new_window_list = list_create(); + int i = 0; while (1) { /* We wait for a series of WINDOW_ADVERTISE messsages */ yutani_msg_t * m = yutani_wait_for(yctx, YUTANI_MSG_WINDOW_ADVERTISE); @@ -368,6 +448,12 @@ static void update_window_list(void) { ad->flags = wa->flags; ad->wid = wa->wid; + wids_by_z[i] = ad->wid; + ads_by_z[i] = ad; + i++; + wids_by_z[i] = 0; + ads_by_z[i] = NULL; + node_t * next = NULL; /* And insert it, ordered by wid, into the window list */ @@ -387,9 +473,9 @@ static void update_window_list(void) { } free(m); } + active_window = i-1; - int i = 0; - int new_active_window = 0; + i = 0; /* * Update each of the wid entries in our array so we can map * clicks to window focus events for each window @@ -399,13 +485,9 @@ static void update_window_list(void) { if (i < 19) { icon_wids[i] = ad->wid; icon_wids[i+1] = 0; - if (ad->flags & 1) { - new_active_window = i; - } } i++; } - active_window = new_active_window; /* Then free up the old list and replace it with the new list */ spin_lock(&lock); @@ -525,6 +607,9 @@ int main (int argc, char ** argv) { yutani_key_bind(yctx, '\t', KEY_MOD_LEFT_ALT, YUTANI_BIND_STEAL); yutani_key_bind(yctx, '\t', KEY_MOD_LEFT_ALT | KEY_MOD_LEFT_SHIFT, YUTANI_BIND_STEAL); + /* This lets us receive all just-modifier key releases */ + yutani_key_bind(yctx, 0, 0, YUTANI_BIND_PASSTHROUGH); + while (_continue) { /* Respond to Yutani events */ yutani_msg_t * m = yutani_poll(yctx); diff --git a/userspace/lib/yutani.c b/userspace/lib/yutani.c index fcae70c6..46b526f6 100644 --- a/userspace/lib/yutani.c +++ b/userspace/lib/yutani.c @@ -485,6 +485,16 @@ void yutani_close(yutani_t * y, yutani_window_t * win) { yutani_msg_t * m = yutani_msg_build_window_close(win->wid); int result = yutani_msg_send(y, m); free(m); + + /* Now destroy our end of the window */ + { + char key[1024]; + YUTANI_SHMKEY_EXP(key, 1024, win->bufid); + syscall_shm_release(key); + } + + hashmap_remove(y->windows, (void*)win->wid); + free(win); } void yutani_window_move(yutani_t * yctx, yutani_window_t * window, int x, int y) {