处理 clientmessage 事件和全屏窗口

This commit is contained in:
2025-08-26 00:42:19 +08:00
parent b28c9cd58b
commit f225dc7011
8 changed files with 104 additions and 7 deletions

View File

@@ -125,8 +125,17 @@ typedef struct property_notify_event_t {
property_state_t state; property_state_t state;
} property_notify_event_t; } property_notify_event_t;
typedef union client_message_data_t {
uint8_t data8[20];
uint16_t data16[10];
uint32_t data32[5];
} client_message_data_t;
typedef struct client_message_event_t { typedef struct client_message_event_t {
event_type_t type; event_type_t type;
xcb_window_t window;
uint32_t message_type;
client_message_data_t data;
uint8_t format;
} client_message_event_t; } client_message_event_t;
typedef union generic_event_t { typedef union generic_event_t {

View File

@@ -62,6 +62,7 @@ void place_window_below_sibling(xcb_window_t window, xcb_window_t sibling);
* @brief 关闭窗口并清理资源 * @brief 关闭窗口并清理资源
*/ */
void kill_window(xcb_window_t window); void kill_window(xcb_window_t window);
void set_window_fullscreen_state(xcb_window_t window, bool fullscreen);
typedef struct bar_cairo_params_t { typedef struct bar_cairo_params_t {
xcb_connection_t *conn; xcb_connection_t *conn;
@@ -119,6 +120,8 @@ typedef enum atom_t {
atom_wm_delete, atom_wm_delete,
atom_wm_take_focus, atom_wm_take_focus,
atom_net_wm_name, atom_net_wm_name,
atom_net_wm_state,
atom_net_wm_fullscreen,
atom__xrootpmap_id, atom__xrootpmap_id,
atom_esetroot_pmap_id, atom_esetroot_pmap_id,
} atom_t; } atom_t;

View File

@@ -22,12 +22,23 @@ void maximize_client(client_t *c) {
c->height = m->window_height - 2 * c->border_width; c->height = m->window_height - 2 * c->border_width;
} }
/**
* @brief 设置全屏窗口的几何属性
*/
void fullscreen_client(client_t *c) { void fullscreen_client(client_t *c) {
if (c->floating) {
c->old_x = c->x;
c->old_y = c->y;
c->old_width = c->width;
c->old_height = c->height;
}
monitor_t *m = c->monitor; monitor_t *m = c->monitor;
c->x = m->monitor_x; c->x = m->monitor_x;
c->y = m->monitor_y; c->y = m->monitor_y;
c->width = m->monitor_width - 2 * c->border_width; c->width = m->monitor_width;
c->height = m->monitor_height - 2 * c->border_width; c->height = m->monitor_height;
c->old_border_width = c->border_width;
c->border_width = 0;
} }
void monocle(monitor_t *monitor) { void monocle(monitor_t *monitor) {

View File

@@ -47,6 +47,7 @@ static void motion_notify(motion_notify_event_t *event);
static void enter_notify(enter_notify_event_t *event); static void enter_notify(enter_notify_event_t *event);
static void focus_in(focus_in_event_t *event); static void focus_in(focus_in_event_t *event);
static void property_notify(property_notify_event_t *event); static void property_notify(property_notify_event_t *event);
static void client_message(client_message_event_t *event);
static void manage_window(xcb_window_t window); static void manage_window(xcb_window_t window);
static void unmanage_client(client_t *client, bool destroyed); static void unmanage_client(client_t *client, bool destroyed);
static void update_client_list(void); static void update_client_list(void);
@@ -62,6 +63,7 @@ static void show_hide_client(client_t *client);
static void restack(monitor_t *monitor); static void restack(monitor_t *monitor);
static void focus(client_t *client); static void focus(client_t *client);
static void unfocus(client_t *client, bool set_focus); static void unfocus(client_t *client, bool set_focus);
static void set_client_fullscreen(client_t *client, bool fullscreen);
static void apply_monitor_layout(monitor_t *monitor); static void apply_monitor_layout(monitor_t *monitor);
static client_t *get_client_of_window(xcb_window_t window); static client_t *get_client_of_window(xcb_window_t window);
static monitor_t *get_monitor_of_window(xcb_window_t window); static monitor_t *get_monitor_of_window(xcb_window_t window);
@@ -516,6 +518,7 @@ void xcb_event_handler(generic_event_t *event, void *user_data) {
EVENT(EVENT_TYPE_ENTER_NOTIFY, enter_notify); EVENT(EVENT_TYPE_ENTER_NOTIFY, enter_notify);
EVENT(EVENT_TYPE_FOCUS_IN, focus_in); EVENT(EVENT_TYPE_FOCUS_IN, focus_in);
EVENT(EVENT_TYPE_PROPERTY_NOTIFY, property_notify); EVENT(EVENT_TYPE_PROPERTY_NOTIFY, property_notify);
EVENT(EVENT_TYPE_CLIENT_MESSAGE, client_message);
#undef EVENT #undef EVENT
@@ -651,6 +654,35 @@ void property_notify(property_notify_event_t *event) {
} }
} }
void client_message(client_message_event_t *event) {
client_t *c = get_client_of_window(event->window);
if (!c) return;
/**
* _NET_WM_STATE_FULLSCREEN 协议数据格式为
* data.data32 = {
* action, // 操作类型(添加/移除/切换)
* property1, // 第一个状态原子如_NET_WM_STATE_FULLSCREEN
* property2, // 第二个状态原子可选通常为0
* 0, // 预留字段固定为0
* 0 // 预留字段固定为0
* };
* action 值可能为
* _NET_WM_STATE_ADD1添加全屏状态窗口进入全屏模式
* _NET_WM_STATE_REMOVE0移除全屏状态窗口退出全屏模式
* _NET_WM_STATE_TOGGLE2切换全屏状态若当前全屏则退出反之进入
*/
if (event->message_type == get_xcb_atom(atom_net_wm_state)) {
uint32_t fullscreen_atom = get_xcb_atom(atom_net_wm_fullscreen);
if (event->data.data32[1] == fullscreen_atom ||
event->data.data32[2] == fullscreen_atom) {
bool set_fullscreen = event->data.data32[0] == 1;
bool toggle_to_fullscreen = event->data.data32[0] == 2 && !c->fullscreen;
set_client_fullscreen(c, set_fullscreen || toggle_to_fullscreen);
}
}
}
void manage_window(xcb_window_t window) { void manage_window(xcb_window_t window) {
window_geometry_t *geometry = get_window_geometry(window); window_geometry_t *geometry = get_window_geometry(window);
client_t *c = ecalloc(1, sizeof(client_t)); client_t *c = ecalloc(1, sizeof(client_t));
@@ -677,13 +709,18 @@ void manage_window(xcb_window_t window) {
apply_rules(c); apply_rules(c);
} }
attach_client(c);
attach_stack_client(c);
set_window_event_mask(window, false); set_window_event_mask(window, false);
change_window_border_color(window, color_set->border_color.argb); change_window_border_color(window, color_set->border_color.argb);
attach_client(c);
attach_stack_client(c);
update_client_list();
set_window_state(window, wm_state_normal);
if (c->monitor == current_monitor) { if (c->monitor == current_monitor) {
unfocus(current_monitor->selected_client, false); unfocus(current_monitor->selected_client, false);
} }
c->monitor->selected_client = c;
arrange(c->monitor); arrange(c->monitor);
map_window(window); map_window(window);
focus(NULL); focus(NULL);
@@ -884,12 +921,32 @@ void unfocus(client_t *client, bool set_focus) {
if (set_focus) focus_window(WINDOW_NONE); if (set_focus) focus_window(WINDOW_NONE);
} }
void set_client_fullscreen(client_t *client, bool fullscreen) {
set_window_fullscreen_state(client->window, true);
client->fullscreen = fullscreen;
if (fullscreen) {
fullscreen_client(client);
configure_window(client->window, client->x, client->y, client->width,
client->height, client->border_width);
raise_window(client->window);
} else {
client->x = client->old_x;
client->y = client->old_y;
client->width = client->old_width;
client->height = client->old_height;
client->border_width = client->old_border_width;
configure_window(client->window, client->x, client->y, client->width,
client->height, client->border_width);
arrange(client->monitor);
}
}
void show_hide_client(client_t *client) { void show_hide_client(client_t *client) {
if (!client) return; if (!client) return;
if (CLIENT_VISIBLE(client)) { if (CLIENT_VISIBLE(client)) {
if (client->fullscreen) { if (client->fullscreen) {
fullscreen_client(client); set_client_fullscreen(client, true);
} else if (client->maximize) { } else if (client->maximize) {
maximize_client(client); maximize_client(client);
} else if (!client->monitor->layout->arrange || client->floating) { } else if (!client->monitor->layout->arrange || client->floating) {

View File

@@ -1,5 +1,6 @@
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include <xcb/xcb.h> #include <xcb/xcb.h>
#include <xcb/xcb_event.h> #include <xcb/xcb_event.h>
#include <xcb/xproto.h> #include <xcb/xproto.h>
@@ -112,9 +113,15 @@ static void convert_event(xcb_generic_event_t *xcb_event,
event->property_notify.state = ev->state; event->property_notify.state = ev->state;
break; break;
} }
case XCB_CLIENT_MESSAGE: case XCB_CLIENT_MESSAGE: {
event->type = EVENT_TYPE_CLIENT_MESSAGE; xcb_client_message_event_t *ev = (xcb_client_message_event_t *)xcb_event;
event->client_message.type = EVENT_TYPE_CLIENT_MESSAGE;
event->client_message.window = ev->window;
event->client_message.message_type = ev->type;
event->client_message.format = ev->format;
memcpy(&event->client_message.data, &ev->data, sizeof(ev->data));
break; break;
}
default: default:
event->type = -1; event->type = -1;
return; return;

View File

@@ -323,3 +323,10 @@ void kill_window(xcb_window_t window) {
xcb_flush(conn); xcb_flush(conn);
xcb_ungrab_server(conn); xcb_ungrab_server(conn);
} }
void set_window_fullscreen_state(xcb_window_t window, bool fullscreen) {
xcb_atom_t atom = fullscreen ? ewmh->_NET_WM_STATE_FULLSCREEN : 0;
uint32_t data_len = fullscreen ? 1 : 0;
xcb_change_property(conn, XCB_PROP_MODE_REPLACE, window, atom,
XCB_ATOM_WINDOW, 32, data_len, &atom);
}

View File

@@ -26,6 +26,8 @@ static atom_map_t atom_map[] = {
{.atom = atom_wm_delete, .name = "WM_DELETE_WINDOW"}, {.atom = atom_wm_delete, .name = "WM_DELETE_WINDOW"},
{.atom = atom_wm_take_focus, .name = "WM_TAKE_FOCUS"}, {.atom = atom_wm_take_focus, .name = "WM_TAKE_FOCUS"},
{.atom = atom_net_wm_name, .name = "_NET_WM_NAME"}, {.atom = atom_net_wm_name, .name = "_NET_WM_NAME"},
{.atom = atom_net_wm_state, .name = "_NET_WM_STATE"},
{.atom = atom_net_wm_fullscreen, .name = "_NET_WM_STATE_FULLSCREEN"},
{.atom = atom__xrootpmap_id, .name = "_XROOTPMAP_ID"}, {.atom = atom__xrootpmap_id, .name = "_XROOTPMAP_ID"},
{.atom = atom_esetroot_pmap_id, .name = "ESETROOT_PMAP_ID"}, {.atom = atom_esetroot_pmap_id, .name = "ESETROOT_PMAP_ID"},
}; };

View File

@@ -285,6 +285,7 @@ void free_window_list(window_list_t *window_list) {
} }
void clean_xcb(void) { void clean_xcb(void) {
focus_window(XCB_WINDOW_NONE);
clean_xcb_cursor(); clean_xcb_cursor();
clean_ewmh_extension(); clean_ewmh_extension();
xcb_disconnect(conn); xcb_disconnect(conn);