diff --git a/src/backend/x11/backend.c b/src/backend/x11/backend.c index 452be2f..626bb83 100644 --- a/src/backend/x11/backend.c +++ b/src/backend/x11/backend.c @@ -35,6 +35,52 @@ static void atoms_init(backend_t *backend) { {"WM_WINDOW_ROLE", sizeof("WM_WINDOW_ROLE") - 1, &atoms->WM_WINDOW_ROLE}, {"WM_NAME", sizeof("WM_NAME") - 1, &atoms->WM_NAME}, {"_NET_WM_NAME", sizeof("_NET_WM_NAME") - 1, &atoms->_NET_WM_NAME}, + + {"_NET_WM_STATE", sizeof("_NET_WM_STATE") - 1, &atoms->_NET_WM_STATE}, + {"_NET_WM_STATE_FULLSCREEN", sizeof("_NET_WM_STATE_FULLSCREEN") - 1, + &atoms->_NET_WM_STATE_FULLSCREEN}, + {"_NET_WM_STATE_ABOVE", sizeof("_NET_WM_STATE_ABOVE") - 1, + &atoms->_NET_WM_STATE_ABOVE}, + {"_NET_WM_STATE_STICKY", sizeof("_NET_WM_STATE_STICKY") - 1, + &atoms->_NET_WM_STATE_STICKY}, + {"_NET_WM_STATE_MODAL", sizeof("_NET_WM_STATE_MODAL") - 1, + &atoms->_NET_WM_STATE_MODAL}, + {"_NET_WM_STATE_SKIP_TASKBAR", sizeof("_NET_WM_STATE_SKIP_TASKBAR") - 1, + &atoms->_NET_WM_STATE_SKIP_TASKBAR}, + + {"_NET_WM_WINDOW_TYPE", sizeof("_NET_WM_WINDOW_TYPE") - 1, + &atoms->_NET_WM_WINDOW_TYPE}, + {"_NET_WM_WINDOW_TYPE_NORMAL", sizeof("_NET_WM_WINDOW_TYPE_NORMAL") - 1, + &atoms->_NET_WM_WINDOW_TYPE_NORMAL}, + {"_NET_WM_WINDOW_TYPE_DESKTOP", sizeof("_NET_WM_WINDOW_TYPE_DESKTOP") - 1, + &atoms->_NET_WM_WINDOW_TYPE_DESKTOP}, + {"_NET_WM_WINDOW_TYPE_DOCK", sizeof("_NET_WM_WINDOW_TYPE_DOCK") - 1, + &atoms->_NET_WM_WINDOW_TYPE_DOCK}, + {"_NET_WM_WINDOW_TYPE_TOOLBAR", sizeof("_NET_WM_WINDOW_TYPE_TOOLBAR") - 1, + &atoms->_NET_WM_WINDOW_TYPE_TOOLBAR}, + {"_NET_WM_WINDOW_TYPE_DIALOG", sizeof("_NET_WM_WINDOW_TYPE_DIALOG") - 1, + &atoms->_NET_WM_WINDOW_TYPE_DIALOG}, + {"_NET_WM_WINDOW_TYPE_UTILITY", sizeof("_NET_WM_WINDOW_TYPE_UTILITY") - 1, + &atoms->_NET_WM_WINDOW_TYPE_UTILITY}, + {"_NET_WM_WINDOW_TYPE_SPLASH", sizeof("_NET_WM_WINDOW_TYPE_SPLASH") - 1, + &atoms->_NET_WM_WINDOW_TYPE_SPLASH}, + {"_NET_WM_WINDOW_TYPE_MENU", sizeof("_NET_WM_WINDOW_TYPE_MENU") - 1, + &atoms->_NET_WM_WINDOW_TYPE_MENU}, + {"_NET_WM_WINDOW_TYPE_DROPDOWN_MENU", + sizeof("_NET_WM_WINDOW_TYPE_DROPDOWN_MENU") - 1, + &atoms->_NET_WM_WINDOW_TYPE_DROPDOWN_MENU}, + {"_NET_WM_WINDOW_TYPE_POPUP_MENU", + sizeof("_NET_WM_WINDOW_TYPE_POPUP_MENU") - 1, + &atoms->_NET_WM_WINDOW_TYPE_POPUP_MENU}, + {"_NET_WM_WINDOW_TYPE_TOOLTIP", sizeof("_NET_WM_WINDOW_TYPE_TOOLTIP") - 1, + &atoms->_NET_WM_WINDOW_TYPE_TOOLTIP}, + {"_NET_WM_WINDOW_TYPE_COMBO", sizeof("_NET_WM_WINDOW_TYPE_COMBO") - 1, + &atoms->_NET_WM_WINDOW_TYPE_COMBO}, + {"_NET_WM_WINDOW_TYPE_DND", sizeof("_NET_WM_WINDOW_TYPE_DND") - 1, + &atoms->_NET_WM_WINDOW_TYPE_DND}, + {"_NET_WM_WINDOW_TYPE_NOTIFICATION", + sizeof("_NET_WM_WINDOW_TYPE_NOTIFICATION") - 1, + &atoms->_NET_WM_WINDOW_TYPE_NOTIFICATION}, }; xcb_intern_atom_cookie_t cookies[countof(atom_list)]; diff --git a/src/backend/x11/event.c b/src/backend/x11/event.c index ec0482c..09e75d8 100644 --- a/src/backend/x11/event.c +++ b/src/backend/x11/event.c @@ -17,12 +17,25 @@ static bool handle_map_request(backend_t *backend, event_t *event, xcb_window_t window = xcb_event->window; event->type = ZDWM_EVENT_WINDOW_MAP_REQUEST; event->data.window_map_request.window = window; + window_metadata_t *metadata = &event->data.window_map_request.metadata; metadata->role = window_get_role(backend, window); metadata->title = window_get_title(backend, window); window_get_class(backend, window, &metadata->class_name, &metadata->instance_name); + + window_props_t *props = &event->data.window_map_request.props; + props->transient_for = window_get_transient_for(backend, window); + xcb_get_window_attributes_reply_t *wa_reply = + window_get_attributes(backend, window); + props->override_redirect = (bool)wa_reply->override_redirect; + p_delete(&wa_reply); + props->skip_taskbar = window_get_skip_taskbar(backend, window); + props->urgent = window_get_urgency(backend, window); + window_get_types(backend, window, &props->types, &props->type_count); + window_get_states(backend, window, &props->states, &props->state_count); /* TODO: */ + return true; } diff --git a/src/backend/x11/internal.h b/src/backend/x11/internal.h index bf4adf7..30a7bdb 100644 --- a/src/backend/x11/internal.h +++ b/src/backend/x11/internal.h @@ -11,6 +11,28 @@ typedef struct atoms_t { xcb_atom_t WM_WINDOW_ROLE; xcb_atom_t WM_NAME; xcb_atom_t _NET_WM_NAME; + xcb_atom_t _NET_WM_STATE; + xcb_atom_t _NET_WM_STATE_FULLSCREEN; + xcb_atom_t _NET_WM_STATE_ABOVE; + xcb_atom_t _NET_WM_STATE_STICKY; + xcb_atom_t _NET_WM_STATE_MODAL; + xcb_atom_t _NET_WM_STATE_SKIP_TASKBAR; + + xcb_atom_t _NET_WM_WINDOW_TYPE; + xcb_atom_t _NET_WM_WINDOW_TYPE_NORMAL; + xcb_atom_t _NET_WM_WINDOW_TYPE_DESKTOP; + xcb_atom_t _NET_WM_WINDOW_TYPE_DOCK; + xcb_atom_t _NET_WM_WINDOW_TYPE_TOOLBAR; + xcb_atom_t _NET_WM_WINDOW_TYPE_DIALOG; + xcb_atom_t _NET_WM_WINDOW_TYPE_UTILITY; + xcb_atom_t _NET_WM_WINDOW_TYPE_SPLASH; + xcb_atom_t _NET_WM_WINDOW_TYPE_MENU; + xcb_atom_t _NET_WM_WINDOW_TYPE_DROPDOWN_MENU; + xcb_atom_t _NET_WM_WINDOW_TYPE_POPUP_MENU; + xcb_atom_t _NET_WM_WINDOW_TYPE_TOOLTIP; + xcb_atom_t _NET_WM_WINDOW_TYPE_COMBO; + xcb_atom_t _NET_WM_WINDOW_TYPE_DND; + xcb_atom_t _NET_WM_WINDOW_TYPE_NOTIFICATION; } atoms_t; struct backend_t { diff --git a/src/backend/x11/window.c b/src/backend/x11/window.c index 894410f..487dc05 100644 --- a/src/backend/x11/window.c +++ b/src/backend/x11/window.c @@ -61,3 +61,168 @@ void window_get_class(backend_t *backend, xcb_window_t window, char **class_out, xcb_icccm_get_wm_class_reply_wipe(&prop); } + +xcb_window_t window_get_transient_for(backend_t *backend, xcb_window_t window) { + xcb_connection_t *conn = backend->conn; + xcb_get_property_cookie_t cookie = + xcb_icccm_get_wm_transient_for_unchecked(conn, window); + xcb_window_t trans_for = XCB_WINDOW_NONE; + if (xcb_icccm_get_wm_transient_for_reply(conn, cookie, &trans_for, nullptr)) { + return trans_for; + } + + return XCB_WINDOW_NONE; +} + +xcb_get_window_attributes_reply_t *window_get_attributes(backend_t *backend, + xcb_window_t window) { + xcb_get_window_attributes_cookie_t cookie = + xcb_get_window_attributes(backend->conn, window); + return xcb_get_window_attributes_reply(backend->conn, cookie, nullptr); +} + +bool window_get_skip_taskbar(backend_t *backend, xcb_window_t window) { + xcb_get_property_cookie_t cookie = xcb_get_property_unchecked( + backend->conn, false, window, backend->atoms._NET_WM_STATE, XCB_ATOM_ATOM, + 0, UINT32_MAX); + xcb_get_property_reply_t *reply = + xcb_get_property_reply(backend->conn, cookie, nullptr); + if (!reply) return false; + + bool found = false; + if (reply->type == XCB_ATOM_ATOM && reply->format == 32) { + xcb_atom_t *atoms = (xcb_atom_t *)xcb_get_property_value(reply); + uint32_t len = reply->value_len; + for (uint32_t i = 0; i < len; i++) { + if (atoms[i] == backend->atoms._NET_WM_STATE_SKIP_TASKBAR) { + found = true; + break; + } + } + } + + p_delete(&reply); + return found; +} + +bool window_get_urgency(backend_t *backend, xcb_window_t window) { + xcb_get_property_cookie_t cookie = + xcb_icccm_get_wm_hints_unchecked(backend->conn, window); + xcb_icccm_wm_hints_t hints; + xcb_icccm_get_wm_hints_reply(backend->conn, cookie, &hints, nullptr); + return (bool)xcb_icccm_wm_hints_get_urgency(&hints); +} + +static xcb_atom_t *window_get_atom_array(backend_t *backend, + xcb_window_t window, + xcb_atom_t property, + uint32_t *out_len) { + xcb_get_property_cookie_t cookie = xcb_get_property_unchecked( + backend->conn, false, window, property, XCB_ATOM_ATOM, 0, UINT32_MAX); + xcb_get_property_reply_t *reply = + xcb_get_property_reply(backend->conn, cookie, nullptr); + if (!reply) return nullptr; + + xcb_atom_t *result = nullptr; + if (reply->type == XCB_ATOM_ATOM && reply->format == 32 && reply->value_len) { + *out_len = reply->value_len; + result = + p_copy((xcb_atom_t *)xcb_get_property_value(reply), reply->value_len); + } + + p_delete(&reply); + return result; +} + +static window_type_t atom_to_window_type(const atoms_t *atoms, + xcb_atom_t atom) { + if (atom == atoms->_NET_WM_WINDOW_TYPE_DESKTOP) + return ZDWM_WINDOW_TYPE_DESKTOP; + if (atom == atoms->_NET_WM_WINDOW_TYPE_DOCK) return ZDWM_WINDOW_TYPE_DOCK; + if (atom == atoms->_NET_WM_WINDOW_TYPE_TOOLBAR) + return ZDWM_WINDOW_TYPE_TOOLBAR; + if (atom == atoms->_NET_WM_WINDOW_TYPE_DIALOG) return ZDWM_WINDOW_TYPE_DIALOG; + if (atom == atoms->_NET_WM_WINDOW_TYPE_UTILITY) + return ZDWM_WINDOW_TYPE_UTILITY; + if (atom == atoms->_NET_WM_WINDOW_TYPE_SPLASH) return ZDWM_WINDOW_TYPE_SPLASH; + if (atom == atoms->_NET_WM_WINDOW_TYPE_MENU) return ZDWM_WINDOW_TYPE_MENU; + if (atom == atoms->_NET_WM_WINDOW_TYPE_DROPDOWN_MENU) + return ZDWM_WINDOW_TYPE_DROPDOWN_MENU; + if (atom == atoms->_NET_WM_WINDOW_TYPE_POPUP_MENU) + return ZDWM_WINDOW_TYPE_POPUP_MENU; + if (atom == atoms->_NET_WM_WINDOW_TYPE_TOOLTIP) + return ZDWM_WINDOW_TYPE_TOOLTIP; + if (atom == atoms->_NET_WM_WINDOW_TYPE_COMBO) return ZDWM_WINDOW_TYPE_COMBO; + if (atom == atoms->_NET_WM_WINDOW_TYPE_DND) return ZDWM_WINDOW_TYPE_DND; + if (atom == atoms->_NET_WM_WINDOW_TYPE_NOTIFICATION) + return ZDWM_WINDOW_TYPE_NOTIFICATION; + return ZDWM_WINDOW_TYPE_NORMAL; +} + +bool window_get_types(backend_t *backend, xcb_window_t window, + window_type_t **types, size_t *count) { + if (!types || !count) return false; + + uint32_t atom_count = 0; + xcb_atom_t *atoms = window_get_atom_array( + backend, window, backend->atoms._NET_WM_WINDOW_TYPE, &atom_count); + if (!atoms) { + *types = nullptr; + *count = 0; + return false; + } + + *count = atom_count; + *types = p_new(window_type_t, atom_count); + for (uint32_t i = 0; i < atom_count; i++) { + (*types)[i] = atom_to_window_type(&backend->atoms, atoms[i]); + } + + p_delete(&atoms); + return true; +} + +static window_state_t atom_to_window_state(const atoms_t *atoms, + xcb_atom_t atom) { + if (atom == atoms->_NET_WM_STATE_FULLSCREEN) + return ZDWM_WINDOW_STATE_FULLSCREEN; + if (atom == atoms->_NET_WM_STATE_ABOVE) return ZDWM_WINDOW_STATE_ABOVE; + if (atom == atoms->_NET_WM_STATE_STICKY) return ZDWM_WINDOW_STATE_STICKY; + if (atom == atoms->_NET_WM_STATE_MODAL) return ZDWM_WINDOW_STATE_MODAL; + return (window_state_t)-1; +} + +bool window_get_states(backend_t *backend, xcb_window_t window, + window_state_t **states, size_t *count) { + if (!states || !count) return false; + + uint32_t atom_count = 0; + xcb_atom_t *atoms = window_get_atom_array( + backend, window, backend->atoms._NET_WM_STATE, &atom_count); + if (!atoms) { + *states = nullptr; + *count = 0; + return false; + } + + /* 只保留已知 state,跳过未识别的 atom */ + window_state_t *buf = p_new(window_state_t, atom_count); + size_t valid = 0; + for (uint32_t i = 0; i < atom_count; i++) { + window_state_t s = atom_to_window_state(&backend->atoms, atoms[i]); + if (s != (window_state_t)-1) buf[valid++] = s; + } + + p_delete(&atoms); + + if (valid == 0) { + p_delete(&buf); + *states = nullptr; + *count = 0; + return false; + } + + *states = buf; + *count = valid; + return true; +} diff --git a/src/backend/x11/window.h b/src/backend/x11/window.h index 728590c..3dc14f5 100644 --- a/src/backend/x11/window.h +++ b/src/backend/x11/window.h @@ -1,8 +1,10 @@ #pragma once +#include #include #include "core/backend.h" +#include "core/window.h" /** * @brief 获取窗口的 role 属性 @@ -31,3 +33,14 @@ char *window_get_title(backend_t *backend, xcb_window_t window); */ void window_get_class(backend_t *backend, xcb_window_t window, char **class_out, char **instance_out); + +xcb_window_t window_get_transient_for(backend_t *backend, xcb_window_t window); + +xcb_get_window_attributes_reply_t *window_get_attributes(backend_t *backend, + xcb_window_t window); +bool window_get_skip_taskbar(backend_t *backend, xcb_window_t window); +bool window_get_urgency(backend_t *backend, xcb_window_t window); +bool window_get_types(backend_t *backend, xcb_window_t window, + window_type_t **types, size_t *count); +bool window_get_states(backend_t *backend, xcb_window_t window, + window_state_t **states, size_t *count);