Compare commits
7 Commits
a6d6798714
...
fc1f1455c1
| Author | SHA1 | Date | |
|---|---|---|---|
|
fc1f1455c1
|
|||
|
b99b8368aa
|
|||
|
2ccf2a3f53
|
|||
|
a26a903a19
|
|||
|
b45eea11fa
|
|||
|
2df0b58cb0
|
|||
|
e5927094d1
|
@@ -18,3 +18,6 @@ indent_size = 4
|
||||
|
||||
[Makefile]
|
||||
indent_style = tab
|
||||
|
||||
[atoms-list.txt]
|
||||
insert_final_newline = true
|
||||
@@ -1,7 +1,7 @@
|
||||
cmake_minimum_required(VERSION 3.24)
|
||||
|
||||
set(APP_NAME "zdwm")
|
||||
set(SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
set(SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/src")
|
||||
set(BUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}")
|
||||
|
||||
add_compile_definitions(LOG_FILE="$ENV{HOME}/${APP_NAME}-log.txt")
|
||||
@@ -20,18 +20,20 @@ project(${APP_NAME}
|
||||
)
|
||||
|
||||
add_executable(${APP_NAME}
|
||||
wm.c
|
||||
utils.c
|
||||
buffer.c
|
||||
backtrace.c
|
||||
monitor.c
|
||||
client.c
|
||||
text.c
|
||||
color.c
|
||||
xcursor.c
|
||||
xwindow.c
|
||||
event.c
|
||||
action.c
|
||||
${SOURCE_DIR}/wm.c
|
||||
${SOURCE_DIR}/utils.c
|
||||
${SOURCE_DIR}/buffer.c
|
||||
${SOURCE_DIR}/backtrace.c
|
||||
${SOURCE_DIR}/monitor.c
|
||||
${SOURCE_DIR}/client.c
|
||||
${SOURCE_DIR}/text.c
|
||||
${SOURCE_DIR}/color.c
|
||||
${SOURCE_DIR}/xcursor.c
|
||||
${SOURCE_DIR}/xwindow.c
|
||||
${SOURCE_DIR}/event.c
|
||||
${SOURCE_DIR}/action.c
|
||||
${SOURCE_DIR}/xkb.c
|
||||
${SOURCE_DIR}/atoms.c
|
||||
)
|
||||
|
||||
# use C23
|
||||
@@ -46,9 +48,13 @@ pkg_check_modules(deps REQUIRED
|
||||
xcb
|
||||
xcb-aux
|
||||
xcb-cursor
|
||||
xcb-icccm
|
||||
xcb-keysyms
|
||||
xcb-randr
|
||||
xcb-xfixes
|
||||
xcb-xinerama
|
||||
xcb-xkb
|
||||
xkbcommon-x11
|
||||
|
||||
cairo
|
||||
cairo-xcb
|
||||
@@ -91,3 +97,27 @@ configure_file("${SOURCE_DIR}/app.h.in"
|
||||
ESCAPE_QUOTES
|
||||
@ONLY
|
||||
)
|
||||
|
||||
set(ATOM_LIST_FILE "${SOURCE_DIR}/atoms-list.txt")
|
||||
add_custom_command(
|
||||
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/build-utils/atoms-extern.sh
|
||||
ARGS ${ATOM_LIST_FILE} > ${BUILD_DIR}/atoms-extern.h
|
||||
OUTPUT ${BUILD_DIR}/atoms-extern.h
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
DEPENDS ${ATOM_LIST_FILE}
|
||||
COMMENT "Generating atoms-extern.h"
|
||||
VERBATIM
|
||||
)
|
||||
add_custom_command(
|
||||
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/build-utils/atoms-intern.sh
|
||||
ARGS ${ATOM_LIST_FILE} > ${BUILD_DIR}/atoms-intern.h
|
||||
OUTPUT ${BUILD_DIR}/atoms-intern.h
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
DEPENDS ${ATOM_LIST_FILE}
|
||||
COMMENT "Generating atoms-intern.h"
|
||||
VERBATIM
|
||||
)
|
||||
add_custom_target(generate_atoms
|
||||
DEPENDS ${BUILD_DIR}/atoms-extern.h ${BUILD_DIR}/atoms-intern.h
|
||||
)
|
||||
add_dependencies(${APP_NAME} generate_atoms)
|
||||
2
Makefile
2
Makefile
@@ -2,7 +2,7 @@ MAKEFILE_ABS_PATH := $(abspath $(lastword $(MAKEFILE_LIST)))
|
||||
MAKEFILE_DIR := $(dir $(MAKEFILE_ABS_PATH))
|
||||
PWD_DIR := $(CURDIR)/
|
||||
BUILD_DIR := $(addprefix $(MAKEFILE_DIR),build)
|
||||
SRC_DIR := $(addprefix $(MAKEFILE_DIR),src/)
|
||||
SRC_DIR := $(addprefix $(MAKEFILE_DIR),/)
|
||||
|
||||
TARGET_NAME := zdwm
|
||||
TARGET := $(addprefix $(MAKEFILE_DIR),$(TARGET_NAME))
|
||||
|
||||
17
build-utils/atoms-extern.sh
Executable file
17
build-utils/atoms-extern.sh
Executable file
@@ -0,0 +1,17 @@
|
||||
#!/bin/sh
|
||||
|
||||
echo "/* This file is autogenerated by $0 - do not edit */"
|
||||
echo
|
||||
echo "#include <xcb/xproto.h>"
|
||||
echo
|
||||
|
||||
while read atom
|
||||
do
|
||||
# 去掉首位空格
|
||||
atom_clean=$(echo "$atom" | sed 's/^[[:space:]]*//;s/[[:space:]]$//')
|
||||
|
||||
# 跳过空行
|
||||
if [[ -n "$atom_clean" ]]; then
|
||||
echo "extern xcb_atom_t ${atom_clean};"
|
||||
fi
|
||||
done < "$1"
|
||||
39
build-utils/atoms-intern.sh
Executable file
39
build-utils/atoms-intern.sh
Executable file
@@ -0,0 +1,39 @@
|
||||
#!/bin/sh
|
||||
|
||||
echo "/* This file is autogenerated by $0 - do not edit */"
|
||||
echo
|
||||
echo "#include <xcb/xproto.h>"
|
||||
echo
|
||||
|
||||
while read atom
|
||||
do
|
||||
# 去掉首位空格
|
||||
atom_clean=$(echo "$atom" | sed 's/^[[:space:]]*//;s/[[:space:]]$//')
|
||||
|
||||
# 跳过空行
|
||||
if [[ -n "$atom_clean" ]]; then
|
||||
echo "xcb_atom_t ${atom_clean};"
|
||||
fi
|
||||
done < "$1"
|
||||
|
||||
echo
|
||||
echo "typedef struct atom_item_t {"
|
||||
echo " const char *name;"
|
||||
echo " size_t len;"
|
||||
echo " xcb_atom_t *atom;"
|
||||
echo "} atom_item_t;"
|
||||
echo
|
||||
echo "static atom_item_t ATOM_LIST[] = {"
|
||||
|
||||
while read atom
|
||||
do
|
||||
# 去掉首位空格
|
||||
atom_clean=$(echo "$atom" | sed 's/^[[:space:]]*//;s/[[:space:]]$//')
|
||||
|
||||
# 跳过空行
|
||||
if [[ -n "$atom_clean" ]]; then
|
||||
echo " {\"${atom_clean}\", sizeof(\"${atom_clean}\") - 1, &${atom_clean}},"
|
||||
fi
|
||||
done < "$1"
|
||||
|
||||
echo "};"
|
||||
19
src/action.c
19
src/action.c
@@ -1,8 +1,27 @@
|
||||
#include "action.h"
|
||||
|
||||
#include <glib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "monitor.h"
|
||||
#include "wm.h"
|
||||
|
||||
void select_tag_of_current_monitor(const user_action_arg_t *arg) {
|
||||
monitor_select_tag(wm.current_monitor, arg->ui);
|
||||
}
|
||||
|
||||
void spawn(const user_action_arg_t *arg) {
|
||||
const char *cmd = (const char *)arg->ptr;
|
||||
if (cmd == nullptr || strlen(cmd) == 0) return;
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
17
src/action.h
17
src/action.h
@@ -7,7 +7,7 @@ typedef union user_action_arg_t {
|
||||
bool b;
|
||||
int32_t i;
|
||||
uint32_t ui;
|
||||
void *ptr;
|
||||
const void *ptr;
|
||||
} user_action_arg_t;
|
||||
|
||||
typedef enum click_area_t {
|
||||
@@ -24,6 +24,8 @@ typedef enum modifier_t {
|
||||
modifier_any = XCB_MOD_MASK_ANY,
|
||||
} modifier_t;
|
||||
|
||||
typedef uint16_t modifier_value_t;
|
||||
|
||||
typedef enum button_index_t {
|
||||
button_any = XCB_BUTTON_INDEX_ANY,
|
||||
button_left = XCB_BUTTON_INDEX_1,
|
||||
@@ -32,11 +34,20 @@ typedef enum button_index_t {
|
||||
} button_index_t;
|
||||
|
||||
typedef struct button_t {
|
||||
click_area_t click_area;
|
||||
modifier_t modifiers;
|
||||
click_area_t click_area : 16;
|
||||
modifier_value_t modifiers;
|
||||
button_index_t button;
|
||||
void (*func)(const user_action_arg_t *arg);
|
||||
const user_action_arg_t arg;
|
||||
} button_t;
|
||||
|
||||
typedef struct keyboard_t {
|
||||
modifier_value_t modifiers;
|
||||
xcb_keysym_t keysym;
|
||||
void (*func)(const user_action_arg_t *arg);
|
||||
const user_action_arg_t arg;
|
||||
} keyboard_t;
|
||||
|
||||
void select_tag_of_current_monitor(const user_action_arg_t *arg);
|
||||
void spawn(const user_action_arg_t *arg);
|
||||
void quit(const user_action_arg_t *arg);
|
||||
|
||||
6
src/atoms-list.txt
Normal file
6
src/atoms-list.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
_XKB_RULES_NAMES
|
||||
|
||||
WM_PROTOCOLS
|
||||
WM_TAKE_FOCUS
|
||||
|
||||
_NET_ACTIVE_WINDOW
|
||||
23
src/atoms.c
Normal file
23
src/atoms.c
Normal file
@@ -0,0 +1,23 @@
|
||||
#include "atoms.h"
|
||||
|
||||
#include <xcb/xproto.h>
|
||||
|
||||
#include "atoms-intern.h"
|
||||
#include "utils.h"
|
||||
|
||||
void atoms_init(xcb_connection_t *conn) {
|
||||
xcb_intern_atom_cookie_t cookies[countof(ATOM_LIST)];
|
||||
for (int i = 0; i < countof(ATOM_LIST); i++) {
|
||||
atom_item_t atom = ATOM_LIST[i];
|
||||
cookies[i] = xcb_intern_atom_unchecked(conn, false, atom.len, atom.name);
|
||||
}
|
||||
|
||||
xcb_intern_atom_reply_t *reply = nullptr;
|
||||
for (int i = 0; i < countof(ATOM_LIST); i++) {
|
||||
reply = xcb_intern_atom_reply(conn, cookies[i], nullptr);
|
||||
if (reply) {
|
||||
*ATOM_LIST[i].atom = reply->atom;
|
||||
p_delete(&reply);
|
||||
}
|
||||
}
|
||||
}
|
||||
5
src/atoms.h
Normal file
5
src/atoms.h
Normal file
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <xcb/xcb.h>
|
||||
|
||||
void atoms_init(xcb_connection_t *conn);
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <X11/keysym.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "action.h"
|
||||
@@ -23,3 +24,11 @@ static const char *const active_tag_color = "#eeeeee";
|
||||
static const button_t button_list[] = {
|
||||
{click_tag, modifier_none, button_left, select_tag_of_current_monitor, {0}},
|
||||
};
|
||||
|
||||
static const char launcher[] =
|
||||
"rofi -show combi -modes window,drun,run,ssh,combi,windowcd";
|
||||
static const keyboard_t key_list[] = {
|
||||
{modifier_alt, XK_p, spawn, {.ptr = launcher}},
|
||||
{modifier_alt, XK_q, quit, {.b = false}},
|
||||
{modifier_alt, XK_r, quit, {.b = true}},
|
||||
};
|
||||
|
||||
33
src/event.c
33
src/event.c
@@ -5,13 +5,16 @@
|
||||
#include <stdio.h>
|
||||
#include <xcb/xcb.h>
|
||||
#include <xcb/xcb_event.h>
|
||||
#include <xcb/xcb_keysyms.h>
|
||||
#include <xcb/xproto.h>
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
|
||||
#include "action.h"
|
||||
#include "default_config.h"
|
||||
#include "types.h"
|
||||
#include "utils.h"
|
||||
#include "wm.h"
|
||||
#include "xkb.h"
|
||||
|
||||
static void button_press(xcb_button_press_event_t *ev) {
|
||||
click_area_t click_area = click_none;
|
||||
@@ -46,22 +49,48 @@ static void button_press(xcb_button_press_event_t *ev) {
|
||||
}
|
||||
}
|
||||
|
||||
static void key_press(xcb_key_press_event_t *ev) { printf("key press\n"); }
|
||||
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) {
|
||||
printf("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) {
|
||||
key.func(&key.arg);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
printf("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); \
|
||||
break
|
||||
return
|
||||
|
||||
EVENT(XCB_BUTTON_PRESS, button_press);
|
||||
EVENT(XCB_KEY_PRESS, key_press);
|
||||
EVENT(XCB_KEY_RELEASE, key_press);
|
||||
|
||||
#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,
|
||||
|
||||
53
src/wm.c
53
src/wm.c
@@ -12,10 +12,12 @@
|
||||
#include <xcb/randr.h>
|
||||
#include <xcb/xcb.h>
|
||||
#include <xcb/xcb_aux.h>
|
||||
#include <xcb/xcb_keysyms.h>
|
||||
#include <xcb/xfixes.h>
|
||||
#include <xcb/xinerama.h>
|
||||
#include <xcb/xproto.h>
|
||||
|
||||
#include "atoms.h"
|
||||
#include "backtrace.h"
|
||||
#include "buffer.h"
|
||||
#include "color.h"
|
||||
@@ -26,6 +28,7 @@
|
||||
#include "types.h"
|
||||
#include "utils.h"
|
||||
#include "xcursor.h"
|
||||
#include "xkb.h"
|
||||
#include "xwindow.h"
|
||||
|
||||
static void wm_setup_signal(void);
|
||||
@@ -36,6 +39,7 @@ static void wm_scan_clients(void);
|
||||
static void wm_clean(void);
|
||||
static void wm_setup(void);
|
||||
static void wm_init_color_set(void);
|
||||
static void wm_setup_keybindings(void);
|
||||
|
||||
wm_t wm;
|
||||
|
||||
@@ -45,11 +49,6 @@ static char **cmd_argv;
|
||||
/** A pipe that is used to asynchronously handle SIGCHLD */
|
||||
static int sigchld_pipe[2];
|
||||
|
||||
static void wm_atexit(bool restart) {
|
||||
wm.need_restart = restart;
|
||||
wm_clean();
|
||||
}
|
||||
|
||||
static gboolean restart_on_signal(gpointer data) {
|
||||
wm_restart();
|
||||
return TRUE;
|
||||
@@ -265,6 +264,10 @@ void wm_clean(void) {
|
||||
}
|
||||
|
||||
xcursor_clean();
|
||||
xkb_free();
|
||||
xcb_ungrab_key(wm.xcb_conn, XCB_GRAB_ANY, wm.screen->root, modifier_any);
|
||||
xcb_key_symbols_free(wm.key_symbols);
|
||||
xcb_aux_sync(wm.xcb_conn);
|
||||
xcb_disconnect(wm.xcb_conn);
|
||||
}
|
||||
|
||||
@@ -285,7 +288,17 @@ static void wm_setup(void) {
|
||||
monitor_draw_bar(m);
|
||||
}
|
||||
|
||||
xwindow_change_cursor(wm.screen->root, cursor_normal);
|
||||
xcb_params_cw_t params = {
|
||||
.cursor = xcursor_get_xcb_cursor(cursor_normal),
|
||||
.event_mask =
|
||||
XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_KEY_PRESS,
|
||||
};
|
||||
xcb_aux_change_window_attributes(wm.xcb_conn, wm.screen->root, XCB_CW_CURSOR,
|
||||
¶ms);
|
||||
|
||||
wm_setup_keybindings();
|
||||
atoms_init(wm.xcb_conn);
|
||||
xkb_init();
|
||||
xcb_flush(wm.xcb_conn);
|
||||
}
|
||||
|
||||
@@ -297,13 +310,25 @@ void wm_init_color_set(void) {
|
||||
color_parse(active_tag_color, &wm.color_set.active_tag_color);
|
||||
}
|
||||
|
||||
void wm_setup_keybindings(void) {
|
||||
if (wm.key_symbols) {
|
||||
xcb_key_symbols_free(wm.key_symbols);
|
||||
p_delete(&wm.key_symbols);
|
||||
}
|
||||
|
||||
wm.key_symbols = xcb_key_symbols_alloc(wm.xcb_conn);
|
||||
xwindow_grab_keys(wm.screen->root, key_list, countof(key_list));
|
||||
}
|
||||
|
||||
void wm_restart(void) {
|
||||
wm_atexit(true);
|
||||
execvp(cmd_argv[0], cmd_argv);
|
||||
fatal("execv() failed: %s", strerror(errno));
|
||||
wm.need_restart = true;
|
||||
if (g_main_loop_is_running(wm.loop)) {
|
||||
g_main_loop_quit(wm.loop);
|
||||
}
|
||||
}
|
||||
|
||||
void wm_quit(void) {
|
||||
wm.need_restart = false;
|
||||
if (g_main_loop_is_running(wm.loop)) {
|
||||
g_main_loop_quit(wm.loop);
|
||||
}
|
||||
@@ -335,12 +360,11 @@ int main(int argc, char *argv[]) {
|
||||
wm_check_xcb_extensions();
|
||||
|
||||
wm_detect_monitor();
|
||||
setup_event_loop();
|
||||
wm_setup();
|
||||
|
||||
debug_show_monitor_list();
|
||||
|
||||
setup_event_loop();
|
||||
|
||||
if (wm.loop == nullptr) {
|
||||
wm.loop = g_main_loop_new(nullptr, FALSE);
|
||||
g_main_loop_run(wm.loop);
|
||||
@@ -348,7 +372,12 @@ int main(int argc, char *argv[]) {
|
||||
g_main_loop_unref(wm.loop);
|
||||
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;
|
||||
}
|
||||
|
||||
10
src/wm.h
10
src/wm.h
@@ -1,6 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <xcb/xcb_keysyms.h>
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
|
||||
#include "types.h"
|
||||
|
||||
@@ -19,10 +21,18 @@ typedef struct wm_t {
|
||||
|
||||
xcb_connection_t *xcb_conn;
|
||||
xcb_screen_t *screen;
|
||||
xcb_key_symbols_t *key_symbols;
|
||||
int default_screen;
|
||||
bool have_xfixes;
|
||||
bool have_xinerama;
|
||||
bool have_randr;
|
||||
bool have_xkb;
|
||||
|
||||
uint8_t event_base_xkb;
|
||||
bool xkb_reload_keymap;
|
||||
bool xkb_update_pending;
|
||||
struct xkb_context *xkb_ctx;
|
||||
struct xkb_state *xkb_state;
|
||||
|
||||
color_set_t color_set;
|
||||
} wm_t;
|
||||
|
||||
216
src/xkb.c
Normal file
216
src/xkb.c
Normal file
@@ -0,0 +1,216 @@
|
||||
#include "xkb.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <glib.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <xcb/xcb.h>
|
||||
#include <xcb/xcb_keysyms.h>
|
||||
#include <xcb/xkb.h>
|
||||
#include <xcb/xproto.h>
|
||||
#include <xkbcommon/xkbcommon-x11.h>
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
|
||||
#include "atoms-extern.h"
|
||||
#include "default_config.h"
|
||||
#include "utils.h"
|
||||
#include "wm.h"
|
||||
#include "xwindow.h"
|
||||
|
||||
static bool xkb_fill_rule_names_from_root(struct xkb_rule_names *xkb_names) {
|
||||
xcb_get_property_cookie_t cookie = xcb_get_property_unchecked(
|
||||
wm.xcb_conn, false, wm.screen->root, _XKB_RULES_NAMES,
|
||||
XCB_GET_PROPERTY_TYPE_ANY, 0, UINT_MAX);
|
||||
xcb_get_property_reply_t *reply =
|
||||
xcb_get_property_reply(wm.xcb_conn, cookie, nullptr);
|
||||
if (!reply) return false;
|
||||
|
||||
if (reply->value_len == 0) {
|
||||
p_delete(&reply);
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *walk = xcb_get_property_value(reply);
|
||||
size_t remaining = xcb_get_property_value_length(reply);
|
||||
for (int i = 0; i < 5 && remaining > 0; i++) {
|
||||
size_t len = strnlen(walk, remaining);
|
||||
switch (i) {
|
||||
case 0:
|
||||
xkb_names->rules = strndup(walk, len);
|
||||
break;
|
||||
case 1:
|
||||
xkb_names->model = strndup(walk, len);
|
||||
break;
|
||||
case 2:
|
||||
xkb_names->layout = strndup(walk, len);
|
||||
break;
|
||||
case 3:
|
||||
xkb_names->variant = strndup(walk, len);
|
||||
break;
|
||||
case 4:
|
||||
xkb_names->options = strndup(walk, len);
|
||||
break;
|
||||
}
|
||||
remaining -= len + 1;
|
||||
walk = &walk[len + 1];
|
||||
}
|
||||
|
||||
p_delete(&reply);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void xkb_fill_state(void) {
|
||||
int32_t device_id = -1;
|
||||
if (wm.have_xkb) {
|
||||
device_id = xkb_x11_get_core_keyboard_device_id(wm.xcb_conn);
|
||||
if (device_id == -1) warn("Failed get XKB device id");
|
||||
}
|
||||
|
||||
if (device_id == -1) {
|
||||
struct xkb_rule_names names = {nullptr, nullptr, nullptr, nullptr, nullptr};
|
||||
if (!xkb_fill_rule_names_from_root(&names)) {
|
||||
warn(
|
||||
"Could not get _XKB_RULES_NAMES from root window, falling back to "
|
||||
"defaults.");
|
||||
}
|
||||
|
||||
struct xkb_keymap *xkb_keymap = xkb_keymap_new_from_names(
|
||||
wm.xkb_ctx, &names, XKB_KEYMAP_COMPILE_NO_FLAGS);
|
||||
wm.xkb_state = xkb_state_new(xkb_keymap);
|
||||
|
||||
if (!wm.xkb_state) fatal("Failed create XKB state");
|
||||
|
||||
xkb_keymap_unref(xkb_keymap);
|
||||
p_delete(&names.rules);
|
||||
p_delete(&names.model);
|
||||
p_delete(&names.layout);
|
||||
p_delete(&names.variant);
|
||||
p_delete(&names.options);
|
||||
} else {
|
||||
struct xkb_keymap *xkb_keymap = xkb_x11_keymap_new_from_device(
|
||||
wm.xkb_ctx, wm.xcb_conn, device_id, XKB_KEYMAP_COMPILE_NO_FLAGS);
|
||||
if (!xkb_keymap) fatal("Failed get XKB keymap from device");
|
||||
wm.xkb_state =
|
||||
xkb_x11_state_new_from_device(xkb_keymap, wm.xcb_conn, device_id);
|
||||
if (!wm.xkb_state) fatal("Failed get XKB state from device");
|
||||
xkb_keymap_unref(xkb_keymap);
|
||||
}
|
||||
}
|
||||
|
||||
static void xkb_init_keymap(void) {
|
||||
wm.xkb_ctx = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
||||
if (!wm.xkb_ctx) fatal("Cannot get XKB context");
|
||||
|
||||
xkb_fill_state();
|
||||
}
|
||||
|
||||
#define DEVICE_SPEC XCB_XKB_ID_USE_CORE_KBD
|
||||
|
||||
void xkb_init(void) {
|
||||
wm.xkb_update_pending = false;
|
||||
wm.xkb_reload_keymap = false;
|
||||
|
||||
wm.have_xkb = xkb_x11_setup_xkb_extension(
|
||||
wm.xcb_conn, XKB_X11_MIN_MAJOR_XKB_VERSION, XKB_X11_MIN_MINOR_XKB_VERSION,
|
||||
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) {
|
||||
warn("XKB not found or not supported");
|
||||
xkb_init_keymap();
|
||||
return;
|
||||
}
|
||||
|
||||
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,
|
||||
XCB_XKB_PER_CLIENT_FLAG_DETECTABLE_AUTO_REPEAT, 0, 0, 0);
|
||||
xcb_discard_reply(wm.xcb_conn, cookie.sequence);
|
||||
uint16_t xkb_event = XCB_XKB_EVENT_TYPE_STATE_NOTIFY |
|
||||
XCB_XKB_EVENT_TYPE_MAP_NOTIFY |
|
||||
XCB_XKB_EVENT_TYPE_NEW_KEYBOARD_NOTIFY;
|
||||
uint16_t map_parts =
|
||||
XCB_XKB_MAP_PART_KEY_TYPES | XCB_XKB_MAP_PART_KEY_SYMS |
|
||||
XCB_XKB_MAP_PART_MODIFIER_MAP | XCB_XKB_MAP_PART_EXPLICIT_COMPONENTS |
|
||||
XCB_XKB_MAP_PART_KEY_ACTIONS | XCB_XKB_MAP_PART_KEY_BEHAVIORS |
|
||||
XCB_XKB_MAP_PART_VIRTUAL_MODS | XCB_XKB_MAP_PART_VIRTUAL_MOD_MAP;
|
||||
xcb_xkb_select_events(wm.xcb_conn, DEVICE_SPEC, xkb_event, 0, xkb_event,
|
||||
map_parts, map_parts, nullptr);
|
||||
|
||||
xkb_init_keymap();
|
||||
}
|
||||
|
||||
void xkb_free(void) {
|
||||
if (wm.have_xkb) {
|
||||
xcb_xkb_select_events(wm.xcb_conn, DEVICE_SPEC, 0, 0, 0, 0, 0, nullptr);
|
||||
}
|
||||
|
||||
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) {
|
||||
assert(wm.have_xkb);
|
||||
|
||||
xcb_key_symbols_free(wm.key_symbols);
|
||||
wm.key_symbols = xcb_key_symbols_alloc(wm.xcb_conn);
|
||||
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) {
|
||||
wm.xkb_update_pending = false;
|
||||
if (wm.xkb_reload_keymap) xkb_reload_keymap();
|
||||
|
||||
wm.xkb_reload_keymap = false;
|
||||
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
static void xkb_schedule_refresh(void) {
|
||||
if (wm.xkb_update_pending) return;
|
||||
|
||||
wm.xkb_update_pending = true;
|
||||
g_idle_add_full(G_PRIORITY_LOW, xkb_refresh, nullptr, nullptr);
|
||||
}
|
||||
|
||||
void xkb_handle_event(xcb_generic_event_t *event) {
|
||||
assert(wm.have_xkb);
|
||||
|
||||
printf("xkb event type: %u\n", event->pad0);
|
||||
|
||||
/* XKB 中没有对应所有事件的泛型类型,xcb_generic_event_t 类型中的 pad0
|
||||
* 字段对应 XKB 事件中的 xkbType 字段,故用该字段判断 XKB 事件类型 */
|
||||
switch (event->pad0) {
|
||||
case XCB_XKB_NEW_KEYBOARD_NOTIFY: {
|
||||
wm.xkb_reload_keymap = true;
|
||||
xkb_schedule_refresh();
|
||||
break;
|
||||
}
|
||||
case XCB_XKB_MAP_NOTIFY: {
|
||||
wm.xkb_reload_keymap = true;
|
||||
xkb_schedule_refresh();
|
||||
break;
|
||||
}
|
||||
case XCB_XKB_STATE_NOTIFY: {
|
||||
xcb_xkb_state_notify_event_t *state_notify_event = (void *)event;
|
||||
xkb_state_update_mask(
|
||||
wm.xkb_state, state_notify_event->baseMods,
|
||||
state_notify_event->latchedMods, state_notify_event->lockedMods,
|
||||
state_notify_event->baseGroup, state_notify_event->latchedGroup,
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
7
src/xkb.h
Normal file
7
src/xkb.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <xcb/xcb.h>
|
||||
|
||||
void xkb_init(void);
|
||||
void xkb_free(void);
|
||||
void xkb_handle_event(xcb_generic_event_t *event);
|
||||
@@ -1,7 +1,14 @@
|
||||
#include "xwindow.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <xcb/xcb.h>
|
||||
#include <xcb/xcb_aux.h>
|
||||
#include <xcb/xcb_icccm.h>
|
||||
#include <xcb/xcb_keysyms.h>
|
||||
#include <xcb/xproto.h>
|
||||
|
||||
#include "action.h"
|
||||
#include "atoms-extern.h"
|
||||
#include "utils.h"
|
||||
#include "wm.h"
|
||||
#include "xcursor.h"
|
||||
@@ -105,3 +112,62 @@ void xwindow_change_cursor(xcb_window_t window, cursor_t cursor) {
|
||||
xcb_params_cw_t params = {.cursor = xcursor_get_xcb_cursor(cursor)};
|
||||
xcb_aux_change_window_attributes(wm.xcb_conn, window, XCB_CW_CURSOR, ¶ms);
|
||||
}
|
||||
|
||||
void xwindow_grab_keys(xcb_window_t window, const keyboard_t *keys,
|
||||
int keys_length) {
|
||||
xcb_connection_t *conn = wm.xcb_conn;
|
||||
xcb_ungrab_key(conn, XCB_GRAB_ANY, window, modifier_any);
|
||||
|
||||
xcb_grab_mode_t mode = XCB_GRAB_MODE_ASYNC;
|
||||
for (int i = 0; i < keys_length; i++) {
|
||||
xcb_keycode_t *keycodes =
|
||||
xcb_key_symbols_get_keycode(wm.key_symbols, keys[i].keysym);
|
||||
if (keycodes) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
#include <stdint.h>
|
||||
#include <xcb/xproto.h>
|
||||
|
||||
#include "action.h"
|
||||
#include "app.h"
|
||||
#include "xcursor.h"
|
||||
|
||||
typedef struct visual_t {
|
||||
@@ -12,3 +14,18 @@ typedef struct visual_t {
|
||||
|
||||
visual_t *xwindow_get_xcb_visual(bool prefer_alpha);
|
||||
void xwindow_change_cursor(xcb_window_t window, cursor_t cursor);
|
||||
void xwindow_grab_keys(xcb_window_t window, const keyboard_t *keys,
|
||||
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)
|
||||
|
||||
Reference in New Issue
Block a user