From 42ee022a0ece832ab8e52681d0527d5b2fa64228 Mon Sep 17 00:00:00 2001 From: Zedhugh Chen Date: Thu, 27 Nov 2025 07:53:28 +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/atoms-list.txt | 8 ++++ src/client.c | 108 +++++++++++++++++++++++++++++++++++++++++++++ src/client.h | 4 +- src/event.c | 50 +++++++++++++++++++++ src/xwindow.c | 30 +++++++++++++ src/xwindow.h | 1 + 6 files changed, 200 insertions(+), 1 deletion(-) diff --git a/src/atoms-list.txt b/src/atoms-list.txt index 00b45bc..0001a17 100644 --- a/src/atoms-list.txt +++ b/src/atoms-list.txt @@ -2,5 +2,13 @@ _XKB_RULES_NAMES WM_PROTOCOLS WM_TAKE_FOCUS +WM_NAME +WM_ICON_NAME +WM_WINDOW_ROLE + +UTF8_STRING +COMPOUND_TEXT _NET_ACTIVE_WINDOW +_NET_WM_NAME +_NET_WM_ICON_NAME diff --git a/src/client.c b/src/client.c index f679c0d..365ce18 100644 --- a/src/client.c +++ b/src/client.c @@ -1 +1,109 @@ #include "client.h" + +#include +#include +#include +#include +#include + +#include "atoms-extern.h" +#include "types.h" +#include "utils.h" +#include "wm.h" +#include "xwindow.h" + +client_t *client_get_by_window(xcb_window_t window) { + for (client_t *c = wm.client_list; c; c = c->next) { + if (c->window == window) return c; + } + + return nullptr; +} + +static void client_attach_list(client_t *client) { + client->next = wm.client_list; + wm.client_list = client; +} + +static void client_detach_list(client_t *client) { + for (client_t **c = &wm.client_list; *c && *c != client; c = &(*c)->next) { + *c = client->next; + } +} + +static void client_attach_stack(client_t *client) { + client->stack_next = wm.client_stack_list; + wm.client_stack_list = client; +} + +static void client_detach_stack(client_t *client) { + client_t **tc = &wm.client_stack_list; + for (; *tc && *tc != client; tc = &(*tc)->stack_next); + *tc = client->stack_next; + + if (client == wm.client_focused) { + client_t *t = wm.client_stack_list; + for (; t && !client_is_visible(t); t = t->stack_next); + wm.client_focused = t; + } +} + +void client_manage(xcb_window_t window, + xcb_get_geometry_reply_t *geometry_reply) { + client_t *c = p_new(client_t, 1); + c->window = window; + + c->name = xwindow_get_text_property(c->window, WM_NAME); + c->icon_name = xwindow_get_text_property(c->window, WM_ICON_NAME); + c->net_name = xwindow_get_text_property(c->window, _NET_WM_NAME); + c->net_icon_name = xwindow_get_text_property(c->window, _NET_WM_ICON_NAME); + c->role = xwindow_get_text_property(c->window, WM_WINDOW_ROLE); + + client_attach_list(c); + client_attach_stack(c); + + { + xcb_icccm_get_wm_class_reply_t prop; + xcb_get_property_cookie_t cookie = + xcb_icccm_get_wm_class_unchecked(wm.xcb_conn, c->window); + if (xcb_icccm_get_wm_class_reply(wm.xcb_conn, cookie, &prop, nullptr)) { + c->class = strdup(prop.class_name); + c->instance = strdup(prop.instance_name); + xcb_icccm_get_wm_class_reply_wipe(&prop); + } + } + + { + xcb_get_property_cookie_t cookie = + xcb_icccm_get_wm_transient_for_unchecked(wm.xcb_conn, window); + xcb_icccm_get_wm_transient_for_reply(wm.xcb_conn, cookie, + &c->transient_for_window, nullptr); + + client_t *transient_for_client = nullptr; + if (c->transient_for_window && (transient_for_client = client_get_by_window( + c->transient_for_window))) { + c->monitor = transient_for_client->monitor; + c->tags = transient_for_client->tags; + } else { + c->monitor = wm.current_monitor; + c->tags = c->monitor->selected_tag->mask; + } + } + + { + const 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(wm.xcb_conn, window, XCB_CW_EVENT_MASK, + ¶ms); + xcb_map_window(wm.xcb_conn, window); + } + + xcb_flush(wm.xcb_conn); +} + +bool client_is_visible(client_t *client) { + return client->tags & client->monitor->selected_tag->mask; +} diff --git a/src/client.h b/src/client.h index 9943dda..5854e47 100644 --- a/src/client.h +++ b/src/client.h @@ -5,7 +5,9 @@ #include "color.h" #include "types.h" -void client_manage(xcb_window_t window); +client_t *client_get_by_window(xcb_window_t window); +void client_manage(xcb_window_t window, + xcb_get_geometry_reply_t *geometry_reply); void client_send_to_tag(client_t *client, uint32_t tag_mask); void client_send_to_monitor(client_t *client, monitor_t *monitor); diff --git a/src/event.c b/src/event.c index a818037..3801103 100644 --- a/src/event.c +++ b/src/event.c @@ -10,6 +10,7 @@ #include #include "action.h" +#include "client.h" #include "default_config.h" #include "types.h" #include "utils.h" @@ -49,6 +50,23 @@ static void button_press(xcb_button_press_event_t *ev) { } } +static void configure_request(xcb_configure_request_event_t *ev) { + client_t *client = client_get_by_window(ev->window); + if (!client) { + uint16_t value_mask = ev->value_mask; + xcb_configure_window_value_list_t value_list = { + .x = ev->x, + .y = ev->y, + .width = ev->width, + .height = ev->height, + .border_width = ev->border_width, + .sibling = ev->sibling, + .stack_mode = ev->stack_mode, + }; + xcb_configure_window_aux(wm.xcb_conn, ev->window, value_mask, &value_list); + } +} + static void key_press(xcb_key_press_event_t *ev) { bool is_press = XCB_EVENT_RESPONSE_TYPE(ev) == XCB_KEY_PRESS; xcb_keysym_t keysym = xcb_key_press_lookup_keysym(wm.key_symbols, ev, 0); @@ -68,6 +86,36 @@ static void key_press(xcb_key_press_event_t *ev) { } } +static void map_request(xcb_map_request_event_t *ev) { + xcb_get_window_attributes_cookie_t wa_cookie = + xcb_get_window_attributes_unchecked(wm.xcb_conn, ev->window); + xcb_get_window_attributes_reply_t *wa_reply = + xcb_get_window_attributes_reply(wm.xcb_conn, wa_cookie, nullptr); + + if (!wa_reply) return; + if (wa_reply->override_redirect) { + p_delete(&wa_reply); + return; + } + + client_t *c = client_get_by_window(ev->window); + if (!c) { + xcb_get_geometry_cookie_t geo_cookie = + xcb_get_geometry(wm.xcb_conn, ev->window); + xcb_get_geometry_reply_t *geo_reply = + xcb_get_geometry_reply(wm.xcb_conn, geo_cookie, nullptr); + if (!geo_reply) { + p_delete(&wa_reply); + return; + } + + client_manage(ev->window, geo_reply); + p_delete(&geo_reply); + } + + p_delete(&wa_reply); +} + static void handle_xcb_event(xcb_generic_event_t *event) { uint8_t event_type = XCB_EVENT_RESPONSE_TYPE(event); const char *label = xcb_event_get_label(event_type); @@ -83,6 +131,8 @@ static void handle_xcb_event(xcb_generic_event_t *event) { EVENT(XCB_BUTTON_PRESS, button_press); EVENT(XCB_KEY_PRESS, key_press); EVENT(XCB_KEY_RELEASE, key_press); + EVENT(XCB_CONFIGURE_REQUEST, configure_request); + EVENT(XCB_MAP_REQUEST, map_request); #undef EVENT } diff --git a/src/xwindow.c b/src/xwindow.c index 6acf407..51852f4 100644 --- a/src/xwindow.c +++ b/src/xwindow.c @@ -1,6 +1,7 @@ #include "xwindow.h" #include +#include #include #include #include @@ -171,3 +172,32 @@ void xwindow_focus(xcb_window_t window) { xwindow_send_event(window, WM_TAKE_FOCUS); } } + +/** + * 获取目标窗口的文本属性 + * @param window 目标窗口 + * @param property 属性名 + * @returns 返回的信息使用后记得使用 free 释放 + */ +char *xwindow_get_text_property(xcb_window_t window, xcb_atom_t property) { + xcb_get_property_cookie_t cookie = xcb_get_property_unchecked( + wm.xcb_conn, false, window, property, XCB_ATOM_STRING, 0, UINT32_MAX); + xcb_get_property_reply_t *reply = + xcb_get_property_reply(wm.xcb_conn, cookie, nullptr); + int length = xcb_get_property_value_length(reply); + + char *value = nullptr; + + if (reply && + (reply->type == XCB_ATOM_STRING || reply->type == UTF8_STRING || + reply->type == COMPOUND_TEXT) && + reply->format == 8 && length) { + value = p_new(char, length + 1); + memcpy(value, xcb_get_property_value(reply), length); + value[length] = '\0'; + } + + p_delete(&reply); + + return value; +} diff --git a/src/xwindow.h b/src/xwindow.h index d0d720b..9cc1716 100644 --- a/src/xwindow.h +++ b/src/xwindow.h @@ -18,6 +18,7 @@ void xwindow_grab_keys(xcb_window_t window, const keyboard_t *keys, int keys_length); bool xwindow_send_event(xcb_window_t window, xcb_atom_t atom); void xwindow_focus(xcb_window_t window); +char *xwindow_get_text_property(xcb_window_t window, xcb_atom_t property); #define xwindow_set_name_static(window, name) \ xcb_icccm_set_wm_name(wm.xcb_conn, window, XCB_ATOM_STRING, 8, \