summaryrefslogtreecommitdiff
path: root/schewm.c
diff options
context:
space:
mode:
Diffstat (limited to 'schewm.c')
-rw-r--r--schewm.c982
1 files changed, 822 insertions, 160 deletions
diff --git a/schewm.c b/schewm.c
index 9eafd2e..a515e1b 100644
--- a/schewm.c
+++ b/schewm.c
@@ -1,4 +1,5 @@
#include <stdbool.h>
+#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
@@ -31,28 +32,34 @@ struct Rect {
uint16_t width, height;
};
-int16_t rect_center_x(const struct Rect *rect) {
+int16_t
+rect_center_x(const struct Rect *rect) {
return rect->x + rect->width / 2;
}
-int16_t rect_center_y(const struct Rect *rect) {
+int16_t
+rect_center_y(const struct Rect *rect) {
return rect->y + rect->height / 2;
}
-void rect_center(const struct Rect *rect, struct Point *p) {
+void
+rect_center(const struct Rect *rect, struct Point *p) {
p->x = rect_center_x(rect);
p->y = rect_center_y(rect);
}
-int16_t max(int16_t a, int16_t b) {
+int16_t
+max(int16_t a, int16_t b) {
return a > b ? a : b;
}
-int16_t min(int16_t a, int16_t b) {
+int16_t
+min(int16_t a, int16_t b) {
return a < b ? a : b;
}
-uint16_t rect_intersect_area(const struct Rect *r1, const struct Rect *r2) {
+uint16_t
+rect_intersect_area(const struct Rect *r1, const struct Rect *r2) {
int16_t x1 = max(r1->x, r2->x);
int16_t y1 = max(r1->y, r2->y);
int16_t x2 = min(r1->x + r1->width, r2->x + r2->width);
@@ -67,33 +74,27 @@ enum WindowState {
WS_FULLSCREEN,
};
-struct Client;
-
struct Client {
xcb_window_t id;
- bool user_coord;
int16_t x, y;
uint16_t width, height;
uint16_t min_width, min_height, max_width, max_height;
uint16_t width_inc, height_inc, base_width, base_height;
uint32_t workspace;
+ bool user_coord;
enum WindowState state, prev_state;
bool is_unkillable;
bool should_ignore_borders;
- struct Monitor *monitor;
bool has_old_size;
int16_t old_x, old_y;
uint16_t old_width, old_height;
+ struct Monitor *monitor;
struct Client *prev, *next;
};
-struct ClientList {
- struct Client *client;
- struct ClientList *next;
-};
-
-struct Client *client_new(xcb_window_t id) {
- struct Client *client = (struct Client *) calloc(1, sizeof(struct Client));
+struct Client *
+client_new(xcb_window_t id) {
+ struct Client *client = calloc(1, sizeof(struct Client));
if (client == NULL) {
return NULL;
}
@@ -111,11 +112,15 @@ struct Client *client_new(xcb_window_t id) {
return client;
}
-void client_free(struct Client *client) {
+void
+client_free(struct Client *client) {
+ // NOTE: We ignore monitor, next, and prev on purpose. The client
+ // does not own that memory.
free(client);
}
-struct Client *client_ring_add(struct Client *ring, struct Client *client) {
+struct Client *
+client_ring_add(struct Client *ring, struct Client *client) {
if (ring == NULL || ring->next == NULL || ring->prev == NULL) {
ring = client;
}
@@ -127,7 +132,8 @@ struct Client *client_ring_add(struct Client *ring, struct Client *client) {
return client;
}
-struct Client *client_ring_erase(struct Client *ring, struct Client *client) {
+struct Client *
+client_ring_erase(struct Client *ring, struct Client *client) {
if (ring == NULL) {
return NULL;
}
@@ -145,7 +151,8 @@ struct Client *client_ring_erase(struct Client *ring, struct Client *client) {
return ring;
}
-void client_store_size(struct Client *client) {
+void
+client_store_size(struct Client *client) {
client->old_x = client->x;
client->old_y = client->y;
client->old_width = client->width;
@@ -153,7 +160,8 @@ void client_store_size(struct Client *client) {
client->has_old_size = true;
}
-void client_restore_size(struct Client *client) {
+void
+client_restore_size(struct Client *client) {
if (!client->has_old_size) {
return;
}
@@ -164,6 +172,152 @@ void client_restore_size(struct Client *client) {
client->height = client->old_height;
}
+struct ClientList {
+ struct Client *client;
+ struct ClientList *next;
+};
+
+/*
+ * Prepends a new client to list of clients.
+ */
+struct ClientList *
+client_list_add(struct ClientList *list, struct Client *client) {
+ struct ClientList *node = calloc(1, sizeof(struct ClientList));
+ if (node == NULL) {
+ // Cannot alloc node, just abort. If list was NULL, stays so.
+ return list;
+ }
+ node->client = client;
+ // Also works when list is NULL
+ node->next = list;
+ list = node;
+ return list;
+}
+
+/*
+ * `deep' indicates whether we should also free the clients referenced
+ * by the list elements.
+ */
+void
+client_list_free(struct ClientList *list, bool deep) {
+ struct ClientList *next;
+ while (list != NULL) {
+ next = list->next;
+ if (deep) {
+ client_free(list->client);
+ }
+ free(list);
+ list = next;
+ }
+}
+
+// xcb_window_t is uint32_t
+uint32_t
+wid_hash(uint32_t x) {
+ x = ((x >> 16) ^ x) * 0x45d9f3b;
+ x = ((x >> 16) ^ x) * 0x45d9f3b;
+ x = (x >> 16) ^ x;
+ return x;
+}
+
+uint32_t
+wid_unhash(uint32_t x) {
+ x = ((x >> 16) ^ x) * 0x119de1f3;
+ x = ((x >> 16) ^ x) * 0x119de1f3;
+ x = (x >> 16) ^ x;
+ return x;
+}
+
+struct ClientsMap {
+ struct ClientList **table;
+ // Please never modify this
+ size_t table_size;
+ size_t size;
+};
+
+struct ClientsMap *
+clients_map_new(size_t size) {
+ struct ClientsMap *map = calloc(1, sizeof(struct ClientsMap));
+ if (!map) {
+ return NULL;
+ }
+
+ map->table = calloc(size, sizeof(struct ClientList *));
+ if (!map->table) {
+ free(map);
+ return NULL;
+ }
+
+ map->table_size = size;
+ map->size = 0;
+ return map;
+}
+
+void
+clients_map_add(struct ClientsMap *clients, struct Client *client) {
+ uint32_t hash = wid_hash(client->id);
+ uint32_t i = hash % clients->table_size;
+ // collision if (clients->table[i] != NULL)
+ clients->table[i] = client_list_add(clients->table[i], client);
+ clients->size++;
+}
+
+struct Client *
+clients_map_erase(struct ClientsMap *clients, struct Client *client) {
+ uint32_t hash = wid_hash(client->id);
+ uint32_t i = hash % clients->table_size;
+ struct ClientList *prev = NULL;
+ for (struct ClientList *list = clients->table[i];
+ list != NULL;
+ list = list->next) {
+ if (list->client == client) {
+ if (prev == NULL) {
+ // It matched the first, update the starting node of
+ // the list
+ clients->table[i] = list->next;
+ }
+ if (prev != NULL) {
+ // We are not at the first, make prev skip the node we
+ // just found
+ prev->next = list->next;
+ }
+ free(list);
+ clients->size--;
+ return client;
+ }
+ prev = list;
+ }
+ // Not found
+ return NULL;
+}
+
+struct Client *
+clients_map_find(const struct ClientsMap *clients, xcb_window_t id) {
+ uint32_t hash = wid_hash(id);
+ uint32_t i = hash % clients->table_size;
+ for (struct ClientList *list = clients->table[i];
+ list != NULL;
+ list = list->next) {
+ if (list->client->id == id) {
+ return list->client;
+ }
+ }
+ // Not found
+ return NULL;
+}
+
+/*
+ * This function frees the memory of clients as well.
+ */
+void
+clients_map_free(struct ClientsMap *clients) {
+ for (size_t i = 0; i < clients->table_size; i++) {
+ client_list_free(clients->table[i], true);
+ }
+ free(clients->table);
+ free(clients);
+}
+
struct Monitor {
xcb_randr_output_t id;
const char *name;
@@ -172,7 +326,8 @@ struct Monitor {
struct Monitor *prev, *next;
};
-void monitor_rect(const struct Monitor *monitor, struct Rect *rect) {
+void
+monitor_rect(const struct Monitor *monitor, struct Rect *rect) {
rect->x = monitor->x;
rect->y = monitor->y;
rect->width = monitor->width;
@@ -183,11 +338,6 @@ struct Workspace {
struct Client *ring;
};
-struct WorkspaceList {
- struct Workspace *workspace;
- struct WorkspaceList *next;
-};
-
static struct {
uint16_t inner_border_width, outer_border_width, magnet_border_width;
int16_t offset_x, offset_y;
@@ -196,12 +346,17 @@ static struct {
struct {
uint32_t focused, unfocused, unkillable, empty, outer;
} colors;
-} config;
+} cfg;
+/*
+ * Display server-specific information.
+ */
static struct {
xcb_connection_t *conn;
xcb_ewmh_connection_t *ewmh;
bool has_error;
+ xcb_atom_t delete_window_atom, change_state_atom;
+ xcb_key_symbols_t *keysyms;
uint16_t num_lock, caps_lock, scroll_lock;
xcb_font_t cursor_font;
xcb_cursor_t fleur_cursor, sizing_cursor;
@@ -209,7 +364,7 @@ static struct {
bool has_randr;
int randr_base, screen_num;
struct Monitor monitors;
-} display_server;
+} dpy;
/*
* Function pointer type for event handlers that take a generic event
@@ -218,27 +373,25 @@ static struct {
typedef void (*generic_event_handler_t)(xcb_generic_event_t *);
static struct {
- struct ClientList clients;
- struct WorkspaceList workspaces;
- struct xcb_connection_t *conn;
- uint32_t cur_workspace;
+ uint32_t cur_workspace, num_workspaces;
bool is_running;
- /*
- * Array of (internal) event handlers. They cast the event pointer
- * to the proper type and handle them.
- */
+ struct Client *focus;
+ struct ClientsMap *clients;
+ struct Workspace *workspaces;
+ struct xcb_connection_t *conn;
generic_event_handler_t events[XCB_NO_OPERATION];
} wm;
static xcb_screen_t *get_screen();
static bool request_has_error(xcb_void_cookie_t);
-static bool conn_init();
-static void conn_flush();
+static bool dpy_init();
+static void dpy_flush();
-static xcb_screen_t *get_screen() {
- const xcb_setup_t* setup = xcb_get_setup(display_server.conn);
+static xcb_screen_t *
+get_screen() {
+ const xcb_setup_t* setup = xcb_get_setup(dpy.conn);
xcb_screen_iterator_t iter = xcb_setup_roots_iterator(setup);
- for (int i = display_server.screen_num; iter.rem; --i) {
+ for (int i = dpy.screen_num; iter.rem; --i) {
if (i == 0) {
return iter.data;
}
@@ -247,8 +400,9 @@ static xcb_screen_t *get_screen() {
return NULL;
}
-static bool request_has_error(xcb_void_cookie_t cookie) {
- xcb_generic_error_t *error = xcb_request_check(display_server.conn, cookie);
+static bool
+request_has_error(xcb_void_cookie_t cookie) {
+ xcb_generic_error_t *error = xcb_request_check(dpy.conn, cookie);
if (error) {
free(error);
return true;
@@ -261,24 +415,25 @@ struct WindowsQueryReply {
int length;
};
-void windows_query(xcb_window_t root, struct WindowsQueryReply *reply) {
- xcb_query_tree_cookie_t cookie = xcb_query_tree(display_server.conn, root);
- xcb_query_tree_reply_t *data = xcb_query_tree_reply(display_server.conn, cookie, NULL);
- reply->length = 0;
+void
+windows_query(xcb_window_t root, struct WindowsQueryReply *reply) {
+ xcb_query_tree_cookie_t cookie = xcb_query_tree(dpy.conn, root);
+ xcb_query_tree_reply_t *data = xcb_query_tree_reply(dpy.conn, cookie, NULL);
if (!data) {
return;
}
+ reply->length = 0;
int length = xcb_query_tree_children_length(data);
// Assume length is OK, but reply->length hold the length of
// clients we are really interested in
- reply->data = (xcb_window_t *) calloc(length, sizeof(xcb_window_t));
+ reply->data = calloc(length, sizeof(xcb_window_t));
xcb_window_t *children = xcb_query_tree_children(data);
for (int i = 0; i < length; i++) {
xcb_get_window_attributes_cookie_t attr_cookie = xcb_get_window_attributes(
- display_server.conn,
+ dpy.conn,
children[i]);
xcb_get_window_attributes_reply_t *attr_reply = xcb_get_window_attributes_reply(
- display_server.conn,
+ dpy.conn,
attr_cookie,
NULL);
/*
@@ -298,12 +453,19 @@ void windows_query(xcb_window_t root, struct WindowsQueryReply *reply) {
}
}
-void windows_query_reply_free(struct WindowsQueryReply *reply) {
+void
+windows_query_reply_free(struct WindowsQueryReply *reply) {
if (reply->length > 0) {
free(reply->data);
}
}
+/*
+ * Window Manager API.
+ */
+struct Client *wm_find_client(xcb_window_t id);
+void wm_erase_client(xcb_window_t id);
+bool wm_update_outputs();
bool wm_has_error();
bool wm_init();
void wm_quit();
@@ -330,97 +492,542 @@ void wm_set_client_workspace(uint32_t workspace);
void wm_client_monitor_prev();
void wm_client_monitor_next();
-bool wm_has_error() {
- return display_server.has_error || xcb_connection_has_error(display_server.conn) > 0;
+struct Client *
+wm_find_client(xcb_window_t id) {
+ return clients_map_find(wm.clients, id);
+}
+
+static void
+dpy_set_window_state(xcb_window_t window, enum WindowState state) {
+ if (state == WS_NORMAL) {
+ long data[] = { XCB_ICCCM_WM_STATE_NORMAL, XCB_NONE };
+ xcb_change_property(
+ dpy.conn,
+ XCB_PROP_MODE_REPLACE,
+ window,
+ dpy.ewmh->_NET_WM_STATE,
+ dpy.ewmh->_NET_WM_STATE,
+ 32,
+ 2,
+ data);
+ } else if (state == WS_ICONIFIED) {
+ xcb_atom_t data[] = { dpy.ewmh->_NET_WM_STATE_HIDDEN };
+ xcb_ewmh_set_wm_state(dpy.ewmh, window, 1, data);
+ } else if (state == WS_MAXIMIZED) {
+ xcb_atom_t data[] = {
+ dpy.ewmh->_NET_WM_STATE_MAXIMIZED_VERT,
+ dpy.ewmh->_NET_WM_STATE_MAXIMIZED_HORZ,
+ };
+ xcb_ewmh_set_wm_state(dpy.ewmh, window, 2, data);
+ } else if (state == WS_FULLSCREEN) {
+ xcb_atom_t data[] = { dpy.ewmh->_NET_WM_STATE_FULLSCREEN };
+ xcb_ewmh_set_wm_state(dpy.ewmh, window, 1, data);
+ }
+}
+
+static void
+dpy_set_focus(xcb_window_t window) {
+ xcb_window_t focus_window = window;
+ uint8_t revert_to = XCB_INPUT_FOCUS_POINTER_ROOT;
+ if (window == XCB_NONE) {
+ focus_window = XCB_INPUT_FOCUS_POINTER_ROOT;
+ revert_to = XCB_INPUT_FOCUS_NONE;
+ } else {
+ dpy_set_window_state(window, WS_NORMAL);
+ }
+ xcb_set_input_focus(dpy.conn, revert_to, focus_window, XCB_CURRENT_TIME);
+ xcb_ewmh_set_active_window(dpy.ewmh, dpy.screen_num, window);
+}
+
+void
+wm_erase_client(xcb_window_t id) {
+ struct Client *client = wm_find_client(id);
+ if (client == NULL) {
+ return;
+ }
+
+ struct Workspace *workspace = &wm.workspaces[client->workspace];
+ // Workspace being NULL is a bug!
+ workspace->ring = client_ring_erase(workspace->ring, client);
+ clients_map_erase(wm.clients, client);
+ if (!wm.focus) {
+ dpy_set_focus(XCB_NONE);
+ }
+}
+
+bool
+wm_has_error() {
+ return dpy.has_error || xcb_connection_has_error(dpy.conn) > 0;
+}
+
+static void
+dpy_update_outputs() {
+}
+
+static void
+dpy_flush() {
+ xcb_flush(dpy.conn);
+}
+
+static uint16_t
+mod_from_keycode(xcb_keycode_t keycode) {
+ uint16_t mod = 0;
+ xcb_get_modifier_mapping_cookie_t cookie =
+ xcb_get_modifier_mapping(dpy.conn);
+ xcb_get_modifier_mapping_reply_t *reply =
+ xcb_get_modifier_mapping_reply(dpy.conn, cookie, NULL);
+ if (!reply || reply->keycodes_per_modifier == 0) {
+ goto mod_from_keycode_end;
+ }
+
+ xcb_keycode_t *mod_keycodes = xcb_get_modifier_mapping_keycodes(reply);
+ if (!mod_keycodes) {
+ goto mod_from_keycode_end;
+ }
+
+ int length = xcb_get_modifier_mapping_keycodes_length(reply) / reply->keycodes_per_modifier;
+ for (int i = 0; i < length; i++) {
+ for (int j = 0; j < reply->keycodes_per_modifier; j++) {
+ xcb_keycode_t mod_keycode = mod_keycodes[i * reply->keycodes_per_modifier + j];
+ if (mod_keycode != XCB_NO_SYMBOL && mod_keycode == keycode) {
+ mod |= 1 << i;
+ }
+ }
+ }
+
+mod_from_keycode_end:
+ free(reply);
+ return mod;
}
-static bool conn_init(const char *wm_name) {
- // TODO: init keysyms
- display_server.has_error = false;
- display_server.has_randr = false;
- display_server.randr_base = -1;
- display_server.conn = xcb_connect(NULL, &display_server.screen_num);
+static uint16_t
+mod_from_keysym(xcb_keysym_t keysym) {
+ uint16_t mod = 0;
+ const xcb_setup_t *setup = xcb_get_setup(dpy.conn);
+ if (!setup) {
+ return mod;
+ }
+
+ /*
+ * We go through every keycode in the setup, looking for the ones
+ * matching our keysym request. Then, we transform those into a
+ * mod we can use and return.
+ *
+ * Also, don't use xcb_keycode_t for kc. It overflows when
+ * max_keycode coincides with the maximum value of xcb_keycode_t.
+ */
+ unsigned int kc;
+ for (kc = setup->min_keycode; kc <= setup->max_keycode; kc++) {
+ for (unsigned int col = 0; col < 4 /* KEYSYMS_PER_KEYCODE */; col++) {
+ xcb_keysym_t ks = xcb_key_symbols_get_keysym(dpy.keysyms, kc, col);
+ if (ks == keysym) {
+ mod |= mod_from_keycode(kc);
+ }
+ }
+ }
+ return mod;
+}
+
+static xcb_atom_t
+dpy_get_atom(const char *name) {
+ xcb_intern_atom_cookie_t cookie = xcb_intern_atom(dpy.conn, 0, strlen(name), name);
+ xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(dpy.conn, cookie, NULL);
+ if (!reply) {
+ return 0;
+ }
+
+ xcb_atom_t atom = reply->atom;
+ free(reply);
+ return atom;
+}
+
+static bool
+dpy_init(const char *wm_name) {
+ // This is initially NULL on purpose: we always try to dealloc
+ // later and it might happen that we dealloc before we even tried
+ // to setup the keysym stuff. This way we ensure we know whether
+ // keysyms is a valid ptr or not.
+ dpy.keysyms = NULL;
+ dpy.has_error = false;
+ dpy.has_randr = false;
+ dpy.randr_base = -1;
+
+ dpy.conn = xcb_connect(NULL, &dpy.screen_num);
if (wm_has_error()) {
return false;
}
- display_server.screen = get_screen();
- if (!display_server.screen) {
- display_server.has_error = true;
+ dpy.screen = get_screen();
+ if (!dpy.screen) {
+ dpy.has_error = true;
return false;
}
- display_server.ewmh = (xcb_ewmh_connection_t *) calloc(1, sizeof(xcb_ewmh_connection_t));
- if (!display_server.ewmh) {
- display_server.has_error = true;
+ dpy.ewmh = calloc(1, sizeof(xcb_ewmh_connection_t));
+ if (!dpy.ewmh) {
+ dpy.has_error = true;
return false;
}
xcb_intern_atom_cookie_t *ewmh_cookie = xcb_ewmh_init_atoms(
- display_server.conn,
- display_server.ewmh);
- if (xcb_ewmh_init_atoms_replies(display_server.ewmh, ewmh_cookie, NULL) == 0) {
- display_server.has_error = true;
+ dpy.conn,
+ dpy.ewmh);
+ if (xcb_ewmh_init_atoms_replies(dpy.ewmh, ewmh_cookie, NULL) == 0) {
+ dpy.has_error = true;
return false;
}
xcb_atom_t net_atoms[] = {
- display_server.ewmh->_NET_SUPPORTED,
- display_server.ewmh->_NET_WM_DESKTOP,
- display_server.ewmh->_NET_NUMBER_OF_DESKTOPS,
- display_server.ewmh->_NET_CURRENT_DESKTOP,
- display_server.ewmh->_NET_ACTIVE_WINDOW,
- display_server.ewmh->_NET_WM_ICON,
- display_server.ewmh->_NET_WM_STATE,
- display_server.ewmh->_NET_WM_NAME,
- display_server.ewmh->_NET_SUPPORTING_WM_CHECK,
- display_server.ewmh->_NET_WM_STATE_HIDDEN,
- display_server.ewmh->_NET_WM_ICON_NAME,
- display_server.ewmh->_NET_WM_WINDOW_TYPE,
- display_server.ewmh->_NET_WM_WINDOW_TYPE_DOCK,
- display_server.ewmh->_NET_WM_WINDOW_TYPE_DESKTOP,
- display_server.ewmh->_NET_WM_WINDOW_TYPE_TOOLBAR,
- display_server.ewmh->_NET_WM_PID,
- display_server.ewmh->_NET_CLIENT_LIST,
- display_server.ewmh->WM_PROTOCOLS,
- display_server.ewmh->_NET_WM_STATE,
- display_server.ewmh->_NET_WM_STATE_DEMANDS_ATTENTION,
- display_server.ewmh->_NET_WM_STATE_FULLSCREEN,
+ dpy.ewmh->_NET_SUPPORTED,
+ dpy.ewmh->_NET_WM_DESKTOP,
+ dpy.ewmh->_NET_NUMBER_OF_DESKTOPS,
+ dpy.ewmh->_NET_CURRENT_DESKTOP,
+ dpy.ewmh->_NET_ACTIVE_WINDOW,
+ dpy.ewmh->_NET_WM_ICON,
+ dpy.ewmh->_NET_WM_STATE,
+ dpy.ewmh->_NET_WM_NAME,
+ dpy.ewmh->_NET_SUPPORTING_WM_CHECK,
+ dpy.ewmh->_NET_WM_STATE_HIDDEN,
+ dpy.ewmh->_NET_WM_ICON_NAME,
+ dpy.ewmh->_NET_WM_WINDOW_TYPE,
+ dpy.ewmh->_NET_WM_WINDOW_TYPE_DOCK,
+ dpy.ewmh->_NET_WM_WINDOW_TYPE_DESKTOP,
+ dpy.ewmh->_NET_WM_WINDOW_TYPE_TOOLBAR,
+ dpy.ewmh->_NET_WM_PID,
+ dpy.ewmh->_NET_CLIENT_LIST,
+ dpy.ewmh->WM_PROTOCOLS,
+ dpy.ewmh->_NET_WM_STATE,
+ dpy.ewmh->_NET_WM_STATE_DEMANDS_ATTENTION,
+ dpy.ewmh->_NET_WM_STATE_FULLSCREEN,
};
xcb_ewmh_set_supported(
- display_server.ewmh,
- display_server.screen_num,
+ dpy.ewmh,
+ dpy.screen_num,
sizeof(net_atoms) / sizeof(net_atoms[0]),
net_atoms);
- // set_focus(XCB_NONE);
- // setup_randr();
- // setup_keys();
- // setup_cursors();
+ // Setup RandR
+ const struct xcb_query_extension_reply_t *reply = xcb_get_extension_data(dpy.conn, &xcb_randr_id);
+ if (!reply || !reply->present) {
+ dpy.has_randr = false;
+ } else {
+ dpy.has_randr = true;
+ dpy_update_outputs();
+ dpy.randr_base = reply->first_event;
+ xcb_randr_select_input(
+ dpy.conn,
+ dpy.screen->root,
+ XCB_RANDR_NOTIFY_MASK_SCREEN_CHANGE
+ | XCB_RANDR_NOTIFY_MASK_OUTPUT_CHANGE
+ | XCB_RANDR_NOTIFY_MASK_CRTC_CHANGE
+ | XCB_RANDR_NOTIFY_MASK_OUTPUT_PROPERTY);
+ }
+
+ // Ask display server for window manager capabilities
+ dpy.delete_window_atom = dpy_get_atom("WM_DELETE_WINDOW");
+ dpy.change_state_atom = dpy_get_atom("WM_CHANGE_STATE");
+
+ // Setup keys
+ xcb_ungrab_key(dpy.conn, XCB_GRAB_ANY, dpy.screen->root, XCB_MOD_MASK_ANY);
+ dpy.keysyms = xcb_key_symbols_alloc(dpy.conn);
+ if (!dpy.keysyms) {
+ dpy.has_error = true;
+ return false;
+ }
+ dpy.num_lock = mod_from_keysym(XK_Num_Lock);
+ dpy.caps_lock = mod_from_keysym(XK_Caps_Lock);
+ dpy.scroll_lock = mod_from_keysym(XK_Scroll_Lock);
+
+ // Setup cursors
+ const char *cursor_font_name = "cursor";
+ dpy.cursor_font = xcb_generate_id(dpy.conn);
+ xcb_open_font(dpy.conn, dpy.cursor_font, strlen(cursor_font_name), cursor_font_name);
+ dpy.fleur_cursor = xcb_generate_id(dpy.conn);
+ xcb_create_glyph_cursor(
+ dpy.conn,
+ dpy.fleur_cursor,
+ dpy.cursor_font,
+ dpy.cursor_font,
+ MOVE_GLYPH,
+ MOVE_GLYPH + 1,
+ 0x3232,
+ 0x3232,
+ 0x3232,
+ 0xeeee,
+ 0xeeee,
+ 0xeeec);
+ dpy.sizing_cursor = xcb_generate_id(dpy.conn);
+ xcb_create_glyph_cursor(
+ dpy.conn,
+ dpy.sizing_cursor,
+ dpy.cursor_font,
+ dpy.cursor_font,
+ RESIZE_GLYPH,
+ RESIZE_GLYPH + 1,
+ 0x3232,
+ 0x3232,
+ 0x3232,
+ 0xeeee,
+ 0xeeee,
+ 0xeeec);
xcb_event_mask_t event_mask = XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT
| XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY
| XCB_EVENT_MASK_PROPERTY_CHANGE
| XCB_EVENT_MASK_BUTTON_PRESS;
xcb_void_cookie_t cookie = xcb_change_window_attributes_checked(
- display_server.conn,
- display_server.screen->root,
+ dpy.conn,
+ dpy.screen->root,
XCB_CW_EVENT_MASK,
&event_mask);
if (request_has_error(cookie)) {
- display_server.has_error = true;
+ dpy.has_error = true;
return false;
}
- xcb_ewmh_set_wm_name(display_server.ewmh, display_server.screen->root, strlen(wm_name), wm_name);
- xcb_ewmh_set_wm_pid(display_server.ewmh, display_server.screen->root, getpid());
+ xcb_ewmh_set_wm_name(dpy.ewmh, dpy.screen->root, strlen(wm_name), wm_name);
+ xcb_ewmh_set_wm_pid(dpy.ewmh, dpy.screen->root, getpid());
+ dpy_flush();
+ return !dpy.has_error;
+}
+
+
+static void
+dpy_map_window(xcb_window_t window) {
+ xcb_map_window(dpy.conn, window);
+}
+
+static void
+dpy_unmap_window(xcb_window_t window) {
+ xcb_unmap_window(dpy.conn, window);
+}
+
+static void
+dpy_raise_window(xcb_window_t window) {
+ if (window == dpy.screen->root || window == XCB_NONE) {
+ return;
+ }
- conn_flush();
- return !display_server.has_error;
+ uint32_t mode = XCB_STACK_MODE_ABOVE;
+ xcb_configure_window(dpy.conn, window, XCB_CONFIG_WINDOW_STACK_MODE, &mode);
}
-static void conn_flush() {
- xcb_flush(display_server.conn);
+static void
+dpy_set_workspace(uint32_t workspace) {
+ xcb_ewmh_set_current_desktop(dpy.ewmh, dpy.screen_num, workspace);
+}
+
+void
+dpy_set_window_border_width(xcb_window_t window) {
+ xcb_configure_window(dpy.conn, window, XCB_CONFIG_WINDOW_BORDER_WIDTH, &cfg.inner_border_width);
+}
+
+void
+dpy_set_window_workspace(xcb_window_t window, uint32_t workspace) {
+ xcb_ewmh_set_wm_desktop(dpy.ewmh, window, workspace);
+}
+
+#define RECT_INIT(x, y, w, h) { \
+ (int16_t) (x), \
+ (int16_t) (y), \
+ (uint16_t) (w), \
+ (uint16_t) (h), \
+}
+
+static void
+dpy_draw_borders(struct Client *client, bool is_focused) {
+ if (client->state != WS_NORMAL || client->should_ignore_borders) {
+ return;
+ }
+
+ dpy_set_window_border_width(client->id);
+
+ xcb_rectangle_t inner_rect[] = {
+ RECT_INIT(
+ client->width,
+ 0,
+ cfg.inner_border_width - cfg.outer_border_width,
+ client->height + cfg.inner_border_width - cfg.outer_border_width
+ ),
+ RECT_INIT(
+ client->width + cfg.inner_border_width + cfg.outer_border_width,
+ 0,
+ cfg.inner_border_width - cfg.outer_border_width,
+ client->height + cfg.inner_border_width - cfg.outer_border_width
+ ),
+ RECT_INIT(
+ 0,
+ client->height,
+ client->width + cfg.inner_border_width - cfg.outer_border_width,
+ cfg.inner_border_width - cfg.outer_border_width
+ ),
+ RECT_INIT(
+ 0,
+ client->height + cfg.inner_border_width + cfg.outer_border_width,
+ client->width + cfg.inner_border_width - cfg.outer_border_width,
+ cfg.inner_border_width - cfg.outer_border_width
+ ),
+ RECT_INIT(
+ client->width + cfg.inner_border_width + cfg.outer_border_width,
+ client->height + cfg.inner_border_width + cfg.outer_border_width,
+ cfg.inner_border_width,
+ cfg.inner_border_width
+ ),
+ };
+ xcb_rectangle_t outer_rect[] = {
+ RECT_INIT(
+ client->width + cfg.inner_border_width - cfg.outer_border_width,
+ 0,
+ cfg.outer_border_width,
+ client->height + cfg.inner_border_width * 2
+ ),
+ RECT_INIT(
+ client->width + cfg.inner_border_width,
+ 0,
+ cfg.outer_border_width,
+ client->height + cfg.inner_border_width * 2
+ ),
+ RECT_INIT(
+ 0,
+ client->height + cfg.inner_border_width - cfg.outer_border_width,
+ client->width + cfg.inner_border_width * 2,
+ cfg.outer_border_width
+ ),
+ RECT_INIT(
+ 0,
+ client->height + cfg.inner_border_width,
+ client->width + cfg.inner_border_width * 2,
+ cfg.outer_border_width
+ ),
+ RECT_INIT(
+ 1,
+ 1,
+ 1,
+ 1
+ ),
+ };
+
+ xcb_pixmap_t pixmap = xcb_generate_id(dpy.conn);
+ xcb_create_pixmap(
+ dpy.conn,
+ dpy.screen->root_depth,
+ pixmap,
+ dpy.screen->root,
+ client->width + cfg.inner_border_width * 2,
+ client->height + cfg.inner_border_width * 2);
+
+ xcb_gcontext_t gc = xcb_generate_id(dpy.conn);
+ xcb_create_gc(dpy.conn, gc, pixmap, 0, NULL);
+
+ uint32_t color = client->is_unkillable
+ ? cfg.colors.unkillable : cfg.colors.outer;
+ xcb_change_gc(dpy.conn, gc, XCB_GC_FOREGROUND, &color);
+ xcb_poly_fill_rectangle(dpy.conn, pixmap, gc, 5, outer_rect);
+
+ color = is_focused ? cfg.colors.focused : cfg.colors.unfocused;
+ xcb_change_gc(dpy.conn, gc, XCB_GC_FOREGROUND, &color);
+ xcb_poly_fill_rectangle(dpy.conn, pixmap, gc, 5, inner_rect);
+ xcb_change_window_attributes(dpy.conn, client->id, XCB_CW_BORDER_PIXMAP, &pixmap);
+ xcb_free_gc(dpy.conn, gc);
+ xcb_free_pixmap(dpy.conn, pixmap);
+}
+
+#undef RECT_INIT
+
+void
+dpy_draw_focused_borders(struct Client *client) {
+ dpy_draw_borders(client, true);
+}
+
+void
+dpy_draw_unfocused_borders(struct Client *client) {
+ dpy_draw_borders(client, false);
+}
+
+xcb_window_t
+dpy_window_under_cursor() {
+ xcb_query_pointer_cookie_t cookie =
+ xcb_query_pointer(dpy.conn, dpy.screen->root);
+ xcb_query_pointer_reply_t *reply =
+ xcb_query_pointer_reply(dpy.conn, cookie, NULL);
+ if (!reply) {
+ return XCB_NONE;
+ }
+
+ xcb_window_t window = reply->child;
+ free(reply);
+ return window;
+}
+
+void
+dpy_grab_buttons(struct Client *client) {
+}
+
+void
+wm_set_focus(struct Client *client) {
+ if (wm.focus != NULL) {
+ if (wm.focus == client) {
+ return;
+ }
+ dpy_draw_unfocused_borders(wm.focus);
+ }
+
+ wm.focus = client;
+ dpy_set_focus(client->id);
+ dpy_grab_buttons(client);
+ dpy_draw_focused_borders(client);
+ dpy_flush();
+}
+
+void
+wm_set_workspace(uint32_t workspace) {
+ struct Client *client;
+ if (workspace > wm.num_workspaces) {
+ return;
+ }
+
+ if (workspace != wm.cur_workspace) {
+ // Unmap all clients in current workspace
+ struct Workspace *ws_ptr = &wm.workspaces[wm.cur_workspace];
+ client = ws_ptr->ring;
+ while (client != NULL) {
+ if (client->state != WS_ICONIFIED) {
+ dpy_unmap_window(client->id);
+ }
+ client = client->next;
+
+ if (client == ws_ptr->ring) {
+ break;
+ }
+ }
+
+ wm.cur_workspace = workspace;
+ }
+
+ // Update current workspace and map all clients at new current workspace
+ dpy_set_workspace(wm.cur_workspace);
+ struct Workspace *ws_ptr = &wm.workspaces[wm.cur_workspace];
+ client = ws_ptr->ring;
+ while (client != NULL) {
+ if (client->state != WS_ICONIFIED) {
+ dpy_map_window(client->id);
+ }
+ client = client->next;
+
+ if (client == ws_ptr->ring) {
+ break;
+ }
+ }
+
+ xcb_window_t window = dpy_window_under_cursor();
+ client = clients_map_find(wm.clients, window);
+ if (client == NULL) {
+ wm.focus = NULL;
+ dpy_set_focus(XCB_NONE);
+ dpy_flush();
+ } else {
+ wm_set_focus(client);
+ }
}
/*
@@ -438,23 +1045,31 @@ static void ev_circulate_request(xcb_generic_event_t *);
static void ev_button_press(xcb_generic_event_t *);
static void ev_client_message(xcb_generic_event_t *);
-bool wm_init() {
- if (!conn_init("schewm")) {
+static bool
+setup_client(xcb_window_t window) {
+ return false;
+}
+
+bool
+wm_init() {
+ if (!dpy_init("schewm")) {
return false;
}
wm.is_running = true;
wm.cur_workspace = 0;
+ wm.num_workspaces = 10;
+ wm.workspaces = calloc(wm.num_workspaces, sizeof(struct Workspace));
+ wm.focus = NULL;
+ wm.clients = clients_map_new(128);
- // conn_set_num_workspaces();
-
- // TODO: load config
- // m_delete_window_atom = conn.get_atom("WM_DELETE_WINDOW");
- // m_change_state_atom = conn.get_atom("WM_CHANGE_STATE");
+ // TODO
+ // wm.workspaces = INIT;
+ // dpy_set_num_workspaces();
// Look into all existing windows and set them up
struct WindowsQueryReply reply;
- windows_query(display_server.screen->root, &reply);
+ windows_query(dpy.screen->root, &reply);
for (int i = 0; i < reply.length; i++) {
xcb_window_t window = reply.data[i];
if (!setup_client(window)) {
@@ -462,7 +1077,7 @@ bool wm_init() {
* Could not be setup, just map it on screen because we
* probably should not own it.
*/
- conn_map_window(window);
+ dpy_map_window(window);
} else {
/*
* Unmap everything we grab initially because they may be
@@ -470,14 +1085,15 @@ bool wm_init() {
* way, we can map again only the correct windows at the
* set_workspace() call below.
*/
- conn_unmap_window(window);
+ dpy_unmap_window(window);
}
}
windows_query_reply_free(&reply);
- update_client_list();
- set_workspace(wm.cur_workspace);
- grab_keys();
- conn_flush();
+
+ // update_client_list();
+ wm_set_workspace(wm.cur_workspace);
+ // grab_keys();
+ dpy_flush();
memset(wm.events, 0, sizeof(wm.events));
wm.events[XCB_CONFIGURE_REQUEST] = ev_configure_request;
@@ -492,40 +1108,55 @@ bool wm_init() {
wm.events[XCB_BUTTON_PRESS] = ev_button_press;
wm.events[XCB_CLIENT_MESSAGE] = ev_client_message;
- return !display_server.has_error;
+ return !dpy.has_error;
}
-void wm_quit() {
+void
+wm_quit() {
wm.is_running = false;
}
-void wm_destroy() {
- if (display_server.ewmh) {
- xcb_ewmh_connection_wipe(display_server.ewmh);
- free(display_server.ewmh);
+void
+dpy_destroy() {
+ if (dpy.ewmh) {
+ xcb_ewmh_connection_wipe(dpy.ewmh);
+ free(dpy.ewmh);
+ }
+
+ if (dpy.keysyms) {
+ xcb_key_symbols_free(dpy.keysyms);
}
if (!wm_has_error()) {
- xcb_free_cursor(display_server.conn, display_server.fleur_cursor);
- xcb_free_cursor(display_server.conn, display_server.sizing_cursor);
- xcb_close_font(display_server.conn, display_server.cursor_font);
+ xcb_free_cursor(dpy.conn, dpy.fleur_cursor);
+ xcb_free_cursor(dpy.conn, dpy.sizing_cursor);
+ xcb_close_font(dpy.conn, dpy.cursor_font);
xcb_set_input_focus(
- display_server.conn,
+ dpy.conn,
XCB_NONE,
XCB_INPUT_FOCUS_POINTER_ROOT,
XCB_CURRENT_TIME);
- conn_flush();
+ dpy_flush();
}
- xcb_disconnect(display_server.conn);
+ xcb_disconnect(dpy.conn);
+}
+
+void
+wm_destroy() {
+ free(wm.workspaces);
+ clients_map_free(wm.clients);
+ dpy_destroy();
}
-static int ev_type(const xcb_generic_event_t *ev) {
+static int
+ev_type(const xcb_generic_event_t *ev) {
return ev->response_type & 0x80;
}
-void wm_run() {
+void
+wm_run() {
while (wm.is_running) {
- xcb_generic_event_t *ev = xcb_wait_for_event(display_server.conn);
+ xcb_generic_event_t *ev = xcb_wait_for_event(dpy.conn);
generic_event_handler_t handler = wm.events[ev_type(ev)];
if (handler) {
handler(ev);
@@ -538,11 +1169,13 @@ void wm_run() {
static uint16_t mod_key = META;
-void wm_set_mod_key(uint16_t mod) {
+void
+wm_set_mod_key(uint16_t mod) {
mod_key = mod;
}
-uint16_t wm_get_mod_key(bool with_shift) {
+uint16_t
+wm_get_mod_key(bool with_shift) {
if (with_shift) {
return mod_key | SHIFT;
}
@@ -564,11 +1197,14 @@ uint16_t wm_get_mod_key(bool with_shift) {
typedef void (*configure_request_handler_t)();
static configure_request_handler_t configure_request_handler = NULL;
-void wm_set_configure_request_handler(configure_request_handler_t handler) {
+void
+wm_set_configure_request_handler(configure_request_handler_t handler) {
configure_request_handler = handler;
}
-static void ev_configure_request(xcb_generic_event_t *generic_ev) {
+static void
+ev_configure_request(xcb_generic_event_t *generic_ev) {
+ fprintf(stderr, "configure_request\n");
if (configure_request_handler) {
xcb_configure_request_event_t *ev = (xcb_configure_request_event_t *) generic_ev;
configure_request_handler();
@@ -579,11 +1215,14 @@ static void ev_configure_request(xcb_generic_event_t *generic_ev) {
typedef void (*destroy_notify_handler_t)();
static destroy_notify_handler_t destroy_notify_handler = NULL;
-void wm_set_destroy_notify_handler(destroy_notify_handler_t handler) {
+void
+wm_set_destroy_notify_handler(destroy_notify_handler_t handler) {
destroy_notify_handler = handler;
}
-static void ev_destroy_notify(xcb_generic_event_t *generic_ev) {
+static void
+ev_destroy_notify(xcb_generic_event_t *generic_ev) {
+ fprintf(stderr, "destroy_notify\n");
if (destroy_notify_handler) {
xcb_destroy_notify_event_t *ev = (xcb_destroy_notify_event_t *) generic_ev;
destroy_notify_handler();
@@ -594,11 +1233,14 @@ static void ev_destroy_notify(xcb_generic_event_t *generic_ev) {
typedef void (*enter_notify_handler_t)();
static enter_notify_handler_t enter_notify_handler = NULL;
-void wm_set_enter_notify_handler(enter_notify_handler_t handler) {
+void
+wm_set_enter_notify_handler(enter_notify_handler_t handler) {
enter_notify_handler = handler;
}
-static void ev_enter_notify(xcb_generic_event_t *generic_ev) {
+static void
+ev_enter_notify(xcb_generic_event_t *generic_ev) {
+ fprintf(stderr, "enter_notify\n");
if (enter_notify_handler) {
xcb_enter_notify_event_t *ev = (xcb_enter_notify_event_t *) generic_ev;
enter_notify_handler();
@@ -609,11 +1251,14 @@ static void ev_enter_notify(xcb_generic_event_t *generic_ev) {
typedef void (*key_press_handler_t)();
static key_press_handler_t key_press_handler = NULL;
-void wm_set_key_press_handler(key_press_handler_t handler) {
+void
+wm_set_key_press_handler(key_press_handler_t handler) {
key_press_handler = handler;
}
-static void ev_key_press(xcb_generic_event_t *generic_ev) {
+static void
+ev_key_press(xcb_generic_event_t *generic_ev) {
+ fprintf(stderr, "key_press\n");
if (key_press_handler) {
xcb_key_press_event_t *ev = (xcb_key_press_event_t *) generic_ev;
key_press_handler();
@@ -624,11 +1269,14 @@ static void ev_key_press(xcb_generic_event_t *generic_ev) {
typedef void (*map_request_handler_t)();
static map_request_handler_t map_request_handler = NULL;
-void wm_set_map_request_handler(map_request_handler_t handler) {
+void
+wm_set_map_request_handler(map_request_handler_t handler) {
map_request_handler = handler;
}
-static void ev_map_request(xcb_generic_event_t *generic_ev) {
+static void
+ev_map_request(xcb_generic_event_t *generic_ev) {
+ fprintf(stderr, "map_resquest\n");
if (map_request_handler) {
xcb_map_request_event_t *ev = (xcb_map_request_event_t *) generic_ev;
map_request_handler();
@@ -639,11 +1287,14 @@ static void ev_map_request(xcb_generic_event_t *generic_ev) {
typedef void (*unmap_notify_handler_t)();
static unmap_notify_handler_t unmap_notify_handler = NULL;
-void wm_set_unmap_notify_handler(unmap_notify_handler_t handler) {
+void
+wm_set_unmap_notify_handler(unmap_notify_handler_t handler) {
unmap_notify_handler = handler;
}
-static void ev_unmap_notify(xcb_generic_event_t *generic_ev) {
+static void
+ev_unmap_notify(xcb_generic_event_t *generic_ev) {
+ fprintf(stderr, "unmap_notify\n");
if (unmap_notify_handler) {
xcb_unmap_notify_event_t *ev = (xcb_unmap_notify_event_t *) generic_ev;
unmap_notify_handler();
@@ -654,11 +1305,14 @@ static void ev_unmap_notify(xcb_generic_event_t *generic_ev) {
typedef void (*mapping_notify_handler_t)();
static mapping_notify_handler_t mapping_notify_handler = NULL;
-void wm_set_mapping_notify_handler(mapping_notify_handler_t handler) {
+void
+wm_set_mapping_notify_handler(mapping_notify_handler_t handler) {
mapping_notify_handler = handler;
}
-static void ev_mapping_notify(xcb_generic_event_t *generic_ev) {
+static void
+ev_mapping_notify(xcb_generic_event_t *generic_ev) {
+ fprintf(stderr, "mapping_notify\n");
if (mapping_notify_handler) {
xcb_mapping_notify_event_t *ev = (xcb_mapping_notify_event_t *) generic_ev;
mapping_notify_handler();
@@ -669,11 +1323,13 @@ static void ev_mapping_notify(xcb_generic_event_t *generic_ev) {
typedef void (*configure_notify_handler_t)(int16_t, int16_t, uint16_t, uint16_t);
static configure_notify_handler_t configure_notify_handler = NULL;
-void wm_set_configure_notify_handler(configure_notify_handler_t handler) {
+void
+wm_set_configure_notify_handler(configure_notify_handler_t handler) {
configure_notify_handler = handler;
}
-static void ev_configure_notify(xcb_generic_event_t *generic_ev) {
+static void
+ev_configure_notify(xcb_generic_event_t *generic_ev) {
if (configure_notify_handler) {
xcb_configure_notify_event_t *ev = (xcb_configure_notify_event_t *) generic_ev;
configure_notify_handler(ev->x, ev->y, ev->width, ev->height);
@@ -684,11 +1340,13 @@ static void ev_configure_notify(xcb_generic_event_t *generic_ev) {
typedef void (*circulate_request_handler_t)();
static circulate_request_handler_t circulate_request_handler = NULL;
-void wm_set_circulate_request_handler(circulate_request_handler_t handler) {
+void
+wm_set_circulate_request_handler(circulate_request_handler_t handler) {
circulate_request_handler = handler;
}
-static void ev_circulate_request(xcb_generic_event_t *generic_ev) {
+static void
+ev_circulate_request(xcb_generic_event_t *generic_ev) {
if (circulate_request_handler) {
xcb_circulate_request_event_t *ev = (xcb_circulate_request_event_t *) generic_ev;
circulate_request_handler();
@@ -699,11 +1357,13 @@ static void ev_circulate_request(xcb_generic_event_t *generic_ev) {
typedef void (*button_press_handler_t)();
static button_press_handler_t button_press_handler = NULL;
-void wm_set_button_press_handler(button_press_handler_t handler) {
+void
+wm_set_button_press_handler(button_press_handler_t handler) {
button_press_handler = handler;
}
-static void ev_button_press(xcb_generic_event_t *generic_ev) {
+static void
+ev_button_press(xcb_generic_event_t *generic_ev) {
if (button_press_handler) {
xcb_button_press_event_t *ev = (xcb_button_press_event_t *) generic_ev;
button_press_handler();
@@ -714,11 +1374,13 @@ static void ev_button_press(xcb_generic_event_t *generic_ev) {
typedef void (*client_message_handler_t)();
static client_message_handler_t client_message_handler = NULL;
-void wm_set_client_message_handler(client_message_handler_t handler) {
+void
+wm_set_client_message_handler(client_message_handler_t handler) {
client_message_handler = handler;
}
-static void ev_client_message(xcb_generic_event_t *generic_ev) {
+static void
+ev_client_message(xcb_generic_event_t *generic_ev) {
if (client_message_handler) {
xcb_client_message_event_t *ev = (xcb_client_message_event_t *) generic_ev;
client_message_handler();