From c3bcb7ddd2f77de41a820238b4d78d80fc0922da Mon Sep 17 00:00:00 2001 From: Zedhugh Chen Date: Fri, 22 Aug 2025 15:47:58 +0800 Subject: [PATCH] =?UTF-8?q?=E6=98=BE=E7=A4=BA=E7=AA=97=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/config.h | 1 + src/include/event-adapter.h | 11 ++++ src/include/types.h | 5 +- src/include/xcb.h | 27 ++++++++- src/main.c | 114 ++++++++++++++++++++++++++++++++++-- src/xcb/event.c | 25 ++++++-- src/xcb/window.c | 101 +++++++++++++++++++++++++++++++- src/xcb/xcb-private.h | 2 + src/xcb/xcb.c | 2 +- 9 files changed, 274 insertions(+), 14 deletions(-) diff --git a/src/config.h b/src/config.h index 9585e24..b3fca1a 100644 --- a/src/config.h +++ b/src/config.h @@ -92,3 +92,4 @@ const uint32_t dpi_fallback = 96; const uint32_t bar_y_padding = 1; const uint32_t tag_x_padding = 10; +const uint32_t border_width = 1; diff --git a/src/include/event-adapter.h b/src/include/event-adapter.h index 17ef13b..ae416bd 100644 --- a/src/include/event-adapter.h +++ b/src/include/event-adapter.h @@ -2,6 +2,8 @@ #include +#include "types.h" + typedef enum event_type_t { EVENT_TYPE_MAPPING_NOTIFY, EVENT_TYPE_KEY_PRESS, @@ -35,6 +37,13 @@ typedef struct button_press_event_t { typedef struct configure_request_event_t { event_type_t type; + xcb_window_t window; + xcb_window_t parent; + xcb_window_t sibling; + int32_t x, y; + uint32_t width, height, border_width; + uint32_t value_mask; + uint8_t stack_mode; } configure_request_event_t; typedef struct configure_notify_event_t { @@ -43,6 +52,8 @@ typedef struct configure_notify_event_t { typedef struct map_request_event_t { event_type_t type; + xcb_window_t window; + xcb_window_t parent; } map_request_event_t; typedef struct unmap_notify_event_t { diff --git a/src/include/types.h b/src/include/types.h index 6a8d6a5..d7202cb 100644 --- a/src/include/types.h +++ b/src/include/types.h @@ -38,11 +38,12 @@ struct monitor_t { struct client_t { char name[256]; - int32_t x, y; - uint32_t width, height, border_width; + int32_t x, y, old_x, old_y; + uint32_t width, height, border_width, old_width, old_height, old_border_width; uint32_t tags; bool fullscreen; bool floating; + bool maximize; monitor_t *monitor; client_t *next; diff --git a/src/include/xcb.h b/src/include/xcb.h index 6072a10..4691563 100644 --- a/src/include/xcb.h +++ b/src/include/xcb.h @@ -5,8 +5,11 @@ #include #include #include + #include "types.h" +#define WINDOW_NONE XCB_WINDOW_NONE + bool has_other_wm_running(void); int get_xcb_connection_fd(void); void flush_xcb_connection(void); @@ -17,7 +20,27 @@ typedef enum cursor_t { cursor_move = XC_fleur, } cursor_t; void change_window_cursor(xcb_window_t window, cursor_t cursor); -void set_root_cursor(cursor_t cursor); +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_geometry_t { + int32_t x, y; + uint32_t width, height, border_width; +} window_geometry_t; +/** + * @brief 获取窗口的几何信息 + * @returns 返回几何信息指针,使用完后记得使用 free 释放内存 + */ +window_geometry_t *get_window_geometry(xcb_window_t window); +void map_window(xcb_window_t window); +/** + * @brief 获取窗口名 + * @returns 返回窗口名字符串指针,使用完后记得使用 free 释放内存 + */ +char *get_window_name(xcb_window_t window); +void init_window_event_mask(xcb_window_t window); +void change_window_border_color(xcb_window_t window, uint32_t argb); typedef struct bar_cairo_params_t { xcb_connection_t *conn; @@ -32,6 +55,8 @@ bar_cairo_params_t *create_bar_window(int16_t x, int16_t y, uint16_t width, uint16_t height, uint32_t background_argb, cursor_t cursor); void destroy_window(xcb_window_t window); +void configure_window(xcb_window_t window, int32_t x, int32_t y, uint32_t width, + uint32_t height, uint32_t border_width); typedef struct monitor_rect_t monitor_rect_t; struct monitor_rect_t { diff --git a/src/main.c b/src/main.c index 24b1e1c..2606d20 100644 --- a/src/main.c +++ b/src/main.c @@ -34,6 +34,15 @@ static void update_bars(void); static void init_xcb_event_loop(void); static void xcb_event_handler(generic_event_t *event, void *user_data); static void key_press(key_press_event_t *event); +static void configure_request(configure_request_event_t *event); +static void map_request(map_request_event_t *event); +static void manage_window(xcb_window_t window); +static void update_client_name(client_t *client); +static void apply_rules(client_t *client); +static void attach_client(client_t *client); +static void show_hide_client(client_t *client); +static void apply_monitor_layout(monitor_t *monitor); +static client_t *get_client_of_window(xcb_window_t window); static gboolean handle_xcb_event(GIOChannel *channel, GIOCondition condition, gpointer userdata); static void cleanup(void); @@ -50,6 +59,7 @@ static uint32_t dpi = dpi_fallback; static uint32_t bar_y_pad = bar_y_padding; static uint32_t tag_x_pad = tag_x_padding; static uint32_t bar_height = 0; +static uint32_t border_px = border_width; static bool need_restart = false; @@ -358,6 +368,7 @@ void get_xresource_config(void) { get_xresource_uint32("zswm.font_size", &font_size); get_xresource_uint32("zswm.bar_y_padding", &bar_y_pad); get_xresource_uint32("zswm.tag_x_padding", &tag_x_pad); + get_xresource_uint32("zswm.border_width", &border_px); printf("dpi: %u, font family: %s, font size: %u\n", dpi, font_family, font_size); @@ -383,9 +394,17 @@ void init_xcb_event_loop(void) { void xcb_event_handler(generic_event_t *event, void *user_data) { switch (event->type) { - case EVENT_TYPE_KEY_PRESS: - key_press((key_press_event_t *)event); - break; +#define EVENT(type, callback) \ + case type: \ + callback((void *)event); \ + break + + EVENT(EVENT_TYPE_KEY_PRESS, key_press); + EVENT(EVENT_TYPE_CONFIGURE_REQUEST, configure_request); + EVENT(EVENT_TYPE_MAP_REQUEST, map_request); + +#undef EVENT + default: break; } @@ -402,6 +421,93 @@ void key_press(key_press_event_t *event) { } } +void configure_request(configure_request_event_t *event) { + client_t *client = get_client_of_window(event->window); + if (!client) { + configure_window(event->window, event->x, event->y, event->width, + event->height, event->border_width); + flush_xcb_connection(); + return; + } + + 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); +} + +void map_request(map_request_event_t *event) { + if (!get_window_visible(event->window)) return; + if (get_client_of_window(event->window)) return; + if (event->parent != get_root_window()) return; + + manage_window(event->window); +} + +void manage_window(xcb_window_t window) { + window_geometry_t *geometry = get_window_geometry(window); + client_t *c = ecalloc(1, sizeof(client_t)); + c->window = window; + c->x = c->old_x = geometry->x; + c->y = c->old_y = geometry->y; + c->width = c->old_width = geometry->width; + c->height = c->old_height = geometry->height; + c->old_border_width = geometry->border_width; + c->border_width = border_px; + update_client_name(c); + + printf("== manage window: %u\n", window); + printf("== x: %d, y: %d, w: %u, h: %u, bw: %u\n", c->x, c->y, c->width, + c->height, c->border_width); + + xcb_window_t tw = get_transient_window_for(window); + client_t *tc = NULL; + if (tw != WINDOW_NONE && (tc = get_client_of_window(tw))) { + c->monitor = tc->monitor; + c->tags = tc->tags; + } else { + c->monitor = current_monitor; + apply_rules(c); + } + + attach_client(c); + apply_monitor_layout(c->monitor); + init_window_event_mask(window); + change_window_border_color(window, color_set->border_color.argb); + map_window(window); + show_hide_client(c); + flush_xcb_connection(); + + update_bars(); +} + +void update_client_name(client_t *client) { + char *name = get_window_name(client->window); + strncpy(client->name, name, sizeof(client->name) - 1); + free(name); +} + +void apply_rules(client_t *client) { /* TODO: */ } + +void attach_client(client_t *client) { + client->next = client->monitor->clients; + client->monitor->clients = client; +} + +void show_hide_client(client_t *client) { /* TODO: */ } + +void apply_monitor_layout(monitor_t *monitor) { /* TODO: */ } + +client_t *get_client_of_window(xcb_window_t window) { + for (monitor_t *m = monitors; m; m = m->next) { + for (client_t *c = m->clients; c; c = c->next) { + if (c->window == window) return c; + } + } + return NULL; +} + gboolean handle_xcb_event(GIOChannel *channel, GIOCondition condition, gpointer userdata) { if (condition & (G_IO_HUP | G_IO_ERR)) { @@ -456,7 +562,7 @@ int main(int argc, char *argv[]) { init_monitors(); init_color_set(); init_monitor_bar(); - set_root_cursor(cursor_normal); + change_window_cursor(get_root_window(), cursor_normal); init_wallpaper(); update_bars(); diff --git a/src/xcb/event.c b/src/xcb/event.c index 0ca8819..67711b3 100644 --- a/src/xcb/event.c +++ b/src/xcb/event.c @@ -36,15 +36,32 @@ static void convert_event(xcb_generic_event_t *xcb_event, case XCB_BUTTON_PRESS: event->type = EVENT_TYPE_BUTTON_PRESS; break; - case XCB_CONFIGURE_REQUEST: - event->type = EVENT_TYPE_CONFIGURE_REQUEST; + case XCB_CONFIGURE_REQUEST: { + xcb_configure_request_event_t *ev = + (xcb_configure_request_event_t *)xcb_event; + event->configure_request.type = EVENT_TYPE_CONFIGURE_REQUEST; + event->configure_request.window = ev->window; + event->configure_request.parent = ev->parent; + event->configure_request.sibling = ev->sibling; + event->configure_request.x = ev->x; + event->configure_request.y = ev->y; + event->configure_request.width = ev->width; + event->configure_request.height = ev->height; + event->configure_request.border_width = ev->border_width; + event->configure_request.value_mask = ev->value_mask; + event->configure_request.stack_mode = ev->stack_mode; break; + } case XCB_CONFIGURE_NOTIFY: event->type = EVENT_TYPE_CONFIGURE_NOTIFY; break; - case XCB_MAP_REQUEST: - event->type = EVENT_TYPE_MAP_REQUEST; + case XCB_MAP_REQUEST: { + xcb_map_request_event_t *ev = (xcb_map_request_event_t *)xcb_event; + event->map_request.type = EVENT_TYPE_MAP_REQUEST; + event->map_request.window = ev->window; + event->map_request.parent = ev->parent; break; + } case XCB_UNMAP_NOTIFY: event->type = EVENT_TYPE_UNMAP_NOTIFY; break; diff --git a/src/xcb/window.c b/src/xcb/window.c index 0445c73..6645fb1 100644 --- a/src/xcb/window.c +++ b/src/xcb/window.c @@ -5,6 +5,8 @@ #include #include #include +#include +#include #include #include @@ -155,6 +157,21 @@ bar_cairo_params_t *create_bar_window(int16_t x, int16_t y, uint16_t width, void destroy_window(xcb_window_t window) { xcb_destroy_window(conn, window); } +void configure_window(xcb_window_t window, int32_t x, int32_t y, uint32_t width, + uint32_t height, uint32_t border_width) { + xcb_config_window_t config_mask = + XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | + XCB_CONFIG_WINDOW_HEIGHT | XCB_CONFIG_WINDOW_BORDER_WIDTH; + xcb_params_configure_window_t window_config = { + .x = x, + .y = y, + .width = width, + .height = height, + .border_width = border_width, + }; + xcb_aux_configure_window(conn, window, config_mask, &window_config); +} + void change_window_cursor(xcb_window_t window, cursor_t cursor) { xcb_change_window_attributes_value_list_t value_list = { .cursor = get_xcb_cursor(cursor), @@ -163,6 +180,86 @@ void change_window_cursor(xcb_window_t window, cursor_t cursor) { xcb_flush(conn); } -void set_root_cursor(cursor_t cursor) { - change_window_cursor(screen->root, cursor); +xcb_window_t get_root_window(void) { return screen->root; } + +bool get_window_visible(xcb_window_t window) { + xcb_get_window_attributes_cookie_t cookie = + xcb_get_window_attributes(conn, window); + xcb_get_window_attributes_reply_t *reply = + xcb_get_window_attributes_reply(conn, cookie, NULL); + bool visible = + !reply->override_redirect && reply->map_state != XCB_MAP_STATE_UNVIEWABLE; + free(reply); + + return visible; +} + +xcb_window_t get_transient_window_for(xcb_window_t window) { + xcb_window_t transient_for = XCB_NONE; + xcb_get_property_cookie_t cookie = + xcb_icccm_get_wm_transient_for(conn, window); + xcb_icccm_get_wm_transient_for_reply(conn, cookie, &transient_for, NULL); + return transient_for; +} + +window_geometry_t *get_window_geometry(xcb_window_t window) { + xcb_get_geometry_cookie_t cookie = xcb_get_geometry(conn, window); + xcb_get_geometry_reply_t *reply = xcb_get_geometry_reply(conn, cookie, NULL); + + window_geometry_t *geometry = ecalloc(1, sizeof(window_geometry_t)); + geometry->x = reply->x; + geometry->y = reply->y; + geometry->width = reply->width; + geometry->height = reply->height; + geometry->border_width = reply->border_width; + + free(reply); + return geometry; +} + +void map_window(xcb_window_t window) { + xcb_map_window(conn, window); + xcb_map_subwindows(conn, window); +} + +char *get_window_name(xcb_window_t window) { + xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_name(ewmh, window); + xcb_ewmh_get_utf8_strings_reply_t reply; + xcb_ewmh_get_wm_name_reply(ewmh, cookie, &reply, NULL); + if (reply.strings_len) { + char *name = ecalloc(reply.strings_len + 1, sizeof(char)); + strncpy(name, reply.strings, reply.strings_len); + return name; + } + + cookie = xcb_icccm_get_wm_name(conn, window); + xcb_icccm_get_text_property_reply_t prop; + xcb_icccm_get_wm_name_reply(conn, cookie, &prop, NULL); + if (prop.name_len) { + char *name = ecalloc(prop.name_len + 1, sizeof(char)); + strncpy(name, prop.name, prop.name_len); + return name; + } + + { + const char *broken = "broken"; + char *name = ecalloc(strlen(broken) + 1, sizeof(char)); + strncpy(name, broken, strlen(broken)); + return name; + } +} + +void init_window_event_mask(xcb_window_t window) { + xcb_cw_t change_mask = XCB_CW_EVENT_MASK; + xcb_params_cw_t params = { + .event_mask = XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_FOCUS_CHANGE | + XCB_EVENT_MASK_PROPERTY_CHANGE | + XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY, + }; + xcb_aux_change_window_attributes(conn, window, change_mask, ¶ms); +} + +void change_window_border_color(xcb_window_t window, uint32_t argb) { + xcb_params_cw_t params = {.border_pixel = argb}; + xcb_aux_change_window_attributes(conn, window, XCB_CW_BORDER_PIXEL, ¶ms); } diff --git a/src/xcb/xcb-private.h b/src/xcb/xcb-private.h index 0b54ff5..b1dc116 100644 --- a/src/xcb/xcb-private.h +++ b/src/xcb/xcb-private.h @@ -1,9 +1,11 @@ #pragma once +#include #include #include "xcb.h" +extern xcb_ewmh_connection_t *ewmh; extern xcb_connection_t *conn; extern xcb_screen_t *screen; extern xcb_key_symbols_t *key_symbols; diff --git a/src/xcb/xcb.c b/src/xcb/xcb.c index 62a8a4e..ddb21e7 100644 --- a/src/xcb/xcb.c +++ b/src/xcb/xcb.c @@ -170,7 +170,7 @@ void set_wallpaper(uint32_t *wallpaper_data) { xcb_flush(conn); } -static xcb_ewmh_connection_t *ewmh = NULL; +xcb_ewmh_connection_t *ewmh = NULL; static bool ewmh_init_failed = false; static xcb_window_t wm_check_window = XCB_NONE; static void init_ewmh_extension(void) {