Implement fullscreen (_NET_WM_STATE_FULLSCREEN)
This commit is contained in:
parent
031cf4ccda
commit
df7621d5a5
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
14
src/layout.c
14
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;
|
||||
|
|
31
src/mainx.c
31
src/mainx.c
|
@ -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; \
|
||||
|
|
|
@ -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]));
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
Loading…
Reference in New Issue