]> git.sur5r.net Git - i3/i3/blobdiff - mainx.c
Implement focus on click
[i3/i3] / mainx.c
diff --git a/mainx.c b/mainx.c
index dfe1abdf41dcd7d5f41b107aa36e4e965eb24a38..8d29b4288b9132474b3c4146188ce233587e45c1 100644 (file)
--- a/mainx.c
+++ b/mainx.c
 
 #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;
@@ -347,7 +348,6 @@ uint32_t get_colorpixel(xcb_connection_t *conn, xcb_window_t window, char *hex)
 void decorate_window(xcb_connection_t *conn, Client *client) {
        uint32_t mask = 0;
        uint32_t values[3];
-       xcb_screen_t *root_screen = xcb_setup_roots_iterator(xcb_get_setup(conn)).data;
        i3Font *font = load_font(conn, pattern);
        uint32_t background_color,
                 text_color,
@@ -426,6 +426,7 @@ void render_container(xcb_connection_t *connection, Container *container) {
 
                int current_client = 0;
                CIRCLEQ_FOREACH(client, &(container->clients), clients) {
+                       /* TODO: rewrite this block so that the need to puke vanishes :) */
                        /* TODO: at the moment, every column/row is 200px. This
                         * needs to be changed to "percentage of the screen" by
                         * default and adjustable by the user if necessary.
@@ -433,24 +434,33 @@ void render_container(xcb_connection_t *connection, Container *container) {
                        values[0] = container->col * container->width; /* x */
                        values[1] = container->row * container->height +
                                (container->height / num_clients) * current_client; /* y */
-                       /* TODO: vertical default layout */
-                       values[2] = container->width; /* width */
-                       values[3] = container->height / num_clients; /* height */
-                       printf("frame will be at %dx%d with size %dx%d\n",
-                                       values[0], values[1], values[2], values[3]);
 
-                       client->width = values[2];
-                       client->height = values[3];
-
-                       /* TODO: update only if necessary */
-                       xcb_configure_window(connection, client->frame, mask, values);
+                       if (client->x != values[0] || client->y != values[1]) {
+                               printf("frame needs to be pushed to %dx%d\n",
+                                               values[0], values[1]);
+                               client->x = values[0];
+                               client->y = values[1];
+                               xcb_configure_window(connection, client->frame,
+                                               XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y, values);
+                       }
+                       /* TODO: vertical default layout */
+                       values[0] = container->width; /* width */
+                       values[1] = container->height / num_clients; /* height */
+
+                       if (client->width != values[0] || client->height != values[1]) {
+                               client->width = values[0];
+                               client->height = values[1];
+                               xcb_configure_window(connection, client->frame,
+                                               XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT, values);
+                       }
 
+                       /* TODO: hmm, only do this for new wins */
                        /* The coordinates of the child are relative to its frame, we
                         * add a border of 2 pixel to each value */
                        values[0] = 2;
                        values[1] = font->height + 2 + 2;
-                       values[2] -= values[0] + 2;
-                       values[3] -= values[1] + 2;
+                       values[2] = client->width - (values[0] + 2);
+                       values[3] = client->height - (values[1] + 2);
                        printf("child itself will be at %dx%d with size %dx%d\n",
                                        values[0], values[1], values[2], values[3]);
 
@@ -508,10 +518,11 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child,
        if (new == NULL) {
                printf("oh, it's new\n");
                new = calloc(sizeof(Client), 1);
+               new->x = -1;
+               new->y = -1;
        }
        uint32_t mask = 0;
        uint32_t values[3];
-       xcb_screen_t *root_screen = xcb_setup_roots_iterator(xcb_get_setup(conn)).data;
 
        /* Insert into the currently active container */
        CIRCLEQ_INSERT_TAIL(&(CUR_CELL->clients), new, clients);
@@ -525,16 +536,16 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child,
        new->width = width;
        new->height = height;
 
-       /* TODO: what do these mean? */
-       mask |= XCB_CW_BACK_PIXEL;
-       values[0] = root_screen->white_pixel;
-
+       /* Don’t generate events for our new window, it should *not* be managed */
        mask |= XCB_CW_OVERRIDE_REDIRECT;
-       values[1] = 1;
+       values[0] = 1;
 
+       /* We want to know when… */
        mask |= XCB_CW_EVENT_MASK;
-       values[2] = XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE
-               | XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_ENTER_WINDOW;
+       values[1] =     XCB_EVENT_MASK_BUTTON_PRESS | /* …mouse is pressed/released */
+                       XCB_EVENT_MASK_BUTTON_RELEASE |
+                       XCB_EVENT_MASK_EXPOSURE | /* …our window needs to be redrawn */
+                       XCB_EVENT_MASK_ENTER_WINDOW /* …user moves cursor inside our window */;
 
        printf("Reparenting 0x%08x under 0x%08x.\n", child, new->frame);
 
@@ -576,15 +587,19 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child,
        mask = XCB_CW_EVENT_MASK;
        values[0] =     XCB_EVENT_MASK_PROPERTY_CHANGE |
                        XCB_EVENT_MASK_STRUCTURE_NOTIFY |
-                       XCB_EVENT_MASK_ENTER_WINDOW;
+                       XCB_EVENT_MASK_ENTER_WINDOW |
+                       XCB_EVENT_MASK_BUTTON_PRESS;
        xcb_change_window_attributes(conn, child, mask, values);
 
-       /* TODO: At the moment, new windows just get focus */
-       xcb_set_input_focus(conn, XCB_INPUT_FOCUS_NONE, new->frame, XCB_CURRENT_TIME);
+       /* We need to grab the mouse buttons for click to focus */
+       xcb_grab_button(conn, false, child, XCB_EVENT_MASK_BUTTON_PRESS,
+                       XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, root, XCB_NONE, XCB_BUTTON_MASK_1,
+                       XCB_BUTTON_MASK_ANY /* don’t filter for any modifiers */);
 
-       render_layout(conn);
+       /* Focus the new window */
+       xcb_set_input_focus(conn, XCB_INPUT_FOCUS_NONE, new->child, XCB_CURRENT_TIME);
 
-       xcb_flush(conn);
+       render_layout(conn);
 }
 
 static bool focus_window_in_container(xcb_connection_t *connection, Container *container,
@@ -607,7 +622,6 @@ static bool focus_window_in_container(xcb_connection_t *connection, Container *c
        container->currently_focused = candidad;
        xcb_set_input_focus(connection, XCB_INPUT_FOCUS_NONE, candidad->child, XCB_CURRENT_TIME);
        render_layout(connection);
-       xcb_flush(connection);
 
        return true;
 }
@@ -637,7 +651,6 @@ static void focus_window(xcb_connection_t *connection, direction_t direction) {
                        xcb_set_input_focus(connection, XCB_INPUT_FOCUS_NONE,
                                        CUR_CELL->currently_focused->child, XCB_CURRENT_TIME);
                        render_layout(connection);
-                       xcb_flush(connection);
                }
 
        } else {
@@ -739,6 +752,73 @@ static void move_current_window(xcb_connection_t *connection, direction_t direct
        render_layout(connection);
 }
 
+/*
+ * "Snaps" the current container (not possible for windows, because it works at table base)
+ * to the given direction, that is, adjusts cellspan/rowspan
+ *
+ */
+static void snap_current_container(xcb_connection_t *connection, direction_t direction) {
+       printf("snapping container to direction %d\n", direction);
+
+       Container *container = CUR_CELL;
+       int i;
+
+       assert(container != NULL);
+
+       switch (direction) {
+               case D_LEFT:
+                       /* Snap to the left is actually a move to the left and then a snap right */
+                       move_current_window(connection, D_LEFT);
+                       snap_current_container(connection, D_RIGHT);
+                       return;
+               case D_RIGHT:
+                       /* Check if the cell is used */
+                       if (!cell_exists(container->col + 1, container->row) ||
+                               table[container->col+1][container->row]->currently_focused != NULL) {
+                               printf("cannot snap to right - the cell is already used\n");
+                               return;
+                       }
+
+                       /* Check if there are other cells with rowspan, which are in our way.
+                        * If so, reduce their rowspan. */
+                       for (i = container->row-1; i >= 0; i--) {
+                               printf("we got cell %d, %d with rowspan %d\n",
+                                               container->col+1, i, table[container->col+1][i]->rowspan);
+                               while ((table[container->col+1][i]->rowspan-1) >= (container->row - i))
+                                       table[container->col+1][i]->rowspan--;
+                               printf("new rowspan = %d\n", table[container->col+1][i]->rowspan);
+                       }
+
+                       container->colspan++;
+                       break;
+               case D_UP:
+                       move_current_window(connection, D_UP);
+                       snap_current_container(connection, D_DOWN);
+                       return;
+               case D_DOWN:
+                       printf("snapping down\n");
+                       if (!cell_exists(container->col, container->row+1) ||
+                               table[container->col][container->row+1]->currently_focused != NULL) {
+                               printf("cannot snap down - the cell is already used\n");
+                               return;
+                       }
+
+                       for (i = container->col-1; i >= 0; i--) {
+                               printf("we got cell %d, %d with colspan %d\n",
+                                               i, container->row+1, table[i][container->row+1]->colspan);
+                               while ((table[i][container->row+1]->colspan-1) >= (container->col - i))
+                                       table[i][container->row+1]->colspan--;
+                               printf("new colspan = %d\n", table[i][container->row+1]->colspan);
+
+                       }
+
+                       container->rowspan++;
+                       break;
+       }
+
+       render_layout(connection);
+}
+
 int format_event(xcb_generic_event_t *e)
 {           
     uint8_t sendEvent;
@@ -797,84 +877,153 @@ static void start_application(char *path, char *args) {
        }
 }
 
+/*
+ * Due to bindings like Mode_switch + <a>, 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 <exec>? */
+       if (strncmp(command, "exec ", strlen("exec ")) == 0) {
+               printf("starting \"%s\"\n", command + strlen("exec "));
+               start_application(command+strlen("exec "), NULL);
+               return;
+       }
+
+       /* Is it a <with>? */
+       if (command[0] == 'w') {
+               /* TODO: implement */
+               printf("not yet implemented.\n");
+               return;
+       }
+
+       /* It's a normal <cmd> */
+       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 <where> */
+       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)
+                       snap_current_container(conn, direction);
+
+               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);
 
-
-       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);
+       /* No match? Then it was an actively grabbed key, that is with Mode_switch, and
+          the user did not press Mode_switch, so just pass it… */
+       if (best_match == TAILQ_END(&bindings)) {
+               xcb_allow_events(conn, ReplayKeyboard, event->time);
                xcb_flush(conn);
                return 1;
-       } else {
-               printf("don't want this.\n");
+       }
+
+       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;
        }
 
-       /* 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) */
+       parse_command(conn, best_match->command);
+       return 1;
+}
+
+static void set_focus(xcb_connection_t *conn, Client *client) {
+       /* Update container */
+       Client *old_client = client->container->currently_focused;
+       client->container->currently_focused = client;
+
+       current_col = client->container->col;
+       current_row = client->container->row;
 
-        return 1;
+       /* Set focus to the entered window, and flush xcb buffer immediately */
+       xcb_set_input_focus(conn, XCB_INPUT_FOCUS_NONE, client->child, XCB_CURRENT_TIME);
+       /* Update last/current client’s titlebar */
+       if (old_client != NULL)
+               decorate_window(conn, old_client);
+       decorate_window(conn, client);
+       xcb_flush(conn);
 }
 
 /*
@@ -882,9 +1031,10 @@ static int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press
  *
  */
 static int handle_enter_notify(void *ignored, xcb_connection_t *conn, xcb_enter_notify_event_t *event) {
+       printf("enter_notify\n");
+
        /* This was either a focus for a client’s parent (= titlebar)… */
-       Client *client = table_get(byParent, event->event),
-              *old_client;
+       Client *client = table_get(byParent, event->event);
        /* …or the client itself */
        if (client == NULL)
                client = table_get(byChild, event->event);
@@ -895,25 +1045,22 @@ static int handle_enter_notify(void *ignored, xcb_connection_t *conn, xcb_enter_
                return 1;
        }
 
-       /* Update container */
-       old_client = client->container->currently_focused;
-       client->container->currently_focused = client;
+       set_focus(conn, client);
 
-       current_col = client->container->col;
-       current_row = client->container->row;
+       return 1;
+}
 
-       /* Set focus to the entered window, and flush xcb buffer immediately */
-       xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, client->child, XCB_CURRENT_TIME);
-       /* Update last/current client’s titlebar */
-       if (old_client != NULL)
-               decorate_window(conn, old_client);
-       decorate_window(conn, client);
-       xcb_flush(conn);
+static int handle_button_press(void *ignored, xcb_connection_t *conn, xcb_button_press_event_t *event) {
+       printf("button press!\n");
+       /* This was either a focus for a client’s parent (= titlebar)… */
+       Client *client = table_get(byChild, event->event);
+
+       printf("gots win %08x\n", client);
 
+       set_focus(conn, client);
        return 1;
 }
 
-
 int handle_map_notify_event(void *prophs, xcb_connection_t *c, xcb_map_notify_event_t *e)
 {
        window_attributes_t wa = { TAG_VALUE };
@@ -1036,10 +1183,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 +1207,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,12 +1220,16 @@ 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);
 
+       /* Button press = user pushed a mouse button over one of our windows */
+       xcb_event_set_button_press_handler(&evenths, handle_button_press, 0);
+
        xcb_event_set_unmap_notify_handler(&evenths, handle_unmap_notify_event, 0);
 
        xcb_property_handlers_init(&prophs, &evenths);
@@ -1090,27 +1242,42 @@ 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, "sh");
+       BIND(45, BIND_MOD_1 | BIND_CONTROL, "sj");
+       BIND(46, BIND_MOD_1 | BIND_CONTROL, "sk");
+       BIND(47, BIND_MOD_1 | BIND_CONTROL, "sl");
 
+       BIND(44, BIND_MOD_1 | BIND_SHIFT, "mh");
+       BIND(45, BIND_MOD_1 | BIND_SHIFT, "mj");
+       BIND(46, BIND_MOD_1 | BIND_SHIFT, "mk");
+       BIND(47, BIND_MOD_1 | BIND_SHIFT, "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);