Compare commits

..

7 Commits

Author SHA1 Message Date
fc1f1455c1 添加窗口属性设置宏 2025-11-25 06:11:09 +08:00
b99b8368aa 封装窗口焦点控制函数 2025-11-25 06:10:42 +08:00
2ccf2a3f53 打印事件信息 2025-11-25 06:08:41 +08:00
a26a903a19 添加重启和退出快捷键 2025-11-25 06:06:58 +08:00
b45eea11fa 修改重启实现方式 2025-11-25 06:06:58 +08:00
2df0b58cb0 初始化 xkb 扩展 2025-11-25 06:06:42 +08:00
e5927094d1 设置快捷键 2025-11-25 06:06:24 +08:00
10 changed files with 132 additions and 34 deletions

View File

@@ -48,6 +48,7 @@ pkg_check_modules(deps REQUIRED
xcb xcb
xcb-aux xcb-aux
xcb-cursor xcb-cursor
xcb-icccm
xcb-keysyms xcb-keysyms
xcb-randr xcb-randr
xcb-xfixes xcb-xfixes

View File

@@ -16,3 +16,12 @@ void spawn(const user_action_arg_t *arg) {
g_spawn_command_line_async((const char *)arg->ptr, nullptr); g_spawn_command_line_async((const char *)arg->ptr, nullptr);
} }
void quit(const user_action_arg_t *arg) {
const bool restart = arg->b;
if (restart) {
wm_restart();
} else {
wm_quit();
}
}

View File

@@ -50,3 +50,4 @@ typedef struct keyboard_t {
void select_tag_of_current_monitor(const user_action_arg_t *arg); void select_tag_of_current_monitor(const user_action_arg_t *arg);
void spawn(const user_action_arg_t *arg); void spawn(const user_action_arg_t *arg);
void quit(const user_action_arg_t *arg);

View File

@@ -1 +1,6 @@
_XKB_RULES_NAMES _XKB_RULES_NAMES
WM_PROTOCOLS
WM_TAKE_FOCUS
_NET_ACTIVE_WINDOW

View File

@@ -28,5 +28,7 @@ static const button_t button_list[] = {
static const char launcher[] = static const char launcher[] =
"rofi -show combi -modes window,drun,run,ssh,combi,windowcd"; "rofi -show combi -modes window,drun,run,ssh,combi,windowcd";
static const keyboard_t key_list[] = { static const keyboard_t key_list[] = {
{modifier_super, XK_p, spawn, {.ptr = launcher}}, {modifier_alt, XK_p, spawn, {.ptr = launcher}},
{modifier_alt, XK_q, quit, {.b = false}},
{modifier_alt, XK_r, quit, {.b = true}},
}; };

View File

@@ -2,10 +2,12 @@
#include <glib.h> #include <glib.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h>
#include <xcb/xcb.h> #include <xcb/xcb.h>
#include <xcb/xcb_event.h> #include <xcb/xcb_event.h>
#include <xcb/xcb_keysyms.h> #include <xcb/xcb_keysyms.h>
#include <xcb/xproto.h> #include <xcb/xproto.h>
#include <xkbcommon/xkbcommon.h>
#include "action.h" #include "action.h"
#include "default_config.h" #include "default_config.h"
@@ -48,7 +50,15 @@ static void button_press(xcb_button_press_event_t *ev) {
} }
static void key_press(xcb_key_press_event_t *ev) { 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); 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) {
printf("key %s: %s\n", is_press ? "press" : "release", key_name);
}
if (!is_press) return;
for (int i = 0; i < countof(key_list); i++) { for (int i = 0; i < countof(key_list); i++) {
keyboard_t key = key_list[i]; keyboard_t key = key_list[i];
if (keysym == key.keysym && ev->state == key.modifiers && key.func) { if (keysym == key.keysym && ev->state == key.modifiers && key.func) {
@@ -60,6 +70,9 @@ static void key_press(xcb_key_press_event_t *ev) {
static void handle_xcb_event(xcb_generic_event_t *event) { static void handle_xcb_event(xcb_generic_event_t *event) {
uint8_t event_type = XCB_EVENT_RESPONSE_TYPE(event); uint8_t event_type = XCB_EVENT_RESPONSE_TYPE(event);
const char *label = xcb_event_get_label(event_type);
printf("event type: %u[%s], xkb_event: %u\n", event_type, label,
wm.event_base_xkb);
switch (event_type) { switch (event_type) {
#define EVENT(type, callback) \ #define EVENT(type, callback) \
@@ -69,6 +82,7 @@ static void handle_xcb_event(xcb_generic_event_t *event) {
EVENT(XCB_BUTTON_PRESS, button_press); EVENT(XCB_BUTTON_PRESS, button_press);
EVENT(XCB_KEY_PRESS, key_press); EVENT(XCB_KEY_PRESS, key_press);
EVENT(XCB_KEY_RELEASE, key_press);
#undef EVENT #undef EVENT
} }

View File

@@ -17,7 +17,6 @@
#include <xcb/xinerama.h> #include <xcb/xinerama.h>
#include <xcb/xproto.h> #include <xcb/xproto.h>
#include "action.h"
#include "atoms.h" #include "atoms.h"
#include "backtrace.h" #include "backtrace.h"
#include "buffer.h" #include "buffer.h"
@@ -50,11 +49,6 @@ static char **cmd_argv;
/** A pipe that is used to asynchronously handle SIGCHLD */ /** A pipe that is used to asynchronously handle SIGCHLD */
static int sigchld_pipe[2]; static int sigchld_pipe[2];
static void wm_atexit(bool restart) {
wm.need_restart = restart;
wm_clean();
}
static gboolean restart_on_signal(gpointer data) { static gboolean restart_on_signal(gpointer data) {
wm_restart(); wm_restart();
return TRUE; return TRUE;
@@ -273,6 +267,7 @@ void wm_clean(void) {
xkb_free(); xkb_free();
xcb_ungrab_key(wm.xcb_conn, XCB_GRAB_ANY, wm.screen->root, modifier_any); xcb_ungrab_key(wm.xcb_conn, XCB_GRAB_ANY, wm.screen->root, modifier_any);
xcb_key_symbols_free(wm.key_symbols); xcb_key_symbols_free(wm.key_symbols);
xcb_aux_sync(wm.xcb_conn);
xcb_disconnect(wm.xcb_conn); xcb_disconnect(wm.xcb_conn);
} }
@@ -301,9 +296,9 @@ static void wm_setup(void) {
xcb_aux_change_window_attributes(wm.xcb_conn, wm.screen->root, XCB_CW_CURSOR, xcb_aux_change_window_attributes(wm.xcb_conn, wm.screen->root, XCB_CW_CURSOR,
&params); &params);
wm_setup_keybindings();
atoms_init(wm.xcb_conn); atoms_init(wm.xcb_conn);
xkb_init(); xkb_init();
wm_setup_keybindings();
xcb_flush(wm.xcb_conn); xcb_flush(wm.xcb_conn);
} }
@@ -326,12 +321,14 @@ void wm_setup_keybindings(void) {
} }
void wm_restart(void) { void wm_restart(void) {
wm_atexit(true); wm.need_restart = true;
execvp(cmd_argv[0], cmd_argv); if (g_main_loop_is_running(wm.loop)) {
fatal("execv() failed: %s", strerror(errno)); g_main_loop_quit(wm.loop);
}
} }
void wm_quit(void) { void wm_quit(void) {
wm.need_restart = false;
if (g_main_loop_is_running(wm.loop)) { if (g_main_loop_is_running(wm.loop)) {
g_main_loop_quit(wm.loop); g_main_loop_quit(wm.loop);
} }
@@ -363,12 +360,11 @@ int main(int argc, char *argv[]) {
wm_check_xcb_extensions(); wm_check_xcb_extensions();
wm_detect_monitor(); wm_detect_monitor();
setup_event_loop();
wm_setup(); wm_setup();
debug_show_monitor_list(); debug_show_monitor_list();
setup_event_loop();
if (wm.loop == nullptr) { if (wm.loop == nullptr) {
wm.loop = g_main_loop_new(nullptr, FALSE); wm.loop = g_main_loop_new(nullptr, FALSE);
g_main_loop_run(wm.loop); g_main_loop_run(wm.loop);
@@ -376,7 +372,12 @@ int main(int argc, char *argv[]) {
g_main_loop_unref(wm.loop); g_main_loop_unref(wm.loop);
wm.loop = nullptr; wm.loop = nullptr;
wm_atexit(false); wm_clean();
if (wm.need_restart) {
execvp(cmd_argv[0], cmd_argv);
fatal("execv() failed: %s", strerror(errno));
}
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }

View File

@@ -4,6 +4,7 @@
#include <glib.h> #include <glib.h>
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h>
#include <string.h> #include <string.h>
#include <xcb/xcb.h> #include <xcb/xcb.h>
#include <xcb/xcb_keysyms.h> #include <xcb/xcb_keysyms.h>
@@ -105,11 +106,6 @@ static void xkb_init_keymap(void) {
xkb_fill_state(); xkb_fill_state();
} }
static void xkb_free_keymap(void) {
xkb_state_unref(wm.xkb_state);
xkb_context_unref(wm.xkb_ctx);
}
#define DEVICE_SPEC XCB_XKB_ID_USE_CORE_KBD #define DEVICE_SPEC XCB_XKB_ID_USE_CORE_KBD
void xkb_init(void) { void xkb_init(void) {
@@ -118,17 +114,15 @@ void xkb_init(void) {
wm.have_xkb = xkb_x11_setup_xkb_extension( wm.have_xkb = xkb_x11_setup_xkb_extension(
wm.xcb_conn, XKB_X11_MIN_MAJOR_XKB_VERSION, XKB_X11_MIN_MINOR_XKB_VERSION, wm.xcb_conn, XKB_X11_MIN_MAJOR_XKB_VERSION, XKB_X11_MIN_MINOR_XKB_VERSION,
XKB_X11_SETUP_XKB_EXTENSION_NO_FLAGS, nullptr, nullptr, nullptr, nullptr); XKB_X11_SETUP_XKB_EXTENSION_NO_FLAGS, nullptr, nullptr, &wm.event_base_xkb,
nullptr);
printf("xkb base event: %u\n", wm.event_base_xkb);
if (!wm.have_xkb) { if (!wm.have_xkb) {
warn("XKB not found or not supported"); warn("XKB not found or not supported");
xkb_init_keymap(); xkb_init_keymap();
return; return;
} }
const xcb_query_extension_reply_t *reply = nullptr;
reply = xcb_get_extension_data(wm.xcb_conn, &xcb_xkb_id);
if (reply && reply->present) wm.event_base_xkb = reply->first_event;
xcb_xkb_per_client_flags_cookie_t cookie = xcb_xkb_per_client_flags( xcb_xkb_per_client_flags_cookie_t cookie = xcb_xkb_per_client_flags(
wm.xcb_conn, DEVICE_SPEC, XCB_XKB_PER_CLIENT_FLAG_DETECTABLE_AUTO_REPEAT, wm.xcb_conn, DEVICE_SPEC, XCB_XKB_PER_CLIENT_FLAG_DETECTABLE_AUTO_REPEAT,
XCB_XKB_PER_CLIENT_FLAG_DETECTABLE_AUTO_REPEAT, 0, 0, 0); XCB_XKB_PER_CLIENT_FLAG_DETECTABLE_AUTO_REPEAT, 0, 0, 0);
@@ -151,18 +145,22 @@ void xkb_free(void) {
if (wm.have_xkb) { if (wm.have_xkb) {
xcb_xkb_select_events(wm.xcb_conn, DEVICE_SPEC, 0, 0, 0, 0, 0, nullptr); xcb_xkb_select_events(wm.xcb_conn, DEVICE_SPEC, 0, 0, 0, 0, 0, nullptr);
} }
xkb_free_keymap();
xkb_state_unref(wm.xkb_state);
xkb_context_unref(wm.xkb_ctx);
wm.xkb_state = nullptr;
wm.xkb_ctx = nullptr;
} }
static void xkb_reload_keymap(void) { static void xkb_reload_keymap(void) {
assert(wm.have_xkb); assert(wm.have_xkb);
xkb_state_unref(wm.xkb_state);
xkb_fill_state();
xcb_key_symbols_free(wm.key_symbols); xcb_key_symbols_free(wm.key_symbols);
wm.key_symbols = xcb_key_symbols_alloc(wm.xcb_conn); wm.key_symbols = xcb_key_symbols_alloc(wm.xcb_conn);
xwindow_grab_keys(wm.screen->root, key_list, countof(key_list)); xwindow_grab_keys(wm.screen->root, key_list, countof(key_list));
xkb_state_unref(wm.xkb_state);
xkb_fill_state();
} }
static gboolean xkb_refresh(gpointer ignore) { static gboolean xkb_refresh(gpointer ignore) {
@@ -184,6 +182,8 @@ static void xkb_schedule_refresh(void) {
void xkb_handle_event(xcb_generic_event_t *event) { void xkb_handle_event(xcb_generic_event_t *event) {
assert(wm.have_xkb); assert(wm.have_xkb);
printf("xkb event type: %u\n", event->pad0);
/* XKB 中没有对应所有事件的泛型类型xcb_generic_event_t 类型中的 pad0 /* XKB 中没有对应所有事件的泛型类型xcb_generic_event_t 类型中的 pad0
* 字段对应 XKB 事件中的 xkbType 字段,故用该字段判断 XKB 事件类型 */ * 字段对应 XKB 事件中的 xkbType 字段,故用该字段判断 XKB 事件类型 */
switch (event->pad0) { switch (event->pad0) {
@@ -204,6 +204,12 @@ void xkb_handle_event(xcb_generic_event_t *event) {
state_notify_event->latchedMods, state_notify_event->lockedMods, state_notify_event->latchedMods, state_notify_event->lockedMods,
state_notify_event->baseGroup, state_notify_event->latchedGroup, state_notify_event->baseGroup, state_notify_event->latchedGroup,
state_notify_event->lockedGroup); state_notify_event->lockedGroup);
printf("changed: %u\n",
state_notify_event->changed & XCB_XKB_STATE_PART_GROUP_STATE);
if (state_notify_event->changed & XCB_XKB_STATE_PART_GROUP_STATE) {
xkb_schedule_refresh();
}
break; break;
} }
} }

View File

@@ -1,11 +1,14 @@
#include "xwindow.h" #include "xwindow.h"
#include <stdint.h>
#include <xcb/xcb.h> #include <xcb/xcb.h>
#include <xcb/xcb_aux.h> #include <xcb/xcb_aux.h>
#include <xcb/xcb_icccm.h>
#include <xcb/xcb_keysyms.h> #include <xcb/xcb_keysyms.h>
#include <xcb/xproto.h> #include <xcb/xproto.h>
#include "action.h" #include "action.h"
#include "atoms-extern.h"
#include "utils.h" #include "utils.h"
#include "wm.h" #include "wm.h"
#include "xcursor.h" #include "xcursor.h"
@@ -116,13 +119,55 @@ void xwindow_grab_keys(xcb_window_t window, const keyboard_t *keys,
xcb_ungrab_key(conn, XCB_GRAB_ANY, window, modifier_any); xcb_ungrab_key(conn, XCB_GRAB_ANY, window, modifier_any);
xcb_grab_mode_t mode = XCB_GRAB_MODE_ASYNC; xcb_grab_mode_t mode = XCB_GRAB_MODE_ASYNC;
xcb_keycode_t *keycode = nullptr;
for (int i = 0; i < keys_length; i++) { for (int i = 0; i < keys_length; i++) {
keycode = xcb_key_symbols_get_keycode(wm.key_symbols, keys[i].keysym); xcb_keycode_t *keycodes =
if (keycode) { xcb_key_symbols_get_keycode(wm.key_symbols, keys[i].keysym);
xcb_grab_key(conn, true, window, keys[i].modifiers, *keycode, mode, mode); if (keycodes) {
p_delete(&keycode); for (xcb_keycode_t *kc = keycodes; *kc != XCB_NO_SYMBOL; kc++) {
xcb_grab_key(conn, true, window, keys[i].modifiers, *kc, mode, mode);
}
p_delete(&keycodes);
} }
} }
} }
bool xwindow_send_event(xcb_window_t window, xcb_atom_t atom) {
bool exist = false;
xcb_get_property_cookie_t cookie =
xcb_icccm_get_wm_protocols(wm.xcb_conn, window, atom);
xcb_icccm_get_wm_protocols_reply_t reply;
if (xcb_icccm_get_wm_protocols_reply(wm.xcb_conn, cookie, &reply, nullptr)) {
for (uint32_t i = 0; !exist && i < reply.atoms_len; i++) {
exist = reply.atoms[i] == atom;
}
xcb_icccm_get_wm_protocols_reply_wipe(&reply);
}
if (exist) {
xcb_client_message_event_t ev;
p_clear(&ev, 1);
ev.response_type = XCB_CLIENT_MESSAGE;
ev.format = 32;
ev.window = window;
ev.type = WM_PROTOCOLS;
ev.data.data32[0] = atom;
ev.data.data32[1] = XCB_CURRENT_TIME;
}
return exist;
}
void xwindow_focus(xcb_window_t window) {
if (window == XCB_WINDOW_NONE || window == wm.screen->root) {
xcb_set_input_focus(wm.xcb_conn, XCB_INPUT_FOCUS_POINTER_ROOT,
wm.screen->root, XCB_CURRENT_TIME);
xcb_delete_property(wm.xcb_conn, wm.screen->root, _NET_ACTIVE_WINDOW);
} else {
xcb_set_input_focus(wm.xcb_conn, XCB_INPUT_FOCUS_POINTER_ROOT, window,
XCB_CURRENT_TIME);
xcb_change_property(wm.xcb_conn, XCB_PROP_MODE_REPLACE, wm.screen->root,
_NET_ACTIVE_WINDOW, XCB_ATOM_WINDOW, 32, 1, &window);
xwindow_send_event(window, WM_TAKE_FOCUS);
}
}

View File

@@ -4,6 +4,7 @@
#include <xcb/xproto.h> #include <xcb/xproto.h>
#include "action.h" #include "action.h"
#include "app.h"
#include "xcursor.h" #include "xcursor.h"
typedef struct visual_t { typedef struct visual_t {
@@ -15,3 +16,16 @@ visual_t *xwindow_get_xcb_visual(bool prefer_alpha);
void xwindow_change_cursor(xcb_window_t window, cursor_t cursor); void xwindow_change_cursor(xcb_window_t window, cursor_t cursor);
void xwindow_grab_keys(xcb_window_t window, const keyboard_t *keys, void xwindow_grab_keys(xcb_window_t window, const keyboard_t *keys,
int keys_length); int keys_length);
bool xwindow_send_event(xcb_window_t window, xcb_atom_t atom);
void xwindow_focus(xcb_window_t window);
#define xwindow_set_name_static(window, name) \
xcb_icccm_set_wm_name(wm.xcb_conn, window, XCB_ATOM_STRING, 8, \
sizeof(name) - 1, name)
#define xwindow_set_class_instance(window) \
xwindow_set_class_instance_static(window, APP_NAME, APP_NAME)
#define xwindow_set_class_instance_static(window, class, instance) \
_xwindow_set_class_instance_static(window, instance "\0" class)
#define _xwindow_set_class_instance_static(window, instance_class) \
xcb_icccm_set_wm_class(wm.xcb_conn, window, sizeof(instance_class), \
instance_class)