From df7621d5a5288c30dd31b6e03479fa8059af0263 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sat, 14 Feb 2009 08:38:07 +0100 Subject: [PATCH] Implement fullscreen (_NET_WM_STATE_FULLSCREEN) --- include/data.h | 14 ++++++++++ include/handlers.h | 1 + include/i3.h | 1 + include/xcb.h | 12 +++++++++ src/handlers.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++ src/layout.c | 14 +++++++--- src/mainx.c | 31 ++++++++++++++++++++++ src/table.c | 7 ++--- 8 files changed, 140 insertions(+), 6 deletions(-) diff --git a/include/data.h b/include/data.h index 74268c79..32a69307 100644 --- a/include/data.h +++ b/include/data.h @@ -9,6 +9,7 @@ * */ #include +#include #ifndef _DATA_H #define _DATA_H @@ -57,6 +58,8 @@ struct Workspace { 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; @@ -118,6 +121,14 @@ struct Client { 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 */ @@ -147,6 +158,9 @@ struct Container { 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; diff --git a/include/handlers.h b/include/handlers.h index 15236b31..bd0c9d78 100644 --- a/include/handlers.h +++ b/include/handlers.h @@ -19,5 +19,6 @@ int handle_map_notify_event(void *prophs, xcb_connection_t *conn, xcb_map_notify 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 diff --git a/include/i3.h b/include/i3.h index 0ac30514..46687113 100644 --- a/include/i3.h +++ b/include/i3.h @@ -23,5 +23,6 @@ extern TAILQ_HEAD(bindings_head, Binding) bindings; extern xcb_event_handlers_t evenths; extern char *pattern; extern int num_screens; +extern xcb_atom_t atoms[6]; #endif diff --git a/include/xcb.h b/include/xcb.h index 979a9621..5a7a9a51 100644 --- a/include/xcb.h +++ b/include/xcb.h @@ -11,6 +11,18 @@ #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 diff --git a/src/handlers.c b/src/handlers.c index f18993e7..47a517ea 100644 --- a/src/handlers.c +++ b/src/handlers.c @@ -24,6 +24,7 @@ #include "commands.h" #include "data.h" #include "font.h" +#include "xcb.h" static void set_focus(xcb_connection_t *conn, Client *client) { /* Update container */ @@ -42,6 +43,41 @@ static void set_focus(xcb_connection_t *conn, Client *client) { 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 + , we need to bind some keys in XCB_GRAB_MODE_SYNC. @@ -334,3 +370,33 @@ int handle_expose_event(void *data, xcb_connection_t *conn, xcb_expose_event_t * 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"); + } +} diff --git a/src/layout.c b/src/layout.c index 174e1d83..7efc5798 100644 --- a/src/layout.c +++ b/src/layout.c @@ -110,7 +110,8 @@ static void render_container(xcb_connection_t *connection, Container *container) /* 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); @@ -121,7 +122,8 @@ static void render_container(xcb_connection_t *connection, Container *container) } /* 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, @@ -139,12 +141,15 @@ static void render_container(xcb_connection_t *connection, Container *container) 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 { @@ -157,6 +162,9 @@ void render_layout(xcb_connection_t *conn) { 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; diff --git a/src/mainx.c b/src/mainx.c index 22452efa..8966c15b 100644 --- a/src/mainx.c +++ b/src/mainx.c @@ -17,6 +17,7 @@ #include #include #include +#include #include @@ -39,6 +40,7 @@ #include "debug.h" #include "handlers.h" #include "util.h" +#include "xcb.h" #define TERMINAL "/usr/pkg/bin/urxvt" @@ -51,6 +53,8 @@ xcb_event_handlers_t evenths; 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; @@ -344,6 +348,8 @@ int main(int argc, char *argv[], char *env[]) { 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; @@ -353,6 +359,31 @@ int main(int argc, char *argv[], char *env[]) { 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; \ diff --git a/src/table.c b/src/table.c index c0f52f11..76928f90 100644 --- a/src/table.c +++ b/src/table.c @@ -45,12 +45,13 @@ void init_table() { } } -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; } /* @@ -64,7 +65,7 @@ void expand_table_rows(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])); } } @@ -80,7 +81,7 @@ void expand_table_cols(Workspace *workspace) { 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])); } /*