Implement fullscreen (_NET_WM_STATE_FULLSCREEN)

next
Michael Stapelberg 2009-02-14 08:38:07 +01:00
parent 031cf4ccda
commit df7621d5a5
8 changed files with 140 additions and 6 deletions

View 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. Ive 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 titlebars 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;

View 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

View 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

View File

@ -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

View 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, lets 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");
}
}

View 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 dont render at all */
continue;
/* TODO: get the workspace which is active on the screen */
int width = workspaces[screen].width;
int height = workspaces[screen].height;

View 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; \

View 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]));
}
/*