Files
zdwm/src/event.c
Zedhugh Chen 123fe77bbb refactor: 移除 focus_in 事件处理
- 移除独立的 focus_in 事件处理器
- 在 enter_notify 中直接调用 wm_set_current_monitor
2026-03-11 06:46:19 +08:00

386 lines
12 KiB
C
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "event.h"
#include <glib.h>
#include <stdint.h>
#include <xcb/xcb.h>
#include <xcb/xcb_event.h>
#include <xcb/xcb_icccm.h>
#include <xcb/xcb_keysyms.h>
#include <xcb/xproto.h>
#include <xkbcommon/xkbcommon.h>
#include "action.h"
#include "atoms-extern.h"
#include "base.h"
#include "client.h"
#include "default_config.h"
#include "monitor.h"
#include "tray.h"
#include "types.h"
#include "utils.h"
#include "wm.h"
#include "xkb.h"
#include "xwindow.h"
static void button_press(xcb_button_press_event_t *ev) {
click_area_t click_area = click_none;
user_action_arg_t arg = {0};
monitor_t *monitor = nullptr;
for (monitor_t *m = wm.monitor_list; m; m = m->next) {
if (ev->event == m->bar_window) {
monitor = m;
break;
}
}
if (!monitor) return;
wm_set_current_monitor(monitor, false);
if (ev->event_x >= monitor->tag_extent.start &&
ev->event_x <= monitor->tag_extent.end) {
click_area = click_tag;
for (tag_t *tag = monitor->tag_list; tag; tag = tag->next) {
if (ev->event_x >= tag->bar_extent.start &&
ev->event_x <= tag->bar_extent.end) {
arg.ui = tag->mask;
break;
}
}
}
if (click_area == click_none) {
for (task_in_tag_t *task = monitor->selected_tag->task_list; task;
task = task->next) {
if (ev->event_x >= task->bar_extent.start &&
ev->event_x <= task->bar_extent.end) {
click_area = click_client_name;
arg.ptr = task->client;
break;
}
}
}
if (click_area == click_none) return;
for (int i = 0; i < countof(button_list); i++) {
if (click_area == button_list[i].click_area &&
button_list[i].button == ev->detail &&
button_list[i].modifiers == ev->state) {
button_list[i].func(&arg);
}
}
}
static void configure_request(xcb_configure_request_event_t *ev) {
if (tray_handle_configure_request(ev)) return;
client_t *client = client_get_by_window(ev->window);
if (client) {
if (ev->value_mask & XCB_CONFIG_WINDOW_BORDER_WIDTH) {
client_change_border_width(client, ev->border_width);
} else {
tag_t *tag = client->monitor->selected_tag;
task_in_tag_t *task = client_get_task_in_tag(client, tag);
if (!task || !(client->floating || !tag->layout->arrange)) return;
if (ev->value_mask & XCB_CONFIG_WINDOW_X) task->geometry.x = ev->x;
if (ev->value_mask & XCB_CONFIG_WINDOW_Y) task->geometry.y = ev->y;
if (ev->value_mask & XCB_CONFIG_WINDOW_WIDTH)
task->geometry.width = ev->width;
if (ev->value_mask & XCB_CONFIG_WINDOW_HEIGHT)
task->geometry.height = ev->height;
client_apply_geometry(client, task->geometry);
}
} else {
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);
xcb_flush(wm.xcb_conn);
}
}
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);
char key_name[16];
if (xkb_keysym_get_name(keysym, key_name, sizeof(key_name)) != -1) {
logger("key %s: %s\n", is_press ? "press" : "release", key_name);
}
if (!is_press) return;
for (int i = 0; i < countof(key_list); i++) {
keyboard_t key = key_list[i];
if (keysym == key.keysym && ev->state == key.modifiers && key.func) {
/* 查询光标所在屏幕并将其设置为当前屏幕 */
point_t point = {.x = ev->root_x, .y = ev->root_y};
monitor_t *monitor = wm_get_monitor_by_point(point);
wm_set_current_monitor(monitor, false);
key.func(&key.arg);
return;
}
}
}
static void map_request(xcb_map_request_event_t *ev) {
if (tray_handle_map_request(ev)) return;
xcb_get_window_attributes_reply_t *wa_reply =
xwindow_get_attributes_reply(ev->window);
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_reply_t *geo_reply =
xwindow_get_geometry_reply(ev->window);
if (!geo_reply) {
p_delete(&wa_reply);
return;
}
client_manage(ev->window, geo_reply);
p_delete(&geo_reply);
}
p_delete(&wa_reply);
}
typedef enum _NET_WM_STATE_ACTION : uint32_t {
_NET_WM_STATE_ADD = 1,
_NET_WM_STATE_REMOVE = 0,
_NET_WM_STATE_TOGGLE = 2,
} _NET_WM_STATE_ACTION;
static inline bool get_bool_property(bool init_property,
_NET_WM_STATE_ACTION action) {
switch (action) {
case _NET_WM_STATE_ADD:
return true;
case _NET_WM_STATE_REMOVE:
return false;
case _NET_WM_STATE_TOGGLE:
return !init_property;
default:
fatal(
"Invalid _NET_WM_STATE_ACTION: %u\n"
"Only supported:"
"_NET_WM_STATE_ADD: %u"
"_NET_WM_STATE_REMOVE: %u"
"_NET_WM_STATE_TOGGLE: %u",
action, _NET_WM_STATE_ADD, _NET_WM_STATE_REMOVE, _NET_WM_STATE_TOGGLE);
}
}
static void client_message(xcb_client_message_event_t *ev) {
if (tray_handle_client_message(ev)) return;
client_t *c = client_get_by_window(ev->window);
if (c == nullptr) return;
if (ev->type == _NET_ACTIVE_WINDOW) {
for (tag_t *t = c->monitor->tag_list; t; t = t->next) {
if ((t->mask & c->tags) == 0) continue;
logger("== _NET_ACTIVE_WINDOW: %s[%u]\n", c->class, ev->data.data32[0]);
switch (ev->data.data32[0]) {
case 2: /* 来自 pager */
wm_set_current_monitor(c->monitor, true);
monitor_select_tag(c->monitor, t->mask);
client_focus(c);
break;
case 1: /* 来自应用程序 */
c->urgent = true;
monitor_draw_bar(c->monitor);
break;
case 0: /* 来自老版本标志 */
default:
break;
}
}
} else if (ev->type == _NET_WM_STATE) {
/**
* _NET_WM_STATE 协议数据格式为
* 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切换全屏状态若当前全屏则退出反之进入
*/
_NET_WM_STATE_ACTION action = ev->data.data32[0];
if (ev->data.data32[1] == _NET_WM_STATE_FULLSCREEN ||
ev->data.data32[2] == _NET_WM_STATE_FULLSCREEN) {
client_set_fullscreen(c, get_bool_property(c->fullscreen, action));
} else if (ev->data.data32[1] == _NET_WM_STATE_MAXIMIZED_HORZ ||
ev->data.data32[1] == _NET_WM_STATE_MAXIMIZED_VERT ||
ev->data.data32[2] == _NET_WM_STATE_MAXIMIZED_HORZ ||
ev->data.data32[2] == _NET_WM_STATE_MAXIMIZED_VERT) {
client_set_maximize(c, get_bool_property(c->maximize, action));
}
} else if (ev->type == WM_CHANGE_STATE) {
switch (ev->data.data32[0]) {
case XCB_ICCCM_WM_STATE_ICONIC:
client_set_minimize(c, true);
break;
case XCB_ICCCM_WM_STATE_NORMAL:
client_set_minimize(c, false);
break;
}
}
}
static void property_notify(xcb_property_notify_event_t *ev) {
if (tray_handle_property_notify(ev)) return;
client_t *client = client_get_by_window(ev->window);
if (client == nullptr) return;
if (ev->atom == WM_NAME) {
xwindow_get_text_property(ev->window, ev->atom, &client->name);
monitor_draw_bar(client->monitor);
xcb_flush(wm.xcb_conn);
} else if (ev->atom == _NET_WM_NAME) {
xwindow_get_text_property(ev->window, ev->atom, &client->net_name);
monitor_draw_bar(client->monitor);
xcb_flush(wm.xcb_conn);
} else if (ev->atom == XCB_ATOM_WM_HINTS) {
client_update_wm_hints(client);
monitor_draw_bar(client->monitor);
xcb_flush(wm.xcb_conn);
}
}
static void destroy_notify(xcb_destroy_notify_event_t *ev) {
if (tray_handle_destroy_notify(ev)) return;
client_t *client = client_get_by_window(ev->window);
if (client) client_unmanage(client, true);
}
static void unmap_notify(xcb_unmap_notify_event_t *ev) {
if (tray_handle_unmap_notify(ev)) return;
client_t *client = client_get_by_window(ev->window);
if (!client || client->minimize) return;
if (XCB_EVENT_SENT(ev)) {
xwindow_set_state(ev->window, XCB_ICCCM_WM_STATE_WITHDRAWN);
} else {
client_unmanage(client, false);
}
}
static void map_notify(xcb_map_notify_event_t *ev) {
/* 锁屏重新进入桌面后绘制 bar 内容以免 bar 不显示内容 */
for (monitor_t *m = wm.monitor_list; m; m = m->next) {
if (ev->window == m->bar_window) {
monitor_draw_bar(m);
xcb_flush(wm.xcb_conn);
return;
}
}
}
static void expose(xcb_expose_event_t *ev) {
if (ev->count > 0) return;
monitor_t *monitor = wm_get_monitor_by_window(ev->window);
monitor_draw_bar(monitor);
xcb_flush(wm.xcb_conn);
}
static void enter_notify(xcb_enter_notify_event_t *ev) {
client_t *client = client_get_by_window(ev->event);
if (!client || wm.client_focused == client) return;
client_focus(client);
wm_set_current_monitor(client->monitor, false);
}
static void selection_clear(xcb_selection_clear_event_t *ev) {
tray_handle_selection_clear(ev);
}
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);
logger("event type: %u[%s], xkb_event: %u\n", event_type, label,
wm.event_base_xkb);
switch (event_type) {
#define EVENT(type, callback) \
case type: \
callback((void *)event); \
return
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);
EVENT(XCB_MAP_NOTIFY, map_notify);
EVENT(XCB_EXPOSE, expose);
EVENT(XCB_CLIENT_MESSAGE, client_message);
EVENT(XCB_PROPERTY_NOTIFY, property_notify);
EVENT(XCB_UNMAP_NOTIFY, unmap_notify);
EVENT(XCB_DESTROY_NOTIFY, destroy_notify);
EVENT(XCB_ENTER_NOTIFY, enter_notify);
EVENT(XCB_SELECTION_CLEAR, selection_clear);
#undef EVENT
}
/* 处理 XKB 事件 */
if (wm.event_base_xkb != 0 && event_type == wm.event_base_xkb) {
xkb_handle_event(event);
}
}
static gboolean xcb_event_loop(GIOChannel *channel, GIOCondition condition,
gpointer user_data) {
if (condition & (G_IO_HUP | G_IO_ERR)) {
wm_quit();
return false;
}
xcb_generic_event_t *event = nullptr;
while ((event = xcb_poll_for_event(wm.xcb_conn))) {
handle_xcb_event(event);
p_delete(&event);
}
return true;
}
void setup_event_loop(void) {
int fd = xcb_get_file_descriptor(wm.xcb_conn);
GIOChannel *channel = g_io_channel_unix_new(fd);
g_io_channel_set_encoding(channel, nullptr, nullptr);
GIOCondition cond = G_IO_IN | G_IO_HUP | G_IO_ERR;
g_io_add_watch(channel, cond, xcb_event_loop, nullptr);
g_io_channel_unref(channel);
}