kbgwm

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

commit 60a78ac8bf0f69597a269c65a5b3d204b2161f13
parent 2ad34c220c7c4df47061acfacd205450aa8ea315
Author: Kebigon <git@kebigon.xyz>
Date:   Wed, 11 Aug 2021 16:49:23 +0900

First attempt at handling multiple monitors using randr

Diffstat:
MMakefile | 4++--
Mclient.c | 25++++++++++++++++++++++++-
Mclient.h | 1+
Mkbgwm.c | 2++
Amonitor.c | 204+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Amonitor.h | 40++++++++++++++++++++++++++++++++++++++++
6 files changed, 273 insertions(+), 3 deletions(-)

diff --git a/Makefile b/Makefile @@ -1,7 +1,7 @@ -OBJ = kbgwm.o xcbutils.o events.o client.o list.o log.o +OBJ = kbgwm.o xcbutils.o events.o client.o list.o log.o monitor.o CFLAGS+=-g -std=c99 -Wall -Wextra -pedantic -Wstrict-overflow -fno-strict-aliasing -I/usr/local/include -I/usr/X11R6/include -march=native -LDFLAGS+=-L/usr/local/lib -L/usr/X11R6/lib -lxcb -lxcb-icccm -lxcb-keysyms +LDFLAGS+=-L/usr/local/lib -L/usr/X11R6/lib -lxcb -lxcb-icccm -lxcb-keysyms -lxcb-randr all: clean kbgwm diff --git a/client.c b/client.c @@ -19,6 +19,7 @@ #include "kbgwm.h" #include "list.h" #include "log.h" +#include "monitor.h" #include "xcbutils.h" #include <assert.h> @@ -61,6 +62,21 @@ void client_add_workspace(struct client *client, uint_fast8_t workspace) list_add(&workspaces[workspace], client); } +// Update the monitor of a client based on its current position +void client_update_monitor(struct client *client) +{ + const uint16_t center_x = client->x + (client->width >> 1); // x + width/2 + const uint16_t center_y = client->y + (client->height >> 1); // y + height/2 + + // We're still in the current monitor -> nothing to be done + if (client->monitor != NULL && monitor_contains(client->monitor, center_x, center_y)) + return; + + struct item *item = monitor_find_by_position(center_x, center_y); + if (item != NULL) + client->monitor = item->data; +} + void client_create(xcb_window_t id) { LOG_DEBUG_VA("client_create: id=%d", id); @@ -96,6 +112,7 @@ void client_create(xcb_window_t id) new_client->max_height = max_size ? hints.max_height : INT32_MAX; client_sanitize_dimensions(new_client); + client_update_monitor(new_client); LOG_DEBUG_VA( "new window: id=%d x=%d y=%d width=%d height=%d min_width=%d min_height=%d max_width=%d " @@ -245,8 +262,14 @@ void client_maximize(struct client *client) assert(!client->maximized); client->maximized = true; + uint32_t *values; + + if (client->monitor != NULL) + values = (uint32_t[]){client->monitor->x, client->monitor->y, client->monitor->width, + client->monitor->height, 0}; + else + values = (uint32_t[]){0, 0, screen->width_in_pixels, screen->height_in_pixels, 0}; - uint32_t values[] = {0, 0, screen->width_in_pixels, screen->height_in_pixels, 0}; xcb_configure_window(c, client->id, XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT | XCB_CONFIG_WINDOW_BORDER_WIDTH, diff --git a/client.h b/client.h @@ -28,6 +28,7 @@ struct client int32_t min_width, min_height; int32_t max_width, max_height; bool maximized; + struct monitor *monitor; }; void client_grab_buttons(struct client *, bool); diff --git a/kbgwm.c b/kbgwm.c @@ -19,6 +19,7 @@ #include "events.h" #include "list.h" #include "log.h" +#include "monitor.h" #include "xcbutils.h" #include <X11/keysym.h> #include <assert.h> @@ -371,6 +372,7 @@ int main(void) setup_keyboard(); setup_screen(); + setup_monitors(); setup_events(); // Event loop diff --git a/monitor.c b/monitor.c @@ -0,0 +1,204 @@ +/* + * kbgwm, a sucklessy floating window manager + * Copyright (c) 2020-2021, Kebigon <git@kebigon.xyz> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "monitor.h" + +#include <stdlib.h> + +#include "list.h" +#include "log.h" +#include "xcbutils.h" + +extern xcb_connection_t *c; +extern xcb_screen_t *screen; +extern const uint_least8_t workspaces_length; +extern struct item *workspaces[]; + +uint8_t randr_base = -1; +struct item *monitors; + +void setup_randr_monitors(xcb_randr_get_screen_resources_current_reply_t *); +void monitor_add(xcb_randr_output_t, int16_t, int16_t, uint16_t, uint16_t); +struct item *monitor_find_by_id(const xcb_randr_output_t); + +void setup_monitors() +{ + const xcb_query_extension_reply_t *extension = xcb_get_extension_data(c, &xcb_randr_id); + if (!extension->present) + { + LOG_WARN("Unable to retrieve the RANDR extension"); + return; + } + + const xcb_randr_get_screen_resources_current_cookie_t cookie = + xcb_randr_get_screen_resources_current(c, screen->root); + xcb_randr_get_screen_resources_current_reply_t *res = + xcb_randr_get_screen_resources_current_reply(c, cookie, NULL); + if (NULL == res) + { + LOG_WARN("Unable to retrieve the RANDR screen resources"); + return; + } + + setup_randr_monitors(res); + free(res); + + randr_base = extension->first_event; + xcb_randr_select_input( + c, 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); +} + +void monitor_add(xcb_randr_output_t id, int16_t x, int16_t y, uint16_t width, uint16_t height) +{ + LOG_DEBUG_VA("monitor_add: id=%d, x=%d, y=%d, width=%d, height=%d", id, x, y, width, height); + + struct monitor *monitor = emalloc(sizeof(struct monitor)); + monitor->id = id; + monitor->x = x; + monitor->y = y; + monitor->width = width; + monitor->height = height; + + list_add(&monitors, monitor); +} + +void monitor_update(struct monitor *monitor, xcb_randr_get_crtc_info_reply_t *crtc) +{ + if (crtc->x != monitor->x) + monitor->x = crtc->x; + if (crtc->y != monitor->y) + monitor->y = crtc->y; + if (crtc->width != monitor->width) + monitor->width = crtc->width; + if (crtc->height != monitor->height) + monitor->height = crtc->height; + + // TODO: rearrange clients +} + +void monitor_delete(struct item *mon_item) +{ + // Replace the deleted by the next one, or the first one + struct monitor *new_mon = (mon_item->next != NULL ? mon_item->next : monitors)->data; + struct monitor *monitor = list_remove(&monitors, mon_item); + + struct client *client; + struct item *cli_item; + + // move clients to another monitor + for (uint_fast8_t workspace = 0; workspace != workspaces_length; workspace++) + { + for (cli_item = workspaces[workspace]; cli_item != NULL; cli_item = cli_item->next) + { + client = cli_item->data; + + // Not the removed monitor + if (client->monitor->id != monitor->id) + continue; + + client->monitor = new_mon; + client_sanitize_position(client); + client_sanitize_dimensions(client); + } + } +} + +void setup_randr_monitors(xcb_randr_get_screen_resources_current_reply_t *res) +{ + int32_t len = xcb_randr_get_screen_resources_current_outputs_length(res); + xcb_randr_output_t *outputs = xcb_randr_get_screen_resources_current_outputs(res); + + int_fast32_t i; + + // Request information for all outputs. + xcb_randr_get_output_info_cookie_t ocookie[len]; + for (i = 0; i != len; i++) + { + ocookie[i] = xcb_randr_get_output_info(c, outputs[i], res->config_timestamp); + } + + xcb_randr_get_output_info_reply_t *info; + xcb_randr_get_crtc_info_reply_t *crtc; + struct item *item; + + // Process information replies + for (i = 0; i != len; i++) + { + info = xcb_randr_get_output_info_reply(c, ocookie[i], NULL); + if (info == NULL) + continue; + + // Disabled / disconnected output + if (info->crtc == XCB_NONE || info->connection == XCB_RANDR_CONNECTION_DISCONNECTED) + { + // Already registered monitor -> delete it + if ((item = monitor_find_by_id(outputs[i])) != NULL) + { + } + + continue; + } + + crtc = xcb_randr_get_crtc_info_reply( + c, xcb_randr_get_crtc_info(c, info->crtc, res->config_timestamp), NULL); + + // CRTC info not found -> skipping + if (crtc == NULL) + continue; + + // Already registered monitor -> update it + if ((item = monitor_find_by_id(outputs[i])) != NULL) + { + monitor_update(item->data, crtc); + } + // New monitor -> add it to the list + else + { + monitor_add(outputs[i], crtc->x, crtc->y, crtc->width, crtc->height); + } + + free(crtc); + free(info); + } +} + +bool monitor_contains(struct monitor *monitor, uint16_t x, uint16_t y) +{ + return monitor->x <= x && x <= monitor->x + monitor->width && // + monitor->y <= y && y <= monitor->y + monitor->height; +} + +// Find a monitor from its id +struct item *monitor_find_by_id(const xcb_randr_output_t id) +{ + for (struct item *item = monitors; item != NULL; item = item->next) + if (((struct monitor *)item->data)->id == id) + return item; + + return NULL; +} + +struct item *monitor_find_by_position(const uint16_t x, const uint16_t y) +{ + for (struct item *item = monitors; item != NULL; item = item->next) + if (monitor_contains(item->data, x, y)) + return item; + + return NULL; +} diff --git a/monitor.h b/monitor.h @@ -0,0 +1,40 @@ +/* + * kbgwm, a sucklessy floating window manager + * Copyright (c) 2020-2021, Kebigon <git@kebigon.xyz> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#pragma once + +#include <stdbool.h> +#include <xcb/randr.h> + +struct monitor +{ + xcb_randr_output_t id; + int16_t x, y; + uint16_t width, height; +}; + +void setup_monitors(); + +/** + * Return true when the monitor contains the given position, false otherwise + */ +bool monitor_contains(struct monitor *, uint16_t, uint16_t); + +/** + * Find a monitor from a position + */ +struct item *monitor_find_by_position(const uint16_t, const uint16_t);