From 0039cd2c43483bdfb4daa04513af9182c938db4a Mon Sep 17 00:00:00 2001 From: Zedhugh Chen Date: Sun, 24 Aug 2025 11:35:52 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A4=84=E7=90=86=E7=AA=97=E5=8F=A3=E9=94=80?= =?UTF-8?q?=E6=AF=81=E4=BA=8B=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/include/event-adapter.h | 3 +++ src/include/xcb.h | 3 ++- src/main.c | 51 ++++++++++++++++++++++++++++++++----- src/xcb/event.c | 8 ++++-- src/xcb/window.c | 13 +++++----- src/xcb/wm-extension.c | 12 +++++++++ src/xcb/xcb.c | 22 ++++------------ 7 files changed, 80 insertions(+), 32 deletions(-) diff --git a/src/include/event-adapter.h b/src/include/event-adapter.h index d76b33e..09a6b85 100644 --- a/src/include/event-adapter.h +++ b/src/include/event-adapter.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -78,10 +79,12 @@ typedef struct map_request_event_t { typedef struct unmap_notify_event_t { event_type_t type; xcb_window_t window; + bool send_event; } unmap_notify_event_t; typedef struct destroy_notify_event_t { event_type_t type; + xcb_window_t window; } destroy_notify_event_t; typedef struct expose_event_t { diff --git a/src/include/xcb.h b/src/include/xcb.h index c35de1e..6f2222c 100644 --- a/src/include/xcb.h +++ b/src/include/xcb.h @@ -46,7 +46,7 @@ void map_window(xcb_window_t window); * @returns 返回窗口名字符串指针,使用完后记得使用 free 释放内存 */ char *get_window_name(xcb_window_t window); -void init_window_event_mask(xcb_window_t window); +void set_window_event_mask(xcb_window_t window, bool clear); void change_window_border_color(xcb_window_t window, uint32_t argb); void focus_window(xcb_window_t window); @@ -112,5 +112,6 @@ typedef enum wm_state_t { } wm_state_t; int32_t get_window_state(xcb_window_t window); void set_window_state(xcb_window_t window, wm_state_t state); +void set_window_list(xcb_window_t *list, uint32_t length); bool get_pointer_position(int32_t *x, int32_t *y); diff --git a/src/main.c b/src/main.c index 0d83a0c..a9b7fc0 100644 --- a/src/main.c +++ b/src/main.c @@ -39,11 +39,13 @@ 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 unmap_notify(unmap_notify_event_t *event); +static void destroy_notify(destroy_notify_event_t *event); static void motion_notify(motion_notify_event_t *event); static void enter_notify(enter_notify_event_t *event); static void focus_in(focus_in_event_t *event); static void manage_window(xcb_window_t window); -static void unmanage_client(client_t *client); +static void unmanage_client(client_t *client, bool destroyed); +static void update_client_list(void); static void update_client_name(client_t *client); static void apply_rules(client_t *client); static uint32_t get_tags_mask_of_monitor(monitor_t *monitor); @@ -465,6 +467,7 @@ void xcb_event_handler(generic_event_t *event, void *user_data) { EVENT(EVENT_TYPE_CONFIGURE_REQUEST, configure_request); EVENT(EVENT_TYPE_MAP_REQUEST, map_request); EVENT(EVENT_TYPE_UNMAP_NOTIFY, unmap_notify); + EVENT(EVENT_TYPE_DESTROY_NOTIFY, destroy_notify); EVENT(EVENT_TYPE_MOTION_NOTIFY, motion_notify); EVENT(EVENT_TYPE_ENTER_NOTIFY, enter_notify); EVENT(EVENT_TYPE_FOCUS_IN, focus_in); @@ -512,10 +515,19 @@ void map_request(map_request_event_t *event) { void unmap_notify(unmap_notify_event_t *event) { client_t *c = get_client_of_window(event->window); if (c) { - unmanage_client(c); + if (event->send_event) { + set_window_state(event->window, wm_state_withdrawn); + } else { + unmanage_client(c, false); + } } } +void destroy_notify(destroy_notify_event_t *event) { + client_t *c = get_client_of_window(event->window); + if (c) unmanage_client(c, true); +} + void motion_notify(motion_notify_event_t *event) { logger("motion: window: 0x%x, root: 0x%x\n", event->window, event->root); logger("x: %d, y: %d, root_x: %d, root_y: %d\n", event->x, event->y, @@ -586,7 +598,7 @@ void manage_window(xcb_window_t window) { attach_client(c); attach_stack_client(c); - init_window_event_mask(window); + set_window_event_mask(window, false); change_window_border_color(window, color_set->border_color.argb); if (c->monitor == current_monitor) { unfocus(current_monitor->selected_client, false); @@ -596,14 +608,41 @@ void manage_window(xcb_window_t window) { focus(NULL); } -void unmanage_client(client_t *client) { +void unmanage_client(client_t *client, bool destroyed) { monitor_t *monitor = client->monitor; detach_client(client); detach_stack_client(client); + if (!destroyed) { + set_window_event_mask(client->window, true); + configure_window(client->window, client->x, client->y, client->width, + client->height, client->old_border_width); + set_window_state(client->window, wm_state_withdrawn); + flush_xcb_connection(); + } free(client); + focus(NULL); + update_client_list(); arrange(monitor); - flush_xcb_connection(); +} + +void update_client_list(void) { + uint32_t length = 0; + for (monitor_t *m = monitors; m; m = m->next) { + for (client_t *c = m->clients; c; c = c->next) length++; + } + if (!length) { + set_window_list(NULL, length); + return; + } + + xcb_window_t *list = ecalloc(length, sizeof(xcb_window_t)); + uint32_t i = 0; + for (monitor_t *m = monitors; m; m = m->next) { + for (client_t *c = m->clients; c; c = c->next) list[i++] = c->window; + } + set_window_list(list, length); + free(list); } void update_client_name(client_t *client) { @@ -842,7 +881,7 @@ void cleanup(void) { while (m) { while (c) { client_t *tc = c->next; - free(c); + unmanage_client(c, false); c = tc; } monitor_t *next_monitor = m->next; diff --git a/src/xcb/event.c b/src/xcb/event.c index 91d2f95..4439d5c 100644 --- a/src/xcb/event.c +++ b/src/xcb/event.c @@ -65,10 +65,14 @@ static void convert_event(xcb_generic_event_t *xcb_event, xcb_unmap_notify_event_t *ev = (xcb_unmap_notify_event_t *)xcb_event; event->unmap_notify.type = EVENT_TYPE_UNMAP_NOTIFY; event->unmap_notify.window = ev->window; + event->unmap_notify.send_event = XCB_EVENT_SENT(ev); } break; - case XCB_DESTROY_NOTIFY: - event->type = EVENT_TYPE_DESTROY_NOTIFY; + case XCB_DESTROY_NOTIFY: { + xcb_destroy_notify_event_t *ev = (xcb_destroy_notify_event_t *)xcb_event; + event->destroy_notify.type = EVENT_TYPE_DESTROY_NOTIFY; + event->destroy_notify.window = ev->window; break; + } case XCB_EXPOSE: event->type = EVENT_TYPE_EXPOSE; break; diff --git a/src/xcb/window.c b/src/xcb/window.c index 3c7906d..9371750 100644 --- a/src/xcb/window.c +++ b/src/xcb/window.c @@ -274,13 +274,14 @@ char *get_window_name(xcb_window_t window) { } } -void init_window_event_mask(xcb_window_t window) { +void set_window_event_mask(xcb_window_t window, bool clear) { 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, - }; + uint32_t event_mask = clear ? XCB_EVENT_MASK_NO_EVENT + : XCB_EVENT_MASK_ENTER_WINDOW | + XCB_EVENT_MASK_FOCUS_CHANGE | + XCB_EVENT_MASK_PROPERTY_CHANGE | + XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY; + xcb_params_cw_t params = {.event_mask = event_mask}; xcb_aux_change_window_attributes(conn, window, change_mask, ¶ms); } diff --git a/src/xcb/wm-extension.c b/src/xcb/wm-extension.c index 1f1f260..1e7ff6c 100644 --- a/src/xcb/wm-extension.c +++ b/src/xcb/wm-extension.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include @@ -149,3 +150,14 @@ void set_window_state(xcb_window_t window, wm_state_t state) { } xcb_icccm_set_wm_hints(conn, window, &hints); } + +void set_window_list(xcb_window_t *list, uint32_t length) { + int screen_nbr = 0; + for (int i = 0; i < ewmh->nb_screens; i++) { + if (screen == ewmh->screens[i]) { + screen_nbr = i; + break; + } + } + xcb_ewmh_set_client_list(ewmh, screen_nbr, length, list); +} diff --git a/src/xcb/xcb.c b/src/xcb/xcb.c index 7e65eb5..b2fda9e 100644 --- a/src/xcb/xcb.c +++ b/src/xcb/xcb.c @@ -184,10 +184,11 @@ window_list_t *scan_window_list(void) { xcb_window_t *list = xcb_query_tree_children(tree_reply); int len = xcb_query_tree_children_length(tree_reply); + if (!len) return NULL; uint32_t normal_count = 0, transient_count = 0; - xcb_window_t normal_window_array[len]; - xcb_window_t transient_window_array[len]; + xcb_window_t *normal_window_array = ecalloc(len, sizeof(xcb_window_t)); + xcb_window_t *transient_window_array = ecalloc(len, sizeof(xcb_window_t)); for (int i = 0; i < len; i++) { xcb_window_t window = list[i]; @@ -214,25 +215,12 @@ window_list_t *scan_window_list(void) { } free(tree_reply); - if ((!normal_count) && (!transient_count)) return NULL; - window_list_t *r = ecalloc(1, sizeof(window_list_t)); + r->normal_list = normal_window_array; + r->transient_list = transient_window_array; r->normal_count = normal_count; r->transient_count = transient_count; - if (normal_count) { - r->normal_list = ecalloc(normal_count, sizeof(xcb_window_t)); - for (uint32_t i = 0; i < normal_count; i++) { - r->normal_list[i] = normal_window_array[i]; - } - } - if (transient_count) { - r->transient_list = ecalloc(transient_count, sizeof(xcb_window_t)); - for (uint32_t i = 0; i < transient_count; i++) { - r->transient_list[i] = transient_window_array[i]; - } - } - return r; }