kbgwm

sucklessy floating window manager
git clone https://git.neuralcrash.com/kbgwm.git
Log | Files | Refs | README | LICENSE

commit f9ca3e38c03ef7aedc53e65ea7e10f8907623c76
parent c96c574f8acfce2fcadf3687280fe13c6f5ad8fa
Author: Kebigon <git@kebigon.xyz>
Date:   Tue, 14 Jul 2020 17:27:53 +0900

Rework workspaces, focus and events

Diffstat:
Mconfig.h | 21+++++++++++++++------
Mkbgwm.c | 544++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
Mtypes.h | 13++++++++-----
Mxcbutils.c | 23++++++++---------------
4 files changed, 351 insertions(+), 250 deletions(-)

diff --git a/config.h b/config.h @@ -1,25 +1,34 @@ #define MODKEY XCB_MOD_MASK_4 +#define SHIFT XCB_MOD_MASK_SHIFT #define FOCUS_COLOR 0xFF0000 #define UNFOCUS_COLOR 0x005577 #define BORDER_WIDTH 1 +/* + * Number of workspaces + * They will be numbered from 0 to NB_WORKSPACES-1 + */ +#define NB_WORKSPACES 10 + static const char* termcmd [] = { "alacritty", NULL }; static const char* menucmd [] = { "dmenu_run", NULL }; #define WORKSPACEKEYS(KEY,WORKSPACE) \ - { MODKEY|XCB_MOD_MASK_SHIFT, KEY, sendToWorkspace, {.i = WORKSPACE} }, \ - { MODKEY, KEY, changeWorkspace, {.i = WORKSPACE} }, + { MODKEY|XCB_MOD_MASK_SHIFT, KEY, workspace_send, {.i = WORKSPACE} }, \ + { MODKEY, KEY, workspace_change, {.i = WORKSPACE} }, static Key keys [] = { - { MODKEY, XK_Return, start, { .cmd = termcmd }}, - { MODKEY, XK_p, start, { .cmd = menucmd }}, - { MODKEY, XK_Page_Up, previousWorkspace, { 0 }}, - { MODKEY, XK_Page_Down, nextWorkspace, { 0 }}, + { MODKEY, XK_Return, start, { .cmd = termcmd }}, + { MODKEY, XK_p, start, { .cmd = menucmd }}, + { MODKEY, XK_Page_Up, workspace_previous, { 0 }}, + { MODKEY, XK_Page_Down, workspace_next, { 0 }}, + { MODKEY|SHIFT, XK_Tab, focus_next, { .b = true }}, + { MODKEY, XK_Tab, focus_next, { .b = false }}, WORKSPACEKEYS(XK_Home, 0) WORKSPACEKEYS(XK_1, 0) WORKSPACEKEYS(XK_2, 1) diff --git a/kbgwm.c b/kbgwm.c @@ -1,12 +1,12 @@ #include <stdio.h> #include <stdlib.h> -#include <stdbool.h> +#include <stdint.h> #include <unistd.h> #include <assert.h> #include <xcb/xcb.h> #include <X11/keysym.h> - +#include <inttypes.h> #include "xcbutils.h" #define IGNORE_LOCK(modifier) (modifier & ~(XCB_MOD_MASK_LOCK)) @@ -15,14 +15,27 @@ static void start(const Arg* arg); static void mousemove(const Arg* arg); static void mouseresize(const Arg* arg); -static void nextWorkspace(const Arg* arg); -static void previousWorkspace(const Arg* arg); -static void changeWorkspace(const Arg* arg); -static void sendToWorkspace(const Arg* arg); -#include "config.h" +static void client_add(client*); +static void client_add_workspace(client*, uint_fast8_t); +static client* client_find(xcb_window_t); +static client* client_remove(); +static client* client_remove_workspace(uint_fast8_t); +static void focus_apply(client*); +static void focus_next(const Arg*); +static void handle_button_press(xcb_button_press_event_t*); +static void handle_button_release(xcb_button_release_event_t*); +static void handle_key_press(xcb_key_press_event_t*); +static void handle_map_request(xcb_map_request_event_t*); +static void handle_mapping_notify(xcb_mapping_notify_event_t*); +static void handle_motion_notify(xcb_motion_notify_event_t*); +static void workspace_change(const Arg*); +static void workspace_next(const Arg*); +static void workspace_previous(const Arg*); +static void workspace_send(const Arg*); +static void workspace_set(uint_fast8_t); -static void onWorkspaceChanged(); +#include "config.h" #define BORDER_WIDTH_X2 (BORDER_WIDTH << 1) @@ -35,53 +48,9 @@ xcb_screen_t* screen; uint_least16_t previous_x; uint_least16_t previous_y; -uint_least8_t currentWorkspace = 0; - -static window* focusedWindow; -window* windows; - -window* findWindow(xcb_window_t id) -{ - window* window = windows; - - do - { - if (window->id == id) - return (window); - } - while ((window = window->next) != NULL); - - return NULL; -} - -void focus(xcb_window_t id) -{ - printf("focus: id=%d\n", id); - - uint32_t values[1]; - - window* window = findWindow(id); - assert(window != NULL); - - // A window was previously focused -> we change its color - if (focusedWindow != NULL) - { - xcb_change_window_attributes(c, focusedWindow->id, XCB_CW_BORDER_PIXEL, (uint32_t[]) { 0xFF000000 | UNFOCUS_COLOR }); - } - - xcb_change_window_attributes(c, id, XCB_CW_BORDER_PIXEL, (uint32_t[]) { 0xFF000000 | FOCUS_COLOR }); - - // Raise the window so it is on top - values[0] = XCB_STACK_MODE_TOP_IF; - xcb_configure_window(c, id, XCB_CONFIG_WINDOW_STACK_MODE, values); - - // Set the keyboard on the focused window - xcb_set_input_focus(c, XCB_NONE, id, XCB_CURRENT_TIME); - xcb_flush(c); - - focusedWindow = window; - printf("focus set to: id=%d\n", focusedWindow->id); -} +#define focusedWindow workspaces[current_workspace] +uint_fast8_t current_workspace = 0; +static client* workspaces[NB_WORKSPACES]; void setupEvents() { @@ -107,81 +76,228 @@ void setupEvents() xcb_flush(c); } -void onMappingNotify(xcb_mapping_notify_event_t* event) +void mousemove(__attribute__((unused)) const Arg* arg) { - printf("sequence %d\n", event->sequence); - printf("request %d\n", event->request); - printf("first_keycode %d\n", event->first_keycode); - printf("count %d\n", event->count); + printf("mousemove\n"); + moving = true; + + xcb_grab_pointer(c, 0, screen->root, + XCB_EVENT_MASK_BUTTON_MOTION | XCB_EVENT_MASK_BUTTON_RELEASE, + XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, screen->root, + XCB_NONE, + XCB_CURRENT_TIME); + xcb_flush(c); } -void handle_keypress(xcb_key_press_event_t* event) +void mouseresize(__attribute__((unused)) const Arg* arg) { - xcb_keysym_t keysym = xcb_get_keysym(event->detail); + printf("mouseresize\n"); + resizing = true; - printf("received key: mod %d key %d\n", event->state, keysym); + xcb_grab_pointer(c, 0, screen->root, + XCB_EVENT_MASK_BUTTON_MOTION | XCB_EVENT_MASK_BUTTON_RELEASE, + XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, screen->root, + XCB_NONE, + XCB_CURRENT_TIME); + xcb_flush(c); +} - for (uint_fast8_t i = 0; i < LENGTH(keys); i++) +void eventLoop() +{ + while (!quit) { - printf("testing key: mod %d key %d\n", keys[i].modifiers, - keys[i].keysym); + xcb_generic_event_t* event = xcb_wait_for_event(c); + printf("event: received\n"); - if (keysym == keys[i].keysym && MATCH_MODIFIERS(keys[i].modifiers, event->state)) + switch (event->response_type & ~0x80) { - printf("Key found !\n"); - keys[i].func(&keys[i].arg); - break; + case XCB_BUTTON_PRESS: + handle_button_press((xcb_button_press_event_t*) event); + break; + + case XCB_BUTTON_RELEASE: + handle_button_release((xcb_button_release_event_t*) event); + break; + + case XCB_KEY_PRESS: + handle_key_press((xcb_key_press_event_t*) event); + break; + + case XCB_MAP_REQUEST: + handle_map_request((xcb_map_request_event_t*) event); + break; + + case XCB_MAPPING_NOTIFY: + handle_mapping_notify((xcb_mapping_notify_event_t*) event); + break; + + case XCB_MOTION_NOTIFY: + handle_motion_notify((xcb_motion_notify_event_t*) event); + break; + + default: + printf("Received event, response type %d\n", + event->response_type & ~0x80); + break; } + + printf("event: handled\n"); + free(event); + printf("event: freed\n"); + printf("\n"); } } -void handle_maprequest(xcb_map_request_event_t* event) +void start(const Arg* arg) { - xcb_get_geometry_reply_t* geometry; + printf("start %s\n", arg->cmd[0]); - printf("received map request: parent %d xcb_window_t %d\n", event->parent, event->window); + if (fork() == 0) + { + // Child process + setsid(); - geometry = xcb_get_geometry_reply(c, xcb_get_geometry(c, event->window), NULL); + if (execvp((char*) arg->cmd[0], (char**) arg->cmd) == -1) + { + perror(arg->cmd[0]); + exit(-1); + } + } +} + +/* + * Clients + */ - window* window = emalloc(sizeof window); - window->id = event->window; - window->x = geometry->x; - window->y = geometry->y; - window->width = geometry->width; - window->height = geometry->height; +// Add a client to the current workspace list +void client_add(client* client) +{ + client_add_workspace(client, current_workspace); +} - printf("Setting new window to workspace %d\n", currentWorkspace); - window->workspace = currentWorkspace; +// Add a client to a workspace list +void client_add_workspace(client* client, uint_fast8_t workspace) +{ + assert(client != NULL); + assert(workspace < NB_WORKSPACES); - if (windows != NULL) + if (workspaces[workspace] == NULL) { - window->next = windows; + client->next = client; + client->previous = client; + } + else { + client->next = workspaces[workspace]; + client->previous = workspaces[workspace]->previous; + client->next->previous = client; + client->previous->next = client; + } + + workspaces[workspace] = client; +} + +// Find a client in the current workspace list +client* client_find(xcb_window_t id) +{ + client* client = workspaces[current_workspace]; + + if (client != NULL) + do + { + if (client->id == id) + return client; + } + while ((client = client->next) != workspaces[current_workspace]); + + return NULL; +} + +// Remove the focused client from the current workspace list +client* client_remove() +{ + return client_remove_workspace(current_workspace); +} + +// Remove the focused client from a workspace list +client* client_remove_workspace(uint_fast8_t workspace) +{ + assert(workspace < NB_WORKSPACES); + assert(workspaces[workspace] != NULL); + + client* client = workspaces[workspace]; + if (client->next == client) + workspaces[workspace] = NULL; + else { + client->previous->next = client->next; + client->next->previous = client->previous; + workspaces[workspace] = client->next; } - windows = window; + return client; +} + +/* + * Focus + */ - printf("new window: id=%d x=%d y=%d width=%d height=%d\n", window->id, window->x, window->y, window->width, window->height); +void focus_apply(client* new_focus) +{ + assert(workspaces[current_workspace] != NULL); + assert(new_focus != NULL); + + printf("focus_apply: old=%d new=%d\n", workspaces[current_workspace]->id, new_focus->id); + + uint32_t values[1]; - focus(event->window); + // We change the color of the previously focused client + xcb_change_window_attributes(c, workspaces[current_workspace]->id, XCB_CW_BORDER_PIXEL, (uint32_t[]) { 0xFF000000 | UNFOCUS_COLOR }); - xcb_configure_window(c, window->id, XCB_CONFIG_WINDOW_BORDER_WIDTH, (uint32_t[]) { BORDER_WIDTH }); + // We change the color of the focused client + xcb_change_window_attributes(c, new_focus->id, XCB_CW_BORDER_PIXEL, (uint32_t[]) { 0xFF000000 | FOCUS_COLOR }); - // Display the window - xcb_map_window(c, window->id); + // Raise the window so it is on top + values[0] = XCB_STACK_MODE_TOP_IF; + xcb_configure_window(c, new_focus->id, XCB_CONFIG_WINDOW_STACK_MODE, values); + + // Set the keyboard on the focused window + xcb_set_input_focus(c, XCB_NONE, new_focus->id, XCB_CURRENT_TIME); xcb_flush(c); + + printf("focus_apply: done\n"); } -void handle_buttonpress(xcb_button_press_event_t* event) +// Focus the next client in the current workspace list +// arg->b : reverse mode +void focus_next(const Arg* arg) { - printf("handle_buttonpress: child=%d\n", event->child); + // No clients in the current workspace list + // Only one client in the current workspace list + if (workspaces[current_workspace] == NULL || workspaces[current_workspace]->next == workspaces[current_workspace]) + return; // Nothing to be done + + client* new_focus = arg->b ? workspaces[current_workspace]->previous : workspaces[current_workspace]->next; + + focus_apply(new_focus); + + // Move the newly focused window to the front of the list + workspaces[current_workspace] = new_focus; +} - // Click on the root window -> ignore +/* + * Handling events from the event loop + */ + +void handle_button_press(xcb_button_press_event_t* event) +{ + printf("handle_button_press: client=%d modifier=%d button=%d\n", event->child, event->state, event->detail); + + // Click on the root window if (event->child == 0) - return; + return; // Nothing to be done - // Focus window if needed - if (event->child != focusedWindow->id) - focus(event->child); + // The window clicked is not the one in focus, we have to focus it + if (focusedWindow == NULL || event->child != focusedWindow->id) + focus_apply(client_find(event->child)); for (uint_fast8_t i = 0; i < LENGTH(buttons); i++) { @@ -190,18 +306,19 @@ void handle_buttonpress(xcb_button_press_event_t* event) previous_x = event->root_x; previous_y = event->root_y; - printf("Button found !\n"); buttons[i].func(&buttons[i].arg); break; } } + + printf("handle_button_press: done\n"); } -void handle_buttonrelease(xcb_button_release_event_t* event) +void handle_button_release(__attribute__((unused)) xcb_button_release_event_t* event) { - assert(moving || resizing); + printf("XCB_BUTTON_RELEASE\n"); - printf("handle_buttonrelease: mod=%d button=%d\n", event->state, event->detail); + assert(moving || resizing); xcb_ungrab_pointer(c, XCB_CURRENT_TIME); xcb_flush(c); @@ -210,33 +327,60 @@ void handle_buttonrelease(xcb_button_release_event_t* event) resizing = false; } -void mousemove(__attribute__((unused)) const Arg* arg) +void handle_key_press(xcb_key_press_event_t* event) { - printf("mousemove\n"); - moving = true; + printf("XCB_KEY_PRESS: detail=%d state=%d\n", event->detail, event->state); + xcb_keysym_t keysym = xcb_get_keysym(event->detail); - xcb_grab_pointer(c, 0, screen->root, - XCB_EVENT_MASK_BUTTON_MOTION | XCB_EVENT_MASK_BUTTON_RELEASE, - XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, screen->root, - XCB_NONE, - XCB_CURRENT_TIME); - xcb_flush(c); + for (uint_fast8_t i = 0; i < LENGTH(keys); i++) + { + if (keysym == keys[i].keysym && MATCH_MODIFIERS(keys[i].modifiers, event->state)) + { + keys[i].func(&keys[i].arg); + break; + } + } } -void mouseresize(__attribute__((unused)) const Arg* arg) +void handle_map_request(xcb_map_request_event_t* event) { - printf("mouseresize\n"); - resizing = true; + printf("handle_map_request: parent %d xcb_window_t %d\n", event->parent, event->window); - xcb_grab_pointer(c, 0, screen->root, - XCB_EVENT_MASK_BUTTON_MOTION | XCB_EVENT_MASK_BUTTON_RELEASE, - XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, screen->root, - XCB_NONE, - XCB_CURRENT_TIME); + xcb_get_geometry_reply_t* geometry = xcb_get_geometry_reply(c, xcb_get_geometry_unchecked(c, event->window), NULL); + + client* new_client = emalloc(sizeof(client)); + + new_client->id = event->window; + new_client->x = geometry->x; + new_client->y = geometry->y; + new_client->width = geometry->width; + new_client->height = geometry->height; + + focus_apply(new_client); + client_add(new_client); + + printf("new window: id=%d x=%d y=%d width=%d height=%d\n", new_client->id, new_client->x, new_client->y, new_client->width, new_client->height); + + xcb_configure_window(c, new_client->id, XCB_CONFIG_WINDOW_BORDER_WIDTH, (uint32_t[]) { BORDER_WIDTH }); + + // Display the client + xcb_map_window(c, new_client->id); xcb_flush(c); + + free(geometry); + + printf("handle_map_request: done\n"); +} + +void handle_mapping_notify(xcb_mapping_notify_event_t* event) +{ + printf("sequence %d\n", event->sequence); + printf("request %d\n", event->request); + printf("first_keycode %d\n", event->first_keycode); + printf("count %d\n", event->count); } -void handle_motionnotify(xcb_motion_notify_event_t* event) +void handle_motion_notify(xcb_motion_notify_event_t* event) { assert(moving || resizing); assert(focusedWindow != NULL); @@ -280,135 +424,80 @@ void handle_motionnotify(xcb_motion_notify_event_t* event) xcb_flush(c); } -void eventLoop() -{ - xcb_generic_event_t* event; - - while (!quit) - { - event = xcb_wait_for_event(c); - - switch (event->response_type & ~0x80) - { - case XCB_KEY_PRESS: - printf("\nXCB_KEY_PRESS\n"); - handle_keypress((xcb_key_press_event_t*) event); - break; - - case XCB_BUTTON_PRESS: - printf("\nXCB_BUTTON_PRESS\n"); - handle_buttonpress((xcb_button_press_event_t*) event); - break; - - case XCB_BUTTON_RELEASE: - printf("\nXCB_BUTTON_RELEASE\n"); - handle_buttonrelease((xcb_button_release_event_t*) event); - break; - - case XCB_MAP_REQUEST: - printf("\nXCB_MAP_REQUEST\n"); - handle_maprequest((xcb_map_request_event_t*) event); - break; - - case XCB_MAPPING_NOTIFY: - printf("\nXCB_MAPPING_NOTIFY\n"); - onMappingNotify((xcb_mapping_notify_event_t*) event); - break; - - case XCB_MOTION_NOTIFY: - printf("\nXCB_MOTION_NOTIFY\n"); - handle_motionnotify((xcb_motion_notify_event_t*) event); - break; - - default: - printf("Received event, response type %d\n", - event->response_type & ~0x80); - break; - } - - free(event); - } -} - -void start(const Arg* arg) -{ - printf("start %s\n", arg->cmd[0]); - - if (fork() == 0) - { - // Child process - setsid(); - - if (execvp((char*) arg->cmd[0], (char**) arg->cmd) == -1) - { - perror(arg->cmd[0]); - exit(-1); - } - } -} - /* - * Workspace + * Workspaces */ -void nextWorkspace(__attribute__((unused)) const Arg* arg) +void workspace_change(const Arg* arg) { - printf("nextWorkspace\n"); - - currentWorkspace = (currentWorkspace + 1) % 10; - onWorkspaceChanged(); + printf("workspace_change: i=%d\n", arg->i); + workspace_set(arg->i); + printf("workspace_change: done\n"); } -void previousWorkspace(__attribute__((unused)) const Arg* arg) +void workspace_next(__attribute__((unused)) const Arg* arg) { - printf("previousWorkspace\n"); - - currentWorkspace = (currentWorkspace + 9) % 10; - onWorkspaceChanged(); + printf("workspace_next\n"); + workspace_set(current_workspace + 1 == NB_WORKSPACES ? 0 : current_workspace + 1 ); + printf("workspace_next: done\n"); } -void changeWorkspace(const Arg* arg) +void workspace_previous(__attribute__((unused)) const Arg* arg) { - printf("changeWorkspace: arg=%d\n", arg->i); - - currentWorkspace = arg->i; - onWorkspaceChanged(); + printf("workspace_previous\n"); + workspace_set(current_workspace == 0 ? NB_WORKSPACES - 1 : current_workspace - 1); + printf("workspace_previous: done\n"); } -void sendToWorkspace(__attribute__((unused)) const Arg* arg) +void workspace_send(const Arg* arg) { - printf("sendToWorkspace: arg=%d\n", arg->i); + printf("workspace_send: i=%d\n", arg->i); + uint_fast8_t new_workspace = arg->i; - if (focusedWindow != NULL) - { - focusedWindow->workspace = arg->i; - xcb_unmap_window(c, focusedWindow->id); - xcb_flush(c); - focusedWindow = NULL; - } + if (current_workspace == new_workspace || workspaces[current_workspace] == NULL) + return; // Nothing to be done + + client* client = client_remove(); + client_add_workspace(client, new_workspace); + + xcb_unmap_window(c, client->id); + xcb_flush(c); + printf("workspace_send: done\n"); } -void onWorkspaceChanged() +void workspace_set(uint_fast8_t new_workspace) { - printf("onWorkspaceChanged: currentWorkspace=%d\n", currentWorkspace); - assert(currentWorkspace <= 9); + printf("workspace_set: old=%d new=%d\n", current_workspace, new_workspace); - // We currently have no clients - if (windows == NULL) - return; + if (current_workspace == new_workspace) + return; // Nothing to be done - window* window = windows; + // Unmap the clients of the current workspace (if any) + client* client = workspaces[current_workspace]; + if (client != NULL) + do { + xcb_unmap_window(c, client->id); + } + while ((client = client->next) != workspaces[current_workspace]); - do { - if (window->workspace == currentWorkspace) - xcb_map_window(c, window->id); - else - xcb_unmap_window(c, window->id); - } while ((window = window->next) != NULL); + // Map the clients of the new workspace (if any) + client = workspaces[new_workspace]; + if (client != NULL) + do { + xcb_map_window(c, client->id); + } + while ((client = client->next) != workspaces[new_workspace]); xcb_flush(c); + current_workspace = new_workspace; + + printf("workspace_set: done\n"); } +/* + * Main + */ + int main(void) { /* @@ -447,6 +536,13 @@ int main(void) xcb_flush(c); + /* + * Initialize variables + */ + + for (uint_fast8_t i = 0; i != NB_WORKSPACES; i++) + workspaces[i] = NULL; + setupEvents(); // Event loop diff --git a/types.h b/types.h @@ -1,8 +1,11 @@ #ifndef TYPES_H_ #define TYPES_H_ +#include <stdbool.h> + typedef union { + const bool b; const uint_least8_t i; const char** cmd; } @@ -23,7 +26,7 @@ Key; typedef struct { uint16_t modifiers; - xcb_keysym_t keysym; + xcb_button_t keysym; void (*func) ( const Arg* @@ -32,14 +35,14 @@ typedef struct } Button; -typedef struct window window; -struct window +typedef struct clientstruct client; +struct clientstruct { xcb_window_t id; int_least16_t x, y; uint_least16_t width, height; - uint_least8_t workspace; - window* next; + client* previous; + client* next; }; #endif /* TYPES_H_ */ diff --git a/xcbutils.c b/xcbutils.c @@ -6,25 +6,19 @@ extern xcb_connection_t* c; extern xcb_window_t root; -void* emalloc -( - size_t size -) +void* emalloc(size_t size) { void* p; - if ( !(p = malloc ( - size )) - ) + printf("client size=%zd\n", size); + + if (!(p = malloc( size ))) { - printf ( - "Out of memory" ); - exit ( - -1 ); + printf ("Out of memory" ); + exit(-1); } - return - (p); + return p; } /* @@ -145,4 +139,4 @@ xcb_keysym_t xcb_get_keysym keysyms ); return (keysym); -} -\ No newline at end of file +}