]> git.sur5r.net Git - i3/i3/commitdiff
Implement fullscreen (_NET_WM_STATE_FULLSCREEN)
authorMichael Stapelberg <michael+git@stapelberg.de>
Sat, 14 Feb 2009 07:38:07 +0000 (08:38 +0100)
committerMichael Stapelberg <michael+git@stapelberg.de>
Sat, 14 Feb 2009 07:38:07 +0000 (08:38 +0100)
include/data.h
include/handlers.h
include/i3.h
include/xcb.h
src/handlers.c
src/layout.c
src/mainx.c
src/table.c

index 74268c79c53f67730b7101253fec4a7f98541449..32a693079804ee61fa092cea9318e30bff4a7086 100644 (file)
@@ -9,6 +9,7 @@
  *
  */
 #include <xcb/xcb.h>
+#include <stdbool.h>
 
 #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;
index 15236b31f5af893b045ff44caaeda084e0511592..bd0c9d78786e8c97b8eea38895c4363d5a045063 100644 (file)
@@ -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
index 0ac30514183247105f8ab7db782e3282d4b7b341..466871135443eb76d0d2278f8e80fc16c9d0b09c 100644 (file)
@@ -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
index 979a9621e7eb566645dc09cf6bae725426903f35..5a7a9a51b6eaec7347a0fda7f7f1263940d493e0 100644 (file)
 #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
index f18993e72687c633fe877af992960f45ac3d21b9..47a517ea0cc404f5a52c54b557a2329e49a297f3 100644 (file)
@@ -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 + <a>, 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");
+        }
+}
index 174e1d83b1198b662a5c4af069f751e499cf17eb..7efc579812048bfa35c81b6bc7b30b31a07db896 100644 (file)
@@ -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;
index 22452efae3350a695c7fdf945109681fd1536059..8966c15b3d1cc90ed0633e12a980f84d67644a1f 100644 (file)
@@ -17,6 +17,7 @@
 #include <unistd.h>
 #include <stdbool.h>
 #include <assert.h>
+#include <limits.h>
 
 #include <xcb/xcb.h>
 
@@ -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; \
index c0f52f1172b748819a3c59d1f499a71eb2958dc8..76928f9079bbf5b103beddde081dd4e60911d4ec 100644 (file)
@@ -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]));
 }
 
 /*