From 69c69200d8cb18195bfb805d5ea5201a64182cfe Mon Sep 17 00:00:00 2001 From: Zedhugh Chen Date: Fri, 22 Aug 2025 23:01:25 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=B8=83=E5=B1=80=E7=AE=97?= =?UTF-8?q?=E6=B3=95=E5=B9=B6=E5=9C=A8=E6=96=B0=E5=BB=BA=E7=AA=97=E5=8F=A3?= =?UTF-8?q?=E6=97=B6=E5=BA=94=E7=94=A8=E8=A7=84=E5=88=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/CMakeLists.txt | 3 +- src/config.h | 7 +++ src/include/types.h | 6 +-- src/include/xcb.h | 7 +++ src/layout.c | 98 ++++++++++++++++++++++++++++++++++++++++++ src/layout.h | 14 ++++++ src/main.c | 102 +++++++++++++++++++++++++++++++++++++++----- src/renderer/text.c | 2 +- src/xcb/window.c | 28 ++++++++++-- 9 files changed, 248 insertions(+), 19 deletions(-) create mode 100644 src/layout.c create mode 100644 src/layout.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 97280a8..6066212 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -15,7 +15,7 @@ project(${APP_NAME} LANGUAGES C ) -add_executable(${APP_NAME} main.c utils.c) +add_executable(${APP_NAME} main.c utils.c layout.c) # use C17 set_property(TARGET ${APP_NAME} PROPERTY C_STANDARD 17) @@ -58,6 +58,7 @@ target_include_directories(${APP_NAME} ) target_link_libraries(${APP_NAME} + PRIVATE m PRIVATE ${GLIB_LIBRARIES} PRIVATE ${CAIRO_XCB_LIBRARIES} PRIVATE ${LIB_XCB_NAME} diff --git a/src/config.h b/src/config.h index b3fca1a..0a2d1b8 100644 --- a/src/config.h +++ b/src/config.h @@ -5,6 +5,7 @@ #include #include "action.h" +#include "layout.h" #include "types.h" /* 壁纸列表,支持通配符和 shell 环境变量拼接 */ @@ -44,6 +45,12 @@ const char *const default_monitor_tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9", NULL, }; +const layout_t layouts[] = { + {"[]=", tile}, + {"[M]", monocle}, + {"><>", NULL}, +}; + /* 新建窗口规则,以 NULL 结束 */ const rule_t rules[] = { /* client 固定规则 */ diff --git a/src/include/types.h b/src/include/types.h index d7202cb..d0a113c 100644 --- a/src/include/types.h +++ b/src/include/types.h @@ -19,7 +19,7 @@ struct monitor_t { int32_t monitor_x, monitor_y, window_x, window_y; uint32_t monitor_width, monitor_height, window_width, window_height; - uint32_t selected_tag; + uint32_t selected_tags; char **tags; /* 为了避免引入 cairo 相关头文件,这里用 void * @@ -65,8 +65,8 @@ typedef struct rule_t { /* 规则适用的屏幕总数,若设置为 0 ,则此条规则适用于任意屏幕数 */ const uint32_t monitor_amount; - uint32_t tags; - uint32_t monitor_index; + uint32_t tags; /* tags 必须设置为大于 0 才是有效设置 */ + uint32_t monitor_index; /* 仅当 tags 为有效设置时该设置才生效 */ bool switch_to_tag; bool fullscreen; diff --git a/src/include/xcb.h b/src/include/xcb.h index 4691563..d2a9b16 100644 --- a/src/include/xcb.h +++ b/src/include/xcb.h @@ -24,6 +24,13 @@ xcb_window_t get_root_window(void); bool get_window_visible(xcb_window_t window); xcb_window_t get_transient_window_for(xcb_window_t window); +typedef struct window_class_instance_t { + char class[128]; + char instance[128]; +} window_class_instance_t; +void get_window_class_instance(xcb_window_t window, + window_class_instance_t *class_instance); + typedef struct window_geometry_t { int32_t x, y; uint32_t width, height, border_width; diff --git a/src/layout.c b/src/layout.c new file mode 100644 index 0000000..7d6cd63 --- /dev/null +++ b/src/layout.c @@ -0,0 +1,98 @@ +#include "layout.h" + +#include +#include +#include + +#include "types.h" + +client_t *next_visible_client(client_t *c) { + while (c && + (c->floating || c->fullscreen || c->maximize || !CLIENT_VISIBLE(c))) { + c = c->next; + } + return c; +} + +void maximize_client(client_t *c) { + monitor_t *m = c->monitor; + c->x = m->window_x; + c->y = m->window_y; + c->width = m->window_width; + c->height = m->window_height; +} + +void fullscreen_client(client_t *c) { + monitor_t *m = c->monitor; + c->x = m->monitor_x; + c->y = m->monitor_y; + c->width = m->monitor_width; + c->height = m->monitor_height; +} + +void monocle(monitor_t *monitor) { + client_t *c = next_visible_client(monitor->clients); + for (; c; c = next_visible_client(c->next)) maximize_client(c); +} + +void tile(monitor_t *monitor) { + uint32_t n; + client_t *c = NULL; + for (n = 0, c = next_visible_client(monitor->clients); c; + c = next_visible_client(c->next), ++n); + + if (n == 0) { + return; + } + + uint32_t columns = (uint32_t)floor(sqrt(n)); + if (columns * (columns + 1) <= n) { + columns++; + } + uint32_t rows_in_other_cols = n / columns; + uint32_t rows_in_main_col; + while ((rows_in_main_col = n - (columns - 1) * rows_in_other_cols) > + rows_in_other_cols) { + rows_in_other_cols++; + } + + uint32_t width_avg = monitor->window_width / columns; + uint32_t width_for_main_col = + monitor->window_width - (columns - 1) * width_avg; + uint32_t width_for_other_cols = width_avg; + + uint32_t i = 0, row = 0, col = 0, row_count, width, height; + int32_t y = monitor->window_y, x = monitor->window_x; + for (c = next_visible_client(monitor->clients); c; + c = next_visible_client(c->next), i++) { + if (i < rows_in_main_col) { + row = i; + width = width_for_main_col; + row_count = rows_in_main_col; + } else { + width = width_for_other_cols; + row_count = rows_in_other_cols; + if (((i - rows_in_main_col) % row_count) == 0) { + col++; + row = 0; + x += col == 1 ? width_for_main_col : width_for_other_cols; + y = monitor->window_y; + } + } + + uint32_t h_avg = monitor->window_height / row_count; + uint32_t h_main = monitor->window_height - h_avg * (row_count - 1); + height = row == 0 ? h_main : h_avg; + c->x = x; + c->y = y; + c->width = width - 2 * c->border_width; + c->height = height - 2 * c->border_width; + y += height; + } +} + +void calc_clients_geometry_of_monitor(monitor_t *monitor) { + if (monitor && monitor->layout && monitor->layout->arrange) { + monitor->layout->arrange(monitor); + } +} diff --git a/src/layout.h b/src/layout.h new file mode 100644 index 0000000..8820c61 --- /dev/null +++ b/src/layout.h @@ -0,0 +1,14 @@ +#pragma once + +#include "types.h" + +#define CLIENT_VISIBLE(C) ((C)->tags & (C)->monitor->selected_tags) + +/** + * 获取下一个需要被布局算法自动管理的可见 client + */ +client_t *next_visible_client(client_t *client); +void maximize_client(client_t *client); +void fullscreen_client(client_t *client); +void monocle(monitor_t *monitor); +void tile(monitor_t *monitor); diff --git a/src/main.c b/src/main.c index 2606d20..5d104d4 100644 --- a/src/main.c +++ b/src/main.c @@ -14,6 +14,7 @@ #include "action.h" #include "config.h" #include "event-adapter.h" +#include "layout.h" #include "renderer.h" #include "types.h" #include "utils.h" @@ -124,6 +125,21 @@ static void print_window_list(window_list_t *window_list) { printf("window[%u]: 0x%x\n", i, window_list->list[i]); } } +static char *bool2string(bool b) { return b ? "true" : "false"; } +static void print_client(client_t *c) { + if (!c) return; + + printf("======= client info ========\n"); + printf("window: 0x%x, x: %d, y: %d, w: %u, h: %u, bw: %u\n", c->window, c->x, + c->y, c->width, c->height, c->border_width); + printf("floating: %s, fullscreen: %s, maximize: %s\n", + bool2string(c->floating), bool2string(c->fullscreen), + bool2string(c->maximize)); + printf("tags: %b, monitor selected tags: %b\n", c->tags, + c->monitor->selected_tags); + printf("name: %s\n", c->name); + printf("===== client info end ======\n"); +} /* 调试函数结束 */ void init_monitors(void) { @@ -137,6 +153,7 @@ void init_monitors(void) { m->monitor_width = mr->width; m->monitor_height = mr->height; m->index = i; + m->layout = &layouts[0]; if (prev_monitor) { prev_monitor->next = m; @@ -153,7 +170,7 @@ void init_monitors(void) { } for (monitor_t *m = monitors; m; m = m->next) { m->tags = get_monitor_tags(i, m->index); - m->selected_tag = 0x1; + m->selected_tags = 0x1; } } @@ -426,15 +443,13 @@ void configure_request(configure_request_event_t *event) { if (!client) { configure_window(event->window, event->x, event->y, event->width, event->height, event->border_width); - flush_xcb_connection(); - return; + } else { + configure_window(client->window, client->x, client->y, client->width, + client->height, client->border_width); + printf("configure request: %s\n", client->name); } - printf("window: %u, parent: %u, sibling: %u\n", event->window, event->parent, - event->sibling); - printf("x: %d, y: %d, w: %u, h: %u, bw: %u\n", event->x, event->y, - event->width, event->height, event->border_width); - printf("mask: %u, stack: %u\n", event->value_mask, event->stack_mode); + flush_xcb_connection(); } void map_request(map_request_event_t *event) { @@ -488,16 +503,81 @@ void update_client_name(client_t *client) { free(name); } -void apply_rules(client_t *client) { /* TODO: */ } +void apply_rules(client_t *client) { + uint32_t monitor_amount = 0; + for (monitor_t *m = monitors; m; m = m->next) monitor_amount++; + + window_class_instance_t class_instance; + get_window_class_instance(client->window, &class_instance); + printf("class: %s, instance: %s\n", class_instance.class, + class_instance.instance); + + bool switch_to_tag = 0; + for (int i = 0; i < LENGTH(rules); i++) { + const rule_t *r = &rules[i]; + if (!((!r->monitor_amount || monitor_amount == r->monitor_amount) && + (!r->title || strstr(client->name, r->title)) && + (!r->class || strstr(class_instance.class, r->class)) && + (!r->instance || strstr(class_instance.instance, r->instance)))) { + continue; + } + + switch_to_tag = r->switch_to_tag; + client->fullscreen = r->fullscreen; + client->maximize = r->maximize; + client->floating = r->floating; + + if (r->tags) { + monitor_t *m = monitors; + for (uint32_t i = 0; i < r->monitor_index; i++) m = m ? m->next : NULL; + if (!m) continue; + uint32_t tags_mask = 0; + for (uint32_t i = 0; m->tags[i]; i++) { + tags_mask |= 1 << 0; + } + client->tags = r->tags & tags_mask; + } + } + if (!client->tags) { + client->monitor = current_monitor; + client->tags = current_monitor->selected_tags; + } + if (switch_to_tag) { + current_monitor = client->monitor; + current_monitor->selected_tags = client->tags; + } + + printf("apply rules done\n"); +} void attach_client(client_t *client) { client->next = client->monitor->clients; client->monitor->clients = client; } -void show_hide_client(client_t *client) { /* TODO: */ } +void show_hide_client(client_t *client) { + if (!client || CLIENT_VISIBLE(client)) return; + client->x = client->monitor->monitor_x; + client->y = client->monitor->monitor_y + client->monitor->monitor_height; + configure_window(client->window, client->x, client->y, client->window, + client->height, client->border_width); +} -void apply_monitor_layout(monitor_t *monitor) { /* TODO: */ } +void apply_monitor_layout(monitor_t *monitor) { + if (!monitor || !monitor->layout || !monitor->layout->arrange) return; + + monitor->layout->arrange(monitor); + print_client(monitor->clients); + for (client_t *c = next_visible_client(monitor->clients); c; + c = next_visible_client(c->next)) { + printf("apply window[0x%x] rect: x: %d, y: %d, w: %u, h: %u, bw: %u\n", + c->window, c->x, c->y, c->width, c->height, c->border_width); + configure_window(c->window, c->x, c->y, c->width, c->height, + c->border_width); + } + + printf("apply monitor layout\n"); +} client_t *get_client_of_window(xcb_window_t window) { for (monitor_t *m = monitors; m; m = m->next) { diff --git a/src/renderer/text.c b/src/renderer/text.c index f4e7dd8..8ace20a 100644 --- a/src/renderer/text.c +++ b/src/renderer/text.c @@ -125,7 +125,7 @@ void draw_bar(monitor_t *monitor, bool is_current_monitor, /* 绘制 tags */ for (int i = 0; i < g_strv_length(monitor->tags); i++) { - bool selected = (monitor->selected_tag >> i) & 0x1; + bool selected = (monitor->selected_tags >> i) & 0x1; text = monitor->tags[i]; width = get_text_width(text) + tag_x_padding * 2; diff --git a/src/xcb/window.c b/src/xcb/window.c index 6645fb1..fa48802 100644 --- a/src/xcb/window.c +++ b/src/xcb/window.c @@ -1,6 +1,7 @@ #include "window.h" #include +#include #include #include #include @@ -34,6 +35,28 @@ void set_window_class_instance(xcb_window_t window, const char *class, free(str); } +static char broken[] = "broken"; + +void get_window_class_instance(xcb_window_t window, + window_class_instance_t *class_instance) { + xcb_get_property_cookie_t cookie = xcb_icccm_get_wm_class(conn, window); + xcb_icccm_get_wm_class_reply_t prop; + const char *class = NULL; + const char *instance = NULL; + if (xcb_icccm_get_wm_class_reply(conn, cookie, &prop, NULL)) { + class = prop.class_name; + instance = prop.instance_name; + } else { + class = broken; + instance = broken; + } + + strncpy(class_instance->class, class, LENGTH(class_instance->class)); + strncpy(class_instance->instance, instance, LENGTH(class_instance->instance)); + + xcb_icccm_get_wm_class_reply_wipe(&prop); +} + typedef struct visual_t { uint8_t depth; xcb_visualtype_t *visual; @@ -242,9 +265,8 @@ char *get_window_name(xcb_window_t window) { } { - const char *broken = "broken"; - char *name = ecalloc(strlen(broken) + 1, sizeof(char)); - strncpy(name, broken, strlen(broken)); + char *name = ecalloc(LENGTH(broken), sizeof(char)); + strncpy(name, broken, LENGTH(broken)); return name; } }