diff --git a/Makefile b/Makefile index b7b3fc40..9e919dc8 100644 --- a/Makefile +++ b/Makefile @@ -4,9 +4,9 @@ CFLAGS += -Wall # Extended debugging flags, macros shall be available in gcc CFLAGS += -gdwarf-2 CFLAGS += -g3 -CFLAGS += -I/usr/include/xcb +#CFLAGS += -I/usr/include/xcb CFLAGS += -I/usr/local/include/ -CFLAGS += -I/usr/local/include/xcb +#CFLAGS += -I/usr/local/include/xcb CFLAGS += -I/usr/pkg/include LDFLAGS += -lxcb-wm diff --git a/commands.c b/commands.c new file mode 100644 index 00000000..8e3161cf --- /dev/null +++ b/commands.c @@ -0,0 +1,348 @@ +#include +#include +#include +#include +#include +#include + +#include + +#include "util.h" +#include "data.h" +#include "table.h" +#include "layout.h" +#include "i3.h" + +static bool focus_window_in_container(xcb_connection_t *connection, Container *container, + direction_t direction) { + /* If this container is empty, we’re done */ + if (container->currently_focused == NULL) + return false; + + Client *candidad; + if (direction == D_UP) + candidad = CIRCLEQ_PREV(container->currently_focused, clients); + else if (direction == D_DOWN) + candidad = CIRCLEQ_NEXT(container->currently_focused, clients); + + /* If we don’t have anything to select, we’re done */ + if (candidad == CIRCLEQ_END(&(container->clients))) + return false; + + /* Set focus if we could successfully move */ + container->currently_focused = candidad; + xcb_set_input_focus(connection, XCB_INPUT_FOCUS_NONE, candidad->child, XCB_CURRENT_TIME); + render_layout(connection); + + return true; +} + +static void focus_window(xcb_connection_t *connection, direction_t direction) { + printf("focusing direction %d\n", direction); + /* TODO: for horizontal default layout, this has to be expanded to LEFT/RIGHT */ + if (direction == D_UP || direction == D_DOWN) { + /* Let’s see if we can perform up/down focus in the current container */ + Container *container = CUR_CELL; + + /* There always is a container. If not, current_col or current_row is wrong */ + assert(container != NULL); + + if (focus_window_in_container(connection, container, direction)) + return; + } else if (direction == D_LEFT || direction == D_RIGHT) { + if (direction == D_RIGHT && cell_exists(current_col+1, current_row)) + current_col++; + else if (direction == D_LEFT && cell_exists(current_col-1, current_row)) + current_col--; + else { + printf("nah, not possible\n"); + return; + } + if (CUR_CELL->currently_focused != NULL) { + xcb_set_input_focus(connection, XCB_INPUT_FOCUS_NONE, + CUR_CELL->currently_focused->child, XCB_CURRENT_TIME); + render_layout(connection); + } + + } else { + printf("direction unhandled\n"); + } +} + +/* + * Tries to move the window inside its current container. + * + * Returns true if the window could be moved, false otherwise. + * + */ +static bool move_current_window_in_container(xcb_connection_t *connection, Client *client, + direction_t direction) { + Client *other = (direction == D_UP ? CIRCLEQ_PREV(client, clients) : + CIRCLEQ_NEXT(client, clients)); + + if (other == CIRCLEQ_END(&(client->container->clients))) + return false; + + printf("i can do that\n"); + /* We can move the client inside its current container */ + CIRCLEQ_REMOVE(&(client->container->clients), client, clients); + if (direction == D_UP) + CIRCLEQ_INSERT_BEFORE(&(client->container->clients), other, client, clients); + else CIRCLEQ_INSERT_AFTER(&(client->container->clients), other, client, clients); + render_layout(connection); + return true; +} + +/* + * Moves the current window to the given direction, creating a column/row if + * necessary + * + */ +static void move_current_window(xcb_connection_t *connection, direction_t direction) { + printf("moving window to direction %d\n", direction); + /* Get current window */ + Container *container = CUR_CELL, + *new; + + /* There has to be a container, see focus_window() */ + assert(container != NULL); + + /* If there is no window, we’re done */ + if (container->currently_focused == NULL) + return; + + /* As soon as the client is moved away, the next client in the old + * container needs to get focus, if any. Therefore, we save it here. */ + Client *current_client = container->currently_focused; + Client *to_focus = CIRCLEQ_NEXT(current_client, clients); + if (to_focus == CIRCLEQ_END(&(container->clients))) + to_focus = NULL; + + switch (direction) { + case D_LEFT: + if (current_col == 0) + return; + + new = CUR_TABLE[--current_col][current_row]; + break; + case D_RIGHT: + if (current_col == (c_ws->cols-1)) + expand_table_cols(c_ws); + + new = CUR_TABLE[++current_col][current_row]; + break; + case D_UP: + /* TODO: if we’re at the up-most position, move the rest of the table down */ + if (move_current_window_in_container(connection, current_client, D_UP) || + current_row == 0) + return; + + new = CUR_TABLE[current_col][--current_row]; + break; + case D_DOWN: + if (move_current_window_in_container(connection, current_client, D_DOWN)) + return; + + if (current_row == (c_ws->rows-1)) + expand_table_rows(c_ws); + + new = CUR_TABLE[current_col][++current_row]; + break; + } + + /* Remove it from the old container and put it into the new one */ + CIRCLEQ_REMOVE(&(container->clients), current_client, clients); + CIRCLEQ_INSERT_TAIL(&(new->clients), current_client, clients); + + /* Update data structures */ + current_client->container = new; + container->currently_focused = to_focus; + new->currently_focused = current_client; + + /* TODO: delete all empty columns/rows */ + + 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) || + CUR_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, CUR_TABLE[container->col+1][i]->rowspan); + while ((CUR_TABLE[container->col+1][i]->rowspan-1) >= (container->row - i)) + CUR_TABLE[container->col+1][i]->rowspan--; + printf("new rowspan = %d\n", CUR_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) || + CUR_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, CUR_TABLE[i][container->row+1]->colspan); + while ((CUR_TABLE[i][container->row+1]->colspan-1) >= (container->col - i)) + CUR_TABLE[i][container->row+1]->colspan--; + printf("new colspan = %d\n", CUR_TABLE[i][container->row+1]->colspan); + + } + + container->rowspan++; + break; + } + + render_layout(connection); +} + +static void show_workspace(xcb_connection_t *conn, int workspace) { + int cols, rows; + Client *client; + printf("show_workspace(%d)\n", workspace); + + xcb_window_t root = xcb_setup_roots_iterator(xcb_get_setup(conn)).data->root; + + /* Store current_row/current_col */ + c_ws->current_row = current_row; + c_ws->current_col = current_col; + + /* TODO: does grabbing the server actually bring us any (speed)advantages? */ + //xcb_grab_server(conn); + + /* Unmap all clients */ + for (cols = 0; cols < c_ws->cols; cols++) + for (rows = 0; rows < c_ws->rows; rows++) { + CIRCLEQ_FOREACH(client, &(c_ws->table[cols][rows]->clients), clients) + xcb_unmap_window(conn, client->frame); + } + + c_ws = &workspaces[workspace-1]; + current_row = c_ws->current_row; + current_col = c_ws->current_col; + printf("new current row = %d, current col = %d\n", current_row, current_col); + + /* Map all clients on the new workspace */ + for (cols = 0; cols < c_ws->cols; cols++) + for (rows = 0; rows < c_ws->rows; rows++) { + CIRCLEQ_FOREACH(client, &(c_ws->table[cols][rows]->clients), clients) + xcb_map_window(conn, client->frame); + } + + /* Restore focus on the new workspace */ + if (CUR_CELL->currently_focused != NULL) + xcb_set_input_focus(conn, XCB_INPUT_FOCUS_NONE, CUR_CELL->currently_focused->child, XCB_CURRENT_TIME); + else xcb_set_input_focus(conn, XCB_INPUT_FOCUS_NONE, root, XCB_CURRENT_TIME); + + //xcb_ungrab_server(conn); + + render_layout(conn); +} + +/* + * Parses a command, see file CMDMODE for more information + * + */ +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++; + } + + if (*rest == '\0') { + /* No rest? This was a tag number, not a times specification */ + show_workspace(conn, times); + return; + } + + /* Now perform action to */ + while (*rest != '\0') { + 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"); +} diff --git a/commands.h b/commands.h new file mode 100644 index 00000000..148f56fa --- /dev/null +++ b/commands.h @@ -0,0 +1,8 @@ +#include + +#ifndef _COMMANDS_H +#define _COMMANDS_H + +void parse_command(xcb_connection_t *conn, const char *command); + +#endif diff --git a/debug.c b/debug.c new file mode 100644 index 00000000..cbdbd368 --- /dev/null +++ b/debug.c @@ -0,0 +1,235 @@ +#include +#include + +/* Debug functions here, especially the FormatEvent-stuff, which prints unhandled events */ + +static const char *labelError[] = { + "Success", + "BadRequest", + "BadValue", + "BadWindow", + "BadPixmap", + "BadAtom", + "BadCursor", + "BadFont", + "BadMatch", + "BadDrawable", + "BadAccess", + "BadAlloc", + "BadColor", + "BadGC", + "BadIDChoice", + "BadName", + "BadLength", + "BadImplementation", +}; + +static const char *labelRequest[] = { + "no request", + "CreateWindow", + "ChangeWindowAttributes", + "GetWindowAttributes", + "DestroyWindow", + "DestroySubwindows", + "ChangeSaveSet", + "ReparentWindow", + "MapWindow", + "MapSubwindows", + "UnmapWindow", + "UnmapSubwindows", + "ConfigureWindow", + "CirculateWindow", + "GetGeometry", + "QueryTree", + "InternAtom", + "GetAtomName", + "ChangeProperty", + "DeleteProperty", + "GetProperty", + "ListProperties", + "SetSelectionOwner", + "GetSelectionOwner", + "ConvertSelection", + "SendEvent", + "GrabPointer", + "UngrabPointer", + "GrabButton", + "UngrabButton", + "ChangeActivePointerGrab", + "GrabKeyboard", + "UngrabKeyboard", + "GrabKey", + "UngrabKey", + "AllowEvents", + "GrabServer", + "UngrabServer", + "QueryPointer", + "GetMotionEvents", + "TranslateCoords", + "WarpPointer", + "SetInputFocus", + "GetInputFocus", + "QueryKeymap", + "OpenFont", + "CloseFont", + "QueryFont", + "QueryTextExtents", + "ListFonts", + "ListFontsWithInfo", + "SetFontPath", + "GetFontPath", + "CreatePixmap", + "FreePixmap", + "CreateGC", + "ChangeGC", + "CopyGC", + "SetDashes", + "SetClipRectangles", + "FreeGC", + "ClearArea", + "CopyArea", + "CopyPlane", + "PolyPoint", + "PolyLine", + "PolySegment", + "PolyRectangle", + "PolyArc", + "FillPoly", + "PolyFillRectangle", + "PolyFillArc", + "PutImage", + "GetImage", + "PolyText", + "PolyText", + "ImageText", + "ImageText", + "CreateColormap", + "FreeColormap", + "CopyColormapAndFree", + "InstallColormap", + "UninstallColormap", + "ListInstalledColormaps", + "AllocColor", + "AllocNamedColor", + "AllocColorCells", + "AllocColorPlanes", + "FreeColors", + "StoreColors", + "StoreNamedColor", + "QueryColors", + "LookupColor", + "CreateCursor", + "CreateGlyphCursor", + "FreeCursor", + "RecolorCursor", + "QueryBestSize", + "QueryExtension", + "ListExtensions", + "ChangeKeyboardMapping", + "GetKeyboardMapping", + "ChangeKeyboardControl", + "GetKeyboardControl", + "Bell", + "ChangePointerControl", + "GetPointerControl", + "SetScreenSaver", + "GetScreenSaver", + "ChangeHosts", + "ListHosts", + "SetAccessControl", + "SetCloseDownMode", + "KillClient", + "RotateProperties", + "ForceScreenSaver", + "SetPointerMapping", + "GetPointerMapping", + "SetModifierMapping", + "GetModifierMapping", + "major 120", + "major 121", + "major 122", + "major 123", + "major 124", + "major 125", + "major 126", + "NoOperation", +}; + +static const char *labelEvent[] = { + "error", + "reply", + "KeyPress", + "KeyRelease", + "ButtonPress", + "ButtonRelease", + "MotionNotify", + "EnterNotify", + "LeaveNotify", + "FocusIn", + "FocusOut", + "KeymapNotify", + "Expose", + "GraphicsExpose", + "NoExpose", + "VisibilityNotify", + "CreateNotify", + "DestroyNotify", + "UnmapNotify", + "MapNotify", + "MapRequest", + "ReparentNotify", + "ConfigureNotify", + "ConfigureRequest", + "GravityNotify", + "ResizeRequest", + "CirculateNotify", + "CirculateRequest", + "PropertyNotify", + "SelectionClear", + "SelectionRequest", + "SelectionNotify", + "ColormapNotify", + "ClientMessage", + "MappingNotify", +}; + +static const char *labelSendEvent[] = { + "", + " (from SendEvent)", +}; + +int format_event(xcb_generic_event_t *e) { + uint8_t sendEvent; + uint16_t seqnum; + + sendEvent = (e->response_type & 0x80) ? 1 : 0; + e->response_type &= ~0x80; + seqnum = *((uint16_t *) e + 1); + + switch(e->response_type) { + case 0: + printf("Error %s on seqnum %d (%s).\n", + labelError[*((uint8_t *) e + 1)], + seqnum, + labelRequest[*((uint8_t *) e + 10)]); + break; + default: + printf("Event %s following seqnum %d%s.\n", + labelEvent[e->response_type], + seqnum, + labelSendEvent[sendEvent]); + break; + case XCB_KEYMAP_NOTIFY: + printf("Event %s%s.\n", + labelEvent[e->response_type], + labelSendEvent[sendEvent]); + break; + } + + fflush(stdout); + return 1; +} + +int handle_event(void *ignored, xcb_connection_t *c, xcb_generic_event_t *e) { + return format_event(e); +} diff --git a/debug.h b/debug.h new file mode 100644 index 00000000..3d9cf3e7 --- /dev/null +++ b/debug.h @@ -0,0 +1,6 @@ +#ifndef _DEBUG_H +#define _DEBUG_H + +int handle_event(void *ignored, xcb_connection_t *c, xcb_generic_event_t *e); + +#endif diff --git a/font.c b/font.c index 407955a0..803ac0a6 100644 --- a/font.c +++ b/font.c @@ -8,17 +8,7 @@ #include #include "data.h" - -/* TODO: This is just here to be somewhere. Move it somewhere else. */ -void check_error(xcb_connection_t *connection, xcb_void_cookie_t cookie, char *errMessage) { - xcb_generic_error_t *error = xcb_request_check (connection, cookie); - if (error != NULL) { - fprintf(stderr, "ERROR: %s : %d\n", errMessage , error->error_code); - xcb_disconnect(connection); - exit(-1); - } -} - +#include "util.h" i3Font *load_font(xcb_connection_t *c, const char *pattern) { /* TODO: this function should be caching */ diff --git a/handlers.c b/handlers.c new file mode 100644 index 00000000..58640368 --- /dev/null +++ b/handlers.c @@ -0,0 +1,326 @@ +#include +#include +#include +#include +#include + +#include +#include + +#include "i3.h" +#include "debug.h" +#include "table.h" +#include "layout.h" +#include "commands.h" +#include "data.h" +#include "font.h" + +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; + + /* 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); +} + + +/* + * Due to bindings like Mode_switch + , we need to bind some keys in XCB_GRAB_MODE_SYNC. + * Therefore, we just replay all key presses. + * + */ +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, XCB_ALLOW_REPLAY_KEYBOARD, event->time); + xcb_flush(conn); + return 1; +} + +/* + * 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 + * + */ +int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press_event_t *event) { + 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 && (state.group+1) == 2) + event->state |= 0x2; + + printf("state %d\n", event->state); + + /* Find the binding */ + /* TODO: event->state durch eine bitmask filtern und dann direkt vergleichen */ + 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; + } + } + + /* 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; + } + + 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; + } + + parse_command(conn, best_match->command); + return 1; +} + + +/* + * When the user moves the mouse pointer onto a window, this callback gets called. + * + */ +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); + /* …or the client itself */ + if (client == NULL) + client = table_get(byChild, event->event); + + /* If not, then this event is not interesting. This should not happen */ + if (client == NULL) { + printf("DEBUG: Uninteresting enter_notify-event?\n"); + return 1; + } + + set_focus(conn, client); + + return 1; +} + +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); + if (client == NULL) + client = table_get(byParent, event->event); + if (client == NULL) + return 1; + + xcb_window_t root = xcb_setup_roots_iterator(xcb_get_setup(conn)).data->root; + xcb_screen_t *root_screen = xcb_setup_roots_iterator(xcb_get_setup(conn)).data; + + /* Set focus in any case */ + set_focus(conn, client); + + /* Let’s see if this was on the borders (= resize). If not, we’re done */ + i3Font *font = load_font(conn, pattern); + printf("press button on x=%d, y=%d\n", event->event_x, event->event_y); + if (event->event_y <= (font->height + 2)) + return 1; + + printf("that was resize\n"); + + /* Open a new window, the resizebar. Grab the pointer and move the window around + as the user moves the pointer. */ + + + /* TODO: the whole logic is missing. this is just a proof of concept */ + xcb_window_t grabwin = xcb_generate_id(conn); + + uint32_t mask = 0; + uint32_t values[3]; + + xcb_create_window(conn, + 0, + grabwin, + root, + 0, /* x */ + 0, /* y */ + root_screen->width_in_pixels, /* width */ + root_screen->height_in_pixels, /* height */ + /* border_width */ 0, + XCB_WINDOW_CLASS_INPUT_ONLY, + root_screen->root_visual, + 0, + values); + + /* Map the window on the screen (= make it visible) */ + xcb_map_window(conn, grabwin); + + xcb_window_t helpwin = xcb_generate_id(conn); + + mask = XCB_CW_BACK_PIXEL; + values[0] = root_screen->white_pixel; + xcb_create_window(conn, root_screen->root_depth, helpwin, root, + event->root_x, + 0, + 5, + root_screen->height_in_pixels, + /* bordor */ 0, + XCB_WINDOW_CLASS_INPUT_OUTPUT, + root_screen->root_visual, + mask, + values); + + xcb_map_window(conn, helpwin); + xcb_circulate_window(conn, XCB_CIRCULATE_RAISE_LOWEST, helpwin); + + xcb_grab_pointer(conn, false, root, XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_POINTER_MOTION, + XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, grabwin, XCB_NONE, XCB_CURRENT_TIME); + + xcb_flush(conn); + + xcb_generic_event_t *inside_event; + /* I’ve always wanted to have my own eventhandler… */ + while ((inside_event = xcb_wait_for_event(conn))) { + /* Same as get_event_handler in xcb */ + int nr = inside_event->response_type; + if (nr == 0) { + handle_event(NULL, conn, inside_event); + continue; + } + assert(nr < 256); + nr &= XCB_EVENT_RESPONSE_TYPE_MASK; + assert(nr >= 2); + + /* Check if we need to escape this loop… */ + if (nr == XCB_BUTTON_RELEASE) + break; + + switch (nr) { + case XCB_MOTION_NOTIFY: + values[0] = ((xcb_motion_notify_event_t*)inside_event)->root_x; + xcb_configure_window(conn, helpwin, XCB_CONFIG_WINDOW_X, values); + xcb_flush(conn); + break; + case XCB_EXPOSE: + /* Use original handler */ + xcb_event_handle(&evenths, inside_event); + break; + default: + printf("Ignoring event of type %d\n", nr); + break; + } + printf("---\n"); + free(inside_event); + } + + xcb_ungrab_pointer(conn, XCB_CURRENT_TIME); + xcb_destroy_window(conn, helpwin); + xcb_destroy_window(conn, grabwin); + xcb_flush(conn); + + return 1; +} + +int handle_map_notify_event(void *prophs, xcb_connection_t *conn, xcb_map_notify_event_t *event) { + window_attributes_t wa = { TAG_VALUE }; + wa.u.override_redirect = event->override_redirect; + printf("MapNotify for 0x%08x.\n", event->window); + manage_window(prophs, conn, event->window, wa); + return 1; +} + +/* + * Our window decorations were unmapped. That means, the window will be killed now, + * so we better clean up before. + * + */ +int handle_unmap_notify_event(void *data, xcb_connection_t *c, xcb_unmap_notify_event_t *e) { + Client *client = table_remove(byChild, e->event); + xcb_window_t root; + printf("UnmapNotify for 0x%08x (received from 0x%08x): ", e->window, e->event); + if(!client) + { + printf("not a managed window. Ignoring.\n"); + return 0; + } + + int rows, cols; + Client *con_client; + /* TODO: clear this up */ + for (cols = 0; cols < c_ws->cols; cols++) + for (rows = 0; rows < c_ws->rows; rows++) + CIRCLEQ_FOREACH(con_client, &(CUR_TABLE[cols][rows]->clients), clients) + if (con_client == client) { + printf("removing from container\n"); + if (client->container->currently_focused == client) + client->container->currently_focused = NULL; + CIRCLEQ_REMOVE(&(CUR_TABLE[cols][rows]->clients), con_client, clients); + break; + } + + + + root = xcb_setup_roots_iterator(xcb_get_setup(c)).data->root; + printf("child of 0x%08x.\n", client->frame); + xcb_reparent_window(c, client->child, root, 0, 0); + xcb_destroy_window(c, client->frame); + xcb_flush(c); + table_remove(byParent, client->frame); + free(client); + + render_layout(c); + + return 1; +} + +/* + * Called when a window changes its title + * + */ +int handle_windowname_change(void *data, xcb_connection_t *conn, uint8_t state, + xcb_window_t window, xcb_atom_t atom, xcb_get_property_reply_t *prop) { + printf("window's name changed.\n"); + Client *client = table_get(byChild, window); + if (client == NULL) + return 1; + + client->name_len = xcb_get_property_value_length(prop); + client->name = malloc(client->name_len); + strncpy(client->name, xcb_get_property_value(prop), client->name_len); + printf("rename to \"%.*s\".\n", client->name_len, client->name); + + decorate_window(conn, client); + xcb_flush(conn); + + return 1; +} + +/* + * Expose event means we should redraw our windows (= title bar) + * + */ +int handle_expose_event(void *data, xcb_connection_t *conn, xcb_expose_event_t *e) { + printf("handle_expose_event()\n"); + Client *client = table_get(byParent, e->window); + if(!client || e->count != 0) + return 1; + decorate_window(conn, client); + return 1; +} diff --git a/handlers.h b/handlers.h new file mode 100644 index 00000000..7c82ca46 --- /dev/null +++ b/handlers.h @@ -0,0 +1,17 @@ +#ifndef _HANDLERS_H +#define _HANDLERS_H + +int handle_key_release(void *ignored, xcb_connection_t *conn, xcb_key_release_event_t *event); +int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press_event_t *event); +int handle_enter_notify(void *ignored, xcb_connection_t *conn, xcb_enter_notify_event_t *event); +int handle_button_press(void *ignored, xcb_connection_t *conn, xcb_button_press_event_t *event); +int handle_map_notify_event(void *prophs, xcb_connection_t *conn, xcb_map_notify_event_t *event); +int handle_windowname_change(void *data, xcb_connection_t *conn, uint8_t state, + xcb_window_t window, xcb_atom_t atom, xcb_get_property_reply_t *prop); +int handle_expose_event(void *data, xcb_connection_t *conn, xcb_expose_event_t *event); + + + + + +#endif diff --git a/i3.h b/i3.h new file mode 100644 index 00000000..d2453f47 --- /dev/null +++ b/i3.h @@ -0,0 +1,18 @@ +#include +#include + +#include + +#include "queue.h" + +#ifndef _I3_H +#define _I3_H + +extern Display *xkbdpy; +extern TAILQ_HEAD(bindings_head, Binding) bindings; +extern xcb_event_handlers_t evenths; +extern char *pattern; +extern char **environment; +extern int num_screens; + +#endif diff --git a/layout.c b/layout.c new file mode 100644 index 00000000..4a09e416 --- /dev/null +++ b/layout.c @@ -0,0 +1,181 @@ +#include +#include +#include +#include + +#include "font.h" +#include "i3.h" +#include "xcb.h" +#include "table.h" +#include "util.h" + +/* All functions handling layout/drawing of window decorations */ + +/* + * (Re-)draws window decorations for a given Client + * + */ +void decorate_window(xcb_connection_t *conn, Client *client) { + uint32_t mask = 0; + uint32_t values[3]; + i3Font *font = load_font(conn, pattern); + uint32_t background_color, + text_color, + border_color; + + if (client->container->currently_focused == client) { + background_color = get_colorpixel(conn, client->frame, "#285577"); + text_color = get_colorpixel(conn, client->frame, "#ffffff"); + border_color = get_colorpixel(conn, client->frame, "#4c7899"); + } else { + background_color = get_colorpixel(conn, client->frame, "#222222"); + text_color = get_colorpixel(conn, client->frame, "#888888"); + border_color = get_colorpixel(conn, client->frame, "#333333"); + } + + /* Our plan is the following: + - Draw a rect around the whole client in background_color + - Draw two lines in a lighter color + - Draw the window’s title + + Note that xcb_image_text apparently adds 1xp border around the font? Can anyone confirm this? + */ + + /* Draw a green rectangle around the window */ + mask = XCB_GC_FOREGROUND; + values[0] = background_color; + xcb_change_gc(conn, client->titlegc, mask, values); + + xcb_rectangle_t rect = {0, 0, client->width, client->height}; + xcb_poly_fill_rectangle(conn, client->frame, client->titlegc, 1, &rect); + + /* Draw the lines */ + /* TODO: this needs to be more beautiful somewhen. maybe stdarg + change_gc(gc, ...) ? */ +#define DRAW_LINE(colorpixel, x, y, to_x, to_y) { \ + uint32_t draw_values[1]; \ + draw_values[0] = colorpixel; \ + xcb_change_gc(conn, client->titlegc, XCB_GC_FOREGROUND, draw_values); \ + xcb_point_t points[] = {{x, y}, {to_x, to_y}}; \ + xcb_poly_line(conn, XCB_COORD_MODE_ORIGIN, client->frame, client->titlegc, 2, points); \ + } + + DRAW_LINE(border_color, 2, 0, client->width, 0); + DRAW_LINE(border_color, 2, font->height + 3, 2 + client->width, font->height + 3); + + /* Draw the font */ + mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND | XCB_GC_FONT; + + values[0] = text_color; + values[1] = background_color; + values[2] = font->id; + + xcb_change_gc(conn, client->titlegc, mask, values); + + /* TODO: utf8? */ + char *label; + asprintf(&label, "(%08x) %.*s", client->frame, client->name_len, client->name); + xcb_void_cookie_t text_cookie = xcb_image_text_8_checked(conn, strlen(label), client->frame, + client->titlegc, 3 /* X */, font->height /* Y = baseline of font */, label); + check_error(conn, text_cookie, "Could not draw client's title"); + free(label); +} + +void render_container(xcb_connection_t *connection, Container *container) { + Client *client; + uint32_t values[4]; + uint32_t mask = XCB_CONFIG_WINDOW_X | + XCB_CONFIG_WINDOW_Y | + XCB_CONFIG_WINDOW_WIDTH | + XCB_CONFIG_WINDOW_HEIGHT; + i3Font *font = load_font(connection, pattern); + + if (container->mode == MODE_DEFAULT) { + int num_clients = 0; + CIRCLEQ_FOREACH(client, &(container->clients), clients) + num_clients++; + printf("got %d clients in this default container.\n", num_clients); + + 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->x + (container->col * container->width); /* x */ + values[1] = container->y + (container->row * container->height + + (container->height / num_clients) * current_client); /* y */ + + 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] = 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]); + + xcb_configure_window(connection, client->child, mask, values); + + decorate_window(connection, client); + current_client++; + } + } else { + /* TODO: Implement stacking */ + } +} + +void render_layout(xcb_connection_t *conn) { + int cols, rows; + int screen; + for (screen = 0; screen < num_screens; screen++) { + printf("Rendering screen %d\n", screen); + /* TODO: get the workspace which is active on the screen */ + int width = workspaces[screen].width; + int height = workspaces[screen].height; + + printf("got %d rows and %d cols\n", c_ws->rows, c_ws->cols); + printf("each of them therefore is %d px width and %d px height\n", + width / c_ws->cols, height / c_ws->rows); + + /* Go through the whole table and render what’s necessary */ + for (cols = 0; cols < c_ws->cols; cols++) + for (rows = 0; rows < c_ws->rows; rows++) { + Container *con = CUR_TABLE[cols][rows]; + printf("container has %d colspan, %d rowspan\n", + con->colspan, con->rowspan); + /* Update position of the container */ + con->row = rows; + con->col = cols; + con->x = workspaces[screen].x; + con->y = workspaces[screen].y; + con->width = (width / c_ws->cols) * con->colspan; + con->height = (height / c_ws->rows) * con->rowspan; + + /* Render it */ + render_container(conn, CUR_TABLE[cols][rows]); + } + } + + xcb_flush(conn); +} diff --git a/layout.h b/layout.h new file mode 100644 index 00000000..aecb2b4d --- /dev/null +++ b/layout.h @@ -0,0 +1,9 @@ +#include + +#ifndef _LAYOUT_H +#define _LAYOUT_H + +void decorate_window(xcb_connection_t *conn, Client *client); +void render_layout(xcb_connection_t *conn); + +#endif diff --git a/mainx.c b/mainx.c index 421aab5d..359e3f74 100644 --- a/mainx.c +++ b/mainx.c @@ -13,18 +13,22 @@ #include #include -#include "xcb_wm.h" -#include "xcb_aux.h" -#include "xcb_event.h" -#include "xcb_property.h" -#include "xcb_keysyms.h" -#include "xcb_icccm.h" -#include "xinerama.h" +#include +#include +#include +#include +#include +#include +#include #include "data.h" #include "queue.h" #include "table.h" #include "font.h" +#include "layout.h" +#include "debug.h" +#include "handlers.h" +#include "util.h" #define TERMINAL "/usr/pkg/bin/urxvt" @@ -40,7 +44,7 @@ static const int RIGHT = 5; /* This is the filtered environment which will be passed to opened applications. * It contains DISPLAY (naturally) and locales stuff (LC_*, LANG) */ -static char **environment; +char **environment; /* hm, xcb_wm wants us to implement this. */ table_t *byChild = 0; @@ -50,209 +54,6 @@ xcb_window_t root_win; char *pattern = "-misc-fixed-medium-r-normal--13-120-75-75-C-70-iso8859-1"; int num_screens = 0; - -int current_col = 0; -int current_row = 0; - - -int globalc = 0; - - -static const char *labelError[] = { - "Success", - "BadRequest", - "BadValue", - "BadWindow", - "BadPixmap", - "BadAtom", - "BadCursor", - "BadFont", - "BadMatch", - "BadDrawable", - "BadAccess", - "BadAlloc", - "BadColor", - "BadGC", - "BadIDChoice", - "BadName", - "BadLength", - "BadImplementation", -}; - -static const char *labelRequest[] = { - "no request", - "CreateWindow", - "ChangeWindowAttributes", - "GetWindowAttributes", - "DestroyWindow", - "DestroySubwindows", - "ChangeSaveSet", - "ReparentWindow", - "MapWindow", - "MapSubwindows", - "UnmapWindow", - "UnmapSubwindows", - "ConfigureWindow", - "CirculateWindow", - "GetGeometry", - "QueryTree", - "InternAtom", - "GetAtomName", - "ChangeProperty", - "DeleteProperty", - "GetProperty", - "ListProperties", - "SetSelectionOwner", - "GetSelectionOwner", - "ConvertSelection", - "SendEvent", - "GrabPointer", - "UngrabPointer", - "GrabButton", - "UngrabButton", - "ChangeActivePointerGrab", - "GrabKeyboard", - "UngrabKeyboard", - "GrabKey", - "UngrabKey", - "AllowEvents", - "GrabServer", - "UngrabServer", - "QueryPointer", - "GetMotionEvents", - "TranslateCoords", - "WarpPointer", - "SetInputFocus", - "GetInputFocus", - "QueryKeymap", - "OpenFont", - "CloseFont", - "QueryFont", - "QueryTextExtents", - "ListFonts", - "ListFontsWithInfo", - "SetFontPath", - "GetFontPath", - "CreatePixmap", - "FreePixmap", - "CreateGC", - "ChangeGC", - "CopyGC", - "SetDashes", - "SetClipRectangles", - "FreeGC", - "ClearArea", - "CopyArea", - "CopyPlane", - "PolyPoint", - "PolyLine", - "PolySegment", - "PolyRectangle", - "PolyArc", - "FillPoly", - "PolyFillRectangle", - "PolyFillArc", - "PutImage", - "GetImage", - "PolyText", - "PolyText", - "ImageText", - "ImageText", - "CreateColormap", - "FreeColormap", - "CopyColormapAndFree", - "InstallColormap", - "UninstallColormap", - "ListInstalledColormaps", - "AllocColor", - "AllocNamedColor", - "AllocColorCells", - "AllocColorPlanes", - "FreeColors", - "StoreColors", - "StoreNamedColor", - "QueryColors", - "LookupColor", - "CreateCursor", - "CreateGlyphCursor", - "FreeCursor", - "RecolorCursor", - "QueryBestSize", - "QueryExtension", - "ListExtensions", - "ChangeKeyboardMapping", - "GetKeyboardMapping", - "ChangeKeyboardControl", - "GetKeyboardControl", - "Bell", - "ChangePointerControl", - "GetPointerControl", - "SetScreenSaver", - "GetScreenSaver", - "ChangeHosts", - "ListHosts", - "SetAccessControl", - "SetCloseDownMode", - "KillClient", - "RotateProperties", - "ForceScreenSaver", - "SetPointerMapping", - "GetPointerMapping", - "SetModifierMapping", - "GetModifierMapping", - "major 120", - "major 121", - "major 122", - "major 123", - "major 124", - "major 125", - "major 126", - "NoOperation", -}; - -static const char *labelEvent[] = { - "error", - "reply", - "KeyPress", - "KeyRelease", - "ButtonPress", - "ButtonRelease", - "MotionNotify", - "EnterNotify", - "LeaveNotify", - "FocusIn", - "FocusOut", - "KeymapNotify", - "Expose", - "GraphicsExpose", - "NoExpose", - "VisibilityNotify", - "CreateNotify", - "DestroyNotify", - "UnmapNotify", - "MapNotify", - "MapRequest", - "ReparentNotify", - "ConfigureNotify", - "ConfigureRequest", - "GravityNotify", - "ResizeRequest", - "CirculateNotify", - "CirculateRequest", - "PropertyNotify", - "SelectionClear", - "SelectionRequest", - "SelectionNotify", - "ColormapNotify", - "ClientMessage", - "MappingNotify", -}; - -static const char *labelSendEvent[] = { - "", - " (from SendEvent)", -}; - /* * * TODO: what exactly does this, what happens if we leave stuff out? @@ -308,211 +109,6 @@ void manage_window(xcb_property_handlers_t *prophs, xcb_connection_t *c, xcb_win free(geom); } - -/* - * Returns the colorpixel to use for the given hex color (think of HTML). - * - * The hex_color has to start with #, for example #FF00FF. - * - * NOTE that get_colorpixel() does _NOT_ check the given color code for validity. - * This has to be done by the caller. - * - */ -uint32_t get_colorpixel(xcb_connection_t *conn, xcb_window_t window, char *hex) { - #define RGB_8_TO_16(i) (65535 * ((i) & 0xFF) / 255) - char strgroups[3][3] = {{hex[1], hex[2], '\0'}, - {hex[3], hex[4], '\0'}, - {hex[5], hex[6], '\0'}}; - int rgb16[3] = {RGB_8_TO_16(strtol(strgroups[0], NULL, 16)), - RGB_8_TO_16(strtol(strgroups[1], NULL, 16)), - RGB_8_TO_16(strtol(strgroups[2], NULL, 16))}; - - xcb_screen_t *root_screen = xcb_setup_roots_iterator(xcb_get_setup(conn)).data; - - xcb_colormap_t colormapId = xcb_generate_id(conn); - xcb_create_colormap(conn, XCB_COLORMAP_ALLOC_NONE, colormapId, window, root_screen->root_visual); - xcb_alloc_color_reply_t *reply = xcb_alloc_color_reply(conn, - xcb_alloc_color(conn, colormapId, rgb16[0], rgb16[1], rgb16[2]), NULL); - - if (!reply) { - printf("color fail\n"); - exit(1); - } - - uint32_t pixel = reply->pixel; - free(reply); - xcb_free_colormap(conn, colormapId); - return pixel; -} - -/* - * (Re-)draws window decorations for a given Client - * - */ -void decorate_window(xcb_connection_t *conn, Client *client) { - uint32_t mask = 0; - uint32_t values[3]; - i3Font *font = load_font(conn, pattern); - uint32_t background_color, - text_color, - border_color; - - if (client->container->currently_focused == client) { - background_color = get_colorpixel(conn, client->frame, "#285577"); - text_color = get_colorpixel(conn, client->frame, "#ffffff"); - border_color = get_colorpixel(conn, client->frame, "#4c7899"); - } else { - background_color = get_colorpixel(conn, client->frame, "#222222"); - text_color = get_colorpixel(conn, client->frame, "#888888"); - border_color = get_colorpixel(conn, client->frame, "#333333"); - } - - /* Our plan is the following: - - Draw a rect around the whole client in background_color - - Draw two lines in a lighter color - - Draw the window’s title - - Note that xcb_image_text apparently adds 1xp border around the font? Can anyone confirm this? - */ - - /* Draw a green rectangle around the window */ - mask = XCB_GC_FOREGROUND; - values[0] = background_color; - xcb_change_gc(conn, client->titlegc, mask, values); - - xcb_rectangle_t rect = {0, 0, client->width, client->height}; - xcb_poly_fill_rectangle(conn, client->frame, client->titlegc, 1, &rect); - - /* Draw the lines */ - /* TODO: this needs to be more beautiful somewhen. maybe stdarg + change_gc(gc, ...) ? */ -#define DRAW_LINE(colorpixel, x, y, to_x, to_y) { \ - uint32_t draw_values[1]; \ - draw_values[0] = colorpixel; \ - xcb_change_gc(conn, client->titlegc, XCB_GC_FOREGROUND, draw_values); \ - xcb_point_t points[] = {{x, y}, {to_x, to_y}}; \ - xcb_poly_line(conn, XCB_COORD_MODE_ORIGIN, client->frame, client->titlegc, 2, points); \ - } - - DRAW_LINE(border_color, 2, 0, client->width, 0); - DRAW_LINE(border_color, 2, font->height + 3, 2 + client->width, font->height + 3); - - /* Draw the font */ - mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND | XCB_GC_FONT; - - values[0] = text_color; - values[1] = background_color; - values[2] = font->id; - - xcb_change_gc(conn, client->titlegc, mask, values); - - /* TODO: utf8? */ - char *label; - asprintf(&label, "(%08x) %.*s", client->frame, client->name_len, client->name); - xcb_void_cookie_t text_cookie = xcb_image_text_8_checked(conn, strlen(label), client->frame, - client->titlegc, 3 /* X */, font->height /* Y = baseline of font */, label); - free(label); -} - -void render_container(xcb_connection_t *connection, Container *container) { - Client *client; - uint32_t values[4]; - uint32_t mask = XCB_CONFIG_WINDOW_X | - XCB_CONFIG_WINDOW_Y | - XCB_CONFIG_WINDOW_WIDTH | - XCB_CONFIG_WINDOW_HEIGHT; - i3Font *font = load_font(connection, pattern); - - if (container->mode == MODE_DEFAULT) { - int num_clients = 0; - CIRCLEQ_FOREACH(client, &(container->clients), clients) - num_clients++; - printf("got %d clients in this default container.\n", num_clients); - - 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->x + (container->col * container->width); /* x */ - values[1] = container->y + (container->row * container->height + - (container->height / num_clients) * current_client); /* y */ - - 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] = 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]); - - xcb_configure_window(connection, client->child, mask, values); - - decorate_window(connection, client); - current_client++; - } - } else { - /* TODO: Implement stacking */ - } -} - -void render_layout(xcb_connection_t *conn) { - int cols, rows; - int screen; - for (screen = 0; screen < num_screens; screen++) { - printf("Rendering screen %d\n", screen); - /* TODO: get the workspace which is active on the screen */ - int width = workspaces[screen].width; - int height = workspaces[screen].height; - - printf("got %d rows and %d cols\n", c_ws->rows, c_ws->cols); - printf("each of them therefore is %d px width and %d px height\n", - width / c_ws->cols, height / c_ws->rows); - - /* Go through the whole table and render what’s necessary */ - for (cols = 0; cols < c_ws->cols; cols++) - for (rows = 0; rows < c_ws->rows; rows++) { - Container *con = CUR_TABLE[cols][rows]; - printf("container has %d colspan, %d rowspan\n", - con->colspan, con->rowspan); - /* Update position of the container */ - con->row = rows; - con->col = cols; - con->x = workspaces[screen].x; - con->y = workspaces[screen].y; - con->width = (width / c_ws->cols) * con->colspan; - con->height = (height / c_ws->rows) * con->rowspan; - - /* Render it */ - render_container(conn, CUR_TABLE[cols][rows]); - } - } - - xcb_flush(conn); -} - /* * Let’s own this window… * @@ -609,701 +205,6 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child, render_layout(conn); } -static bool focus_window_in_container(xcb_connection_t *connection, Container *container, - direction_t direction) { - /* If this container is empty, we’re done */ - if (container->currently_focused == NULL) - return false; - - Client *candidad; - if (direction == D_UP) - candidad = CIRCLEQ_PREV(container->currently_focused, clients); - else if (direction == D_DOWN) - candidad = CIRCLEQ_NEXT(container->currently_focused, clients); - - /* If we don’t have anything to select, we’re done */ - if (candidad == CIRCLEQ_END(&(container->clients))) - return false; - - /* Set focus if we could successfully move */ - container->currently_focused = candidad; - xcb_set_input_focus(connection, XCB_INPUT_FOCUS_NONE, candidad->child, XCB_CURRENT_TIME); - render_layout(connection); - - return true; -} - -static void focus_window(xcb_connection_t *connection, direction_t direction) { - printf("focusing direction %d\n", direction); - /* TODO: for horizontal default layout, this has to be expanded to LEFT/RIGHT */ - if (direction == D_UP || direction == D_DOWN) { - /* Let’s see if we can perform up/down focus in the current container */ - Container *container = CUR_CELL; - - /* There always is a container. If not, current_col or current_row is wrong */ - assert(container != NULL); - - if (focus_window_in_container(connection, container, direction)) - return; - } else if (direction == D_LEFT || direction == D_RIGHT) { - if (direction == D_RIGHT && cell_exists(current_col+1, current_row)) - current_col++; - else if (direction == D_LEFT && cell_exists(current_col-1, current_row)) - current_col--; - else { - printf("nah, not possible\n"); - return; - } - if (CUR_CELL->currently_focused != NULL) { - xcb_set_input_focus(connection, XCB_INPUT_FOCUS_NONE, - CUR_CELL->currently_focused->child, XCB_CURRENT_TIME); - render_layout(connection); - } - - } else { - printf("direction unhandled\n"); - } -} - -/* - * Tries to move the window inside its current container. - * - * Returns true if the window could be moved, false otherwise. - * - */ -static bool move_current_window_in_container(xcb_connection_t *connection, Client *client, - direction_t direction) { - Client *other = (direction == D_UP ? CIRCLEQ_PREV(client, clients) : - CIRCLEQ_NEXT(client, clients)); - - if (other == CIRCLEQ_END(&(client->container->clients))) - return false; - - printf("i can do that\n"); - /* We can move the client inside its current container */ - CIRCLEQ_REMOVE(&(client->container->clients), client, clients); - if (direction == D_UP) - CIRCLEQ_INSERT_BEFORE(&(client->container->clients), other, client, clients); - else CIRCLEQ_INSERT_AFTER(&(client->container->clients), other, client, clients); - render_layout(connection); - return true; -} - -/* - * Moves the current window to the given direction, creating a column/row if - * necessary - * - */ -static void move_current_window(xcb_connection_t *connection, direction_t direction) { - printf("moving window to direction %d\n", direction); - /* Get current window */ - Container *container = CUR_CELL, - *new; - - /* There has to be a container, see focus_window() */ - assert(container != NULL); - - /* If there is no window, we’re done */ - if (container->currently_focused == NULL) - return; - - /* As soon as the client is moved away, the next client in the old - * container needs to get focus, if any. Therefore, we save it here. */ - Client *current_client = container->currently_focused; - Client *to_focus = CIRCLEQ_NEXT(current_client, clients); - if (to_focus == CIRCLEQ_END(&(container->clients))) - to_focus = NULL; - - switch (direction) { - case D_LEFT: - if (current_col == 0) - return; - - new = CUR_TABLE[--current_col][current_row]; - break; - case D_RIGHT: - if (current_col == (c_ws->cols-1)) - expand_table_cols(c_ws); - - new = CUR_TABLE[++current_col][current_row]; - break; - case D_UP: - /* TODO: if we’re at the up-most position, move the rest of the table down */ - if (move_current_window_in_container(connection, current_client, D_UP) || - current_row == 0) - return; - - new = CUR_TABLE[current_col][--current_row]; - break; - case D_DOWN: - if (move_current_window_in_container(connection, current_client, D_DOWN)) - return; - - if (current_row == (c_ws->rows-1)) - expand_table_rows(c_ws); - - new = CUR_TABLE[current_col][++current_row]; - break; - } - - /* Remove it from the old container and put it into the new one */ - CIRCLEQ_REMOVE(&(container->clients), current_client, clients); - CIRCLEQ_INSERT_TAIL(&(new->clients), current_client, clients); - - /* Update data structures */ - current_client->container = new; - container->currently_focused = to_focus; - new->currently_focused = current_client; - - /* TODO: delete all empty columns/rows */ - - 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) || - CUR_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, CUR_TABLE[container->col+1][i]->rowspan); - while ((CUR_TABLE[container->col+1][i]->rowspan-1) >= (container->row - i)) - CUR_TABLE[container->col+1][i]->rowspan--; - printf("new rowspan = %d\n", CUR_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) || - CUR_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, CUR_TABLE[i][container->row+1]->colspan); - while ((CUR_TABLE[i][container->row+1]->colspan-1) >= (container->col - i)) - CUR_TABLE[i][container->row+1]->colspan--; - printf("new colspan = %d\n", CUR_TABLE[i][container->row+1]->colspan); - - } - - container->rowspan++; - break; - } - - render_layout(connection); -} - -int format_event(xcb_generic_event_t *e) -{ - uint8_t sendEvent; - uint16_t seqnum; - - sendEvent = (e->response_type & 0x80) ? 1 : 0; - e->response_type &= ~0x80; - seqnum = *((uint16_t *) e + 1); - - switch(e->response_type) - { - case 0: - printf("Error %s on seqnum %d (%s).\n", - labelError[*((uint8_t *) e + 1)], - seqnum, - labelRequest[*((uint8_t *) e + 10)]); - break; - default: - printf("Event %s following seqnum %d%s.\n", - labelEvent[e->response_type], - seqnum, - labelSendEvent[sendEvent]); - break; - case XCB_KEYMAP_NOTIFY: - printf("Event %s%s.\n", - labelEvent[e->response_type], - labelSendEvent[sendEvent]); - break; - } - - fflush(stdout); - return 1; -} - - -static int handleEvent(void *ignored, xcb_connection_t *c, xcb_generic_event_t *e) -{ - return format_event(e); -} - -/* - * Starts the given application with the given args. - * - */ -static void start_application(const char *path, const char *args) { - pid_t pid; - if ((pid = vfork()) == 0) { - /* This is the child */ - char *argv[2]; - /* TODO: For now, we ignore args. Later on, they should be parsed - correctly (like in the shell?) */ - argv[0] = strdup(path); - argv[1] = NULL; - execve(path, argv, environment); - /* not reached */ - } -} - -/* - * 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; -} - -static void show_workspace(xcb_connection_t *conn, int workspace) { - int cols, rows; - Client *client; - printf("show_workspace(%d)\n", workspace); - - /* Store current_row/current_col */ - c_ws->current_row = current_row; - c_ws->current_col = current_col; - - /* TODO: does grabbing the server actually bring us any (speed)advantages? */ - //xcb_grab_server(conn); - - /* Unmap all clients */ - for (cols = 0; cols < c_ws->cols; cols++) - for (rows = 0; rows < c_ws->rows; rows++) { - CIRCLEQ_FOREACH(client, &(c_ws->table[cols][rows]->clients), clients) - xcb_unmap_window(conn, client->frame); - } - - c_ws = &workspaces[workspace-1]; - current_row = c_ws->current_row; - current_col = c_ws->current_col; - printf("new current row = %d, current col = %d\n", current_row, current_col); - - /* Map all clients on the new workspace */ - for (cols = 0; cols < c_ws->cols; cols++) - for (rows = 0; rows < c_ws->rows; rows++) { - CIRCLEQ_FOREACH(client, &(c_ws->table[cols][rows]->clients), clients) - xcb_map_window(conn, client->frame); - } - - /* Restore focus on the new workspace */ - if (CUR_CELL->currently_focused != NULL) - xcb_set_input_focus(conn, XCB_INPUT_FOCUS_NONE, CUR_CELL->currently_focused->child, XCB_CURRENT_TIME); - else xcb_set_input_focus(conn, XCB_INPUT_FOCUS_NONE, root_win, XCB_CURRENT_TIME); - - //xcb_ungrab_server(conn); - - render_layout(conn); -} - -/* - * 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++; - } - - if (*rest == '\0') { - /* No rest? This was a tag number, not a times specification */ - show_workspace(conn, times); - return; - } - - /* Now perform action to */ - while (*rest != '\0') { - 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) { - 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 && (state.group+1) == 2) - event->state |= 0x2; - - printf("state %d\n", event->state); - - /* Find the binding */ - /* TODO: event->state durch eine bitmask filtern und dann direkt vergleichen */ - 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; - } - } - - /* 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; - } - - 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; - } - - 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; - - /* 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); -} - -/* - * When the user moves the mouse pointer onto a window, this callback gets called. - * - */ -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); - /* …or the client itself */ - if (client == NULL) - client = table_get(byChild, event->event); - - /* If not, then this event is not interesting. This should not happen */ - if (client == NULL) { - printf("DEBUG: Uninteresting enter_notify-event?\n"); - return 1; - } - - set_focus(conn, client); - - return 1; -} - -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); - if (client == NULL) - client = table_get(byParent, event->event); - if (client == NULL) - return 1; - - xcb_window_t root = xcb_setup_roots_iterator(xcb_get_setup(conn)).data->root; - xcb_screen_t *root_screen = xcb_setup_roots_iterator(xcb_get_setup(conn)).data; - - /* Set focus in any case */ - set_focus(conn, client); - - /* Let’s see if this was on the borders (= resize). If not, we’re done */ - i3Font *font = load_font(conn, pattern); - printf("press button on x=%d, y=%d\n", event->event_x, event->event_y); - if (event->event_y <= (font->height + 2)) - return 1; - - printf("that was resize\n"); - - /* Open a new window, the resizebar. Grab the pointer and move the window around - as the user moves the pointer. */ - - - /* TODO: the whole logic is missing. this is just a proof of concept */ - xcb_window_t grabwin = xcb_generate_id(conn); - - uint32_t mask = 0; - uint32_t values[3]; - - xcb_create_window(conn, - 0, - grabwin, - root, - 0, /* x */ - 0, /* y */ - root_screen->width_in_pixels, /* width */ - root_screen->height_in_pixels, /* height */ - /* border_width */ 0, - XCB_WINDOW_CLASS_INPUT_ONLY, - root_screen->root_visual, - 0, - values); - - /* Map the window on the screen (= make it visible) */ - xcb_map_window(conn, grabwin); - - xcb_window_t helpwin = xcb_generate_id(conn); - - mask = XCB_CW_BACK_PIXEL; - values[0] = root_screen->white_pixel; - xcb_create_window(conn, root_screen->root_depth, helpwin, root, - event->root_x, - 0, - 5, - root_screen->height_in_pixels, - /* bordor */ 0, - XCB_WINDOW_CLASS_INPUT_OUTPUT, - root_screen->root_visual, - mask, - values); - - xcb_map_window(conn, helpwin); - xcb_circulate_window(conn, XCB_CIRCULATE_RAISE_LOWEST, helpwin); - - xcb_grab_pointer(conn, false, root, XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_POINTER_MOTION, - XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, grabwin, XCB_NONE, XCB_CURRENT_TIME); - - xcb_flush(conn); - - xcb_generic_event_t *inside_event; - /* I’ve always wanted to have my own eventhandler… */ - while ((inside_event = xcb_wait_for_event(conn))) { - /* Same as get_event_handler in xcb */ - int nr = inside_event->response_type; - if (nr == 0) { - handleEvent(NULL, conn, inside_event); - continue; - } - assert(nr < 256); - nr &= XCB_EVENT_RESPONSE_TYPE_MASK; - assert(nr >= 2); - - /* Check if we need to escape this loop… */ - if (nr == XCB_BUTTON_RELEASE) - break; - - switch (nr) { - case XCB_MOTION_NOTIFY: - values[0] = ((xcb_motion_notify_event_t*)inside_event)->root_x; - xcb_configure_window(conn, helpwin, XCB_CONFIG_WINDOW_X, values); - xcb_flush(conn); - break; - case XCB_EXPOSE: - /* Use original handler */ - xcb_event_handle(&evenths, inside_event); - break; - default: - printf("Ignoring event of type %d\n", nr); - break; - } - printf("---\n"); - free(inside_event); - } - - xcb_ungrab_pointer(conn, XCB_CURRENT_TIME); - xcb_destroy_window(conn, helpwin); - xcb_destroy_window(conn, grabwin); - xcb_flush(conn); - - 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 }; - wa.u.override_redirect = e->override_redirect; - printf("MapNotify for 0x%08x.\n", e->window); - manage_window(prophs, c, e->window, wa); - return 1; -} - -/* - * Our window decorations were unmapped. That means, the window will be killed now, - * so we better clean up before. - * - */ -int handle_unmap_notify_event(void *data, xcb_connection_t *c, xcb_unmap_notify_event_t *e) { - Client *client = table_remove(byChild, e->event); - xcb_window_t root; - printf("UnmapNotify for 0x%08x (received from 0x%08x): ", e->window, e->event); - if(!client) - { - printf("not a managed window. Ignoring.\n"); - return 0; - } - - int rows, cols; - Client *con_client; - /* TODO: clear this up */ - for (cols = 0; cols < c_ws->cols; cols++) - for (rows = 0; rows < c_ws->rows; rows++) - CIRCLEQ_FOREACH(con_client, &(CUR_TABLE[cols][rows]->clients), clients) - if (con_client == client) { - printf("removing from container\n"); - if (client->container->currently_focused == client) - client->container->currently_focused = NULL; - CIRCLEQ_REMOVE(&(CUR_TABLE[cols][rows]->clients), con_client, clients); - break; - } - - - - root = xcb_setup_roots_iterator(xcb_get_setup(c)).data->root; - printf("child of 0x%08x.\n", client->frame); - xcb_reparent_window(c, client->child, root, 0, 0); - xcb_destroy_window(c, client->frame); - xcb_flush(c); - table_remove(byParent, client->frame); - free(client); - - render_layout(c); - - return 1; -} - -/* - * Called when a window changes its title - * - */ -static int handle_windowname_change(void *data, xcb_connection_t *conn, uint8_t state, - xcb_window_t window, xcb_atom_t atom, xcb_get_property_reply_t *prop) { - printf("window's name changed.\n"); - Client *client = table_get(byChild, window); - if (client == NULL) - return 1; - - client->name_len = xcb_get_property_value_length(prop); - client->name = malloc(client->name_len); - strncpy(client->name, xcb_get_property_value(prop), client->name_len); - printf("rename to \"%.*s\".\n", client->name_len, client->name); - - decorate_window(conn, client); - xcb_flush(conn); - - return 1; -} - - -static int handleExposeEvent(void *data, xcb_connection_t *c, xcb_expose_event_t *e) { -printf("exposeevent\n"); - Client *client = table_get(byParent, e->window); - if(!client || e->count != 0) - return 1; - decorate_window(c, client); - return 1; -} void manage_existing_windows(xcb_connection_t *c, xcb_property_handlers_t *prophs, xcb_window_t root) { xcb_query_tree_cookie_t wintree; xcb_query_tree_reply_t *rep; @@ -1429,14 +330,14 @@ int main(int argc, char *argv[], char *env[]) { xcb_event_handlers_init(c, &evenths); for(i = 2; i < 128; ++i) - xcb_event_set_handler(&evenths, i, handleEvent, 0); + xcb_event_set_handler(&evenths, i, handle_event, 0); for(i = 0; i < 256; ++i) - xcb_event_set_error_handler(&evenths, i, (xcb_generic_error_handler_t) handleEvent, 0); + xcb_event_set_error_handler(&evenths, i, (xcb_generic_error_handler_t)handle_event, 0); /* Expose = an Application should redraw itself. That is, we have to redraw our * contents (= top/bottom bar, titlebars for each window) */ - xcb_event_set_expose_handler(&evenths, handleExposeEvent, 0); + xcb_event_set_expose_handler(&evenths, handle_expose_event, 0); /* Key presses/releases are pretty obvious, I think */ xcb_event_set_key_press_handler(&evenths, handle_key_press, 0); diff --git a/table.c b/table.c index 9c53237d..33d1b4fe 100644 --- a/table.c +++ b/table.c @@ -18,6 +18,8 @@ int current_workspace = 0; Workspace workspaces[10]; /* Convenience pointer to the current workspace */ Workspace *c_ws = &workspaces[0]; +int current_col = 0; +int current_row = 0; /* * Initialize table @@ -25,7 +27,6 @@ Workspace *c_ws = &workspaces[0]; */ void init_table() { int i; - printf("sizof(workspaces) = %d\n", sizeof(workspaces)); memset(workspaces, 0, sizeof(workspaces)); for (i = 0; i < 10; i++) { diff --git a/table.h b/table.h index fff8be51..1d6ca8f7 100644 --- a/table.h +++ b/table.h @@ -9,6 +9,8 @@ extern Workspace *c_ws; extern Workspace workspaces[10]; +extern int current_col; +extern int current_row; void init_table(); void expand_table_rows(Workspace *workspace); diff --git a/util.c b/util.c new file mode 100644 index 00000000..1263cdc4 --- /dev/null +++ b/util.c @@ -0,0 +1,38 @@ +#include +#include +#include +#include + +#include "i3.h" + +/* + * Starts the given application with the given args. + * + */ +void start_application(const char *path, const char *args) { + pid_t pid; + if ((pid = vfork()) == 0) { + /* This is the child */ + char *argv[2]; + /* TODO: For now, we ignore args. Later on, they should be parsed + correctly (like in the shell?) */ + argv[0] = strdup(path); + argv[1] = NULL; + execve(path, argv, environment); + /* not reached */ + } +} + +/* + * Checks a generic cookie for errors and quits with the given message if there + * was an error. + * + */ +void check_error(xcb_connection_t *connection, xcb_void_cookie_t cookie, char *err_message) { + xcb_generic_error_t *error = xcb_request_check(connection, cookie); + if (error != NULL) { + fprintf(stderr, "ERROR: %s : %d\n", err_message , error->error_code); + xcb_disconnect(connection); + exit(-1); + } +} diff --git a/util.h b/util.h new file mode 100644 index 00000000..9eb2a447 --- /dev/null +++ b/util.h @@ -0,0 +1,7 @@ +#ifndef _UTIL_H +#define _UTIL_H + +void start_application(const char *path, const char *args); +void check_error(xcb_connection_t *connection, xcb_void_cookie_t cookie, char *err_message); + +#endif diff --git a/xcb.c b/xcb.c new file mode 100644 index 00000000..b445437a --- /dev/null +++ b/xcb.c @@ -0,0 +1,42 @@ +#include +#include +#include + +#include +/* All the helper functions needed for efficiently using XCB */ + +/* + * Returns the colorpixel to use for the given hex color (think of HTML). + * + * The hex_color has to start with #, for example #FF00FF. + * + * NOTE that get_colorpixel() does _NOT_ check the given color code for validity. + * This has to be done by the caller. + * + */ +uint32_t get_colorpixel(xcb_connection_t *conn, xcb_window_t window, char *hex) { + #define RGB_8_TO_16(i) (65535 * ((i) & 0xFF) / 255) + char strgroups[3][3] = {{hex[1], hex[2], '\0'}, + {hex[3], hex[4], '\0'}, + {hex[5], hex[6], '\0'}}; + int rgb16[3] = {RGB_8_TO_16(strtol(strgroups[0], NULL, 16)), + RGB_8_TO_16(strtol(strgroups[1], NULL, 16)), + RGB_8_TO_16(strtol(strgroups[2], NULL, 16))}; + + xcb_screen_t *root_screen = xcb_setup_roots_iterator(xcb_get_setup(conn)).data; + + xcb_colormap_t colormapId = xcb_generate_id(conn); + xcb_create_colormap(conn, XCB_COLORMAP_ALLOC_NONE, colormapId, window, root_screen->root_visual); + xcb_alloc_color_reply_t *reply = xcb_alloc_color_reply(conn, + xcb_alloc_color(conn, colormapId, rgb16[0], rgb16[1], rgb16[2]), NULL); + + if (!reply) { + printf("color fail\n"); + exit(1); + } + + uint32_t pixel = reply->pixel; + free(reply); + xcb_free_colormap(conn, colormapId); + return pixel; +} diff --git a/xcb.h b/xcb.h new file mode 100644 index 00000000..c8224ef9 --- /dev/null +++ b/xcb.h @@ -0,0 +1,6 @@ +#ifndef _XCB_H +#define _XCB_H + +uint32_t get_colorpixel(xcb_connection_t *conn, xcb_window_t window, char *hex); + +#endif