From: Michael Stapelberg Date: Fri, 13 Feb 2009 18:09:25 +0000 (+0100) Subject: Move stuff to include/ and src/ X-Git-Tag: 3.a~196 X-Git-Url: https://git.sur5r.net/?p=i3%2Fi3;a=commitdiff_plain;h=82dd64ff241b2dc376f44cea4e174a5face7c19d Move stuff to include/ and src/ --- diff --git a/Makefile b/Makefile index 9e919dc8..3fd244fb 100644 --- a/Makefile +++ b/Makefile @@ -4,9 +4,8 @@ CFLAGS += -Wall # Extended debugging flags, macros shall be available in gcc CFLAGS += -gdwarf-2 CFLAGS += -g3 -#CFLAGS += -I/usr/include/xcb +CFLAGS += -Iinclude CFLAGS += -I/usr/local/include/ -#CFLAGS += -I/usr/local/include/xcb CFLAGS += -I/usr/pkg/include LDFLAGS += -lxcb-wm @@ -18,13 +17,16 @@ ifeq ($(UNAME),NetBSD) LDFLAGS += -Wl,-rpath,/usr/local/lib -Wl,-rpath,/usr/pkg/lib endif -FILES=$(patsubst %.c,%.o,$(wildcard *.c)) +FILES=$(patsubst %.c,%.o,$(wildcard src/*.c)) -%.o: %.c %.h data.h +src/%.o: src/%.c include/%.h include/data.h $(CC) $(CFLAGS) -c -o $@ $< all: ${FILES} - $(CC) -o mainx ${FILES} $(LDFLAGS) + $(CC) -o i3 ${FILES} $(LDFLAGS) clean: - rm -f *.o + rm -f src/*.o + +distclean: clean + rm -f i3 diff --git a/commands.c b/commands.c deleted file mode 100644 index 8e3161cf..00000000 --- a/commands.c +++ /dev/null @@ -1,348 +0,0 @@ -#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 deleted file mode 100644 index 148f56fa..00000000 --- a/commands.h +++ /dev/null @@ -1,8 +0,0 @@ -#include - -#ifndef _COMMANDS_H -#define _COMMANDS_H - -void parse_command(xcb_connection_t *conn, const char *command); - -#endif diff --git a/data.h b/data.h deleted file mode 100644 index 203e7907..00000000 --- a/data.h +++ /dev/null @@ -1,145 +0,0 @@ -#include - -#ifndef _DATA_H -#define _DATA_H -/* - * This file defines all data structures used by i3 - * - */ -#include "queue.h" - -/* Forward definitions */ -typedef struct Cell Cell; -typedef struct Font i3Font; -typedef struct Container Container; -typedef struct Client Client; -typedef struct Binding Binding; -typedef struct Workspace Workspace; - -/* Helper types */ -typedef enum { D_LEFT, D_RIGHT, D_UP, D_DOWN } direction_t; - -enum { - BIND_NONE = 0, - BIND_MOD_1 = XCB_MOD_MASK_1, - BIND_MOD_2 = XCB_MOD_MASK_2, - BIND_MOD_3 = XCB_MOD_MASK_3, - BIND_MOD_4 = XCB_MOD_MASK_4, - BIND_MOD_5 = XCB_MOD_MASK_5, - BIND_SHIFT = XCB_MOD_MASK_SHIFT, - BIND_CONTROL = XCB_MOD_MASK_CONTROL, - BIND_MODE_SWITCH = (1 << 8) -}; - -struct Workspace { - int x; - int y; - int width; - int height; - int screen_num; - int num; - - /* table dimensions */ - int cols; - int rows; - - /* These are stored here just while this workspace is _not_ shown (see show_workspace()) */ - int current_row; - int current_col; - - /* This is a two-dimensional dynamic array of Container-pointers. I’ve always wanted - * to be a three-star programmer :) */ - Container ***table; -}; - -/* - * Defines a position in the table - * - */ -struct Cell { - int row; - int column; -}; - -struct Binding { - /* Keycode to bind */ - uint32_t keycode; - /* Bitmask consisting of BIND_MOD_1, BIND_MODE_SWITCH, … */ - uint32_t mods; - /* Command, like in command mode */ - char *command; - - TAILQ_ENTRY(Binding) bindings; -}; - -/* - * We need to save the height of a font because it is required for each drawing of - * text but relatively hard to get. As soon as a new font needs to be loaded, a - * Font-entry will be filled for later use. - * - */ -struct Font { - /* The name of the font, that is what the pattern resolves to */ - char *name; - /* A copy of the pattern to build a cache */ - char *pattern; - /* The height of the font, built from font_ascent + font_descent */ - int height; - /* The xcb-id for the font */ - xcb_font_t id; -}; - -/* - * A client is X11-speak for a window. - * - */ -struct Client { - /* TODO: this is NOT final */ - Cell old_position; /* if you set a client to floating and set it back to managed, - it does remember its old position and *tries* to get back there */ - - /* Backpointer. A client is inside a container */ - Container *container; - - int x, y; - int width, height; - - /* Name */ - char *name; - int name_len; - - /* XCB contexts */ - xcb_window_t frame; /* Our window: The frame around the client */ - xcb_gcontext_t titlegc; /* The titlebar’s graphic context inside the frame */ - xcb_window_t child; /* The client’s window */ - - /* The following entry provides the necessary list pointers to use Client with LIST_* macros */ - CIRCLEQ_ENTRY(Client) clients; -}; - -/* - * A container is either in default or stacking mode. It sits inside the table. - * - */ -struct Container { - /* Those are speaking for themselves: */ - Client *currently_focused; - int colspan; - int rowspan; - - /* Position of the container inside our table */ - int row; - int col; - /* Xinerama: X/Y of the container */ - int x; - int y; - /* Width/Height of the container. Changeable by the user */ - int width; - int height; - - /* Ensure MODE_DEFAULT maps to 0 because we use calloc for initialization later */ - enum { MODE_DEFAULT = 0, MODE_STACK = 1 } mode; - CIRCLEQ_HEAD(client_head, Client) clients; -}; - -#endif diff --git a/debug.c b/debug.c deleted file mode 100644 index cbdbd368..00000000 --- a/debug.c +++ /dev/null @@ -1,235 +0,0 @@ -#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 deleted file mode 100644 index 3d9cf3e7..00000000 --- a/debug.h +++ /dev/null @@ -1,6 +0,0 @@ -#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 deleted file mode 100644 index 803ac0a6..00000000 --- a/font.c +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Handles font loading - * - */ -#include -#include -#include -#include - -#include "data.h" -#include "util.h" - -i3Font *load_font(xcb_connection_t *c, const char *pattern) { - /* TODO: this function should be caching */ - i3Font *new = malloc(sizeof(i3Font)); - - xcb_list_fonts_with_info_cookie_t cookie = xcb_list_fonts_with_info(c, 1, strlen(pattern), pattern); - xcb_list_fonts_with_info_reply_t *reply = xcb_list_fonts_with_info_reply(c, cookie, NULL); - if (!reply) { - printf("Could not load font\n"); - exit(1); - } - - /* Oh my, this is so ugly :-(. Why can’t they just return a null-terminated - * string? That’s what abstraction layers are for. */ - char buffer[xcb_list_fonts_with_info_name_length(reply)+1]; - memset(buffer, 0, sizeof(buffer)); - memcpy(buffer, xcb_list_fonts_with_info_name(reply), sizeof(buffer)-1); - new->name = strdup(buffer); - new->pattern = strdup(pattern); - new->height = reply->font_ascent + reply->font_descent; - - /* Actually load the font */ - new->id = xcb_generate_id(c); - xcb_void_cookie_t font_cookie = xcb_open_font_checked(c, new->id, strlen(pattern), pattern); - check_error(c, font_cookie, "Could not open font"); - - return new; -} diff --git a/font.h b/font.h deleted file mode 100644 index 732c369e..00000000 --- a/font.h +++ /dev/null @@ -1,10 +0,0 @@ -#include - -#include "data.h" - -#ifndef _FONT_H -#define _FONT_H - -i3Font *load_font(xcb_connection_t *c, const char *pattern); - -#endif diff --git a/handlers.c b/handlers.c deleted file mode 100644 index 58640368..00000000 --- a/handlers.c +++ /dev/null @@ -1,326 +0,0 @@ -#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 deleted file mode 100644 index 7c82ca46..00000000 --- a/handlers.h +++ /dev/null @@ -1,17 +0,0 @@ -#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 deleted file mode 100644 index d2453f47..00000000 --- a/i3.h +++ /dev/null @@ -1,18 +0,0 @@ -#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/include/commands.h b/include/commands.h new file mode 100644 index 00000000..148f56fa --- /dev/null +++ b/include/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/include/data.h b/include/data.h new file mode 100644 index 00000000..203e7907 --- /dev/null +++ b/include/data.h @@ -0,0 +1,145 @@ +#include + +#ifndef _DATA_H +#define _DATA_H +/* + * This file defines all data structures used by i3 + * + */ +#include "queue.h" + +/* Forward definitions */ +typedef struct Cell Cell; +typedef struct Font i3Font; +typedef struct Container Container; +typedef struct Client Client; +typedef struct Binding Binding; +typedef struct Workspace Workspace; + +/* Helper types */ +typedef enum { D_LEFT, D_RIGHT, D_UP, D_DOWN } direction_t; + +enum { + BIND_NONE = 0, + BIND_MOD_1 = XCB_MOD_MASK_1, + BIND_MOD_2 = XCB_MOD_MASK_2, + BIND_MOD_3 = XCB_MOD_MASK_3, + BIND_MOD_4 = XCB_MOD_MASK_4, + BIND_MOD_5 = XCB_MOD_MASK_5, + BIND_SHIFT = XCB_MOD_MASK_SHIFT, + BIND_CONTROL = XCB_MOD_MASK_CONTROL, + BIND_MODE_SWITCH = (1 << 8) +}; + +struct Workspace { + int x; + int y; + int width; + int height; + int screen_num; + int num; + + /* table dimensions */ + int cols; + int rows; + + /* These are stored here just while this workspace is _not_ shown (see show_workspace()) */ + int current_row; + int current_col; + + /* This is a two-dimensional dynamic array of Container-pointers. I’ve always wanted + * to be a three-star programmer :) */ + Container ***table; +}; + +/* + * Defines a position in the table + * + */ +struct Cell { + int row; + int column; +}; + +struct Binding { + /* Keycode to bind */ + uint32_t keycode; + /* Bitmask consisting of BIND_MOD_1, BIND_MODE_SWITCH, … */ + uint32_t mods; + /* Command, like in command mode */ + char *command; + + TAILQ_ENTRY(Binding) bindings; +}; + +/* + * We need to save the height of a font because it is required for each drawing of + * text but relatively hard to get. As soon as a new font needs to be loaded, a + * Font-entry will be filled for later use. + * + */ +struct Font { + /* The name of the font, that is what the pattern resolves to */ + char *name; + /* A copy of the pattern to build a cache */ + char *pattern; + /* The height of the font, built from font_ascent + font_descent */ + int height; + /* The xcb-id for the font */ + xcb_font_t id; +}; + +/* + * A client is X11-speak for a window. + * + */ +struct Client { + /* TODO: this is NOT final */ + Cell old_position; /* if you set a client to floating and set it back to managed, + it does remember its old position and *tries* to get back there */ + + /* Backpointer. A client is inside a container */ + Container *container; + + int x, y; + int width, height; + + /* Name */ + char *name; + int name_len; + + /* XCB contexts */ + xcb_window_t frame; /* Our window: The frame around the client */ + xcb_gcontext_t titlegc; /* The titlebar’s graphic context inside the frame */ + xcb_window_t child; /* The client’s window */ + + /* The following entry provides the necessary list pointers to use Client with LIST_* macros */ + CIRCLEQ_ENTRY(Client) clients; +}; + +/* + * A container is either in default or stacking mode. It sits inside the table. + * + */ +struct Container { + /* Those are speaking for themselves: */ + Client *currently_focused; + int colspan; + int rowspan; + + /* Position of the container inside our table */ + int row; + int col; + /* Xinerama: X/Y of the container */ + int x; + int y; + /* Width/Height of the container. Changeable by the user */ + int width; + int height; + + /* Ensure MODE_DEFAULT maps to 0 because we use calloc for initialization later */ + enum { MODE_DEFAULT = 0, MODE_STACK = 1 } mode; + CIRCLEQ_HEAD(client_head, Client) clients; +}; + +#endif diff --git a/include/debug.h b/include/debug.h new file mode 100644 index 00000000..3d9cf3e7 --- /dev/null +++ b/include/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/include/font.h b/include/font.h new file mode 100644 index 00000000..732c369e --- /dev/null +++ b/include/font.h @@ -0,0 +1,10 @@ +#include + +#include "data.h" + +#ifndef _FONT_H +#define _FONT_H + +i3Font *load_font(xcb_connection_t *c, const char *pattern); + +#endif diff --git a/include/handlers.h b/include/handlers.h new file mode 100644 index 00000000..7c82ca46 --- /dev/null +++ b/include/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/include/i3.h b/include/i3.h new file mode 100644 index 00000000..d2453f47 --- /dev/null +++ b/include/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/include/layout.h b/include/layout.h new file mode 100644 index 00000000..aecb2b4d --- /dev/null +++ b/include/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/include/queue.h b/include/queue.h new file mode 100644 index 00000000..75bb957a --- /dev/null +++ b/include/queue.h @@ -0,0 +1,527 @@ +/* $OpenBSD: queue.h,v 1.1 2007/10/26 03:14:08 niallo Exp $ */ +/* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */ + +/* + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)queue.h 8.5 (Berkeley) 8/20/94 + */ + +#ifndef _SYS_QUEUE_H_ +#define _SYS_QUEUE_H_ + +/* + * This file defines five types of data structures: singly-linked lists, + * lists, simple queues, tail queues, and circular queues. + * + * + * A singly-linked list is headed by a single forward pointer. The elements + * are singly linked for minimum space and pointer manipulation overhead at + * the expense of O(n) removal for arbitrary elements. New elements can be + * added to the list after an existing element or at the head of the list. + * Elements being removed from the head of the list should use the explicit + * macro for this purpose for optimum efficiency. A singly-linked list may + * only be traversed in the forward direction. Singly-linked lists are ideal + * for applications with large datasets and few or no removals or for + * implementing a LIFO queue. + * + * A list is headed by a single forward pointer (or an array of forward + * pointers for a hash table header). The elements are doubly linked + * so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before + * or after an existing element or at the head of the list. A list + * may only be traversed in the forward direction. + * + * A simple queue is headed by a pair of pointers, one the head of the + * list and the other to the tail of the list. The elements are singly + * linked to save space, so elements can only be removed from the + * head of the list. New elements can be added to the list before or after + * an existing element, at the head of the list, or at the end of the + * list. A simple queue may only be traversed in the forward direction. + * + * A tail queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or + * after an existing element, at the head of the list, or at the end of + * the list. A tail queue may be traversed in either direction. + * + * A circle queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or after + * an existing element, at the head of the list, or at the end of the list. + * A circle queue may be traversed in either direction, but has a more + * complex end of list detection. + * + * For details on the use of these macros, see the queue(3) manual page. + */ + +#if defined(QUEUE_MACRO_DEBUG) || (defined(_KERNEL) && defined(DIAGNOSTIC)) +#define _Q_INVALIDATE(a) (a) = ((void *)-1) +#else +#define _Q_INVALIDATE(a) +#endif + +/* + * Singly-linked List definitions. + */ +#define SLIST_HEAD(name, type) \ +struct name { \ + struct type *slh_first; /* first element */ \ +} + +#define SLIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define SLIST_ENTRY(type) \ +struct { \ + struct type *sle_next; /* next element */ \ +} + +/* + * Singly-linked List access methods. + */ +#define SLIST_FIRST(head) ((head)->slh_first) +#define SLIST_END(head) NULL +#define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head)) +#define SLIST_NEXT(elm, field) ((elm)->field.sle_next) + +#define SLIST_FOREACH(var, head, field) \ + for((var) = SLIST_FIRST(head); \ + (var) != SLIST_END(head); \ + (var) = SLIST_NEXT(var, field)) + +#define SLIST_FOREACH_PREVPTR(var, varp, head, field) \ + for ((varp) = &SLIST_FIRST((head)); \ + ((var) = *(varp)) != SLIST_END(head); \ + (varp) = &SLIST_NEXT((var), field)) + +/* + * Singly-linked List functions. + */ +#define SLIST_INIT(head) { \ + SLIST_FIRST(head) = SLIST_END(head); \ +} + +#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ + (elm)->field.sle_next = (slistelm)->field.sle_next; \ + (slistelm)->field.sle_next = (elm); \ +} while (0) + +#define SLIST_INSERT_HEAD(head, elm, field) do { \ + (elm)->field.sle_next = (head)->slh_first; \ + (head)->slh_first = (elm); \ +} while (0) + +#define SLIST_REMOVE_NEXT(head, elm, field) do { \ + (elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \ +} while (0) + +#define SLIST_REMOVE_HEAD(head, field) do { \ + (head)->slh_first = (head)->slh_first->field.sle_next; \ +} while (0) + +#define SLIST_REMOVE(head, elm, type, field) do { \ + if ((head)->slh_first == (elm)) { \ + SLIST_REMOVE_HEAD((head), field); \ + } else { \ + struct type *curelm = (head)->slh_first; \ + \ + while (curelm->field.sle_next != (elm)) \ + curelm = curelm->field.sle_next; \ + curelm->field.sle_next = \ + curelm->field.sle_next->field.sle_next; \ + _Q_INVALIDATE((elm)->field.sle_next); \ + } \ +} while (0) + +/* + * List definitions. + */ +#define LIST_HEAD(name, type) \ +struct name { \ + struct type *lh_first; /* first element */ \ +} + +#define LIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define LIST_ENTRY(type) \ +struct { \ + struct type *le_next; /* next element */ \ + struct type **le_prev; /* address of previous next element */ \ +} + +/* + * List access methods + */ +#define LIST_FIRST(head) ((head)->lh_first) +#define LIST_END(head) NULL +#define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head)) +#define LIST_NEXT(elm, field) ((elm)->field.le_next) + +#define LIST_FOREACH(var, head, field) \ + for((var) = LIST_FIRST(head); \ + (var)!= LIST_END(head); \ + (var) = LIST_NEXT(var, field)) + +/* + * List functions. + */ +#define LIST_INIT(head) do { \ + LIST_FIRST(head) = LIST_END(head); \ +} while (0) + +#define LIST_INSERT_AFTER(listelm, elm, field) do { \ + if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ + (listelm)->field.le_next->field.le_prev = \ + &(elm)->field.le_next; \ + (listelm)->field.le_next = (elm); \ + (elm)->field.le_prev = &(listelm)->field.le_next; \ +} while (0) + +#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.le_prev = (listelm)->field.le_prev; \ + (elm)->field.le_next = (listelm); \ + *(listelm)->field.le_prev = (elm); \ + (listelm)->field.le_prev = &(elm)->field.le_next; \ +} while (0) + +#define LIST_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.le_next = (head)->lh_first) != NULL) \ + (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ + (head)->lh_first = (elm); \ + (elm)->field.le_prev = &(head)->lh_first; \ +} while (0) + +#define LIST_REMOVE(elm, field) do { \ + if ((elm)->field.le_next != NULL) \ + (elm)->field.le_next->field.le_prev = \ + (elm)->field.le_prev; \ + *(elm)->field.le_prev = (elm)->field.le_next; \ + _Q_INVALIDATE((elm)->field.le_prev); \ + _Q_INVALIDATE((elm)->field.le_next); \ +} while (0) + +#define LIST_REPLACE(elm, elm2, field) do { \ + if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \ + (elm2)->field.le_next->field.le_prev = \ + &(elm2)->field.le_next; \ + (elm2)->field.le_prev = (elm)->field.le_prev; \ + *(elm2)->field.le_prev = (elm2); \ + _Q_INVALIDATE((elm)->field.le_prev); \ + _Q_INVALIDATE((elm)->field.le_next); \ +} while (0) + +/* + * Simple queue definitions. + */ +#define SIMPLEQ_HEAD(name, type) \ +struct name { \ + struct type *sqh_first; /* first element */ \ + struct type **sqh_last; /* addr of last next element */ \ +} + +#define SIMPLEQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).sqh_first } + +#define SIMPLEQ_ENTRY(type) \ +struct { \ + struct type *sqe_next; /* next element */ \ +} + +/* + * Simple queue access methods. + */ +#define SIMPLEQ_FIRST(head) ((head)->sqh_first) +#define SIMPLEQ_END(head) NULL +#define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head)) +#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) + +#define SIMPLEQ_FOREACH(var, head, field) \ + for((var) = SIMPLEQ_FIRST(head); \ + (var) != SIMPLEQ_END(head); \ + (var) = SIMPLEQ_NEXT(var, field)) + +/* + * Simple queue functions. + */ +#define SIMPLEQ_INIT(head) do { \ + (head)->sqh_first = NULL; \ + (head)->sqh_last = &(head)->sqh_first; \ +} while (0) + +#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \ + (head)->sqh_last = &(elm)->field.sqe_next; \ + (head)->sqh_first = (elm); \ +} while (0) + +#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.sqe_next = NULL; \ + *(head)->sqh_last = (elm); \ + (head)->sqh_last = &(elm)->field.sqe_next; \ +} while (0) + +#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\ + (head)->sqh_last = &(elm)->field.sqe_next; \ + (listelm)->field.sqe_next = (elm); \ +} while (0) + +#define SIMPLEQ_REMOVE_HEAD(head, field) do { \ + if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \ + (head)->sqh_last = &(head)->sqh_first; \ +} while (0) + +/* + * Tail queue definitions. + */ +#define TAILQ_HEAD(name, type) \ +struct name { \ + struct type *tqh_first; /* first element */ \ + struct type **tqh_last; /* addr of last next element */ \ +} + +#define TAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).tqh_first } + +#define TAILQ_ENTRY(type) \ +struct { \ + struct type *tqe_next; /* next element */ \ + struct type **tqe_prev; /* address of previous next element */ \ +} + +/* + * tail queue access methods + */ +#define TAILQ_FIRST(head) ((head)->tqh_first) +#define TAILQ_END(head) NULL +#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) +#define TAILQ_LAST(head, headname) \ + (*(((struct headname *)((head)->tqh_last))->tqh_last)) +/* XXX */ +#define TAILQ_PREV(elm, headname, field) \ + (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) +#define TAILQ_EMPTY(head) \ + (TAILQ_FIRST(head) == TAILQ_END(head)) + +#define TAILQ_FOREACH(var, head, field) \ + for((var) = TAILQ_FIRST(head); \ + (var) != TAILQ_END(head); \ + (var) = TAILQ_NEXT(var, field)) + +#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ + for((var) = TAILQ_LAST(head, headname); \ + (var) != TAILQ_END(head); \ + (var) = TAILQ_PREV(var, headname, field)) + +/* + * Tail queue functions. + */ +#define TAILQ_INIT(head) do { \ + (head)->tqh_first = NULL; \ + (head)->tqh_last = &(head)->tqh_first; \ +} while (0) + +#define TAILQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ + (head)->tqh_first->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (head)->tqh_first = (elm); \ + (elm)->field.tqe_prev = &(head)->tqh_first; \ +} while (0) + +#define TAILQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.tqe_next = NULL; \ + (elm)->field.tqe_prev = (head)->tqh_last; \ + *(head)->tqh_last = (elm); \ + (head)->tqh_last = &(elm)->field.tqe_next; \ +} while (0) + +#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ + (elm)->field.tqe_next->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (listelm)->field.tqe_next = (elm); \ + (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ +} while (0) + +#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ + (elm)->field.tqe_next = (listelm); \ + *(listelm)->field.tqe_prev = (elm); \ + (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ +} while (0) + +#define TAILQ_REMOVE(head, elm, field) do { \ + if (((elm)->field.tqe_next) != NULL) \ + (elm)->field.tqe_next->field.tqe_prev = \ + (elm)->field.tqe_prev; \ + else \ + (head)->tqh_last = (elm)->field.tqe_prev; \ + *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ + _Q_INVALIDATE((elm)->field.tqe_prev); \ + _Q_INVALIDATE((elm)->field.tqe_next); \ +} while (0) + +#define TAILQ_REPLACE(head, elm, elm2, field) do { \ + if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \ + (elm2)->field.tqe_next->field.tqe_prev = \ + &(elm2)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm2)->field.tqe_next; \ + (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \ + *(elm2)->field.tqe_prev = (elm2); \ + _Q_INVALIDATE((elm)->field.tqe_prev); \ + _Q_INVALIDATE((elm)->field.tqe_next); \ +} while (0) + +/* + * Circular queue definitions. + */ +#define CIRCLEQ_HEAD(name, type) \ +struct name { \ + struct type *cqh_first; /* first element */ \ + struct type *cqh_last; /* last element */ \ +} + +#define CIRCLEQ_HEAD_INITIALIZER(head) \ + { CIRCLEQ_END(&head), CIRCLEQ_END(&head) } + +#define CIRCLEQ_ENTRY(type) \ +struct { \ + struct type *cqe_next; /* next element */ \ + struct type *cqe_prev; /* previous element */ \ +} + +/* + * Circular queue access methods + */ +#define CIRCLEQ_FIRST(head) ((head)->cqh_first) +#define CIRCLEQ_LAST(head) ((head)->cqh_last) +#define CIRCLEQ_END(head) ((void *)(head)) +#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next) +#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev) +#define CIRCLEQ_EMPTY(head) \ + (CIRCLEQ_FIRST(head) == CIRCLEQ_END(head)) + +#define CIRCLEQ_FOREACH(var, head, field) \ + for((var) = CIRCLEQ_FIRST(head); \ + (var) != CIRCLEQ_END(head); \ + (var) = CIRCLEQ_NEXT(var, field)) + +#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \ + for((var) = CIRCLEQ_LAST(head); \ + (var) != CIRCLEQ_END(head); \ + (var) = CIRCLEQ_PREV(var, field)) + +/* + * Circular queue functions. + */ +#define CIRCLEQ_INIT(head) do { \ + (head)->cqh_first = CIRCLEQ_END(head); \ + (head)->cqh_last = CIRCLEQ_END(head); \ +} while (0) + +#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ + (elm)->field.cqe_next = (listelm)->field.cqe_next; \ + (elm)->field.cqe_prev = (listelm); \ + if ((listelm)->field.cqe_next == CIRCLEQ_END(head)) \ + (head)->cqh_last = (elm); \ + else \ + (listelm)->field.cqe_next->field.cqe_prev = (elm); \ + (listelm)->field.cqe_next = (elm); \ +} while (0) + +#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \ + (elm)->field.cqe_next = (listelm); \ + (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \ + if ((listelm)->field.cqe_prev == CIRCLEQ_END(head)) \ + (head)->cqh_first = (elm); \ + else \ + (listelm)->field.cqe_prev->field.cqe_next = (elm); \ + (listelm)->field.cqe_prev = (elm); \ +} while (0) + +#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \ + (elm)->field.cqe_next = (head)->cqh_first; \ + (elm)->field.cqe_prev = CIRCLEQ_END(head); \ + if ((head)->cqh_last == CIRCLEQ_END(head)) \ + (head)->cqh_last = (elm); \ + else \ + (head)->cqh_first->field.cqe_prev = (elm); \ + (head)->cqh_first = (elm); \ +} while (0) + +#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.cqe_next = CIRCLEQ_END(head); \ + (elm)->field.cqe_prev = (head)->cqh_last; \ + if ((head)->cqh_first == CIRCLEQ_END(head)) \ + (head)->cqh_first = (elm); \ + else \ + (head)->cqh_last->field.cqe_next = (elm); \ + (head)->cqh_last = (elm); \ +} while (0) + +#define CIRCLEQ_REMOVE(head, elm, field) do { \ + if ((elm)->field.cqe_next == CIRCLEQ_END(head)) \ + (head)->cqh_last = (elm)->field.cqe_prev; \ + else \ + (elm)->field.cqe_next->field.cqe_prev = \ + (elm)->field.cqe_prev; \ + if ((elm)->field.cqe_prev == CIRCLEQ_END(head)) \ + (head)->cqh_first = (elm)->field.cqe_next; \ + else \ + (elm)->field.cqe_prev->field.cqe_next = \ + (elm)->field.cqe_next; \ + _Q_INVALIDATE((elm)->field.cqe_prev); \ + _Q_INVALIDATE((elm)->field.cqe_next); \ +} while (0) + +#define CIRCLEQ_REPLACE(head, elm, elm2, field) do { \ + if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == \ + CIRCLEQ_END(head)) \ + (head)->cqh_last = (elm2); \ + else \ + (elm2)->field.cqe_next->field.cqe_prev = (elm2); \ + if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == \ + CIRCLEQ_END(head)) \ + (head)->cqh_first = (elm2); \ + else \ + (elm2)->field.cqe_prev->field.cqe_next = (elm2); \ + _Q_INVALIDATE((elm)->field.cqe_prev); \ + _Q_INVALIDATE((elm)->field.cqe_next); \ +} while (0) + +#endif /* !_SYS_QUEUE_H_ */ diff --git a/include/table.h b/include/table.h new file mode 100644 index 00000000..1d6ca8f7 --- /dev/null +++ b/include/table.h @@ -0,0 +1,20 @@ +#include +#include "data.h" + +#ifndef _TABLE_H +#define _TABLE_H + +#define CUR_TABLE (c_ws->table) +#define CUR_CELL (CUR_TABLE[current_col][current_row]) + +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); +void expand_table_cols(Workspace *workspace); +bool cell_exists(int col, int row); + +#endif diff --git a/include/util.h b/include/util.h new file mode 100644 index 00000000..9eb2a447 --- /dev/null +++ b/include/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/include/xcb.h b/include/xcb.h new file mode 100644 index 00000000..c8224ef9 --- /dev/null +++ b/include/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 diff --git a/layout.c b/layout.c deleted file mode 100644 index 4a09e416..00000000 --- a/layout.c +++ /dev/null @@ -1,181 +0,0 @@ -#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 deleted file mode 100644 index aecb2b4d..00000000 --- a/layout.h +++ /dev/null @@ -1,9 +0,0 @@ -#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 deleted file mode 100644 index 359e3f74..00000000 --- a/mainx.c +++ /dev/null @@ -1,427 +0,0 @@ -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include - -#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" - -Display *xkbdpy; - -TAILQ_HEAD(bindings_head, Binding) bindings; -xcb_event_handlers_t evenths; - -static const int TOP = 20; -static const int LEFT = 5; -static const int BOTTOM = 5; -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) */ -char **environment; - -/* hm, xcb_wm wants us to implement this. */ -table_t *byChild = 0; -table_t *byParent = 0; -xcb_window_t root_win; - -char *pattern = "-misc-fixed-medium-r-normal--13-120-75-75-C-70-iso8859-1"; -int num_screens = 0; - -/* - * - * TODO: what exactly does this, what happens if we leave stuff out? - * - */ -void manage_window(xcb_property_handlers_t *prophs, xcb_connection_t *c, xcb_window_t window, window_attributes_t wa) -{ - printf("managing window.\n"); - xcb_drawable_t d = { window }; - xcb_get_geometry_cookie_t geomc; - xcb_get_geometry_reply_t *geom; - xcb_get_window_attributes_reply_t *attr = 0; - if(wa.tag == TAG_COOKIE) - { - attr = xcb_get_window_attributes_reply(c, wa.u.cookie, 0); - if(!attr) - return; - if(attr->map_state != XCB_MAP_STATE_VIEWABLE) - { - printf("Window 0x%08x is not mapped. Ignoring.\n", window); - free(attr); - return; - } - wa.tag = TAG_VALUE; - wa.u.override_redirect = attr->override_redirect; - } - if(!wa.u.override_redirect && table_get(byChild, window)) - { - printf("Window 0x%08x already managed. Ignoring.\n", window); - free(attr); - return; - } - if(wa.u.override_redirect) - { - printf("Window 0x%08x has override-redirect set. Ignoring.\n", window); - free(attr); - return; - } - geomc = xcb_get_geometry(c, d); - if(!attr) - { - wa.tag = TAG_COOKIE; - wa.u.cookie = xcb_get_window_attributes(c, window); - attr = xcb_get_window_attributes_reply(c, wa.u.cookie, 0); - } - geom = xcb_get_geometry_reply(c, geomc, 0); - if(attr && geom) - { - reparent_window(c, window, attr->visual, geom->root, geom->depth, geom->x, geom->y, geom->width, geom->height); - xcb_property_changed(prophs, XCB_PROPERTY_NEW_VALUE, window, WM_NAME); - } - free(attr); - free(geom); -} - -/* - * Let’s own this window… - * - */ -void reparent_window(xcb_connection_t *conn, xcb_window_t child, - xcb_visualid_t visual, xcb_window_t root, uint8_t depth, - int16_t x, int16_t y, uint16_t width, uint16_t height) { - - Client *new = table_get(byChild, child); - if (new == NULL) { - printf("oh, it's new\n"); - new = calloc(sizeof(Client), 1); - new->x = -1; - new->y = -1; - } - uint32_t mask = 0; - uint32_t values[3]; - - /* Insert into the currently active container */ - CIRCLEQ_INSERT_TAIL(&(CUR_CELL->clients), new, clients); - - printf("currently_focused = %p\n", new); - CUR_CELL->currently_focused = new; - new->container = CUR_CELL; - - new->frame = xcb_generate_id(conn); - new->child = child; - new->width = width; - new->height = height; - - /* Don’t generate events for our new window, it should *not* be managed */ - mask |= XCB_CW_OVERRIDE_REDIRECT; - values[0] = 1; - - /* We want to know when… */ - mask |= XCB_CW_EVENT_MASK; - values[1] = XCB_EVENT_MASK_BUTTON_PRESS | /* …mouse is pressed/released */ - XCB_EVENT_MASK_BUTTON_RELEASE | - XCB_EVENT_MASK_EXPOSURE | /* …our window needs to be redrawn */ - XCB_EVENT_MASK_ENTER_WINDOW /* …user moves cursor inside our window */; - - printf("Reparenting 0x%08x under 0x%08x.\n", child, new->frame); - - /* Yo dawg, I heard you like windows, so I create a window around your window… */ - xcb_create_window(conn, - depth, - new->frame, - root, - x, - y, - width + LEFT + RIGHT, - height + TOP + BOTTOM, - /* border_width */ 0, - XCB_WINDOW_CLASS_INPUT_OUTPUT, - visual, - mask, - values); - xcb_change_save_set(conn, XCB_SET_MODE_INSERT, child); - - /* Map the window on the screen (= make it visible) */ - xcb_map_window(conn, new->frame); - - /* Generate a graphics context for the titlebar */ - new->titlegc = xcb_generate_id(conn); - xcb_create_gc(conn, new->titlegc, new->frame, 0, 0); - - /* Draw decorations */ - decorate_window(conn, new); - - /* Put our data structure (Client) into the table */ - table_put(byParent, new->frame, new); - table_put(byChild, child, new); - - /* Moves the original window into the new frame we've created for it */ - i3Font *font = load_font(conn, pattern); - xcb_reparent_window(conn, child, new->frame, 0, font->height); - - /* We are interested in property changes */ - mask = XCB_CW_EVENT_MASK; - values[0] = XCB_EVENT_MASK_PROPERTY_CHANGE | - XCB_EVENT_MASK_STRUCTURE_NOTIFY | - XCB_EVENT_MASK_ENTER_WINDOW | - XCB_EVENT_MASK_BUTTON_PRESS; - xcb_change_window_attributes(conn, child, mask, values); - - /* We need to grab the mouse buttons for click to focus */ - xcb_grab_button(conn, false, child, XCB_EVENT_MASK_BUTTON_PRESS, - XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, root, XCB_NONE, 1 /* left mouse button */, - XCB_BUTTON_MASK_ANY /* don’t filter for any modifiers */); - - /* Focus the new window */ - xcb_set_input_focus(conn, XCB_INPUT_FOCUS_NONE, new->child, XCB_CURRENT_TIME); - - render_layout(conn); -} - -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; - int i, len; - xcb_window_t *children; - xcb_get_window_attributes_cookie_t *cookies; - - wintree = xcb_query_tree(c, root); - rep = xcb_query_tree_reply(c, wintree, 0); - if(!rep) - return; - len = xcb_query_tree_children_length(rep); - cookies = malloc(len * sizeof(*cookies)); - if(!cookies) - { - free(rep); - return; - } - children = xcb_query_tree_children(rep); - for(i = 0; i < len; ++i) - cookies[i] = xcb_get_window_attributes(c, children[i]); - for(i = 0; i < len; ++i) - { - window_attributes_t wa = { TAG_COOKIE, { cookies[i] } }; - manage_window(prophs, c, children[i], wa); - } - free(rep); -} - -static void initialize_xinerama(xcb_connection_t *conn) { - xcb_xinerama_query_screens_reply_t *reply; - xcb_xinerama_screen_info_t *screen_info; - int screen; - - if (!xcb_get_extension_data(conn, &xcb_xinerama_id)->present) { - printf("Xinerama extension not found, disabling.\n"); - return; - } - - if (!xcb_xinerama_is_active_reply(conn, xcb_xinerama_is_active(conn), NULL)->state) { - printf("Xinerama is not active (in your X-Server), disabling.\n"); - return; - } - - - reply = xcb_xinerama_query_screens_reply(conn, xcb_xinerama_query_screens_unchecked(conn), NULL); - /* TODO: error check */ - screen_info = xcb_xinerama_query_screens_screen_info(reply); - - num_screens = xcb_xinerama_query_screens_screen_info_length(reply); - - /* Just go through each workspace and associate as many screens as we can. */ - for (screen = 0; screen < num_screens; screen++) { - workspaces[screen].x = screen_info[screen].x_org; - workspaces[screen].y = screen_info[screen].y_org; - workspaces[screen].width = screen_info[screen].width; - workspaces[screen].height = screen_info[screen].height; - workspaces[screen].screen_num = screen; - - printf("found Xinerama screen: %d x %d at %d x %d\n", - screen_info[screen].width, screen_info[screen].height, - screen_info[screen].x_org, screen_info[screen].y_org); - } - - free(screen_info); -} - -int main(int argc, char *argv[], char *env[]) { - int i, e = 0; - - for (i = 0; (env[i] != NULL); i++) - if (strncmp(env[i], "LC_", strlen("LC_")) == 0 || - strncmp(env[i], "LANG=", strlen("LANG=")) == 0 || - strncmp(env[i], "DISPLAY=", strlen("DISPLAY=")) == 0) { - printf("Passing environment \"%s\"\n", env[i]); - environment = realloc(environment, sizeof(char*) * ++e); - environment[e-1] = env[i]; - } - - /* environment has to be NULL-terminated */ - environment = realloc(environment, sizeof(char*) * ++e); - environment[e-1] = NULL; - - init_table(); - - xcb_connection_t *c; - xcb_property_handlers_t prophs; - xcb_window_t root; - - int screens; - - memset(&evenths, 0, sizeof(xcb_event_handlers_t)); - memset(&prophs, 0, sizeof(xcb_property_handlers_t)); - - byChild = alloc_table(); - byParent = alloc_table(); - - TAILQ_INIT(&bindings); - - c = xcb_connect(NULL, &screens); - - printf("x screen is %d\n", screens); - - /* TODO: this has to be more beautiful somewhen */ - int major, minor, error; - - major = XkbMajorVersion; - minor = XkbMinorVersion; - - int evBase, errBase; - - if ((xkbdpy = XkbOpenDisplay(getenv("DISPLAY"), &evBase, &errBase, &major, &minor, &error)) == NULL) { - fprintf(stderr, "XkbOpenDisplay() failed\n"); - return 1; - } - - int i1; - if (!XkbQueryExtension(xkbdpy,&i1,&evBase,&errBase,&major,&minor)) { - fprintf(stderr, "XKB not supported by X-server\n"); - return 1; - } - /* end of ugliness */ - - xcb_event_handlers_init(c, &evenths); - for(i = 2; i < 128; ++i) - 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)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, handle_expose_event, 0); - - /* Key presses/releases are pretty obvious, I think */ - xcb_event_set_key_press_handler(&evenths, handle_key_press, 0); - xcb_event_set_key_release_handler(&evenths, handle_key_release, 0); - - /* Enter window = user moved his mouse over the window */ - xcb_event_set_enter_notify_handler(&evenths, handle_enter_notify, 0); - - /* Button press = user pushed a mouse button over one of our windows */ - xcb_event_set_button_press_handler(&evenths, handle_button_press, 0); - - xcb_event_set_unmap_notify_handler(&evenths, handle_unmap_notify_event, 0); - - xcb_property_handlers_init(&prophs, &evenths); - xcb_event_set_map_notify_handler(&evenths, handle_map_notify_event, &prophs); - - xcb_watch_wm_name(&prophs, 128, handle_windowname_change, 0); - - root = xcb_aux_get_screen(c, screens)->root; - root_win = root; - - uint32_t mask = XCB_CW_EVENT_MASK; - uint32_t values[] = { XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_PROPERTY_CHANGE }; - xcb_change_window_attributes(c, root, mask, values); - - #define BIND(key, modifier, cmd) { \ - Binding *new = malloc(sizeof(Binding)); \ - new->keycode = key; \ - new->mods = modifier; \ - new->command = cmd; \ - TAILQ_INSERT_TAIL(&bindings, new, bindings); \ - } - - /* 38 = 'a' */ - BIND(38, BIND_MODE_SWITCH, "foo"); - - BIND(30, 0, "exec /usr/pkg/bin/urxvt"); - - BIND(44, BIND_MOD_1, "h"); - BIND(45, BIND_MOD_1, "j"); - BIND(46, BIND_MOD_1, "k"); - BIND(47, BIND_MOD_1, "l"); - - BIND(44, BIND_MOD_1 | BIND_CONTROL, "sh"); - BIND(45, BIND_MOD_1 | BIND_CONTROL, "sj"); - BIND(46, BIND_MOD_1 | BIND_CONTROL, "sk"); - BIND(47, BIND_MOD_1 | BIND_CONTROL, "sl"); - - BIND(44, BIND_MOD_1 | BIND_SHIFT, "mh"); - BIND(45, BIND_MOD_1 | BIND_SHIFT, "mj"); - BIND(46, BIND_MOD_1 | BIND_SHIFT, "mk"); - BIND(47, BIND_MOD_1 | BIND_SHIFT, "ml"); - - BIND(10, BIND_MOD_1 , "1"); - BIND(11, BIND_MOD_1 , "2"); - BIND(12, BIND_MOD_1 , "3"); - BIND(13, BIND_MOD_1 , "4"); - BIND(14, BIND_MOD_1 , "5"); - BIND(15, BIND_MOD_1 , "6"); - BIND(16, BIND_MOD_1 , "7"); - BIND(17, BIND_MOD_1 , "8"); - BIND(18, BIND_MOD_1 , "9"); - BIND(19, BIND_MOD_1 , "0"); - - Binding *bind; - TAILQ_FOREACH(bind, &bindings, bindings) { - printf("Grabbing %d\n", bind->keycode); - if (bind->mods & BIND_MODE_SWITCH) - xcb_grab_key(c, 0, root, 0, bind->keycode, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_SYNC); - else xcb_grab_key(c, 0, root, bind->mods, bind->keycode, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC); - } - - /* check for Xinerama */ - printf("Checking for Xinerama...\n"); - initialize_xinerama(c); - - start_application(TERMINAL, NULL); - - xcb_flush(c); - - manage_existing_windows(c, &prophs, root); - - xcb_event_wait_for_event_loop(&evenths); - - /* not reached */ - return 0; -} diff --git a/queue.h b/queue.h deleted file mode 100644 index 56d9c4af..00000000 --- a/queue.h +++ /dev/null @@ -1,527 +0,0 @@ -/* $OpenBSD: queue.h,v 1.1 2007/10/26 03:14:08 niallo Exp $ */ -/* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */ - -/* - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)queue.h 8.5 (Berkeley) 8/20/94 - */ - -#ifndef _SYS_QUEUE_H_ -#define _SYS_QUEUE_H_ - -/* - * This file defines five types of data structures: singly-linked lists, - * lists, simple queues, tail queues, and circular queues. - * - * - * A singly-linked list is headed by a single forward pointer. The elements - * are singly linked for minimum space and pointer manipulation overhead at - * the expense of O(n) removal for arbitrary elements. New elements can be - * added to the list after an existing element or at the head of the list. - * Elements being removed from the head of the list should use the explicit - * macro for this purpose for optimum efficiency. A singly-linked list may - * only be traversed in the forward direction. Singly-linked lists are ideal - * for applications with large datasets and few or no removals or for - * implementing a LIFO queue. - * - * A list is headed by a single forward pointer (or an array of forward - * pointers for a hash table header). The elements are doubly linked - * so that an arbitrary element can be removed without a need to - * traverse the list. New elements can be added to the list before - * or after an existing element or at the head of the list. A list - * may only be traversed in the forward direction. - * - * A simple queue is headed by a pair of pointers, one the head of the - * list and the other to the tail of the list. The elements are singly - * linked to save space, so elements can only be removed from the - * head of the list. New elements can be added to the list before or after - * an existing element, at the head of the list, or at the end of the - * list. A simple queue may only be traversed in the forward direction. - * - * A tail queue is headed by a pair of pointers, one to the head of the - * list and the other to the tail of the list. The elements are doubly - * linked so that an arbitrary element can be removed without a need to - * traverse the list. New elements can be added to the list before or - * after an existing element, at the head of the list, or at the end of - * the list. A tail queue may be traversed in either direction. - * - * A circle queue is headed by a pair of pointers, one to the head of the - * list and the other to the tail of the list. The elements are doubly - * linked so that an arbitrary element can be removed without a need to - * traverse the list. New elements can be added to the list before or after - * an existing element, at the head of the list, or at the end of the list. - * A circle queue may be traversed in either direction, but has a more - * complex end of list detection. - * - * For details on the use of these macros, see the queue(3) manual page. - */ - -#if defined(QUEUE_MACRO_DEBUG) || (defined(_KERNEL) && defined(DIAGNOSTIC)) -#define _Q_INVALIDATE(a) (a) = ((void *)-1) -#else -#define _Q_INVALIDATE(a) -#endif - -/* - * Singly-linked List definitions. - */ -#define SLIST_HEAD(name, type) \ -struct name { \ - struct type *slh_first; /* first element */ \ -} - -#define SLIST_HEAD_INITIALIZER(head) \ - { NULL } - -#define SLIST_ENTRY(type) \ -struct { \ - struct type *sle_next; /* next element */ \ -} - -/* - * Singly-linked List access methods. - */ -#define SLIST_FIRST(head) ((head)->slh_first) -#define SLIST_END(head) NULL -#define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head)) -#define SLIST_NEXT(elm, field) ((elm)->field.sle_next) - -#define SLIST_FOREACH(var, head, field) \ - for((var) = SLIST_FIRST(head); \ - (var) != SLIST_END(head); \ - (var) = SLIST_NEXT(var, field)) - -#define SLIST_FOREACH_PREVPTR(var, varp, head, field) \ - for ((varp) = &SLIST_FIRST((head)); \ - ((var) = *(varp)) != SLIST_END(head); \ - (varp) = &SLIST_NEXT((var), field)) - -/* - * Singly-linked List functions. - */ -#define SLIST_INIT(head) { \ - SLIST_FIRST(head) = SLIST_END(head); \ -} - -#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ - (elm)->field.sle_next = (slistelm)->field.sle_next; \ - (slistelm)->field.sle_next = (elm); \ -} while (0) - -#define SLIST_INSERT_HEAD(head, elm, field) do { \ - (elm)->field.sle_next = (head)->slh_first; \ - (head)->slh_first = (elm); \ -} while (0) - -#define SLIST_REMOVE_NEXT(head, elm, field) do { \ - (elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \ -} while (0) - -#define SLIST_REMOVE_HEAD(head, field) do { \ - (head)->slh_first = (head)->slh_first->field.sle_next; \ -} while (0) - -#define SLIST_REMOVE(head, elm, type, field) do { \ - if ((head)->slh_first == (elm)) { \ - SLIST_REMOVE_HEAD((head), field); \ - } else { \ - struct type *curelm = (head)->slh_first; \ - \ - while (curelm->field.sle_next != (elm)) \ - curelm = curelm->field.sle_next; \ - curelm->field.sle_next = \ - curelm->field.sle_next->field.sle_next; \ - _Q_INVALIDATE((elm)->field.sle_next); \ - } \ -} while (0) - -/* - * List definitions. - */ -#define LIST_HEAD(name, type) \ -struct name { \ - struct type *lh_first; /* first element */ \ -} - -#define LIST_HEAD_INITIALIZER(head) \ - { NULL } - -#define LIST_ENTRY(type) \ -struct { \ - struct type *le_next; /* next element */ \ - struct type **le_prev; /* address of previous next element */ \ -} - -/* - * List access methods - */ -#define LIST_FIRST(head) ((head)->lh_first) -#define LIST_END(head) NULL -#define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head)) -#define LIST_NEXT(elm, field) ((elm)->field.le_next) - -#define LIST_FOREACH(var, head, field) \ - for((var) = LIST_FIRST(head); \ - (var)!= LIST_END(head); \ - (var) = LIST_NEXT(var, field)) - -/* - * List functions. - */ -#define LIST_INIT(head) do { \ - LIST_FIRST(head) = LIST_END(head); \ -} while (0) - -#define LIST_INSERT_AFTER(listelm, elm, field) do { \ - if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ - (listelm)->field.le_next->field.le_prev = \ - &(elm)->field.le_next; \ - (listelm)->field.le_next = (elm); \ - (elm)->field.le_prev = &(listelm)->field.le_next; \ -} while (0) - -#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ - (elm)->field.le_prev = (listelm)->field.le_prev; \ - (elm)->field.le_next = (listelm); \ - *(listelm)->field.le_prev = (elm); \ - (listelm)->field.le_prev = &(elm)->field.le_next; \ -} while (0) - -#define LIST_INSERT_HEAD(head, elm, field) do { \ - if (((elm)->field.le_next = (head)->lh_first) != NULL) \ - (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ - (head)->lh_first = (elm); \ - (elm)->field.le_prev = &(head)->lh_first; \ -} while (0) - -#define LIST_REMOVE(elm, field) do { \ - if ((elm)->field.le_next != NULL) \ - (elm)->field.le_next->field.le_prev = \ - (elm)->field.le_prev; \ - *(elm)->field.le_prev = (elm)->field.le_next; \ - _Q_INVALIDATE((elm)->field.le_prev); \ - _Q_INVALIDATE((elm)->field.le_next); \ -} while (0) - -#define LIST_REPLACE(elm, elm2, field) do { \ - if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \ - (elm2)->field.le_next->field.le_prev = \ - &(elm2)->field.le_next; \ - (elm2)->field.le_prev = (elm)->field.le_prev; \ - *(elm2)->field.le_prev = (elm2); \ - _Q_INVALIDATE((elm)->field.le_prev); \ - _Q_INVALIDATE((elm)->field.le_next); \ -} while (0) - -/* - * Simple queue definitions. - */ -#define SIMPLEQ_HEAD(name, type) \ -struct name { \ - struct type *sqh_first; /* first element */ \ - struct type **sqh_last; /* addr of last next element */ \ -} - -#define SIMPLEQ_HEAD_INITIALIZER(head) \ - { NULL, &(head).sqh_first } - -#define SIMPLEQ_ENTRY(type) \ -struct { \ - struct type *sqe_next; /* next element */ \ -} - -/* - * Simple queue access methods. - */ -#define SIMPLEQ_FIRST(head) ((head)->sqh_first) -#define SIMPLEQ_END(head) NULL -#define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head)) -#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) - -#define SIMPLEQ_FOREACH(var, head, field) \ - for((var) = SIMPLEQ_FIRST(head); \ - (var) != SIMPLEQ_END(head); \ - (var) = SIMPLEQ_NEXT(var, field)) - -/* - * Simple queue functions. - */ -#define SIMPLEQ_INIT(head) do { \ - (head)->sqh_first = NULL; \ - (head)->sqh_last = &(head)->sqh_first; \ -} while (0) - -#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \ - if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \ - (head)->sqh_last = &(elm)->field.sqe_next; \ - (head)->sqh_first = (elm); \ -} while (0) - -#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \ - (elm)->field.sqe_next = NULL; \ - *(head)->sqh_last = (elm); \ - (head)->sqh_last = &(elm)->field.sqe_next; \ -} while (0) - -#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ - if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\ - (head)->sqh_last = &(elm)->field.sqe_next; \ - (listelm)->field.sqe_next = (elm); \ -} while (0) - -#define SIMPLEQ_REMOVE_HEAD(head, field) do { \ - if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \ - (head)->sqh_last = &(head)->sqh_first; \ -} while (0) - -/* - * Tail queue definitions. - */ -#define TAILQ_HEAD(name, type) \ -struct name { \ - struct type *tqh_first; /* first element */ \ - struct type **tqh_last; /* addr of last next element */ \ -} - -#define TAILQ_HEAD_INITIALIZER(head) \ - { NULL, &(head).tqh_first } - -#define TAILQ_ENTRY(type) \ -struct { \ - struct type *tqe_next; /* next element */ \ - struct type **tqe_prev; /* address of previous next element */ \ -} - -/* - * tail queue access methods - */ -#define TAILQ_FIRST(head) ((head)->tqh_first) -#define TAILQ_END(head) NULL -#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) -#define TAILQ_LAST(head, headname) \ - (*(((struct headname *)((head)->tqh_last))->tqh_last)) -/* XXX */ -#define TAILQ_PREV(elm, headname, field) \ - (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) -#define TAILQ_EMPTY(head) \ - (TAILQ_FIRST(head) == TAILQ_END(head)) - -#define TAILQ_FOREACH(var, head, field) \ - for((var) = TAILQ_FIRST(head); \ - (var) != TAILQ_END(head); \ - (var) = TAILQ_NEXT(var, field)) - -#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ - for((var) = TAILQ_LAST(head, headname); \ - (var) != TAILQ_END(head); \ - (var) = TAILQ_PREV(var, headname, field)) - -/* - * Tail queue functions. - */ -#define TAILQ_INIT(head) do { \ - (head)->tqh_first = NULL; \ - (head)->tqh_last = &(head)->tqh_first; \ -} while (0) - -#define TAILQ_INSERT_HEAD(head, elm, field) do { \ - if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ - (head)->tqh_first->field.tqe_prev = \ - &(elm)->field.tqe_next; \ - else \ - (head)->tqh_last = &(elm)->field.tqe_next; \ - (head)->tqh_first = (elm); \ - (elm)->field.tqe_prev = &(head)->tqh_first; \ -} while (0) - -#define TAILQ_INSERT_TAIL(head, elm, field) do { \ - (elm)->field.tqe_next = NULL; \ - (elm)->field.tqe_prev = (head)->tqh_last; \ - *(head)->tqh_last = (elm); \ - (head)->tqh_last = &(elm)->field.tqe_next; \ -} while (0) - -#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ - if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ - (elm)->field.tqe_next->field.tqe_prev = \ - &(elm)->field.tqe_next; \ - else \ - (head)->tqh_last = &(elm)->field.tqe_next; \ - (listelm)->field.tqe_next = (elm); \ - (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ -} while (0) - -#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ - (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ - (elm)->field.tqe_next = (listelm); \ - *(listelm)->field.tqe_prev = (elm); \ - (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ -} while (0) - -#define TAILQ_REMOVE(head, elm, field) do { \ - if (((elm)->field.tqe_next) != NULL) \ - (elm)->field.tqe_next->field.tqe_prev = \ - (elm)->field.tqe_prev; \ - else \ - (head)->tqh_last = (elm)->field.tqe_prev; \ - *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ - _Q_INVALIDATE((elm)->field.tqe_prev); \ - _Q_INVALIDATE((elm)->field.tqe_next); \ -} while (0) - -#define TAILQ_REPLACE(head, elm, elm2, field) do { \ - if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \ - (elm2)->field.tqe_next->field.tqe_prev = \ - &(elm2)->field.tqe_next; \ - else \ - (head)->tqh_last = &(elm2)->field.tqe_next; \ - (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \ - *(elm2)->field.tqe_prev = (elm2); \ - _Q_INVALIDATE((elm)->field.tqe_prev); \ - _Q_INVALIDATE((elm)->field.tqe_next); \ -} while (0) - -/* - * Circular queue definitions. - */ -#define CIRCLEQ_HEAD(name, type) \ -struct name { \ - struct type *cqh_first; /* first element */ \ - struct type *cqh_last; /* last element */ \ -} - -#define CIRCLEQ_HEAD_INITIALIZER(head) \ - { CIRCLEQ_END(&head), CIRCLEQ_END(&head) } - -#define CIRCLEQ_ENTRY(type) \ -struct { \ - struct type *cqe_next; /* next element */ \ - struct type *cqe_prev; /* previous element */ \ -} - -/* - * Circular queue access methods - */ -#define CIRCLEQ_FIRST(head) ((head)->cqh_first) -#define CIRCLEQ_LAST(head) ((head)->cqh_last) -#define CIRCLEQ_END(head) ((void *)(head)) -#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next) -#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev) -#define CIRCLEQ_EMPTY(head) \ - (CIRCLEQ_FIRST(head) == CIRCLEQ_END(head)) - -#define CIRCLEQ_FOREACH(var, head, field) \ - for((var) = CIRCLEQ_FIRST(head); \ - (var) != CIRCLEQ_END(head); \ - (var) = CIRCLEQ_NEXT(var, field)) - -#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \ - for((var) = CIRCLEQ_LAST(head); \ - (var) != CIRCLEQ_END(head); \ - (var) = CIRCLEQ_PREV(var, field)) - -/* - * Circular queue functions. - */ -#define CIRCLEQ_INIT(head) do { \ - (head)->cqh_first = CIRCLEQ_END(head); \ - (head)->cqh_last = CIRCLEQ_END(head); \ -} while (0) - -#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ - (elm)->field.cqe_next = (listelm)->field.cqe_next; \ - (elm)->field.cqe_prev = (listelm); \ - if ((listelm)->field.cqe_next == CIRCLEQ_END(head)) \ - (head)->cqh_last = (elm); \ - else \ - (listelm)->field.cqe_next->field.cqe_prev = (elm); \ - (listelm)->field.cqe_next = (elm); \ -} while (0) - -#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \ - (elm)->field.cqe_next = (listelm); \ - (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \ - if ((listelm)->field.cqe_prev == CIRCLEQ_END(head)) \ - (head)->cqh_first = (elm); \ - else \ - (listelm)->field.cqe_prev->field.cqe_next = (elm); \ - (listelm)->field.cqe_prev = (elm); \ -} while (0) - -#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \ - (elm)->field.cqe_next = (head)->cqh_first; \ - (elm)->field.cqe_prev = CIRCLEQ_END(head); \ - if ((head)->cqh_last == CIRCLEQ_END(head)) \ - (head)->cqh_last = (elm); \ - else \ - (head)->cqh_first->field.cqe_prev = (elm); \ - (head)->cqh_first = (elm); \ -} while (0) - -#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \ - (elm)->field.cqe_next = CIRCLEQ_END(head); \ - (elm)->field.cqe_prev = (head)->cqh_last; \ - if ((head)->cqh_first == CIRCLEQ_END(head)) \ - (head)->cqh_first = (elm); \ - else \ - (head)->cqh_last->field.cqe_next = (elm); \ - (head)->cqh_last = (elm); \ -} while (0) - -#define CIRCLEQ_REMOVE(head, elm, field) do { \ - if ((elm)->field.cqe_next == CIRCLEQ_END(head)) \ - (head)->cqh_last = (elm)->field.cqe_prev; \ - else \ - (elm)->field.cqe_next->field.cqe_prev = \ - (elm)->field.cqe_prev; \ - if ((elm)->field.cqe_prev == CIRCLEQ_END(head)) \ - (head)->cqh_first = (elm)->field.cqe_next; \ - else \ - (elm)->field.cqe_prev->field.cqe_next = \ - (elm)->field.cqe_next; \ - _Q_INVALIDATE((elm)->field.cqe_prev); \ - _Q_INVALIDATE((elm)->field.cqe_next); \ -} while (0) - -#define CIRCLEQ_REPLACE(head, elm, elm2, field) do { \ - if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == \ - CIRCLEQ_END(head)) \ - (head)->cqh_last = (elm2); \ - else \ - (elm2)->field.cqe_next->field.cqe_prev = (elm2); \ - if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == \ - CIRCLEQ_END(head)) \ - (head)->cqh_first = (elm2); \ - else \ - (elm2)->field.cqe_prev->field.cqe_next = (elm2); \ - _Q_INVALIDATE((elm)->field.cqe_prev); \ - _Q_INVALIDATE((elm)->field.cqe_next); \ -} while (0) - -#endif /* !_SYS_QUEUE_H_ */ diff --git a/src/commands.c b/src/commands.c new file mode 100644 index 00000000..8e3161cf --- /dev/null +++ b/src/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/src/debug.c b/src/debug.c new file mode 100644 index 00000000..cbdbd368 --- /dev/null +++ b/src/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/src/font.c b/src/font.c new file mode 100644 index 00000000..803ac0a6 --- /dev/null +++ b/src/font.c @@ -0,0 +1,39 @@ +/* + * Handles font loading + * + */ +#include +#include +#include +#include + +#include "data.h" +#include "util.h" + +i3Font *load_font(xcb_connection_t *c, const char *pattern) { + /* TODO: this function should be caching */ + i3Font *new = malloc(sizeof(i3Font)); + + xcb_list_fonts_with_info_cookie_t cookie = xcb_list_fonts_with_info(c, 1, strlen(pattern), pattern); + xcb_list_fonts_with_info_reply_t *reply = xcb_list_fonts_with_info_reply(c, cookie, NULL); + if (!reply) { + printf("Could not load font\n"); + exit(1); + } + + /* Oh my, this is so ugly :-(. Why can’t they just return a null-terminated + * string? That’s what abstraction layers are for. */ + char buffer[xcb_list_fonts_with_info_name_length(reply)+1]; + memset(buffer, 0, sizeof(buffer)); + memcpy(buffer, xcb_list_fonts_with_info_name(reply), sizeof(buffer)-1); + new->name = strdup(buffer); + new->pattern = strdup(pattern); + new->height = reply->font_ascent + reply->font_descent; + + /* Actually load the font */ + new->id = xcb_generate_id(c); + xcb_void_cookie_t font_cookie = xcb_open_font_checked(c, new->id, strlen(pattern), pattern); + check_error(c, font_cookie, "Could not open font"); + + return new; +} diff --git a/src/handlers.c b/src/handlers.c new file mode 100644 index 00000000..58640368 --- /dev/null +++ b/src/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/src/layout.c b/src/layout.c new file mode 100644 index 00000000..4a09e416 --- /dev/null +++ b/src/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/src/mainx.c b/src/mainx.c new file mode 100644 index 00000000..359e3f74 --- /dev/null +++ b/src/mainx.c @@ -0,0 +1,427 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#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" + +Display *xkbdpy; + +TAILQ_HEAD(bindings_head, Binding) bindings; +xcb_event_handlers_t evenths; + +static const int TOP = 20; +static const int LEFT = 5; +static const int BOTTOM = 5; +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) */ +char **environment; + +/* hm, xcb_wm wants us to implement this. */ +table_t *byChild = 0; +table_t *byParent = 0; +xcb_window_t root_win; + +char *pattern = "-misc-fixed-medium-r-normal--13-120-75-75-C-70-iso8859-1"; +int num_screens = 0; + +/* + * + * TODO: what exactly does this, what happens if we leave stuff out? + * + */ +void manage_window(xcb_property_handlers_t *prophs, xcb_connection_t *c, xcb_window_t window, window_attributes_t wa) +{ + printf("managing window.\n"); + xcb_drawable_t d = { window }; + xcb_get_geometry_cookie_t geomc; + xcb_get_geometry_reply_t *geom; + xcb_get_window_attributes_reply_t *attr = 0; + if(wa.tag == TAG_COOKIE) + { + attr = xcb_get_window_attributes_reply(c, wa.u.cookie, 0); + if(!attr) + return; + if(attr->map_state != XCB_MAP_STATE_VIEWABLE) + { + printf("Window 0x%08x is not mapped. Ignoring.\n", window); + free(attr); + return; + } + wa.tag = TAG_VALUE; + wa.u.override_redirect = attr->override_redirect; + } + if(!wa.u.override_redirect && table_get(byChild, window)) + { + printf("Window 0x%08x already managed. Ignoring.\n", window); + free(attr); + return; + } + if(wa.u.override_redirect) + { + printf("Window 0x%08x has override-redirect set. Ignoring.\n", window); + free(attr); + return; + } + geomc = xcb_get_geometry(c, d); + if(!attr) + { + wa.tag = TAG_COOKIE; + wa.u.cookie = xcb_get_window_attributes(c, window); + attr = xcb_get_window_attributes_reply(c, wa.u.cookie, 0); + } + geom = xcb_get_geometry_reply(c, geomc, 0); + if(attr && geom) + { + reparent_window(c, window, attr->visual, geom->root, geom->depth, geom->x, geom->y, geom->width, geom->height); + xcb_property_changed(prophs, XCB_PROPERTY_NEW_VALUE, window, WM_NAME); + } + free(attr); + free(geom); +} + +/* + * Let’s own this window… + * + */ +void reparent_window(xcb_connection_t *conn, xcb_window_t child, + xcb_visualid_t visual, xcb_window_t root, uint8_t depth, + int16_t x, int16_t y, uint16_t width, uint16_t height) { + + Client *new = table_get(byChild, child); + if (new == NULL) { + printf("oh, it's new\n"); + new = calloc(sizeof(Client), 1); + new->x = -1; + new->y = -1; + } + uint32_t mask = 0; + uint32_t values[3]; + + /* Insert into the currently active container */ + CIRCLEQ_INSERT_TAIL(&(CUR_CELL->clients), new, clients); + + printf("currently_focused = %p\n", new); + CUR_CELL->currently_focused = new; + new->container = CUR_CELL; + + new->frame = xcb_generate_id(conn); + new->child = child; + new->width = width; + new->height = height; + + /* Don’t generate events for our new window, it should *not* be managed */ + mask |= XCB_CW_OVERRIDE_REDIRECT; + values[0] = 1; + + /* We want to know when… */ + mask |= XCB_CW_EVENT_MASK; + values[1] = XCB_EVENT_MASK_BUTTON_PRESS | /* …mouse is pressed/released */ + XCB_EVENT_MASK_BUTTON_RELEASE | + XCB_EVENT_MASK_EXPOSURE | /* …our window needs to be redrawn */ + XCB_EVENT_MASK_ENTER_WINDOW /* …user moves cursor inside our window */; + + printf("Reparenting 0x%08x under 0x%08x.\n", child, new->frame); + + /* Yo dawg, I heard you like windows, so I create a window around your window… */ + xcb_create_window(conn, + depth, + new->frame, + root, + x, + y, + width + LEFT + RIGHT, + height + TOP + BOTTOM, + /* border_width */ 0, + XCB_WINDOW_CLASS_INPUT_OUTPUT, + visual, + mask, + values); + xcb_change_save_set(conn, XCB_SET_MODE_INSERT, child); + + /* Map the window on the screen (= make it visible) */ + xcb_map_window(conn, new->frame); + + /* Generate a graphics context for the titlebar */ + new->titlegc = xcb_generate_id(conn); + xcb_create_gc(conn, new->titlegc, new->frame, 0, 0); + + /* Draw decorations */ + decorate_window(conn, new); + + /* Put our data structure (Client) into the table */ + table_put(byParent, new->frame, new); + table_put(byChild, child, new); + + /* Moves the original window into the new frame we've created for it */ + i3Font *font = load_font(conn, pattern); + xcb_reparent_window(conn, child, new->frame, 0, font->height); + + /* We are interested in property changes */ + mask = XCB_CW_EVENT_MASK; + values[0] = XCB_EVENT_MASK_PROPERTY_CHANGE | + XCB_EVENT_MASK_STRUCTURE_NOTIFY | + XCB_EVENT_MASK_ENTER_WINDOW | + XCB_EVENT_MASK_BUTTON_PRESS; + xcb_change_window_attributes(conn, child, mask, values); + + /* We need to grab the mouse buttons for click to focus */ + xcb_grab_button(conn, false, child, XCB_EVENT_MASK_BUTTON_PRESS, + XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, root, XCB_NONE, 1 /* left mouse button */, + XCB_BUTTON_MASK_ANY /* don’t filter for any modifiers */); + + /* Focus the new window */ + xcb_set_input_focus(conn, XCB_INPUT_FOCUS_NONE, new->child, XCB_CURRENT_TIME); + + render_layout(conn); +} + +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; + int i, len; + xcb_window_t *children; + xcb_get_window_attributes_cookie_t *cookies; + + wintree = xcb_query_tree(c, root); + rep = xcb_query_tree_reply(c, wintree, 0); + if(!rep) + return; + len = xcb_query_tree_children_length(rep); + cookies = malloc(len * sizeof(*cookies)); + if(!cookies) + { + free(rep); + return; + } + children = xcb_query_tree_children(rep); + for(i = 0; i < len; ++i) + cookies[i] = xcb_get_window_attributes(c, children[i]); + for(i = 0; i < len; ++i) + { + window_attributes_t wa = { TAG_COOKIE, { cookies[i] } }; + manage_window(prophs, c, children[i], wa); + } + free(rep); +} + +static void initialize_xinerama(xcb_connection_t *conn) { + xcb_xinerama_query_screens_reply_t *reply; + xcb_xinerama_screen_info_t *screen_info; + int screen; + + if (!xcb_get_extension_data(conn, &xcb_xinerama_id)->present) { + printf("Xinerama extension not found, disabling.\n"); + return; + } + + if (!xcb_xinerama_is_active_reply(conn, xcb_xinerama_is_active(conn), NULL)->state) { + printf("Xinerama is not active (in your X-Server), disabling.\n"); + return; + } + + + reply = xcb_xinerama_query_screens_reply(conn, xcb_xinerama_query_screens_unchecked(conn), NULL); + /* TODO: error check */ + screen_info = xcb_xinerama_query_screens_screen_info(reply); + + num_screens = xcb_xinerama_query_screens_screen_info_length(reply); + + /* Just go through each workspace and associate as many screens as we can. */ + for (screen = 0; screen < num_screens; screen++) { + workspaces[screen].x = screen_info[screen].x_org; + workspaces[screen].y = screen_info[screen].y_org; + workspaces[screen].width = screen_info[screen].width; + workspaces[screen].height = screen_info[screen].height; + workspaces[screen].screen_num = screen; + + printf("found Xinerama screen: %d x %d at %d x %d\n", + screen_info[screen].width, screen_info[screen].height, + screen_info[screen].x_org, screen_info[screen].y_org); + } + + free(screen_info); +} + +int main(int argc, char *argv[], char *env[]) { + int i, e = 0; + + for (i = 0; (env[i] != NULL); i++) + if (strncmp(env[i], "LC_", strlen("LC_")) == 0 || + strncmp(env[i], "LANG=", strlen("LANG=")) == 0 || + strncmp(env[i], "DISPLAY=", strlen("DISPLAY=")) == 0) { + printf("Passing environment \"%s\"\n", env[i]); + environment = realloc(environment, sizeof(char*) * ++e); + environment[e-1] = env[i]; + } + + /* environment has to be NULL-terminated */ + environment = realloc(environment, sizeof(char*) * ++e); + environment[e-1] = NULL; + + init_table(); + + xcb_connection_t *c; + xcb_property_handlers_t prophs; + xcb_window_t root; + + int screens; + + memset(&evenths, 0, sizeof(xcb_event_handlers_t)); + memset(&prophs, 0, sizeof(xcb_property_handlers_t)); + + byChild = alloc_table(); + byParent = alloc_table(); + + TAILQ_INIT(&bindings); + + c = xcb_connect(NULL, &screens); + + printf("x screen is %d\n", screens); + + /* TODO: this has to be more beautiful somewhen */ + int major, minor, error; + + major = XkbMajorVersion; + minor = XkbMinorVersion; + + int evBase, errBase; + + if ((xkbdpy = XkbOpenDisplay(getenv("DISPLAY"), &evBase, &errBase, &major, &minor, &error)) == NULL) { + fprintf(stderr, "XkbOpenDisplay() failed\n"); + return 1; + } + + int i1; + if (!XkbQueryExtension(xkbdpy,&i1,&evBase,&errBase,&major,&minor)) { + fprintf(stderr, "XKB not supported by X-server\n"); + return 1; + } + /* end of ugliness */ + + xcb_event_handlers_init(c, &evenths); + for(i = 2; i < 128; ++i) + 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)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, handle_expose_event, 0); + + /* Key presses/releases are pretty obvious, I think */ + xcb_event_set_key_press_handler(&evenths, handle_key_press, 0); + xcb_event_set_key_release_handler(&evenths, handle_key_release, 0); + + /* Enter window = user moved his mouse over the window */ + xcb_event_set_enter_notify_handler(&evenths, handle_enter_notify, 0); + + /* Button press = user pushed a mouse button over one of our windows */ + xcb_event_set_button_press_handler(&evenths, handle_button_press, 0); + + xcb_event_set_unmap_notify_handler(&evenths, handle_unmap_notify_event, 0); + + xcb_property_handlers_init(&prophs, &evenths); + xcb_event_set_map_notify_handler(&evenths, handle_map_notify_event, &prophs); + + xcb_watch_wm_name(&prophs, 128, handle_windowname_change, 0); + + root = xcb_aux_get_screen(c, screens)->root; + root_win = root; + + uint32_t mask = XCB_CW_EVENT_MASK; + uint32_t values[] = { XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_PROPERTY_CHANGE }; + xcb_change_window_attributes(c, root, mask, values); + + #define BIND(key, modifier, cmd) { \ + Binding *new = malloc(sizeof(Binding)); \ + new->keycode = key; \ + new->mods = modifier; \ + new->command = cmd; \ + TAILQ_INSERT_TAIL(&bindings, new, bindings); \ + } + + /* 38 = 'a' */ + BIND(38, BIND_MODE_SWITCH, "foo"); + + BIND(30, 0, "exec /usr/pkg/bin/urxvt"); + + BIND(44, BIND_MOD_1, "h"); + BIND(45, BIND_MOD_1, "j"); + BIND(46, BIND_MOD_1, "k"); + BIND(47, BIND_MOD_1, "l"); + + BIND(44, BIND_MOD_1 | BIND_CONTROL, "sh"); + BIND(45, BIND_MOD_1 | BIND_CONTROL, "sj"); + BIND(46, BIND_MOD_1 | BIND_CONTROL, "sk"); + BIND(47, BIND_MOD_1 | BIND_CONTROL, "sl"); + + BIND(44, BIND_MOD_1 | BIND_SHIFT, "mh"); + BIND(45, BIND_MOD_1 | BIND_SHIFT, "mj"); + BIND(46, BIND_MOD_1 | BIND_SHIFT, "mk"); + BIND(47, BIND_MOD_1 | BIND_SHIFT, "ml"); + + BIND(10, BIND_MOD_1 , "1"); + BIND(11, BIND_MOD_1 , "2"); + BIND(12, BIND_MOD_1 , "3"); + BIND(13, BIND_MOD_1 , "4"); + BIND(14, BIND_MOD_1 , "5"); + BIND(15, BIND_MOD_1 , "6"); + BIND(16, BIND_MOD_1 , "7"); + BIND(17, BIND_MOD_1 , "8"); + BIND(18, BIND_MOD_1 , "9"); + BIND(19, BIND_MOD_1 , "0"); + + Binding *bind; + TAILQ_FOREACH(bind, &bindings, bindings) { + printf("Grabbing %d\n", bind->keycode); + if (bind->mods & BIND_MODE_SWITCH) + xcb_grab_key(c, 0, root, 0, bind->keycode, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_SYNC); + else xcb_grab_key(c, 0, root, bind->mods, bind->keycode, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC); + } + + /* check for Xinerama */ + printf("Checking for Xinerama...\n"); + initialize_xinerama(c); + + start_application(TERMINAL, NULL); + + xcb_flush(c); + + manage_existing_windows(c, &prophs, root); + + xcb_event_wait_for_event_loop(&evenths); + + /* not reached */ + return 0; +} diff --git a/src/table.c b/src/table.c new file mode 100644 index 00000000..33d1b4fe --- /dev/null +++ b/src/table.c @@ -0,0 +1,83 @@ +/* + * This file provides functions for easier accessing of _the_ table + * + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "data.h" +#include "table.h" + +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 + * + */ +void init_table() { + int i; + memset(workspaces, 0, sizeof(workspaces)); + + for (i = 0; i < 10; i++) { + expand_table_cols(&(workspaces[i])); + expand_table_rows(&(workspaces[i])); + } +} + +static void new_container(Container **container) { + Container *new; + new = *container = calloc(sizeof(Container), 1); + CIRCLEQ_INIT(&(new->clients)); + new->colspan = 1; + new->rowspan = 1; +} + +/* + * Add one row to the table + * + */ +void expand_table_rows(Workspace *workspace) { + int c; + + workspace->rows++; + + for (c = 0; c < workspace->cols; c++) { + workspace->table[c] = realloc(workspace->table[c], sizeof(Container*) * workspace->rows); + new_container(&(workspace->table[c][workspace->rows-1])); + } +} + +/* + * Add one column to the table + * + */ +void expand_table_cols(Workspace *workspace) { + int c; + + workspace->cols++; + + workspace->table = realloc(workspace->table, sizeof(Container**) * workspace->cols); + workspace->table[workspace->cols-1] = calloc(sizeof(Container*) * workspace->rows, 1); + for (c = 0; c < workspace->rows; c++) + new_container(&(workspace->table[workspace->cols-1][c])); +} + +/* + * Performs simple bounds checking for the given column/row + * + */ +bool cell_exists(int col, int row) { + return (col >= 0 && col < c_ws->rows) && + (row >= 0 && row < c_ws->cols); +} diff --git a/src/util.c b/src/util.c new file mode 100644 index 00000000..1263cdc4 --- /dev/null +++ b/src/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/src/xcb.c b/src/xcb.c new file mode 100644 index 00000000..b445437a --- /dev/null +++ b/src/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/table.c b/table.c deleted file mode 100644 index 33d1b4fe..00000000 --- a/table.c +++ /dev/null @@ -1,83 +0,0 @@ -/* - * This file provides functions for easier accessing of _the_ table - * - */ -#include -#include -#include -#include -#include -#include -#include -#include - -#include "data.h" -#include "table.h" - -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 - * - */ -void init_table() { - int i; - memset(workspaces, 0, sizeof(workspaces)); - - for (i = 0; i < 10; i++) { - expand_table_cols(&(workspaces[i])); - expand_table_rows(&(workspaces[i])); - } -} - -static void new_container(Container **container) { - Container *new; - new = *container = calloc(sizeof(Container), 1); - CIRCLEQ_INIT(&(new->clients)); - new->colspan = 1; - new->rowspan = 1; -} - -/* - * Add one row to the table - * - */ -void expand_table_rows(Workspace *workspace) { - int c; - - workspace->rows++; - - for (c = 0; c < workspace->cols; c++) { - workspace->table[c] = realloc(workspace->table[c], sizeof(Container*) * workspace->rows); - new_container(&(workspace->table[c][workspace->rows-1])); - } -} - -/* - * Add one column to the table - * - */ -void expand_table_cols(Workspace *workspace) { - int c; - - workspace->cols++; - - workspace->table = realloc(workspace->table, sizeof(Container**) * workspace->cols); - workspace->table[workspace->cols-1] = calloc(sizeof(Container*) * workspace->rows, 1); - for (c = 0; c < workspace->rows; c++) - new_container(&(workspace->table[workspace->cols-1][c])); -} - -/* - * Performs simple bounds checking for the given column/row - * - */ -bool cell_exists(int col, int row) { - return (col >= 0 && col < c_ws->rows) && - (row >= 0 && row < c_ws->cols); -} diff --git a/table.h b/table.h deleted file mode 100644 index 1d6ca8f7..00000000 --- a/table.h +++ /dev/null @@ -1,20 +0,0 @@ -#include -#include "data.h" - -#ifndef _TABLE_H -#define _TABLE_H - -#define CUR_TABLE (c_ws->table) -#define CUR_CELL (CUR_TABLE[current_col][current_row]) - -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); -void expand_table_cols(Workspace *workspace); -bool cell_exists(int col, int row); - -#endif diff --git a/util.c b/util.c deleted file mode 100644 index 1263cdc4..00000000 --- a/util.c +++ /dev/null @@ -1,38 +0,0 @@ -#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 deleted file mode 100644 index 9eb2a447..00000000 --- a/util.h +++ /dev/null @@ -1,7 +0,0 @@ -#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 deleted file mode 100644 index b445437a..00000000 --- a/xcb.c +++ /dev/null @@ -1,42 +0,0 @@ -#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 deleted file mode 100644 index c8224ef9..00000000 --- a/xcb.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef _XCB_H -#define _XCB_H - -uint32_t get_colorpixel(xcb_connection_t *conn, xcb_window_t window, char *hex); - -#endif