TODO list, in order of importance:
- * freely resizable (e.g. using your mouse, for now) percentage of rows/cols
* document stuff!
* more documentation!
* debian package
SLIST_ENTRY(Client) dock_clients;
};
+/*
+ * Contains data for the windows needed to draw the titlebars on in stacking mode
+ *
+ */
+struct Stack_Window {
+ xcb_window_t window;
+ xcb_gcontext_t gc;
+ uint32_t width, height;
+
+ /* Backpointer to the container this stack window is in */
+ Container *container;
+
+ SLIST_ENTRY(Stack_Window) stack_windows;
+};
+
/*
* A container is either in default or stacking mode. It sits inside the table.
*
float width_factor;
float height_factor;
+ /* When in stacking mode, we draw the titlebars of each client onto a separate window */
+ struct Stack_Window stack_win;
+
/* Backpointer to the workspace this container is in */
Workspace *workspace;
extern Display *xkbdpy;
extern TAILQ_HEAD(bindings_head, Binding) bindings;
+extern SLIST_HEAD(stack_wins_head, Stack_Window) stack_wins;
extern xcb_event_handlers_t evenths;
extern char *pattern;
extern int num_screens;
#define _LAYOUT_H
Rect get_unoccupied_space(Workspace *workspace);
-void decorate_window(xcb_connection_t *conn, Client *client);
+void decorate_window(xcb_connection_t *conn, Client *client, xcb_drawable_t drawable, xcb_gcontext_t gc, int offset);
+void render_container(xcb_connection_t *connection, Container *container);
void render_layout(xcb_connection_t *conn);
#endif
void start_application(const char *command);
void check_error(xcb_connection_t *connection, xcb_void_cookie_t cookie, char *err_message);
void set_focus(xcb_connection_t *conn, Client *client);
+void switch_layout_mode(xcb_connection_t *conn, Container *container, int mode);
void warp_pointer_into(xcb_connection_t *connection, Client *client);
void toggle_fullscreen(xcb_connection_t *conn, Client *client);
uint32_t get_colorpixel(xcb_connection_t *conn, xcb_window_t window, char *hex);
xcb_window_t create_window(xcb_connection_t *conn, Rect r, uint16_t window_class, uint32_t mask, uint32_t *values);
+void xcb_change_gc_single(xcb_connection_t *conn, xcb_gcontext_t gc, uint32_t mask, uint32_t value);
+void xcb_draw_line(xcb_connection_t *conn, xcb_drawable_t drawable, xcb_gcontext_t gc,
+ uint32_t colorpixel, uint32_t x, uint32_t y, uint32_t to_x, uint32_t to_y);
#endif
return;
}
+ /* Is it just 's' for stacking or 'd' for default? */
+ if ((command[0] == 's' || command[0] == 'd') && (command[1] == '\0')) {
+ printf("Switching mode for current container\n");
+ switch_layout_mode(conn, CUR_CELL, (command[0] == 's' ? MODE_STACK : MODE_DEFAULT));
+ return;
+ }
+
/* Is it a <with>? */
if (command[0] == 'w') {
/* TODO: implement */
*
*/
int handle_enter_notify(void *ignored, xcb_connection_t *conn, xcb_enter_notify_event_t *event) {
- printf("enter_notify\n");
+ printf("enter_notify for %08x\n", event->event);
/* This was either a focus for a client’s parent (= titlebar)… */
Client *client = table_get(byParent, event->event);
return 1;
}
+ /* When in stacking, enter notifications are ignored. Focus will be changed via keyboard only. */
+ if (client->container->mode == MODE_STACK)
+ return 1;
+
set_focus(conn, client);
return 1;
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);
+ decorate_window(conn, client, client->frame, client->titlegc, 0);
xcb_flush(conn);
return 1;
*
*/
int handle_expose_event(void *data, xcb_connection_t *conn, xcb_expose_event_t *e) {
+ printf("got expose_event\n");
+ /* e->count is the number of minimum remaining expose events for this window, so we
+ skip all events but the last one */
+ if (e->count != 0)
+ return 1;
+
Client *client = table_get(byParent, e->window);
- if(!client || e->count != 0)
+ if (client == NULL) {
+ /* There was no client in the table, so this is probably an expose event for
+ one of our stack_windows. */
+ struct Stack_Window *stack_win;
+ SLIST_FOREACH(stack_win, &stack_wins, stack_windows)
+ if (stack_win->window == e->window) {
+ render_container(conn, stack_win->container);
+ return 1;
+ }
return 1;
+ }
+
printf("handle_expose_event()\n");
- decorate_window(conn, client);
+ if (client->container->mode != MODE_STACK)
+ decorate_window(conn, client, client->frame, client->titlegc, 0);
return 1;
}
#include <string.h>
#include <stdlib.h>
#include <xcb/xcb.h>
+#include <assert.h>
#include "font.h"
#include "i3.h"
* (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];
+void decorate_window(xcb_connection_t *conn, Client *client, xcb_drawable_t drawable, xcb_gcontext_t gc, int offset) {
i3Font *font = load_font(conn, pattern);
uint32_t background_color,
text_color,
- 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_change_gc_single(conn, gc, XCB_GC_FOREGROUND, background_color);
+ printf("drawing at offset %d\n", offset);
- xcb_rectangle_t rect = {0, 0, client->rect.width, client->rect.height};
- xcb_poly_fill_rectangle(conn, client->frame, client->titlegc, 1, &rect);
+ xcb_rectangle_t rect = {0, offset, client->rect.width, offset + client->rect.height};
+ xcb_poly_fill_rectangle(conn, drawable, gc, 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->rect.width, 0);
- DRAW_LINE(border_color, 2, font->height + 3, 2 + client->rect.width, font->height + 3);
+ xcb_draw_line(conn, drawable, gc, border_color, 2, offset, client->rect.width, offset);
+ xcb_draw_line(conn, drawable, gc, border_color, 2, offset + font->height + 3,
+ 2 + client->rect.width, offset + 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);
+ uint32_t mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND | XCB_GC_FONT;
+ uint32_t values[] = { text_color, background_color, font->id };
+ xcb_change_gc(conn, gc, 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);
+ printf("label is %s\n", label);
+ xcb_void_cookie_t text_cookie = xcb_image_text_8_checked(conn, strlen(label), drawable,
+ gc, 3 /* X */, offset + font->height /* Y = baseline of font */, label);
check_error(conn, text_cookie, "Could not draw client's title");
free(label);
}
static void resize_client(xcb_connection_t *connection, Client *client) {
i3Font *font = load_font(connection, pattern);
- printf("resizing client to %d x %d\n", client->rect.width, client->rect.height);
+ printf("resizing client \"%s\" to %d x %d\n", client->name, client->rect.width, client->rect.height);
xcb_configure_window(connection, client->frame,
XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT,
&(client->rect.width));
XCB_CONFIG_WINDOW_WIDTH |
XCB_CONFIG_WINDOW_HEIGHT;
Rect rect;
- if (client->titlebar_position == TITLEBAR_OFF) {
+ if (client->titlebar_position == TITLEBAR_OFF ||
+ client->container->mode == MODE_STACK) {
rect.x = 0;
rect.y = 0;
rect.width = client->rect.width;
xcb_configure_window(connection, client->child, mask, &(rect.x));
}
-static void render_container(xcb_connection_t *connection, Container *container) {
+/*
+ * Renders the given container. Is called by render_layout() or individually (for example
+ * when focus changes in a stacking container)
+ *
+ */
+void render_container(xcb_connection_t *connection, Container *container) {
Client *client;
+ int num_clients = 0, current_client = 0;
+
+ if (container->currently_focused == NULL)
+ return;
+
+ CIRCLEQ_FOREACH(client, &(container->clients), clients)
+ num_clients++;
if (container->mode == MODE_DEFAULT) {
- int num_clients = 0;
- CIRCLEQ_FOREACH(client, &(container->clients), clients)
- if (!client->dock)
- num_clients++;
printf("got %d clients in this default container.\n", num_clients);
-
- int current_client = 0;
CIRCLEQ_FOREACH(client, &(container->clients), clients) {
/* Check if we changed client->x or client->y by updating it.
* Note the bitwise OR instead of logical OR to force evaluation of both statements */
current_client++;
}
} else {
- /* TODO: Implement stacking */
+ i3Font *font = load_font(connection, pattern);
+ int decoration_height = (font->height + 2 + 2);
+ struct Stack_Window *stack_win = &(container->stack_win);
+
+ /* Check if we need to reconfigure our stack title window */
+ if ((stack_win->width != (stack_win->width = container->width)) |
+ (stack_win->height != (stack_win->height = decoration_height * num_clients)))
+ xcb_configure_window(connection, stack_win->window,
+ XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT, &(stack_win->width));
+
+ /* All clients are repositioned */
+ CIRCLEQ_FOREACH(client, &(container->clients), clients) {
+ /* Check if we changed client->x or client->y by updating it.
+ * Note the bitwise OR instead of logical OR to force evaluation of both statements */
+ if (client->force_reconfigure |
+ (client->rect.x != (client->rect.x = container->x)) |
+ (client->rect.y != (client->rect.y = container->y + (decoration_height * num_clients))))
+ reposition_client(connection, client);
+
+ if (client->force_reconfigure |
+ (client->rect.width != (client->rect.width = container->width)) |
+ (client->rect.height !=
+ (client->rect.height = container->height - (decoration_height * num_clients))))
+ resize_client(connection, client);
+
+ client->force_reconfigure = false;
+
+ decorate_window(connection, client, stack_win->window, stack_win->gc,
+ current_client * decoration_height);
+ current_client++;
+ }
+
+ /* Raise the focused window */
+ uint32_t values[] = { XCB_STACK_MODE_ABOVE };
+ xcb_configure_window(connection, container->currently_focused->frame,
+ XCB_CONFIG_WINDOW_STACK_MODE, values);
}
}
else container->height = get_unoccupied_y(r_ws, cols) * container->height_factor;
container->height *= container->rowspan;
- /* Render it */
+ /* Render the container if it is not empty */
render_container(connection, container);
xoffset[rows] += container->width;
Display *xkbdpy;
TAILQ_HEAD(bindings_head, Binding) bindings = TAILQ_HEAD_INITIALIZER(bindings);
+SLIST_HEAD(stack_wins_head, Stack_Window) stack_wins = SLIST_HEAD_INITIALIZER(stack_wins);
xcb_event_handlers_t evenths;
xcb_window_t root_win;
xcb_atom_t atoms[9];
-
char *pattern = "-misc-fixed-medium-r-normal--13-120-75-75-C-70-iso8859-1";
int num_screens = 0;
BIND(41, BIND_MOD_1, "f");
+ BIND(43, BIND_MOD_1, "s");
+ BIND(26, BIND_MOD_1, "d");
+
BIND(44, BIND_MOD_1, "h");
BIND(45, BIND_MOD_1, "j");
BIND(46, BIND_MOD_1, "k");
#include "table.h"
#include "layout.h"
#include "util.h"
+#include "xcb.h"
int min(int a, int b) {
return (a < b ? a : b);
//xcb_warp_pointer(conn, XCB_NONE, client->child, 0, 0, 0, 0, 10, 10);
/* Update last/current client’s titlebar */
if (old_client != NULL)
- decorate_window(conn, old_client);
- decorate_window(conn, client);
+ decorate_window(conn, old_client, old_client->frame, old_client->titlegc, 0);
+ decorate_window(conn, client, client->frame, client->titlegc, 0);
+
+ /* If we’re in stacking mode, we render the container to update changes in the title
+ bars and to raise the focused client */
+ if (client->container->mode == MODE_STACK)
+ render_container(conn, client->container);
+
xcb_flush(conn);
}
+/*
+ * Switches the layout of the given container taking care of the necessary house-keeping
+ *
+ */
+void switch_layout_mode(xcb_connection_t *conn, Container *container, int mode) {
+ if (mode == MODE_STACK) {
+ /* When entering stacking mode, we need to open a window on which we can draw the
+ title bars of the clients */
+ Rect rect = {container->x, container->y, container->width, 15 /* TODO: exact */ };
+
+ /* Don’t generate events for our new window, it should *not* be managed */
+ uint32_t mask = 0;
+ uint32_t values[2];
+
+ 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 */
+ XCB_EVENT_MASK_EXPOSURE; /* …our window needs to be redrawn */
+
+ struct Stack_Window *stack_win = &(container->stack_win);
+ stack_win->window = create_window(conn, rect, XCB_WINDOW_CLASS_INPUT_OUTPUT, mask, values);
+
+ /* Generate a graphics context for the titlebar */
+ stack_win->gc = xcb_generate_id(conn);
+ xcb_create_gc(conn, stack_win->gc, stack_win->window, 0, 0);
+
+ stack_win->container = container;
+
+ SLIST_INSERT_HEAD(&stack_wins, stack_win, stack_windows);
+ } else {
+ if (container->mode == MODE_STACK) {
+ /* When going out of stacking mode, we need to close the window */
+ struct Stack_Window *stack_win = &(container->stack_win);
+
+ SLIST_REMOVE(&stack_wins, stack_win, Stack_Window, stack_windows);
+
+ xcb_free_gc(conn, stack_win->gc);
+ xcb_destroy_window(conn, stack_win->window);
+
+ stack_win->width = -1;
+ stack_win->height = -1;
+ }
+ }
+ container->mode = mode;
+
+ /* Force reconfiguration of each client */
+ Client *client;
+
+ CIRCLEQ_FOREACH(client, &(container->clients), clients)
+ client->force_reconfigure = true;
+
+ render_layout(conn);
+}
+
/*
* Warps the pointer into the given client (in the middle of it, to be specific), therefore
* selecting it
return result;
}
+
+/*
+ * Changes a single value in the graphic context (so one doesn’t have to define an array of values)
+ *
+ */
+void xcb_change_gc_single(xcb_connection_t *conn, xcb_gcontext_t gc, uint32_t mask, uint32_t value) {
+ xcb_change_gc(conn, gc, mask, &value);
+}
+
+/*
+ * Draws a line from x,y to to_x,to_y using the given color
+ *
+ */
+void xcb_draw_line(xcb_connection_t *conn, xcb_drawable_t drawable, xcb_gcontext_t gc,
+ uint32_t colorpixel, uint32_t x, uint32_t y, uint32_t to_x, uint32_t to_y) {
+ xcb_change_gc_single(conn, gc, XCB_GC_FOREGROUND, colorpixel);
+ xcb_point_t points[] = {{x, y}, {to_x, to_y}};
+ xcb_poly_line(conn, XCB_COORD_MODE_ORIGIN, drawable, gc, 2, points);
+}