*
*/
#include <xcb/xcb.h>
+#include <stdbool.h>
#ifndef _DATA_H
#define _DATA_H
int current_row;
int current_col;
+ Client *fullscreen_client;
+
/* This is a two-dimensional dynamic array of Container-pointers. I’ve always wanted
* to be a three-star programmer :) */
Container ***table;
char *name;
int name_len;
+ /* fullscreen is pretty obvious */
+ bool fullscreen;
+
+ /* After leaving fullscreen mode, a client needs to be reconfigured (configuration =
+ setting X, Y, width and height). By setting the force_reconfigure flag, render_layout()
+ will reconfigure the client. */
+ bool force_reconfigure;
+
/* 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 */
int width;
int height;
+ /* Backpointer to the workspace this container is in */
+ Workspace *workspace;
+
/* 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;
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);
+int handle_client_message(void *data, xcb_connection_t *conn, xcb_client_message_event_t *event);
#endif
extern xcb_event_handlers_t evenths;
extern char *pattern;
extern int num_screens;
+extern xcb_atom_t atoms[6];
#endif
#ifndef _XCB_H
#define _XCB_H
+#define _NET_WM_STATE_REMOVE 0
+#define _NET_WM_STATE_ADD 1
+#define _NET_WM_STATE_TOGGLE 2
+
+enum { _NET_SUPPORTED = 0,
+ _NET_SUPPORTING_WM_CHECK = 1,
+ _NET_WM_NAME = 2,
+ _NET_WM_STATE_FULLSCREEN = 3,
+ _NET_WM_STATE = 4,
+ UTF8_STRING = 5
+};
+
uint32_t get_colorpixel(xcb_connection_t *conn, xcb_window_t window, char *hex);
#endif
#include "commands.h"
#include "data.h"
#include "font.h"
+#include "xcb.h"
static void set_focus(xcb_connection_t *conn, Client *client) {
/* Update container */
xcb_flush(conn);
}
+static void toggle_fullscreen(xcb_connection_t *conn, Client *client) {
+ c_ws->fullscreen_client = (client->fullscreen ? NULL : client);
+
+ client->fullscreen = !client->fullscreen;
+
+ if (client->fullscreen) {
+ printf("Entering fullscreen mode...\n");
+ Workspace *workspace = client->container->workspace;
+ /* We just entered fullscreen mode, let’s configure the window */
+ uint32_t mask = XCB_CONFIG_WINDOW_X |
+ XCB_CONFIG_WINDOW_Y |
+ XCB_CONFIG_WINDOW_WIDTH |
+ XCB_CONFIG_WINDOW_HEIGHT;
+ uint32_t values[4] = {workspace->x,
+ workspace->y,
+ workspace->width,
+ workspace->height};
+
+ printf("child itself will be at %dx%d with size %dx%d\n",
+ values[0], values[1], values[2], values[3]);
+
+ /* Raise the window */
+ xcb_circulate_window(conn, XCB_CIRCULATE_RAISE_LOWEST, client->frame);
+
+ xcb_configure_window(conn, client->frame, mask, values);
+ xcb_configure_window(conn, client->child, mask, values);
+
+ xcb_flush(conn);
+ } else {
+ printf("left fullscreen\n");
+ client->force_reconfigure = true;
+ /* We left fullscreen mode, redraw the layout */
+ render_layout(conn);
+ }
+}
/*
* Due to bindings like Mode_switch + <a>, we need to bind some keys in XCB_GRAB_MODE_SYNC.
decorate_window(conn, client);
return 1;
}
+
+/*
+ * Handle client messages (EWMH)
+ *
+ */
+int handle_client_message(void *data, xcb_connection_t *conn, xcb_client_message_event_t *event) {
+ printf("client_message\n");
+
+ if (event->type == atoms[_NET_WM_STATE]) {
+ if (event->format != 32 || event->data.data32[1] != atoms[_NET_WM_STATE_FULLSCREEN])
+ return 0;
+
+ printf("fullscreen\n");
+
+ Client *client = table_get(byChild, event->window);
+ if (client == NULL)
+ return 0;
+
+ /* Check if the fullscreen state should be toggled… */
+ if ((client->fullscreen &&
+ (event->data.data32[0] == _NET_WM_STATE_REMOVE ||
+ event->data.data32[0] == _NET_WM_STATE_TOGGLE)) ||
+ (!client->fullscreen &&
+ (event->data.data32[0] == _NET_WM_STATE_ADD ||
+ event->data.data32[0] == _NET_WM_STATE_TOGGLE)))
+ toggle_fullscreen(conn, client);
+ } else {
+ printf("unhandled clientmessage\n");
+ }
+}
/* 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->x != (client->x = container->x + (container->col * container->width))) |
+ if (client->force_reconfigure |
+ (client->x != (client->x = container->x + (container->col * container->width))) |
(client->y != (client->y = container->y + (container->row * container->height +
(container->height / num_clients) * current_client)))) {
printf("frame needs to be pushed to %dx%d\n", client->x, client->y);
}
/* TODO: vertical default layout */
- if ((client->width != (client->width = container->width)) |
+ if (client->force_reconfigure |
+ (client->width != (client->width = container->width)) |
(client->height != (client->height = container->height / num_clients))) {
xcb_configure_window(connection, client->frame,
XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT,
client->width - (2 + 2), /* width */
client->height - ((font->height + 2 + 2) + 2)}; /* height */
- printf("child itself will be at %dx%d with size %dx%d\n",
+ printf("fullscreen frame/child 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);
}
+ if (client->force_reconfigure)
+ client->force_reconfigure = false;
+
current_client++;
}
} else {
int screen;
for (screen = 0; screen < num_screens; screen++) {
printf("Rendering screen %d\n", screen);
+ if (workspaces[screen].fullscreen_client != NULL)
+ /* This is easy: A client has entered fullscreen mode, so we don’t render at all */
+ continue;
/* TODO: get the workspace which is active on the screen */
int width = workspaces[screen].width;
int height = workspaces[screen].height;
#include <unistd.h>
#include <stdbool.h>
#include <assert.h>
+#include <limits.h>
#include <xcb/xcb.h>
#include "debug.h"
#include "handlers.h"
#include "util.h"
+#include "xcb.h"
#define TERMINAL "/usr/pkg/bin/urxvt"
table_t *byChild = 0;
table_t *byParent = 0;
xcb_window_t root_win;
+xcb_atom_t atoms[6];
+
char *pattern = "-misc-fixed-medium-r-normal--13-120-75-75-C-70-iso8859-1";
int num_screens = 0;
xcb_property_handlers_init(&prophs, &evenths);
xcb_event_set_map_notify_handler(&evenths, handle_map_notify_event, &prophs);
+ xcb_event_set_client_message_handler(&evenths, handle_client_message, 0);
+
xcb_watch_wm_name(&prophs, 128, handle_windowname_change, 0);
root = xcb_aux_get_screen(c, screens)->root;
uint32_t values[] = { XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_PROPERTY_CHANGE };
xcb_change_window_attributes(c, root, mask, values);
+ /* Setup NetWM atoms */
+ /* TODO: needs cleanup, needs more xcb (asynchronous), needs more error checking */
+#define GET_ATOM(name) { \
+ xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(c, xcb_intern_atom(c, 0, strlen(#name), #name), NULL); \
+ if (!reply) { \
+ printf("Could not get atom " #name "\n"); \
+ exit(-1); \
+ } \
+ atoms[name] = reply->atom; \
+ free(reply); \
+}
+
+ GET_ATOM(_NET_SUPPORTED);
+ GET_ATOM(_NET_WM_STATE_FULLSCREEN);
+ GET_ATOM(_NET_SUPPORTING_WM_CHECK);
+ GET_ATOM(_NET_WM_NAME);
+ GET_ATOM(UTF8_STRING);
+ GET_ATOM(_NET_WM_STATE);
+
+ check_error(c, xcb_change_property_checked(c, XCB_PROP_MODE_REPLACE, root, atoms[_NET_SUPPORTED], ATOM, 32, 5, atoms), "Could not set _NET_SUPPORTED");
+
+ xcb_change_property(c, XCB_PROP_MODE_REPLACE, root, atoms[_NET_SUPPORTING_WM_CHECK], WINDOW, 32, 1, &root);
+
+ xcb_change_property(c, XCB_PROP_MODE_REPLACE, root, atoms[_NET_WM_NAME] , atoms[UTF8_STRING], 8, strlen("i3"), "i3");
+
#define BIND(key, modifier, cmd) { \
Binding *new = malloc(sizeof(Binding)); \
new->keycode = key; \
}
}
-static void new_container(Container **container) {
+static void new_container(Workspace *workspace, Container **container) {
Container *new;
new = *container = calloc(sizeof(Container), 1);
CIRCLEQ_INIT(&(new->clients));
new->colspan = 1;
new->rowspan = 1;
+ new->workspace = workspace;
}
/*
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]));
+ new_container(workspace, &(workspace->table[c][workspace->rows-1]));
}
}
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]));
+ new_container(workspace, &(workspace->table[workspace->cols-1][c]));
}
/*