From 7c0317c8a3fa47bf329fc96092d996b40570473f Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Fri, 13 Feb 2009 19:04:45 +0100 Subject: [PATCH] Cleanups, first strike. Move stuff to separate files, eliminate warnings --- Makefile | 4 +- commands.c | 348 ++++++++++++++++ commands.h | 8 + debug.c | 235 +++++++++++ debug.h | 6 + font.c | 12 +- handlers.c | 326 +++++++++++++++ handlers.h | 17 + i3.h | 18 + layout.c | 181 +++++++++ layout.h | 9 + mainx.c | 1129 +--------------------------------------------------- table.c | 3 +- table.h | 2 + util.c | 38 ++ util.h | 7 + xcb.c | 42 ++ xcb.h | 6 + 18 files changed, 1263 insertions(+), 1128 deletions(-) create mode 100644 commands.c create mode 100644 commands.h create mode 100644 debug.c create mode 100644 debug.h create mode 100644 handlers.c create mode 100644 handlers.h create mode 100644 i3.h create mode 100644 layout.c create mode 100644 layout.h create mode 100644 util.c create mode 100644 util.h create mode 100644 xcb.c create mode 100644 xcb.h 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 -- 2.39.5