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:
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);