From 3e7b12ba430a2abeb3e0c80effcb9ecba368bb80 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Tue, 10 Feb 2009 20:49:47 +0100 Subject: [PATCH] Implement keybindings, adjust CMDMODE grammar, update DEPENDS --- CMDMODE | 8 +- DEPENDS | 13 +++ REQUIREMENTS | 3 - data.h | 25 ++++++ mainx.c | 227 +++++++++++++++++++++++++++++++++------------------ 5 files changed, 193 insertions(+), 83 deletions(-) create mode 100644 DEPENDS delete mode 100644 REQUIREMENTS diff --git a/CMDMODE b/CMDMODE index d7c2462e..c7881e1f 100644 --- a/CMDMODE +++ b/CMDMODE @@ -13,7 +13,11 @@ cmd := [ ] [ | ] oder -with := { [ ] }+ +with := { [ ] }+ + +oder + +exec := exec an jeder Stelle kann mit escape abgebrochen werden @@ -29,4 +33,4 @@ Fenster nach rechts verschieben: ml Fenster und Fenster untendrunter nach rechts verschieben: -wkml +wk ml diff --git a/DEPENDS b/DEPENDS new file mode 100644 index 00000000..21e6877e --- /dev/null +++ b/DEPENDS @@ -0,0 +1,13 @@ +You need the following libraries. The version given is to be understand as the minimum +version. However, if any of these libraries changes the API, i3 may not compile anymore. +In that case, please try using the versions mentioned below until a fix is provided. + + * xcb-proto-1.3 (2008-12-10) + * libxcb-1.1.93 (2008-12-11) + * xcb-util-0.3.3 (2009-01-31) + * Xlib, the one that comes with your X-Server + +Get the libraries from: +http://xcb.freedesktop.org/dist/xcb-proto-1.3.tar.bz2 +http://xcb.freedesktop.org/dist/libxcb-1.1.93.tar.bz2 +http://xcb.freedesktop.org/dist/xcb-util-0.3.3.tar.bz2 diff --git a/REQUIREMENTS b/REQUIREMENTS deleted file mode 100644 index be3ecf7d..00000000 --- a/REQUIREMENTS +++ /dev/null @@ -1,3 +0,0 @@ -We’ve developed with: - * libxcb-1.1.93 (2008-12-11) - * xcb-util-0.3.3 (2009-01-31) diff --git a/data.h b/data.h index 9fa7b063..03896109 100644 --- a/data.h +++ b/data.h @@ -13,9 +13,23 @@ typedef struct Cell Cell; typedef struct Font i3Font; typedef struct Container Container; typedef struct Client Client; +typedef struct Binding Binding; /* Helper types */ typedef enum { D_LEFT, D_RIGHT, D_UP, D_DOWN } direction_t; + +enum { + BIND_NONE = 0, + BIND_MOD_1 = XCB_MOD_MASK_1, + BIND_MOD_2 = XCB_MOD_MASK_2, + BIND_MOD_3 = XCB_MOD_MASK_3, + BIND_MOD_4 = XCB_MOD_MASK_4, + BIND_MOD_5 = XCB_MOD_MASK_5, + BIND_SHIFT = XCB_MOD_MASK_SHIFT, + BIND_CONTROL = XCB_MOD_MASK_CONTROL, + BIND_MODE_SWITCH = (1 << 8) +}; + struct table_dimensions_t { int x; int y; @@ -30,6 +44,17 @@ struct Cell { int column; }; +struct Binding { + /* Keycode to bind */ + uint32_t keycode; + /* Bitmask consisting of BIND_MOD_1, BIND_MODE_SWITCH, … */ + uint32_t mods; + /* Command, like in command mode */ + char *command; + + TAILQ_ENTRY(Binding) bindings; +}; + /* * We need to save the height of a font because it is required for each drawing of * text but relatively hard to get. As soon as a new font needs to be loaded, a diff --git a/mainx.c b/mainx.c index dfe1abdf..af1d5e9f 100644 --- a/mainx.c +++ b/mainx.c @@ -26,9 +26,10 @@ #define TERMINAL "/usr/pkg/bin/urxvt" -i3Font *myfont; Display *xkbdpy; +TAILQ_HEAD(bindings_head, Binding) bindings; + static const int TOP = 20; static const int LEFT = 5; static const int BOTTOM = 5; @@ -797,84 +798,142 @@ static void start_application(char *path, char *args) { } } +/* + * Due to bindings like Mode_switch + , we need to bind some keys in XCB_GRAB_MODE_SYNC. + * Therefore, we just replay all key presses. + * + */ +static int handle_key_release(void *ignored, xcb_connection_t *conn, xcb_key_release_event_t *event) { + printf("got key release, just passing\n"); + xcb_allow_events(conn, ReplayKeyboard, event->time); + xcb_flush(conn); + return 1; +} + +/* + * Parses a command, see file CMDMODE for more information + * + */ +static void parse_command(xcb_connection_t *conn, const char *command) { + printf("--- parsing command \"%s\" ---\n", command); + /* Hmm, just to be sure */ + if (command[0] == '\0') + return; + + /* Is it an ? */ + if (strncmp(command, "exec ", strlen("exec ")) == 0) { + printf("starting \"%s\"\n", command + strlen("exec ")); + start_application(command+strlen("exec "), NULL); + return; + } + + /* Is it a ? */ + if (command[0] == 'w') { + /* TODO: implement */ + printf("not yet implemented.\n"); + return; + } + + /* It's a normal */ + int times; + char *rest = NULL; + enum { ACTION_FOCUS, ACTION_MOVE, ACTION_SNAP } action = ACTION_FOCUS; + direction_t direction; + times = strtol(command, &rest, 10); + if (rest == NULL) { + printf("Invalid command: Consists only of a movement\n"); + return; + } + if (*rest == 'm' || *rest == 's') { + action = (*rest == 'm' ? ACTION_MOVE : ACTION_SNAP); + rest++; + } + + /* Now perform action to */ + while (*rest != '\0') { + /* TODO: tags */ + if (*rest == 'h') + direction = D_LEFT; + else if (*rest == 'j') + direction = D_DOWN; + else if (*rest == 'k') + direction = D_UP; + else if (*rest == 'l') + direction = D_RIGHT; + else { + printf("unknown direction: %c\n", *rest); + return; + } + + if (action == ACTION_FOCUS) + focus_window(conn, direction); + else if (action == ACTION_MOVE) + move_current_window(conn, direction); + else if (action == ACTION_SNAP) + /* TODO: implement */ + printf("snap not yet implemented\n"); + + rest++; + + } + + printf("--- done ---\n"); +} + /* * There was a key press. We lookup the key symbol and see if there are any bindings * on that. This allows to do things like binding special characters (think of ä) to * functions to get one more modifier while not losing AltGr :-) + * TODO: this description needs to be more understandable * */ static int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press_event_t *event) { - /* FIXME: We need to translate the keypress + state into a string (like, ä) - because they do not generate keysyms (use xev and see for yourself) */ - - printf("oh yay!\n"); - printf("gots press %d\n", event->detail); + printf("Keypress %d\n", event->detail); /* We need to get the keysym group (There are group 1 to group 4, each holding two keysyms (without shift and with shift) using Xkb because X fails to provide them reliably (it works in Xephyr, it does not in real X) */ XkbStateRec state; - if (XkbGetState(xkbdpy, XkbUseCoreKbd, &state) == Success) { - if (state.group+1 == 2) - event->state |= 0x2; + if (XkbGetState(xkbdpy, XkbUseCoreKbd, &state) == Success && (state.group+1) == 2) + event->state |= 0x2; + + printf("state %d\n", event->state); + + /* Find the binding */ + Binding *bind, *best_match = TAILQ_END(&bindings); + TAILQ_FOREACH(bind, &bindings, bindings) { + if (bind->keycode == event->detail && + (bind->mods & event->state) == bind->mods) { + if (best_match == TAILQ_END(&bindings) || + bind->mods > best_match->mods) + best_match = bind; + } } - printf("i'm in state %d\n", event->state); + if (best_match == TAILQ_END(&bindings)) { + printf("This key was not bound by us?! (most likely a bug)\n"); + return 1; /* TODO: return 0? what do the codes mean? */ + } - xcb_key_symbols_t *keysyms = xcb_key_symbols_alloc(conn); - - xcb_keysym_t k0 = xcb_key_symbols_get_keysym(keysyms, event->detail, event->state); - if (k0 == XCB_NONE) - printf("couldn't get k0\n"); - - printf("gots keysym %d and \n", k0); - - - /* 30 = u - * 57 = n - * 27 = r - * 28 = t - * 40 = d - * - * …uhm, I should probably say that I’ve remapped my keys in hardware :) - */ - direction_t direction; - if (event->detail == 30) { - /* 'u' */ - start_application(TERMINAL, NULL); - - return 1; - } else if (event->detail == 57) { - direction = D_LEFT; - } else if (event->detail == 27) { - direction = D_DOWN; - } else if (event->detail == 28) { - direction = D_UP; - } else if (event->detail == 40) { - direction = D_RIGHT; - } else if (event->detail == 25) { - Container *con = CUR_CELL; - if (con->colspan == 1) - con->colspan++; - else con->colspan--; - render_layout(conn); + if (event->state & 0x2) { + printf("that's mode_switch\n"); + parse_command(conn, best_match->command); + printf("ok, hiding this event.\n"); + xcb_allow_events(conn, SyncKeyboard, event->time); xcb_flush(conn); return 1; - } else { - printf("don't want this.\n"); + } + + /* If this was an actively grabbed key, and we did not handle it, we need to pass it */ + if (best_match->mods & BIND_MODE_SWITCH) { + printf("passing...\n"); + xcb_allow_events(conn, ReplayKeyboard, event->time); + xcb_flush(conn); return 1; } - /* TODO: ctrl -> focus_container(conn, direction) */ - /* FIXME: actually wrong but i'm too lazy to grab my keys all the time */ - if (event->state & XCB_MOD_MASK_CONTROL) { - move_current_window(conn, direction); - } else if (event->state & XCB_MOD_MASK_1) - focus_window(conn, direction); - /* TODO: shift -> move_current_window(conn, direction) */ - /* TODO: shift + ctrl -> move_current_container(conn, direction) */ - - return 1; + parse_command(conn, best_match->command); + return 1; } /* @@ -1036,10 +1095,13 @@ int main(int argc, char *argv[], char *env[]) { byChild = alloc_table(); byParent = alloc_table(); + TAILQ_INIT(&bindings); + c = xcb_connect(NULL, &screens); printf("x screen is %d\n", screens); + /* TODO: this has to be more beautiful somewhen */ int major, minor, error; major = XkbMajorVersion; @@ -1057,9 +1119,7 @@ int main(int argc, char *argv[], char *env[]) { fprintf(stderr, "XKB not supported by X-server\n"); return 1; } - - /* Font loading */ - myfont = load_font(c, pattern); + /* end of ugliness */ xcb_event_handlers_init(c, &evenths); for(i = 2; i < 128; ++i) @@ -1072,8 +1132,9 @@ int main(int argc, char *argv[], char *env[]) { * contents (= top/bottom bar, titlebars for each window) */ xcb_event_set_expose_handler(&evenths, handleExposeEvent, 0); - /* Key presses are pretty obvious, I think */ + /* Key presses/releases are pretty obvious, I think */ xcb_event_set_key_press_handler(&evenths, handle_key_press, 0); + xcb_event_set_key_release_handler(&evenths, handle_key_release, 0); /* Enter window = user moved his mouse over the window */ xcb_event_set_enter_notify_handler(&evenths, handle_enter_notify, 0); @@ -1090,27 +1151,37 @@ int main(int argc, char *argv[], char *env[]) { uint32_t values[] = { XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_PROPERTY_CHANGE }; xcb_change_window_attributes(c, root, mask, values); - /* Grab 'a' */ - //xcb_grab_key(c, 0, root, 0, 38, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC); + #define BIND(key, modifier, cmd) { \ + Binding *new = malloc(sizeof(Binding)); \ + new->keycode = key; \ + new->mods = modifier; \ + new->command = cmd; \ + TAILQ_INSERT_TAIL(&bindings, new, bindings); \ + } - xcb_grab_key(c, 0, root, 0, 30, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC); - xcb_grab_key(c, 0, root, 0, 38, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC); + /* 38 = 'a' */ + BIND(38, BIND_MODE_SWITCH, "foo"); + BIND(30, 0, "exec /usr/pkg/bin/urxvt"); - xcb_grab_key(c, 0, root, XCB_MOD_MASK_1, 57, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC); - xcb_grab_key(c, 0, root, XCB_MOD_MASK_1, 28, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC); - xcb_grab_key(c, 0, root, XCB_MOD_MASK_1, 27, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC); - xcb_grab_key(c, 0, root, XCB_MOD_MASK_1, 40, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC); + BIND(44, BIND_MOD_1, "h"); + BIND(45, BIND_MOD_1, "j"); + BIND(46, BIND_MOD_1, "k"); + BIND(47, BIND_MOD_1, "l"); - xcb_grab_key(c, 0, root, XCB_MOD_MASK_CONTROL, 57, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC); - xcb_grab_key(c, 0, root, XCB_MOD_MASK_CONTROL, 28, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC); - xcb_grab_key(c, 0, root, XCB_MOD_MASK_CONTROL, 27, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC); - xcb_grab_key(c, 0, root, XCB_MOD_MASK_CONTROL, 40, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC); - xcb_grab_key(c, 0, root, XCB_MOD_MASK_CONTROL, 25, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC); + BIND(44, BIND_MOD_1 | BIND_CONTROL, "mh"); + BIND(45, BIND_MOD_1 | BIND_CONTROL, "mj"); + BIND(46, BIND_MOD_1 | BIND_CONTROL, "mk"); + BIND(47, BIND_MOD_1 | BIND_CONTROL, "ml"); + Binding *bind; + TAILQ_FOREACH(bind, &bindings, bindings) { + printf("Grabbing %d\n", bind->keycode); + if (bind->mods & BIND_MODE_SWITCH) + xcb_grab_key(c, 0, root, 0, bind->keycode, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_SYNC); + else xcb_grab_key(c, 0, root, bind->mods, bind->keycode, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC); + } - - //xcb_grab_key(c, 0, root, XCB_BUTTON_MASK_ANY, 40, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC); start_application(TERMINAL, NULL); xcb_flush(c);