#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;
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,
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.
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]);
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);
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);
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,
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;
}
xcb_set_input_focus(connection, XCB_INPUT_FOCUS_NONE,
CUR_CELL->currently_focused->child, XCB_CURRENT_TIME);
render_layout(connection);
- xcb_flush(connection);
}
} else {
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;
}
}
+/*
+ * 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);
}
/*
*
*/
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);
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 };
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;
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)
* 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);
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);