diff options
author | Michael Cardell Widerkrantz <mc@hack.org> | 2011-02-22 16:15:12 +0100 |
---|---|---|
committer | Michael Cardell Widerkrantz <mc@hack.org> | 2011-03-08 12:14:38 +0100 |
commit | 976951dfeb0f36f5ba2c86ade574cff4b64ea8c7 (patch) | |
tree | c96de700b92c5e6b3874da0c68e85a30cb96264a | |
parent | 4762de7978fbba95e3de54a8779cfee949f57d76 (diff) | |
download | mcwm-976951dfeb0f36f5ba2c86ade574cff4b64ea8c7.zip |
Add support for Alt-Tabbing to the last focused window.
-rw-r--r-- | NEWS | 7 | ||||
-rw-r--r-- | TODO | 46 | ||||
-rw-r--r-- | config.h | 1 | ||||
-rw-r--r-- | list.c | 108 | ||||
-rw-r--r-- | list.h | 1 | ||||
-rw-r--r-- | mcwm.c | 173 |
6 files changed, 297 insertions, 39 deletions
@@ -2,6 +2,13 @@ User visible changes +2011-02-23 + + * If you change focus window mcwm remembers where we last had + focus. If you start using MODKEY+Tab to move around the window + list the first Tab will always bring you to where we last had + focus. + 2011-02-19 * Resets per window state of max or vertical max when physical @@ -28,32 +28,50 @@ Save the subwindows of the root window and focus each when pressing modkey + user_key_change. First option always last focused window. - Partly done. I have added a linked list of all windows and can walk - around them in a simple window ring. - I would like it to work like this: modkey-Tab-Tab-Tab... Release modkey to focus on a window *and* to remember the window where we started. The window we remember would be moved to the next position in the window list. + This will also need to work if the change focus with the mouse. + A single modkey-Tab would then get the focus to end up in the window where - we started. + we started tabbing or where we last had our focus if changing with + the mouse. + + This is done, but in a bit ugly way. I have bound Alt_L explicitly + and looking for key release events. + + It is possible to ask for what keys give XCB_MOD_MASK_4 or whatever + MODKEY is. + + GetModifierMapping + + This request returns the keycodes of the keys being used as + modifiers. The number of keycodes in the list is + 8*keycodes-per-modifier. The keycodes are divided into eight sets, + with each set containing keycodes-per-modifier elements. The sets + are assigned to the modifiers Shift, Lock, Control, Mod1, Mod2, + Mod3, Mod4, and Mod5, in order. The keycodes-per-modifier value is + chosen arbitrarily by the server; zeroes are used to fill in + unused elements within each set. If only zero values are given in + a set, the use of the corresponding modifier has been disabled. + The order of keycodes within each set is chosen arbitrarily by the + server. + + xcb_get_modifier_mapping() - It's possible to get a Release Event from the Alt key. Consider what - xev(1) says about the key. + xcb_get_modifier_mapping_unchecked() - I don't know if it's possible to ask the X server what keycode currently - gives you XCB_MOD_MASK_4 or whatever, which would be the nicest way. + xcb_keycode_t * xcb_get_modifier_mapping_keycodes() - I guess, but I haven't yet tried, that I will have to do a specific grab - key for the XK_Alt_L or whatever instead. + xcb_keycode_t * + xcb_get_modifier_mapping_keycodes (const xcb_get_modifier_mapping_reply_t *R /**< */); - This also means we have to add a new state, just like MCWM_MOVE et - cetera, to know that we're currently jumping around the window list - trying to find our next focused window. + xcb_get_modifier_mapping_keycodes_length (const xcb_get_modifier_mapping_reply_t *R /**< */); How do we re-establish stack order after moving around windows to - focus on? + focus on? Do we want to? * Virtual screens/workspaces @@ -76,3 +76,4 @@ #define USERKEY_BOTLEFT XK_B #define USERKEY_BOTRIGHT XK_N #define USERKEY_DELETE XK_End +#define USERKEY_MOD XK_Alt_L @@ -43,6 +43,53 @@ void movetohead(struct item **mainlist, struct item *item) *mainlist = item; } +void movetonext(struct item **mainlist, struct item *item, struct item *next) +{ + if (NULL == item || NULL == next || NULL == mainlist || NULL == *mainlist) + { + return; + } + + if (item->next == next || item == next) + { + /* Already in position. Do nothing. */ + return; + } + + /* Braid together the list where next used to be. */ + + if (NULL != next->next) + { + next->next->prev = next->prev; + } + + if (NULL != next->prev) + { + next->prev->next = next->next; + } + else + { + /* next is currently head. move head one step further along. */ + if (NULL != next->next) + { + *mainlist = next->next; + next->next->prev = NULL; + } + } + + /* Position next after item and braid together list again. */ + next->prev = item; + next->next = item->next; + + if (NULL != next->next) + { + next->next->prev = next; + } + + /* Remember where next is now. */ + item->next = next; +} + /* */ struct item *additem(struct item **mainlist) @@ -120,10 +167,14 @@ void listall(struct item *mainlist) { struct item *item; int i; + + printf("Listing all:\n"); for (item = mainlist, i = 1; item != NULL; item = item->next, i ++) { printf("%d at %p: %s.\n", i, (void *)item, (char *)item->data); + printf(" prev: %p\n", item->prev); + printf(" next: %p\n", item->next); } } @@ -133,7 +184,10 @@ int main(void) struct item *item1; struct item *item2; struct item *item3; - struct item *item4; + struct item *item4; + struct item *item; + struct item *nextitem; + int i; char *foo1 = "1"; char *foo2 = "2"; char *foo3 = "3"; @@ -179,12 +233,58 @@ int main(void) printf("Current elements:\n"); listall(mainlist); - movetohead(&mainlist, item2); + printf("----------------------------------------------------------------------\n"); + + printf("Moving item3 to be after item2\n"); + movetonext(&mainlist, item2, item3); + printf("Current elements:\n"); + listall(mainlist); + + printf("----------------------------------------------------------------------\n"); + + printf("Moving head! item4 to be after item2\n"); + movetonext(&mainlist, item2, item4); printf("Current elements:\n"); listall(mainlist); + + printf("----------------------------------------------------------------------\n"); + printf("Moving tail! item1 to be after item2\n"); + movetonext(&mainlist, item2, item1); + printf("Current elements:\n"); + listall(mainlist); + printf("----------------------------------------------------------------------\n"); + printf("Moving head to be after tail.\n"); + movetonext(&mainlist, item3, item2); + printf("Current elements:\n"); + listall(mainlist); + + printf("Moving all the items after each other.\n"); + /* item3 is tail. work backwards. */ + for (item = mainlist, i = 1; + item != NULL; + item = item->next, i ++) + { + for (nextitem = item2; nextitem != NULL; nextitem = nextitem->prev) + { + movetonext(&mainlist, nextitem, item); + printf("Current elements:\n"); + listall(mainlist); + } + } + + printf("----------------------------------------------------------------------\n"); + +#if 0 + movetohead(&mainlist, item2); + printf("Current elements:\n"); + listall(mainlist); + + printf("----------------------------------------------------------------------\n"); +#endif + printf("Deleting item stored at %p\n", item3); delitem(&mainlist, item3); printf("Current elements:\n"); @@ -203,7 +303,9 @@ int main(void) listall(mainlist); puts(""); - + + printf("----------------------------------------------------------------------\n"); + exit(0); } #endif @@ -6,6 +6,7 @@ struct item }; void movetohead(struct item **mainlist, struct item *item); +void movetonext(struct item **mainlist, struct item *item, struct item *next); struct item *additem(struct item **mainlist); void delitem(struct item **mainlist, struct item *item); void listitems(struct item *mainlist); @@ -30,6 +30,7 @@ #include <getopt.h> #include <string.h> #include <signal.h> +#include <assert.h> #include <sys/types.h> #include <sys/wait.h> @@ -73,6 +74,12 @@ /* We're currently resizing a window with the mouse. */ #define MCWM_RESIZE 3 +/* + * We're currently tabbing around the window list, looking for a new + * window to focus on. + */ +#define MCWM_TABBING 4 + /* Our highest workspace. */ #define WORKSPACE_MAX 9 @@ -112,6 +119,7 @@ typedef enum { KEY_B, KEY_N, KEY_END, + KEY_ALT, KEY_MAX } key_enum_t; @@ -145,7 +153,12 @@ xcb_connection_t *conn; /* Connection to X server. */ xcb_screen_t *screen; /* Our current screen. */ uint32_t curws = 0; /* Current workspace. */ struct client *focuswin; /* Current focus window. */ +struct client *lastfocuswin; /* Last focused window. NOTE! Only + * used to communicate between + * start and end of tabbing + * mode. */ struct item *winlist = NULL; /* Global list of all client windows. */ +int mode = 0; /* Internal mode, such as move or resize */ /* * Workspace list: Every workspace has a list of all visible @@ -172,7 +185,7 @@ struct keys xcb_keycode_t keycode; } keys[KEY_MAX] = { - { USERKEY_FIX, 0 }, + { USERKEY_FIX, 0 }, { USERKEY_MOVE_LEFT, 0 }, { USERKEY_MOVE_DOWN, 0 }, { USERKEY_MOVE_UP, 0 }, @@ -180,8 +193,8 @@ struct keys { USERKEY_MAXVERT, 0 }, { USERKEY_RAISE, 0 }, { USERKEY_TERMINAL, 0 }, - { USERKEY_MAX, 0 }, - { USERKEY_CHANGE, 0, }, + { USERKEY_MAX, 0 }, + { USERKEY_CHANGE, 0 }, { USERKEY_WS1, 0 }, { USERKEY_WS2, 0 }, { USERKEY_WS3, 0 }, @@ -196,7 +209,8 @@ struct keys { USERKEY_TOPRIGHT, 0 }, { USERKEY_BOTLEFT, 0 }, { USERKEY_BOTRIGHT, 0 }, - { USERKEY_DELETE, 0 } + { USERKEY_DELETE, 0 }, + { USERKEY_MOD, 0 }, }; /* Global configuration. */ @@ -985,7 +999,7 @@ int setupkeys(void) /* Get all the keysymbols. */ keysyms = xcb_key_symbols_alloc(conn); - + for (i = KEY_F; i < KEY_MAX; i ++) { keys[i].keycode = keysymtokeycode(keys[i].keysym, keysyms); @@ -999,17 +1013,38 @@ int setupkeys(void) return -1; } - /* Grab this key. */ - xcb_grab_key(conn, 1, screen->root, MODKEY, keys[i].keycode, - XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC); + if (i == KEY_ALT) + { + /* + * Grab Alt with all modifiers. + * + * FIXME: We can ask the X server for the keycode that + * gives us the MODKEY mask with the GetModifierMapping + * request. + */ + xcb_grab_key(conn, 1, screen->root, XCB_MOD_MASK_ANY, + keys[KEY_ALT].keycode, + XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC); + } + else + { + /* Grab other keys with a modifier mask. */ + xcb_grab_key(conn, 1, screen->root, MODKEY, keys[i].keycode, + XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC); - /* - * XXX Also grab it's shifted counterpart. A bit ugly here - * because we grab all of them not just the ones we want. - */ - xcb_grab_key(conn, 1, screen->root, MODKEY | SHIFTMOD, keys[i].keycode, - XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC); + /* + * XXX Also grab it's shifted counterpart. A bit ugly here + * because we grab all of them not just the ones we want. + */ + xcb_grab_key(conn, 1, screen->root, MODKEY | SHIFTMOD, + keys[i].keycode, + XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC); + } } /* for */ + + + /* Need this to take effect NOW! */ + xcb_flush(conn); /* Get rid of the key symbols table. */ free(keysyms); @@ -1196,7 +1231,7 @@ void movewindow(xcb_drawable_t win, uint16_t x, uint16_t y) /* Change focus to next in window ring. */ void focusnext(void) { - struct client *client; + struct client *client = NULL; #if DEBUG if (NULL != focuswin) @@ -1205,7 +1240,26 @@ void focusnext(void) } #endif - /* If we currently have no focus, focus first in list. */ + if (NULL == wslist[curws]) + { + PDEBUG("No windows to focus on in this workspace.\n"); + return; + } + + if (MCWM_TABBING != mode) + { + /* + * Remember what we last focused on. We need this when the + * MODKEY is released and we move the last focused window in + * the tabbing order list. + */ + lastfocuswin = focuswin; + mode = MCWM_TABBING; + + PDEBUG("Began tabbing.\n"); + } + + /* If we currently have no focus focus first in list. */ if (NULL == focuswin) { if (NULL == wslist[curws]) @@ -1213,7 +1267,7 @@ void focusnext(void) PDEBUG("No windows to focus on.\n"); return; } - + PDEBUG("Focusing first in list: %p\n", wslist[curws]); client = wslist[curws]->data; } else @@ -1222,17 +1276,24 @@ void focusnext(void) { /* * We were at the end of list. Focusing on first window in - * list. + * list unless we were already there. */ - client = wslist[curws]->data; + if (focuswin->wsitem[curws] != wslist[curws]->data) + { + PDEBUG("End of list. Focusing first in list: %p\n", + wslist[curws]); + client = wslist[curws]->data; + } } else { /* Otherwise, focus the next in list. */ + PDEBUG("Tabbing. Focusing next: %p.\n", + focuswin->wsitem[curws]->next); client = focuswin->wsitem[curws]->next->data; } } /* if NULL focuswin */ - + /* * Raise window if it's occluded, then warp pointer into it and * set keyboard focus to it. @@ -2251,6 +2312,12 @@ void handle_keypress(xcb_key_press_event_t *ev) return; } + if (MCWM_TABBING == mode && key != KEY_TAB) + { + /* We don't allow any other key while in this mode. */ + return; + } + /* Is it shifted? */ if (ev->state & SHIFTMOD) { @@ -2391,7 +2458,7 @@ void handle_keypress(xcb_key_press_event_t *ev) void events(void) { xcb_generic_event_t *ev; - int mode = 0; /* Internal mode, such as move or resize */ + int16_t mode_x = 0; /* X coord when in special mode */ int16_t mode_y = 0; /* Y coord when in special mode */ int fd; /* Our X file descriptor */ @@ -2472,7 +2539,9 @@ void events(void) e = (xcb_destroy_notify_event_t *) ev; /* - * If we had focus in this window, forget about the focus. + * If we had focus or our last focus in this window, + * forget about the focus. + * * We will get an EnterNotify if there's another window * under the pointer so we can set the focus proper later. */ @@ -2483,6 +2552,13 @@ void events(void) focuswin = NULL; } } + if (NULL != lastfocuswin) + { + if (lastfocuswin->id == e->window) + { + lastfocuswin = NULL; + } + } /* * Find this window in list of clients and forget about @@ -2741,6 +2817,38 @@ void events(void) } break; + case XCB_KEY_RELEASE: + { + xcb_key_release_event_t *e = (xcb_key_release_event_t *)ev; + + PDEBUG("Key %d released.\n", e->detail); + + if (e->detail == keys[KEY_ALT].keycode && MCWM_TABBING == mode) + { + /* MODKEY was released after tabbing around the + * workspace window ring. This means this mode is + * finished and we have found a new focus window. + * + * We need to move first the window we used to focus + * on to the head of the window list and then move the + * new focus to the head of the list as well. The list + * should always start with the window we're focusing + * on. + */ + + mode = 0; + + if (NULL != lastfocuswin) + { + movetohead(&wslist[curws], lastfocuswin->wsitem[curws]); + lastfocuswin = NULL; + } + + movetohead(&wslist[curws], focuswin->wsitem[curws]); + } /* if KEY_ALT */ + } + break; + case XCB_ENTER_NOTIFY: { xcb_enter_notify_event_t *e = (xcb_enter_notify_event_t *)ev; @@ -2783,6 +2891,27 @@ void events(void) client = findclient(e->event); if (NULL != client) { + if (MCWM_TABBING != mode) + { + /* + * We are focusing on a new window. Since + * we're not currently tabbing around the + * window ring, we need to update the + * current workspace window list: Move + * first the old focus to the head of the + * list and then the new focus to the head + * of the list. + */ + if (NULL != focuswin) + { + movetohead(&wslist[curws], + focuswin->wsitem[curws]); + lastfocuswin = NULL; + } + + movetohead(&wslist[curws], client->wsitem[curws]); + } /* if not tabbing */ + setfocus(client); } } |