Implement stacking

next
Michael Stapelberg 2009-02-24 00:30:04 +01:00
parent 6e81d1c5e4
commit 0e3a378c39
12 changed files with 215 additions and 51 deletions

1
TODO
View File

@ -1,6 +1,5 @@
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

View File

@ -164,6 +164,21 @@ struct Client {
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.
*
@ -186,6 +201,9 @@ struct Container {
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;

View File

@ -20,6 +20,7 @@
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;

View File

@ -14,7 +14,8 @@
#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

View File

@ -30,6 +30,7 @@ char *sstrdup(const char *str);
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);

View File

@ -29,5 +29,8 @@ enum { _NET_SUPPORTED = 0,
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

View File

@ -343,6 +343,13 @@ void parse_command(xcb_connection_t *conn, const char *command) {
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 */

View File

@ -89,7 +89,7 @@ int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press_event_
*
*/
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 clients parent (= titlebar)… */
Client *client = table_get(byParent, event->event);
@ -110,6 +110,10 @@ int handle_enter_notify(void *ignored, xcb_connection_t *conn, xcb_enter_notify_
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;
@ -354,7 +358,7 @@ int handle_windowname_change(void *data, xcb_connection_t *conn, uint8_t state,
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;
@ -365,11 +369,28 @@ int handle_windowname_change(void *data, xcb_connection_t *conn, uint8_t state,
*
*/
int handle_expose_event(void *data, xcb_connection_t *conn, xcb_expose_event_t *e) {
Client *client = table_get(byParent, e->window);
if(!client || e->count != 0)
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 == 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;
}

View File

@ -14,6 +14,7 @@
#include <string.h>
#include <stdlib.h>
#include <xcb/xcb.h>
#include <assert.h>
#include "font.h"
#include "i3.h"
@ -81,9 +82,7 @@ int get_unoccupied_y(Workspace *workspace, int col) {
* (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,
@ -107,45 +106,31 @@ void decorate_window(xcb_connection_t *conn, Client *client) {
- Draw a rect around the whole client in background_color
- Draw two lines in a lighter color
- Draw the windows 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);
}
@ -169,7 +154,7 @@ static void reposition_client(xcb_connection_t *connection, Client *client) {
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));
@ -182,7 +167,8 @@ static void resize_client(xcb_connection_t *connection, Client *client) {
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;
@ -199,17 +185,23 @@ static void resize_client(xcb_connection_t *connection, Client *client) {
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 */
@ -233,7 +225,42 @@ static void render_container(xcb_connection_t *connection, Container *container)
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);
}
}
@ -308,7 +335,7 @@ void render_layout(xcb_connection_t *connection) {
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;

View File

@ -47,12 +47,12 @@
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;
@ -393,6 +393,9 @@ int main(int argc, char *argv[], char *env[]) {
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");

View File

@ -23,6 +23,7 @@
#include "table.h"
#include "layout.h"
#include "util.h"
#include "xcb.h"
int min(int a, int b) {
return (a < b ? a : b);
@ -138,11 +139,74 @@ void set_focus(xcb_connection_t *conn, Client *client) {
//xcb_warp_pointer(conn, XCB_NONE, client->child, 0, 0, 0, 0, 10, 10);
/* Update last/current clients 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 were 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 */ };
/* Dont 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

View File

@ -82,3 +82,22 @@ xcb_window_t create_window(xcb_connection_t *conn, Rect dims, uint16_t window_cl
return result;
}
/*
* Changes a single value in the graphic context (so one doesnt 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);
}