diff --git a/src/include/event-adapter.h b/src/include/event-adapter.h index f870331..9fec3d6 100644 --- a/src/include/event-adapter.h +++ b/src/include/event-adapter.h @@ -125,8 +125,17 @@ typedef struct property_notify_event_t { property_state_t state; } 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 { event_type_t type; + xcb_window_t window; + uint32_t message_type; + client_message_data_t data; + uint8_t format; } client_message_event_t; typedef union generic_event_t { diff --git a/src/include/xcb.h b/src/include/xcb.h index 1375f71..30fd372 100644 --- a/src/include/xcb.h +++ b/src/include/xcb.h @@ -62,6 +62,7 @@ void place_window_below_sibling(xcb_window_t window, xcb_window_t sibling); * @brief 关闭窗口并清理资源 */ void kill_window(xcb_window_t window); +void set_window_fullscreen_state(xcb_window_t window, bool fullscreen); typedef struct bar_cairo_params_t { xcb_connection_t *conn; @@ -119,6 +120,8 @@ typedef enum atom_t { atom_wm_delete, atom_wm_take_focus, atom_net_wm_name, + atom_net_wm_state, + atom_net_wm_fullscreen, atom__xrootpmap_id, atom_esetroot_pmap_id, } atom_t; diff --git a/src/layout.c b/src/layout.c index 89754d6..abcc422 100644 --- a/src/layout.c +++ b/src/layout.c @@ -22,12 +22,23 @@ void maximize_client(client_t *c) { c->height = m->window_height - 2 * c->border_width; } +/** + * @brief 设置全屏窗口的几何属性 + */ 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; c->x = m->monitor_x; c->y = m->monitor_y; - c->width = m->monitor_width - 2 * c->border_width; - c->height = m->monitor_height - 2 * c->border_width; + c->width = m->monitor_width; + c->height = m->monitor_height; + c->old_border_width = c->border_width; + c->border_width = 0; } void monocle(monitor_t *monitor) { diff --git a/src/main.c b/src/main.c index eda4be5..f627136 100644 --- a/src/main.c +++ b/src/main.c @@ -47,6 +47,7 @@ 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 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 unmanage_client(client_t *client, bool destroyed); 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 focus(client_t *client); 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 client_t *get_client_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_FOCUS_IN, focus_in); EVENT(EVENT_TYPE_PROPERTY_NOTIFY, property_notify); + EVENT(EVENT_TYPE_CLIENT_MESSAGE, client_message); #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_ADD(1)​​:添加全屏状态,窗口进入全屏模式 + * _NET_WM_STATE_REMOVE(0):移除全屏状态,窗口退出全屏模式 + * _NET_WM_STATE_TOGGLE(2):切换全屏状态(若当前全屏则退出,反之进入) + */ + 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) { window_geometry_t *geometry = get_window_geometry(window); client_t *c = ecalloc(1, sizeof(client_t)); @@ -677,13 +709,18 @@ void manage_window(xcb_window_t window) { apply_rules(c); } - attach_client(c); - attach_stack_client(c); set_window_event_mask(window, false); 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) { unfocus(current_monitor->selected_client, false); } + c->monitor->selected_client = c; arrange(c->monitor); map_window(window); focus(NULL); @@ -884,12 +921,32 @@ void unfocus(client_t *client, bool set_focus) { 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) { if (!client) return; if (CLIENT_VISIBLE(client)) { if (client->fullscreen) { - fullscreen_client(client); + set_client_fullscreen(client, true); } else if (client->maximize) { maximize_client(client); } else if (!client->monitor->layout->arrange || client->floating) { diff --git a/src/xcb/event.c b/src/xcb/event.c index c162056..4f78391 100644 --- a/src/xcb/event.c +++ b/src/xcb/event.c @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -112,9 +113,15 @@ static void convert_event(xcb_generic_event_t *xcb_event, event->property_notify.state = ev->state; break; } - case XCB_CLIENT_MESSAGE: - event->type = EVENT_TYPE_CLIENT_MESSAGE; + case XCB_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; + } default: event->type = -1; return; diff --git a/src/xcb/window.c b/src/xcb/window.c index 87b9131..6b784a9 100644 --- a/src/xcb/window.c +++ b/src/xcb/window.c @@ -323,3 +323,10 @@ void kill_window(xcb_window_t window) { xcb_flush(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); +} diff --git a/src/xcb/wm-extension.c b/src/xcb/wm-extension.c index ace5a6d..1e1d2ce 100644 --- a/src/xcb/wm-extension.c +++ b/src/xcb/wm-extension.c @@ -26,6 +26,8 @@ static atom_map_t atom_map[] = { {.atom = atom_wm_delete, .name = "WM_DELETE_WINDOW"}, {.atom = atom_wm_take_focus, .name = "WM_TAKE_FOCUS"}, {.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_esetroot_pmap_id, .name = "ESETROOT_PMAP_ID"}, }; diff --git a/src/xcb/xcb.c b/src/xcb/xcb.c index b3d16f4..4ba56dc 100644 --- a/src/xcb/xcb.c +++ b/src/xcb/xcb.c @@ -285,6 +285,7 @@ void free_window_list(window_list_t *window_list) { } void clean_xcb(void) { + focus_window(XCB_WINDOW_NONE); clean_xcb_cursor(); clean_ewmh_extension(); xcb_disconnect(conn);