From c777b58d80768d5375c07b2f3b0cac815dcceeee Mon Sep 17 00:00:00 2001 From: Zedhugh Chen Date: Mon, 4 May 2026 03:03:15 +0800 Subject: [PATCH] =?UTF-8?q?feat(backend/X11):=20=E5=A4=84=E7=90=86=20Prope?= =?UTF-8?q?rtyNotify=20=E5=92=8C=20ClientMessage=20=E4=BA=8B=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/x11/event.c | 166 +++++++++++++++++++++++++++++++++++++ src/backend/x11/internal.h | 1 + src/core/event.h | 16 ---- src/core/types.h | 3 + 4 files changed, 170 insertions(+), 16 deletions(-) diff --git a/src/backend/x11/event.c b/src/backend/x11/event.c index 4d18eb6..62101bd 100644 --- a/src/backend/x11/event.c +++ b/src/backend/x11/event.c @@ -275,6 +275,170 @@ static bool handle_enter_notify( return true; } +static bool handle_property_notify( + backend_t *backend, + event_t *event, + const xcb_property_notify_event_t *xcb_event +) { + auto window_id = xcb_event->window; + if (xcb_event->atom == backend->atoms.WM_NAME || + xcb_event->atom == backend->atoms._NET_WM_NAME) { + event->type = ZDWM_EVENT_WINDOW_METADATA_CHANGED; + + auto data = &event->as.window_metadata_change; + data->window = window_id; + data->metadata.title = window_get_title(backend, window_id); + data->changed_fields = ZDWM_WINDOW_METADATA_CHANGE_TITLE; + return true; + } + + if (xcb_event->atom == backend->atoms.WM_WINDOW_ROLE) { + event->type = ZDWM_EVENT_WINDOW_METADATA_CHANGED; + + auto data = &event->as.window_metadata_change; + data->window = window_id; + data->metadata.role = window_get_role(backend, window_id); + data->changed_fields = ZDWM_WINDOW_METADATA_CHANGE_ROLE; + return true; + } + + if (xcb_event->atom == XCB_ATOM_WM_CLASS) { + event->type = ZDWM_EVENT_WINDOW_METADATA_CHANGED; + + auto data = &event->as.window_metadata_change; + data->window = window_id; + window_get_class( + backend, + window_id, + &data->metadata.class_name, + &data->metadata.instance_name + ); + data->changed_fields = + ZDWM_WINDOW_METADATA_CHANGE_CLASS | ZDWM_WINDOW_METADATA_CHANGE_INSTANCE; + return true; + } + + if (xcb_event->atom == XCB_ATOM_WM_HINTS) { + xcb_icccm_wm_hints_t hints = {0}; + auto handled = window_get_wm_hints(backend, window_id, &hints); + if (!handled) return false; + + event->type = ZDWM_EVENT_WINDOW_STATE_REQUEST; + + auto data = &event->as.window_state_request; + data->window = window_id; + if (hints.flags & XCB_ICCCM_WM_HINT_X_URGENCY) { + data->type = ZDWM_WINDOW_STATE_REQUEST_URGENT; + data->action = xcb_icccm_wm_hints_get_urgency(&hints) + ? ZDWM_WINDOW_STATE_ACTION_ADD + : ZDWM_WINDOW_STATE_ACTION_REMOVE; + return true; + } + return false; + } + + if (xcb_event->atom == XCB_ATOM_WM_NORMAL_HINTS) { + bool fixed_size = false; + if (!window_get_fixed_size(backend, window_id, &fixed_size)) return false; + + event->type = ZDWM_EVENT_WINDOW_STATE_REQUEST; + + auto data = &event->as.window_state_request; + data->window = window_id; + data->type = ZDWM_WINDOW_STATE_REQUEST_FIXED_SIZE; + data->action = fixed_size ? ZDWM_WINDOW_STATE_ACTION_ADD + : ZDWM_WINDOW_STATE_ACTION_REMOVE; + return true; + } + + return false; +} + +static bool handle_client_message( + backend_t *backend, + event_t *event, + const xcb_client_message_event_t *xcb_event +) { + auto atoms = &backend->atoms; + + if (xcb_event->type == atoms->_NET_ACTIVE_WINDOW) { + event->type = ZDWM_EVENT_WINDOW_ACTIVATE_REQUEST; + event->as.window_activate_request.window = xcb_event->window; + event->as.window_activate_request.source = xcb_event->data.data32[0]; + return true; + } + + if (xcb_event->type == atoms->WM_CHANGE_STATE) { + if (xcb_event->data.data32[0] == XCB_ICCCM_WM_STATE_ICONIC) { + event->type = ZDWM_EVENT_WINDOW_STATE_REQUEST; + event->as.window_state_request.window = xcb_event->window; + event->as.window_state_request.type = ZDWM_WINDOW_STATE_REQUEST_MINIMIZED; + event->as.window_state_request.action = ZDWM_WINDOW_STATE_ACTION_ADD; + return true; + } + return false; + } + + if (xcb_event->type == atoms->_NET_WM_STATE) { + event->type = ZDWM_EVENT_WINDOW_STATE_REQUEST; + auto data = &event->as.window_state_request; + data->window = xcb_event->window; + /** + * _NET_WM_STATE 协议数据格式为 + * data.data32 = { + * action, // 操作类型(添加/移除/切换) + * property1, // 第一个状态原子(如_NET_WM_STATE_FULLSCREEN) + * property2, // 第二个状态原子(可选,通常为0) + * 0, // 预留字段(固定为0) + * 0 // 预留字段(固定为0) + * }; + * action 值可能为 + * _NET_WM_STATE_ADD(1)​​:添加全屏状态,窗口进入全屏模式 + * _NET_WM_STATE_REMOVE(0):移除全屏状态,窗口退出全屏模式 + * _NET_WM_STATE_TOGGLE(2):切换全屏状态(若当前全屏则退出,反之进入) + */ + auto action = xcb_event->data.data32[0]; + switch (action) { + case 0: + data->action = ZDWM_WINDOW_STATE_ACTION_REMOVE; + break; + case 1: + data->action = ZDWM_WINDOW_STATE_ACTION_ADD; + break; + case 2: + data->action = ZDWM_WINDOW_STATE_ACTION_TOGGLE; + break; + } + auto properties = &xcb_event->data.data32[1]; + uint32_t count = 2; + auto geometry_mode = geometry_mode_from_states(atoms, properties, count); + switch (geometry_mode) { + case ZDWM_GEOMETRY_FULLSCREEN: + data->type = ZDWM_WINDOW_STATE_REQUEST_FULLSCREEN; + return true; + case ZDWM_GEOMETRY_MAXIMIZED: + data->type = ZDWM_WINDOW_STATE_REQUEST_MAXIMIZED; + return true; + case ZDWM_GEOMETRY_MINIMIZED: + case ZDWM_GEOMETRY_NORMAL: + } + + if (derive_skip_taskbar(atoms, properties, count)) { + data->type = ZDWM_WINDOW_STATE_REQUEST_SKIP_TASKBAR; + return true; + } + + if (urgent_from_states(atoms, properties, count)) { + data->type = ZDWM_WINDOW_STATE_REQUEST_URGENT; + return true; + } + + return false; + } + + return false; +} + bool backend_next_event(backend_t *backend, event_t *event) { if (!backend || !backend->conn || !event) return false; @@ -301,6 +465,8 @@ bool backend_next_event(backend_t *backend, event_t *event) { EVENT(XCB_CONFIGURE_REQUEST, handle_configure_request); EVENT(XCB_KEY_PRESS, handle_key_press); EVENT(XCB_ENTER_NOTIFY, handle_enter_notify); + EVENT(XCB_PROPERTY_NOTIFY, handle_property_notify); + EVENT(XCB_CLIENT_MESSAGE, handle_client_message); #undef EVENT diff --git a/src/backend/x11/internal.h b/src/backend/x11/internal.h index 7636ec3..d1cefa4 100644 --- a/src/backend/x11/internal.h +++ b/src/backend/x11/internal.h @@ -14,6 +14,7 @@ \ X(WM_WINDOW_ROLE) \ X(WM_NAME) \ + X(WM_CHANGE_STATE) \ X(_NET_WM_NAME) \ \ X(WM_PROTOCOLS) \ diff --git a/src/core/event.h b/src/core/event.h index 2da4860..ab09735 100644 --- a/src/core/event.h +++ b/src/core/event.h @@ -76,21 +76,6 @@ typedef struct window_metadata_change_event_t { window_metadata_t metadata; } window_metadata_change_event_t; -typedef enum window_hint_changed_flags_t { - ZDWM_WINDOW_HINT_CHANGED_NONE = 0, - ZDWM_WINDOW_HINT_CHANGED_URGENT = 1u << 0, - ZDWM_WINDOW_HINT_CHANGED_FIXED_SIZE = 1u << 1, - ZDWM_WINDOW_HINT_CHANGED_SKIP_TASKBAR = 1u << 2, -} window_hint_changed_flags_t; - -typedef struct window_hints_changed_event_t { - window_id_t window; - uint32_t changed_fields; - bool urgent; - bool fixed_size; - bool skip_taskbar; -} window_hints_changed_event_t; - typedef enum window_activation_source_t { ZDWM_WINDOW_ACTIVATION_SOURCE_LEGACY = 0, ZDWM_WINDOW_ACTIVATION_SOURCE_APPLICATION = 1, @@ -119,7 +104,6 @@ typedef struct event_t { window_map_request_event_t window_map_request; window_remove_event_t window_remove; window_metadata_change_event_t window_metadata_change; - window_hints_changed_event_t window_hints_changed; window_activate_request_event_t window_activate_request; window_state_request_event_t window_state_request; configure_data_t configure_request; diff --git a/src/core/types.h b/src/core/types.h index 2dd81d8..b325569 100644 --- a/src/core/types.h +++ b/src/core/types.h @@ -66,6 +66,9 @@ typedef enum window_state_request_type_t { ZDWM_WINDOW_STATE_REQUEST_FULLSCREEN, ZDWM_WINDOW_STATE_REQUEST_MAXIMIZED, ZDWM_WINDOW_STATE_REQUEST_MINIMIZED, + ZDWM_WINDOW_STATE_REQUEST_SKIP_TASKBAR, + ZDWM_WINDOW_STATE_REQUEST_URGENT, + ZDWM_WINDOW_STATE_REQUEST_FIXED_SIZE, } window_state_request_type_t; typedef enum window_state_request_action_t {