Compare commits

..

17 Commits

Author SHA1 Message Date
c6ade0144c 添加 TODO.org 记录待开发功能及 bug 2025-09-30 21:41:39 +08:00
6d9ff81f78 事件适配器适配 XCB_MAPPING_NOTIFY 和 XCB_BUTTON_PRESS 事件 2025-09-30 21:40:54 +08:00
0626a4c512 调整 set_window_event_mask 函数中窗口默认事件掩码组织方式 2025-09-30 20:58:20 +08:00
4500a5adfd 调整 Makefile 和 CMake 配置 2025-09-30 20:51:57 +08:00
9315505085 manage_window 函数中释放窗口位置信息对应的内存 2025-09-30 16:23:16 +08:00
bec2162890 记录 property_notify 和 client_message 事件中的 xcb atom 名称 2025-09-30 16:22:44 +08:00
b40f499e7b 修复 get_window_tag 函数内存未释放 bug 2025-09-05 23:07:31 +08:00
9dfcf62c3c 修复 get_window_class_instance 函数中的内存释放错误 2025-09-03 00:44:48 +08:00
55c3fa1156 添加鼠标事件拦截 2025-09-02 22:42:47 +08:00
ac91ebbed3 记录光标在每个屏幕中最后停留的位置 2025-09-02 16:27:45 +08:00
6719ccca2b 添加自动运行功能 2025-09-02 01:04:50 +08:00
8cccf23a32 修复 cpu 占用率始终为 0 bug 2025-09-01 20:35:36 +08:00
0c361a6593 处理 EWMH 扩展的 _NET_ACTIVE_WINDOW 协议 2025-09-01 17:55:20 +08:00
645083d3a7 调整音量监听及清除逻辑 2025-09-01 17:26:43 +08:00
f31b18316b 退出时清理 xcb 按键资源避免内存泄露 2025-09-01 17:16:43 +08:00
88d9dfd714 保存图标像素数据指针以便退出时释放对应的内存 2025-09-01 15:31:45 +08:00
e58a43386d 修改 imlib2 重置图片数据方法 2025-09-01 15:30:50 +08:00
17 changed files with 356 additions and 107 deletions

View File

@@ -28,13 +28,14 @@ wm: $(TARGET_NAME)
DISPLAY=:3 $(TARGET) DISPLAY=:3 $(TARGET)
debug: $(TARGET_NAME) debug: $(TARGET_NAME)
DISPLAY=:3 valgrind $(TARGET) DISPLAY=:3 valgrind --leak-check=full $(TARGET)
prepare: prepare:
@cmake -S $(SRC_DIR) -B $(BUILD_DIR) @cmake -S $(SRC_DIR) -B $(BUILD_DIR)
@cp $(BUILD_DIR)/compile_commands.json $(MAKEFILE_DIR) @cp $(BUILD_DIR)/compile_commands.json $(MAKEFILE_DIR)
build: prepare build: prepare
${RM} -f $(TARGET)
@cmake --build $(BUILD_DIR) @cmake --build $(BUILD_DIR)
@cp $(BUILD_DIR)/$(TARGET_NAME) $(TARGET) @cp $(BUILD_DIR)/$(TARGET_NAME) $(TARGET)

21
TODO.org Normal file
View File

@@ -0,0 +1,21 @@
#+title: zswm 代办事项
* TODO 调整光标在每个 monitor 中最后停留位置的逻辑
因为在有些程序(如 Emacs 和 Firefox中移动鼠标不会触发 motion_notify 事件,
因此需调整记录每个 monitor 中光标最后停留位置的逻辑,不能将 motion_notify 事件作为唯一依赖。
可在切换屏幕时主动查询当前光标,将其位置记录到切换前的 monitor 中。
* TODO 处理鼠标点击 bar_window 事件
* TODO 处理鼠标移动窗口事件
* TODO 处理鼠标调整窗口大小事件
* TODO 调整 stack 顺序和焦点逻辑,总感觉现在的情况有 bug
* TODO 调整 stack 逻辑,让 bar_window 在最底部
* TODO 添加切换布局算法功能
* TODO 添加系统托盘显示

View File

@@ -12,6 +12,7 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# compile flags # compile flags
add_compile_options(-g) add_compile_options(-g)
add_compile_options(-Wall) add_compile_options(-Wall)
# add_link_options(-fsanitize=address)
project(${APP_NAME} project(${APP_NAME}
VERSION 0.0.1 VERSION 0.0.1

View File

@@ -80,11 +80,17 @@ const rule_t rules[] = {
}, },
}; };
#define MODIFIER_ANY XCB_MOD_MASK_ANY
#define SUPER XCB_MOD_MASK_4 #define SUPER XCB_MOD_MASK_4
#define ALT XCB_MOD_MASK_1 #define ALT XCB_MOD_MASK_1
#define CTRL XCB_MOD_MASK_CONTROL #define CTRL XCB_MOD_MASK_CONTROL
#define SHIFT XCB_MOD_MASK_SHIFT #define SHIFT XCB_MOD_MASK_SHIFT
#define BUTTON_ANY XCB_BUTTON_INDEX_ANY
#define BUTTON_LEFT XCB_BUTTON_INDEX_1
#define BUTTON_RIGHT XCB_BUTTON_INDEX_2
#define BUTTON_MIDDLE XCB_BUTTON_INDEX_3
const char launcher[] = const char launcher[] =
"rofi -show combi -modes window,drun,run,ssh,combi,windowcd"; "rofi -show combi -modes window,drun,run,ssh,combi,windowcd";
const keybinding_t keys[] = { const keybinding_t keys[] = {
@@ -142,6 +148,11 @@ const keybinding_t keys[] = {
{SUPER | SHIFT, XK_9, move_to_tag, {.ui = 1 << 8}}, {SUPER | SHIFT, XK_9, move_to_tag, {.ui = 1 << 8}},
}; };
const button_t buttons[] = {
{click_tag, 0, BUTTON_LEFT, select_tag, {0}},
{click_tag, SUPER, BUTTON_LEFT, move_to_tag, {0}},
};
const char *const bar_bg = "#222222"; const char *const bar_bg = "#222222";
const char *const tag_bg = "#222222"; const char *const tag_bg = "#222222";
const char *const active_tag_bg = "#005577"; const char *const active_tag_bg = "#005577";
@@ -164,6 +175,7 @@ const char *const active_border_color = "#005577";
const char *const default_font_family = "sans-serif, monospace"; const char *const default_font_family = "sans-serif, monospace";
const uint32_t default_font_size = 10; const uint32_t default_font_size = 10;
const uint32_t dpi_fallback = 96; const uint32_t dpi_fallback = 96;
const uint32_t refresh_rate = 60;
const uint32_t bar_y_padding = 1; const uint32_t bar_y_padding = 1;
const uint32_t tag_x_padding = 10; const uint32_t tag_x_padding = 10;
@@ -172,3 +184,8 @@ const uint32_t client_name_x_padding = 10;
const uint32_t status_x_padding = 4; const uint32_t status_x_padding = 4;
const uint32_t status_icon_padding = 2; const uint32_t status_icon_padding = 2;
const uint32_t border_width = 1; const uint32_t border_width = 1;
const char *autostart_list[] = {
"gentoo-pipewire-launcher restart",
"picom -b --backend xrender",
};

View File

@@ -40,8 +40,15 @@ typedef enum notify_detail_t {
notify_detail_none = 7, notify_detail_none = 7,
} notify_detail_t; } notify_detail_t;
typedef enum mapping_t {
MAPPING_MODIFIER = 0,
MAPPING_KEYBOARD = 1,
MAPPING_POINTER = 2
} mapping_t;
typedef struct mapping_notify_event_t { typedef struct mapping_notify_event_t {
event_type_t type; event_type_t type;
mapping_t request;
} mapping_notify_event_t; } mapping_notify_event_t;
typedef struct key_press_event_t { typedef struct key_press_event_t {
@@ -52,6 +59,10 @@ typedef struct key_press_event_t {
typedef struct button_press_event_t { typedef struct button_press_event_t {
event_type_t type; event_type_t type;
xcb_window_t window;
xcb_window_t root;
int32_t x, y, root_x, root_y;
uint32_t time; /* 时间戳单位ms */
} button_press_event_t; } button_press_event_t;
typedef struct configure_request_event_t { typedef struct configure_request_event_t {
@@ -100,6 +111,7 @@ typedef struct motion_notify_event_t {
xcb_window_t window; xcb_window_t window;
int32_t x; int32_t x;
int32_t y; int32_t y;
uint32_t time; /* 时间戳单位ms */
} motion_notify_event_t; } motion_notify_event_t;
typedef struct enter_notify_event_t { typedef struct enter_notify_event_t {

View File

@@ -1,6 +1,5 @@
#pragma once #pragma once
#include <cairo.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>

View File

@@ -33,6 +33,9 @@ struct monitor_t {
client_t *clients_stack; client_t *clients_stack;
client_t *selected_client; client_t *selected_client;
int32_t pointer_x; /* 记录光标 x 坐标位置 */
int32_t pointer_y; /* 记录光标 y 坐标位置 */
xcb_window_t bar_window; xcb_window_t bar_window;
}; };
@@ -166,20 +169,34 @@ typedef struct {
char time[256]; char time[256];
} status_t; } status_t;
typedef struct icon_res_t {
/* 图像像素数据RGBA 顺序) */
uint32_t *pixels;
/* cairo_surface_t * 类型,为避免引入 cairo 头文件使用 void * 类型代替 */
void *surface;
} icon_res_t;
typedef struct icons_t { typedef struct icons_t {
/* 像素数据 */ icon_res_t net_down;
uint32_t *net_down_pixels; icon_res_t net_up;
uint32_t *net_up_pixels; icon_res_t volume;
uint32_t *volume_pixels; icon_res_t memory;
uint32_t *memory_pixels; icon_res_t cpu;
uint32_t *cpu_pixels; icon_res_t clock;
uint32_t *clock_pixels;
/* cairo_t * 类型,为避免引入 cairo 库使用 void * 类型代替 */
void *net_down;
void *net_up;
void *volume;
void *memory;
void *cpu;
void *clock;
} icons_t; } icons_t;
typedef enum click_area_t {
click_tag,
click_layout_symbol,
click_window_title,
click_status,
click_client_window,
click_root_win,
} click_area_t;
typedef struct button_t {
click_area_t click_area;
uint16_t modifiers;
uint8_t button;
void (*func)(const user_action_arg_t *);
const user_action_arg_t arg;
} button_t;

View File

@@ -126,6 +126,7 @@ typedef enum atom_t {
atom_wm_protocols, atom_wm_protocols,
atom_wm_delete, atom_wm_delete,
atom_wm_take_focus, atom_wm_take_focus,
atom_net_active_window,
atom_net_wm_name, atom_net_wm_name,
atom_net_wm_state, atom_net_wm_state,
atom_net_wm_fullscreen, atom_net_wm_fullscreen,
@@ -134,6 +135,7 @@ typedef enum atom_t {
atom_zswm_client_info, /* 自定义属性,用于存储所在 monitor 和 tags */ atom_zswm_client_info, /* 自定义属性,用于存储所在 monitor 和 tags */
} atom_t; } atom_t;
xcb_atom_t get_xcb_atom(atom_t atom); xcb_atom_t get_xcb_atom(atom_t atom);
char *get_xcb_atom_name(xcb_atom_t atom);
bool send_event(xcb_window_t window, atom_t atom); bool send_event(xcb_window_t window, atom_t atom);
typedef enum wm_state_t { typedef enum wm_state_t {
wm_state_withdrawn = 0, wm_state_withdrawn = 0,
@@ -152,8 +154,12 @@ void set_window_tag(xcb_window_t window, client_tag_info_t *client_info);
void remove_window_tag(xcb_window_t window); void remove_window_tag(xcb_window_t window);
bool get_pointer_position(int32_t *x, int32_t *y); bool get_pointer_position(int32_t *x, int32_t *y);
void set_pointer_position(int32_t x, int32_t y);
/** /**
* @brief 非阻塞检查特定事件掩码(模拟 XCheckMaskEvent * @brief 非阻塞检查特定事件掩码(模拟 XCheckMaskEvent
*/ */
bool xcb_check_mask_event(xcb_event_mask_t event_mask); bool xcb_check_mask_event(xcb_event_mask_t event_mask);
void ungrab_any_button(xcb_window_t window);
void grab_button(xcb_window_t window, uint8_t button, uint16_t modifiers);

View File

@@ -25,6 +25,8 @@
static void init_monitors(void); static void init_monitors(void);
static char **get_monitor_tags(uint32_t monitor_count, uint32_t monitor_index); static char **get_monitor_tags(uint32_t monitor_count, uint32_t monitor_index);
static void record_mointor_pointer_position(int32_t x, int32_t y,
uint32_t timestamp);
static void init_wallpaper(void); static void init_wallpaper(void);
static gboolean update_wallpaper(gpointer user_data); static gboolean update_wallpaper(gpointer user_data);
static void set_wallpaper_by_index(uint32_t index, wallpaper_config_t *config, static void set_wallpaper_by_index(uint32_t index, wallpaper_config_t *config,
@@ -40,6 +42,9 @@ static void get_xresource_config(void);
static void update_status(status_t *status); static void update_status(status_t *status);
static void draw_all_monitor_bars(void); static void draw_all_monitor_bars(void);
static void draw_monitor_bar(monitor_t *monitor); static void draw_monitor_bar(monitor_t *monitor);
static void run_autostart(void);
static void run_once(const char *command);
static bool command_already_running(const char *command);
static void init_xcb_event_loop(void); static void init_xcb_event_loop(void);
static void xcb_event_handler(generic_event_t *event, void *user_data); static void xcb_event_handler(generic_event_t *event, void *user_data);
static void key_press(key_press_event_t *event); static void key_press(key_press_event_t *event);
@@ -54,6 +59,7 @@ 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 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 grab_buttons_for_client(client_t *client, bool focused);
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);
static void update_client_name(client_t *client); static void update_client_name(client_t *client);
@@ -93,6 +99,7 @@ static event_adapter_t *adapter = NULL;
static char *font_family = NULL; static char *font_family = NULL;
static uint32_t font_size = default_font_size; static uint32_t font_size = default_font_size;
static uint32_t dpi = dpi_fallback; static uint32_t dpi = dpi_fallback;
static uint32_t fps = refresh_rate;
static uint32_t bar_y_pad = bar_y_padding; static uint32_t bar_y_pad = bar_y_padding;
static uint32_t tag_x_pad = tag_x_padding; static uint32_t tag_x_pad = tag_x_padding;
static uint32_t layout_symbol_x_pad = layout_symbol_x_padding; static uint32_t layout_symbol_x_pad = layout_symbol_x_padding;
@@ -171,7 +178,8 @@ void focus_monitor(const user_action_arg_t *arg) {
unfocus(current_monitor->selected_client, false); unfocus(current_monitor->selected_client, false);
current_monitor = monitor; current_monitor = monitor;
focus(NULL); focus(NULL);
/* TODO: 调整鼠标光标 */
set_pointer_position(monitor->pointer_x, monitor->pointer_y);
} }
void raise_or_run(const user_action_arg_t *arg) { void raise_or_run(const user_action_arg_t *arg) {
@@ -184,6 +192,8 @@ void raise_or_run(const user_action_arg_t *arg) {
logger("==== raise client: %s, window: 0x%x\n", client->name, logger("==== raise client: %s, window: 0x%x\n", client->name,
client->window); client->window);
current_monitor = client->monitor; current_monitor = client->monitor;
set_pointer_position(current_monitor->pointer_x,
current_monitor->pointer_y);
argument.ui = client->tags; argument.ui = client->tags;
select_tag(&argument); select_tag(&argument);
focus(client); focus(client);
@@ -228,19 +238,22 @@ void move_to_tag(const user_action_arg_t *arg) {
arrange(current_monitor); arrange(current_monitor);
} }
/* 调试函数,开发完成后删除 */ static void print_monitor_info(monitor_t *monitor) {
static void print_monitor_list_info(monitor_t *monitor_list) { logger("========== mon[%u]:\n", monitor->index);
logger("========================= monitor info =========================\n"); logger("x: %d, y: %d, width: %u, height: %u, pointer x: %d, pointer y: %d\n",
for (monitor_t *m = monitor_list; m; m = m->next) { monitor->monitor_x, monitor->monitor_y, monitor->monitor_width,
logger("========== mon[%u]:\n", m->index); monitor->monitor_height, monitor->pointer_x, monitor->pointer_y);
logger("x: %d, y: %d, width: %u, height: %u\n", m->monitor_x, m->monitor_y, logger("mon[%u] tags: ", monitor->index);
m->monitor_width, m->monitor_height); for (int i = 0; monitor->tags[i]; i++) {
logger("mon[%u] tags: ", m->index); logger("%s%s", i > 0 ? ", " : "", monitor->tags[i]);
for (int i = 0; m->tags[i]; i++) {
logger("%s%s", i > 0 ? ", " : "", m->tags[i]);
} }
logger("\n"); logger("\n");
} }
/* 调试函数,开发完成后删除 */
static void print_monitor_list_info(monitor_t *monitor_list) {
logger("========================= monitor info =========================\n");
for (monitor_t *m = monitor_list; m; m = m->next) print_monitor_info(m);
logger("======================= monitor info end =======================\n"); logger("======================= monitor info end =======================\n");
} }
static void print_color(const char *prompt, const color_t *color) { static void print_color(const char *prompt, const color_t *color) {
@@ -353,6 +366,25 @@ void init_monitors(void) {
m->tags = get_monitor_tags(i, m->index); m->tags = get_monitor_tags(i, m->index);
m->selected_tags = 0x1; m->selected_tags = 0x1;
} }
int32_t x, y;
if (get_pointer_position(&x, &y)) {
monitor_t *monitor = rect_to_monitor(monitors, x, y, 1, 1);
print_monitor_info(monitor);
int32_t offset_x = x - monitor->monitor_x;
int32_t offset_y = y - monitor->monitor_y;
logger("==== init monitor pointer position ====\n");
logger("x: %d, y: %d, offset_x: %d, offset_y: %d\n", x, y, offset_x,
offset_y);
logger("== init monitor pointer position end ==\n");
for (monitor_t *m = monitors; m; m = m->next) {
m->pointer_x = m->monitor_x + offset_x;
m->pointer_y = m->monitor_y + offset_y;
}
}
} }
char **get_monitor_tags(uint32_t monitor_count, uint32_t monitor_index) { char **get_monitor_tags(uint32_t monitor_count, uint32_t monitor_index) {
@@ -382,6 +414,15 @@ char **get_monitor_tags(uint32_t monitor_count, uint32_t monitor_index) {
return g_strv_builder_unref_to_strv(builder); return g_strv_builder_unref_to_strv(builder);
} }
void record_mointor_pointer_position(int32_t x, int32_t y, uint32_t timestamp) {
static uint32_t last_timestamp = 0;
if ((timestamp - last_timestamp) < (1000 / fps)) return;
monitor_t *monitor = rect_to_monitor(monitors, x, y, 1, 1);
monitor->pointer_x = x;
monitor->pointer_x = y;
}
void init_wallpaper(void) { void init_wallpaper(void) {
bar_cairo_params_t *p = prepare_wallpaper_surface_params(); bar_cairo_params_t *p = prepare_wallpaper_surface_params();
rect_size_t *screen_size = get_screen_size(); rect_size_t *screen_size = get_screen_size();
@@ -502,37 +543,35 @@ void free_wallpaper_config(wallpaper_config_t *wallpaper_config) {
void init_icons(void) { void init_icons(void) {
typedef struct { typedef struct {
const char *img_path; const char *img_path;
cairo_surface_t **icon; icon_res_t *icon_res;
uint32_t **pixels;
} icon_path_surface_map_t; } icon_path_surface_map_t;
if (icons) return; if (icons) return;
icons = ecalloc(1, sizeof(icons_t)); icons = ecalloc(1, sizeof(icons_t));
icon_path_surface_map_t map[] = { icon_path_surface_map_t map[] = {
{NET_DOWN_ICON, (cairo_surface_t **)&icons->net_down, {NET_DOWN_ICON, &icons->net_down}, {NET_UP_ICON, &icons->net_up},
&icons->net_down_pixels}, {SPEAKER_ICON, &icons->volume}, {MEM_ICON, &icons->memory},
{NET_UP_ICON, (cairo_surface_t **)&icons->net_up, &icons->net_up_pixels}, {CPU_ICON, &icons->cpu}, {CLOCK_ICON, &icons->clock},
{SPEAKER_ICON, (cairo_surface_t **)&icons->volume, &icons->volume_pixels},
{MEM_ICON, (cairo_surface_t **)&icons->memory, &icons->memory_pixels},
{CPU_ICON, (cairo_surface_t **)&icons->cpu, &icons->cpu_pixels},
{CLOCK_ICON, (cairo_surface_t **)&icons->clock, &icons->clock_pixels},
}; };
for (int i = 0; i < LENGTH(map); i++) { for (int i = 0; i < LENGTH(map); i++) {
const char *path = map[i].img_path; const char *path = map[i].img_path;
uint32_t **pixels_addr = map[i].pixels; icon_res_t *res = map[i].icon_res;
cairo_surface_t **surface_addr = map[i].icon;
if (path && strlen(path)) { if (path && strlen(path)) {
*pixels_addr = uint32_t *pixels =
generate_icon_pixels(path, bar_height, bar_height, status_icon_pad); generate_icon_pixels(path, bar_height, bar_height, status_icon_pad);
cairo_format_t format = CAIRO_FORMAT_ARGB32; cairo_format_t format = CAIRO_FORMAT_ARGB32;
int stride = cairo_format_stride_for_width(format, bar_height); int stride = cairo_format_stride_for_width(format, bar_height);
cairo_surface_t *surface = cairo_image_surface_create_for_data( cairo_surface_t *surface = cairo_image_surface_create_for_data(
(uint8_t *)*pixels_addr, format, bar_height, bar_height, stride); (uint8_t *)pixels, format, bar_height, bar_height, stride);
if (cairo_surface_status(surface) == CAIRO_STATUS_SUCCESS) { if (cairo_surface_status(surface) == CAIRO_STATUS_SUCCESS) {
*surface_addr = surface; res->pixels = pixels;
res->surface = surface;
} else { } else {
cairo_surface_destroy(surface); cairo_surface_destroy(surface);
free(pixels);
res->pixels = NULL;
res->surface = NULL;
} }
} }
} }
@@ -541,19 +580,21 @@ void init_icons(void) {
void clean_icons(void) { void clean_icons(void) {
if (!icons) return; if (!icons) return;
if (icons->net_down) cairo_surface_destroy(icons->net_down); icon_res_t icon_res_list[] = {
if (icons->net_up) cairo_surface_destroy(icons->net_up); icons->net_down, icons->net_up, icons->volume,
if (icons->volume) cairo_surface_destroy(icons->volume); icons->memory, icons->cpu, icons->clock,
if (icons->memory) cairo_surface_destroy(icons->memory); };
if (icons->cpu) cairo_surface_destroy(icons->cpu); for (int i = 0; i < LENGTH(icon_res_list); i++) {
if (icons->clock) cairo_surface_destroy(icons->clock); icon_res_t res = icon_res_list[i];
if (res.surface) {
cairo_surface_finish(res.surface);
free(res.pixels);
cairo_surface_destroy(res.surface);
if (icons->net_down_pixels) free(icons->net_down); res.pixels = NULL;
if (icons->net_up_pixels) free(icons->net_up); res.surface = NULL;
if (icons->volume_pixels) free(icons->volume); }
if (icons->memory_pixels) free(icons->memory); }
if (icons->cpu_pixels) free(icons->cpu);
if (icons->clock_pixels) free(icons->clock);
free(icons); free(icons);
icons = NULL; icons = NULL;
@@ -690,6 +731,7 @@ void init_monitor_bar(void) {
void get_xresource_config(void) { void get_xresource_config(void) {
get_xresource_uint32("Xft.dpi", &dpi); get_xresource_uint32("Xft.dpi", &dpi);
get_xresource_uint32("zswm.fps", &fps);
get_xresource_string("zswm.font_family", &font_family, default_font_family); get_xresource_string("zswm.font_family", &font_family, default_font_family);
get_xresource_uint32("zswm.font_size", &font_size); get_xresource_uint32("zswm.font_size", &font_size);
get_xresource_uint32("zswm.bar_y_padding", &bar_y_pad); get_xresource_uint32("zswm.bar_y_padding", &bar_y_pad);
@@ -720,6 +762,34 @@ void draw_monitor_bar(monitor_t *m) {
flush_xcb_connection(); flush_xcb_connection();
} }
void run_autostart(void) {
for (int i = 0; i < LENGTH(autostart_list); i++) run_once(autostart_list[i]);
}
void run_once(const char *command) {
if (command_already_running(command)) return;
g_spawn_command_line_async(command, NULL);
}
bool command_already_running(const char *command) {
char shell_cmd[1024];
const char *user = g_getenv("USER");
snprintf(shell_cmd, sizeof(shell_cmd), "pgrep -u %s -fx '%s'", user, command);
char *output = NULL, *error = NULL;
g_spawn_command_line_sync(shell_cmd, &output, &error, NULL, NULL);
bool result = (error == NULL || !strlen(error)) && (output && strlen(output));
free(output);
free(error);
output = NULL;
error = NULL;
return result;
}
void init_xcb_event_loop(void) { void init_xcb_event_loop(void) {
adapter = create_event_adapter(); adapter = create_event_adapter();
event_adapter_set_handler(adapter, xcb_event_handler, NULL); event_adapter_set_handler(adapter, xcb_event_handler, NULL);
@@ -816,6 +886,8 @@ void expose(expose_event_t *event) {
} }
void motion_notify(motion_notify_event_t *event) { void motion_notify(motion_notify_event_t *event) {
record_mointor_pointer_position(event->root_x, event->root_y, event->time);
if (event->window != get_root_window()) return; if (event->window != get_root_window()) return;
static monitor_t *mon = NULL; static monitor_t *mon = NULL;
@@ -854,8 +926,10 @@ void focus_in(focus_in_event_t *event) {
} }
void property_notify(property_notify_event_t *event) { void property_notify(property_notify_event_t *event) {
logger("window: 0x%x state: %u, atom: %u\n", event->window, event->state, char *atom_name = get_xcb_atom_name(event->xcb_atom);
event->xcb_atom); logger("window: 0x%x state: %u, atom: %u, atom name: %s\n", event->window,
event->state, event->xcb_atom, atom_name);
free(atom_name);
if (event->state == property_delete) return; if (event->state == property_delete) return;
client_t *c = get_client_of_window(event->window); client_t *c = get_client_of_window(event->window);
@@ -884,6 +958,10 @@ void property_notify(property_notify_event_t *event) {
} }
void client_message(client_message_event_t *event) { void client_message(client_message_event_t *event) {
char *atom_name = get_xcb_atom_name(event->message_type);
logger("client message: message type: %s\n", atom_name);
free(atom_name);
client_t *c = get_client_of_window(event->window); client_t *c = get_client_of_window(event->window);
if (!c) return; if (!c) return;
@@ -909,6 +987,26 @@ void client_message(client_message_event_t *event) {
bool toggle_to_fullscreen = event->data.data32[0] == 2 && !c->fullscreen; bool toggle_to_fullscreen = event->data.data32[0] == 2 && !c->fullscreen;
set_client_fullscreen(c, set_fullscreen || toggle_to_fullscreen); set_client_fullscreen(c, set_fullscreen || toggle_to_fullscreen);
} }
} else if (event->message_type == get_xcb_atom(atom_net_active_window)) {
client_t *client = get_client_of_window(event->window);
if (client) {
logger("==== _NET_ACTIVE_WINDOW\n");
print_client(client);
uint32_t tag_mask = 0;
for (uint32_t i = 0; i < g_strv_length(client->monitor->tags); i++) {
tag_mask |= 1 << i;
}
uint32_t target_tag = tag_mask & client->tags;
const user_action_arg_t arg = {.ui = target_tag};
current_monitor = client->monitor;
set_pointer_position(current_monitor->pointer_x,
current_monitor->pointer_y);
select_tag(&arg);
focus(client);
restack(current_monitor);
}
} }
} }
@@ -922,6 +1020,8 @@ void manage_window(xcb_window_t window) {
c->height = c->old_height = geometry->height; c->height = c->old_height = geometry->height;
c->old_border_width = geometry->border_width; c->old_border_width = geometry->border_width;
c->border_width = border_px; c->border_width = border_px;
free(geometry);
update_client_name(c); update_client_name(c);
logger("== manage window: 0x%x\n", window); logger("== manage window: 0x%x\n", window);
@@ -939,8 +1039,10 @@ void manage_window(xcb_window_t window) {
} }
set_client_tag_prop(c); set_client_tag_prop(c);
grab_buttons_for_client(c, false);
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_client(c);
@@ -957,6 +1059,18 @@ void manage_window(xcb_window_t window) {
focus(NULL); focus(NULL);
} }
void grab_buttons_for_client(client_t *client, bool focused) {
ungrab_any_button(client->window);
if (!focused) grab_button(client->window, BUTTON_ANY, MODIFIER_ANY);
for (int i = 0; i < LENGTH(buttons); i++) {
if (buttons[i].click_area == click_client_window) {
grab_button(client->window, buttons[i].button, buttons[i].modifiers);
}
}
}
void unmanage_client(client_t *client, bool destroyed) { void unmanage_client(client_t *client, bool destroyed) {
monitor_t *monitor = client->monitor; monitor_t *monitor = client->monitor;
@@ -1049,6 +1163,8 @@ void apply_rules(client_t *client) {
} }
if (switch_to_tag) { if (switch_to_tag) {
current_monitor = client->monitor; current_monitor = client->monitor;
set_pointer_position(current_monitor->pointer_x,
current_monitor->pointer_y);
current_monitor->selected_tags = client->tags; current_monitor->selected_tags = client->tags;
} }
@@ -1138,6 +1254,7 @@ void focus(client_t *client) {
if (client->monitor != current_monitor) current_monitor = client->monitor; if (client->monitor != current_monitor) current_monitor = client->monitor;
detach_stack_client(client); detach_stack_client(client);
attach_stack_client(client); attach_stack_client(client);
grab_buttons_for_client(client, true);
change_window_border_color(client->window, change_window_border_color(client->window,
color_set->active_border_color.argb); color_set->active_border_color.argb);
focus_window(client->window); focus_window(client->window);
@@ -1151,6 +1268,7 @@ void focus(client_t *client) {
void unfocus(client_t *client, bool set_focus) { void unfocus(client_t *client, bool set_focus) {
if (!client) return; if (!client) return;
grab_buttons_for_client(client, false);
change_window_border_color(client->window, color_set->border_color.argb); change_window_border_color(client->window, color_set->border_color.argb);
if (set_focus) focus_window(WINDOW_NONE); if (set_focus) focus_window(WINDOW_NONE);
} }
@@ -1442,6 +1560,8 @@ int main(int argc, char *argv[]) {
init_xcb_event_loop(); init_xcb_event_loop();
run_autostart();
g_main_loop_run(loop); g_main_loop_run(loop);
cleanup(need_restart); cleanup(need_restart);

View File

@@ -25,11 +25,10 @@ uint32_t *generate_wallpaper(const wallpaper_config_t *wallpaper_config,
if (!final_image) return NULL; if (!final_image) return NULL;
imlib_context_set_image(final_image); imlib_context_set_image(final_image);
imlib_image_set_has_alpha(false); imlib_image_set_has_alpha(true);
/* 填充背景色 */ /* 重置像素数据 */
imlib_context_set_color(0, 0, 0, 255); imlib_image_clear();
imlib_image_fill_rectangle(0, 0, screen_width, screen_height);
const monitor_t *monitor = monitors; const monitor_t *monitor = monitors;
uint32_t monitor_index = 0; uint32_t monitor_index = 0;
@@ -118,9 +117,8 @@ uint32_t *generate_icon_pixels(const char *image_path, uint32_t width,
Imlib_Image target_img = imlib_create_image(width, height); Imlib_Image target_img = imlib_create_image(width, height);
imlib_context_set_image(target_img); imlib_context_set_image(target_img);
imlib_image_set_has_alpha(false); imlib_image_set_has_alpha(true);
imlib_context_set_color(0, 0, 0, 255); imlib_image_clear();
imlib_image_fill_rectangle(0, 0, width, height);
imlib_blend_image_onto_image(img, 1, src_x, src_y, src_width, src_height, imlib_blend_image_onto_image(img, 1, src_x, src_y, src_width, src_height,
padding, padding, dst_width, dst_height); padding, padding, dst_width, dst_height);

View File

@@ -143,7 +143,7 @@ static int32_t draw_status(monitor_t *monitor, status_t *status, icons_t *icons,
typedef struct { typedef struct {
const char *const text; const char *const text;
color_t *color; color_t *color;
cairo_surface_t *surface; icon_res_t *icon_res;
} status_item_t; } status_item_t;
char cpu[16], mem[64], volume[600]; char cpu[16], mem[64], volume[600];
@@ -156,12 +156,12 @@ static int32_t draw_status(monitor_t *monitor, status_t *status, icons_t *icons,
status_item_t items[] = { status_item_t items[] = {
{status->net_speed.down, &color_set->status_net_recv_color, {status->net_speed.down, &color_set->status_net_recv_color,
icons->net_down}, &icons->net_down},
{status->net_speed.up, &color_set->status_net_send_color, icons->net_up}, {status->net_speed.up, &color_set->status_net_send_color, &icons->net_up},
{volume, &color_set->status_volume_color, icons->volume}, {volume, &color_set->status_volume_color, &icons->volume},
{mem, &color_set->status_memory_color, icons->memory}, {mem, &color_set->status_memory_color, &icons->memory},
{cpu, &color_set->status_cpu_color, icons->cpu}, {cpu, &color_set->status_cpu_color, &icons->cpu},
{status->time, &color_set->status_datetime_color, icons->clock}, {status->time, &color_set->status_datetime_color, &icons->clock},
}; };
int32_t x = monitor->monitor_width; int32_t x = monitor->monitor_width;
@@ -177,14 +177,13 @@ static int32_t draw_status(monitor_t *monitor, status_t *status, icons_t *icons,
draw_text(monitor->cr, text, color, x, 0, width, height, false); draw_text(monitor->cr, text, color, x, 0, width, height, false);
cairo_surface_t *surface = items[i].surface; cairo_surface_t *surface = items[i].icon_res->surface;
if (surface) { if (surface) {
if (x - height < end) return x; if (x - height < end) return x;
x -= height; x -= height;
cairo_set_source_surface(monitor->cr, surface, x, 0); cairo_set_source_surface(monitor->cr, surface, x, 0);
cairo_paint(monitor->cr); cairo_paint(monitor->cr);
/* refresh_window(monitor->bar_window); */
} }
x -= x_padding; x -= x_padding;

View File

@@ -3,6 +3,7 @@
#include <glib.h> #include <glib.h>
#include <math.h> #include <math.h>
#include <pulse/context.h> #include <pulse/context.h>
#include <pulse/def.h>
#include <pulse/glib-mainloop.h> #include <pulse/glib-mainloop.h>
#include <pulse/introspect.h> #include <pulse/introspect.h>
#include <pulse/subscribe.h> #include <pulse/subscribe.h>
@@ -47,6 +48,7 @@ static status_changed_notify listener = NULL;
static bool pulse_inited = false; static bool pulse_inited = false;
static pa_cvolume current_volume; static pa_cvolume current_volume;
static pa_context *context = NULL; static pa_context *context = NULL;
static pa_glib_mainloop *loop = NULL;
static double calc_cpu_usage(cpu_stat_t curr, cpu_stat_t prev) { static double calc_cpu_usage(cpu_stat_t curr, cpu_stat_t prev) {
uint64_t curr_idle = curr.idle + curr.iowait; uint64_t curr_idle = curr.idle + curr.iowait;
@@ -100,7 +102,7 @@ static bool get_cpu_usage(double *usage, GError *error) {
if (!inited) { if (!inited) {
prev_cpu_stat = stat; prev_cpu_stat = stat;
inited = false; inited = true;
} }
double percent = calc_cpu_usage(stat, prev_cpu_stat); double percent = calc_cpu_usage(stat, prev_cpu_stat);
@@ -291,7 +293,6 @@ static void sink_info_callback(pa_context *c, const pa_sink_info *info, int eol,
} }
if (info) { if (info) {
context = c;
parse_sink(info, &status.pulse); parse_sink(info, &status.pulse);
current_volume = info->volume; current_volume = info->volume;
pulse_inited = true; pulse_inited = true;
@@ -307,15 +308,11 @@ static void server_info_callback(pa_context *c, const pa_server_info *info,
static void subscribe_callback(pa_context *c, pa_subscription_event_type_t t, static void subscribe_callback(pa_context *c, pa_subscription_event_type_t t,
uint32_t idx, void *userdata) { uint32_t idx, void *userdata) {
pa_subscription_event_type_t event = t & PA_SUBSCRIPTION_EVENT_TYPE_MASK;
uint32_t facility = t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK; uint32_t facility = t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK;
if (facility != PA_SUBSCRIPTION_EVENT_SINK) { if (facility == PA_SUBSCRIPTION_EVENT_SINK) {
return;
}
if (event == PA_SUBSCRIPTION_EVENT_CHANGE) {
pa_context_get_server_info(c, server_info_callback, NULL); pa_context_get_server_info(c, server_info_callback, NULL);
return;
} }
} }
@@ -327,7 +324,9 @@ static void clean_pulse() {
pa_context_unref(context); pa_context_unref(context);
context = NULL; context = NULL;
} }
if (loop) pa_glib_mainloop_free(loop);
pulse_inited = false; pulse_inited = false;
loop = NULL;
} }
static void init_pulse(GMainContext *g_context); static void init_pulse(GMainContext *g_context);
@@ -336,7 +335,7 @@ static void state_callback(pa_context *c, void *userdata) {
if (PA_CONTEXT_IS_GOOD(state)) { if (PA_CONTEXT_IS_GOOD(state)) {
pa_context_set_subscribe_callback(c, subscribe_callback, NULL); pa_context_set_subscribe_callback(c, subscribe_callback, NULL);
pa_context_subscribe(c, PA_SUBSCRIPTION_MASK_SINK, NULL, NULL); pa_context_subscribe(c, PA_SUBSCRIPTION_MASK_SINK, NULL, NULL);
pa_context_get_server_info(c, server_info_callback, userdata); pa_context_get_server_info(c, server_info_callback, NULL);
} else if (state == PA_CONTEXT_FAILED) { } else if (state == PA_CONTEXT_FAILED) {
clean_pulse(); clean_pulse();
init_pulse((GMainContext *)userdata); init_pulse((GMainContext *)userdata);
@@ -344,11 +343,12 @@ static void state_callback(pa_context *c, void *userdata) {
} }
static void init_pulse(GMainContext *g_context) { static void init_pulse(GMainContext *g_context) {
if (loop) pa_glib_mainloop_free(loop);
pa_glib_mainloop *loop = pa_glib_mainloop_new(g_context); pa_glib_mainloop *loop = pa_glib_mainloop_new(g_context);
pa_mainloop_api *api = pa_glib_mainloop_get_api(loop); pa_mainloop_api *api = pa_glib_mainloop_get_api(loop);
pa_context *ctx = pa_context_new(api, "ZSWM-Status-Volume"); context = pa_context_new(api, "ZSWM-Status-Volume");
pa_context_connect(ctx, NULL, PA_CONTEXT_NOFAIL, NULL); pa_context_connect(context, NULL, PA_CONTEXT_NOFAIL, NULL);
pa_context_set_state_callback(ctx, state_callback, g_context); pa_context_set_state_callback(context, state_callback, g_context);
} }
static gboolean update_status(gpointer data) { static gboolean update_status(gpointer data) {

View File

@@ -2,11 +2,14 @@
#include <stdarg.h> #include <stdarg.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include "types.h"
double get_time() { double get_time() {
struct timeval tv; struct timeval tv;
gettimeofday(&tv, NULL); gettimeofday(&tv, NULL);
@@ -106,10 +109,10 @@ void extract_color_channel(const uint32_t rgba, double *red, double *green,
} }
#define INTERSECT(x, y, w, h, m) \ #define INTERSECT(x, y, w, h, m) \
(MAX(0, MIN((x) + (w), (m)->window_x + (m)->window_width) - \ (MAX(0, MIN((x) + (w), (m)->monitor_x + (int)(m)->monitor_width) - \
MAX((x), (m)->window_x)) * \ MAX((x), (m)->monitor_x)) * \
MAX(0, MIN((y) + (h), (m)->window_y + (m)->window_height) - \ MAX(0, MIN((y) + (h), (m)->monitor_y + (int)(m)->monitor_height) - \
MAX((y), (m)->window_y))) MAX((y), (m)->monitor_y)))
monitor_t *rect_to_monitor(monitor_t *monitors, int32_t x, int32_t y, monitor_t *rect_to_monitor(monitor_t *monitors, int32_t x, int32_t y,
uint32_t width, uint32_t height) { uint32_t width, uint32_t height) {
monitor_t *r = monitors; monitor_t *r = monitors;

View File

@@ -18,11 +18,13 @@ struct event_adapter_t {
static void convert_event(xcb_generic_event_t *xcb_event, static void convert_event(xcb_generic_event_t *xcb_event,
generic_event_t *event) { generic_event_t *event) {
uint8_t event_type = XCB_EVENT_RESPONSE_TYPE(xcb_event); uint8_t event_type = XCB_EVENT_RESPONSE_TYPE(xcb_event);
const char *label = xcb_event_get_label(event_type);
switch (event_type) { switch (event_type) {
case XCB_MAPPING_NOTIFY: case XCB_MAPPING_NOTIFY: {
event->type = EVENT_TYPE_MAPPING_NOTIFY; xcb_mapping_notify_event_t *ev = (xcb_mapping_notify_event_t *)xcb_event;
event->mapping_notify.type = EVENT_TYPE_MAPPING_NOTIFY;
event->mapping_notify.request = ev->request;
break; break;
}
case XCB_KEY_PRESS: { case XCB_KEY_PRESS: {
xcb_key_press_event_t *ev = (xcb_key_press_event_t *)xcb_event; xcb_key_press_event_t *ev = (xcb_key_press_event_t *)xcb_event;
int col = ev->state & XCB_MOD_MASK_2 ? 1 : 0; int col = ev->state & XCB_MOD_MASK_2 ? 1 : 0;
@@ -32,9 +34,18 @@ static void convert_event(xcb_generic_event_t *xcb_event,
event->key_press.modifiers = ev->state; event->key_press.modifiers = ev->state;
break; break;
} }
case XCB_BUTTON_PRESS: case XCB_BUTTON_PRESS: {
event->type = EVENT_TYPE_BUTTON_PRESS; xcb_button_press_event_t *ev = (xcb_button_press_event_t *)xcb_event;
event->button_press.type = EVENT_TYPE_BUTTON_PRESS;
event->button_press.window = ev->event;
event->button_press.root = ev->root;
event->button_press.x = ev->event_x;
event->button_press.y = ev->event_y;
event->button_press.root_x = ev->root_x;
event->button_press.root_y = ev->root_y;
event->button_press.time = ev->time;
break; break;
}
case XCB_CONFIGURE_REQUEST: { case XCB_CONFIGURE_REQUEST: {
xcb_configure_request_event_t *ev = xcb_configure_request_event_t *ev =
(xcb_configure_request_event_t *)xcb_event; (xcb_configure_request_event_t *)xcb_event;
@@ -66,7 +77,8 @@ static void convert_event(xcb_generic_event_t *xcb_event,
event->unmap_notify.type = EVENT_TYPE_UNMAP_NOTIFY; event->unmap_notify.type = EVENT_TYPE_UNMAP_NOTIFY;
event->unmap_notify.window = ev->window; event->unmap_notify.window = ev->window;
event->unmap_notify.send_event = XCB_EVENT_SENT(ev); event->unmap_notify.send_event = XCB_EVENT_SENT(ev);
} break; break;
}
case XCB_DESTROY_NOTIFY: { case XCB_DESTROY_NOTIFY: {
xcb_destroy_notify_event_t *ev = (xcb_destroy_notify_event_t *)xcb_event; xcb_destroy_notify_event_t *ev = (xcb_destroy_notify_event_t *)xcb_event;
event->destroy_notify.type = EVENT_TYPE_DESTROY_NOTIFY; event->destroy_notify.type = EVENT_TYPE_DESTROY_NOTIFY;
@@ -89,6 +101,7 @@ static void convert_event(xcb_generic_event_t *xcb_event,
event->motiion_notify.window = ev->event; event->motiion_notify.window = ev->event;
event->motiion_notify.x = ev->event_x; event->motiion_notify.x = ev->event_x;
event->motiion_notify.y = ev->event_y; event->motiion_notify.y = ev->event_y;
event->motiion_notify.time = ev->time;
break; break;
} }
case XCB_ENTER_NOTIFY: { case XCB_ENTER_NOTIFY: {
@@ -126,7 +139,8 @@ static void convert_event(xcb_generic_event_t *xcb_event,
event->type = -1; event->type = -1;
return; return;
} }
if (event->type != EVENT_TYPE_MOTION_NOTIFY) { if (event->type) {
const char *label = xcb_event_get_label(event_type);
logger("========================== %s ==========================\n", label); logger("========================== %s ==========================\n", label);
} }
} }

View File

@@ -43,7 +43,8 @@ void get_window_class_instance(xcb_window_t window,
xcb_icccm_get_wm_class_reply_t prop; xcb_icccm_get_wm_class_reply_t prop;
const char *class = NULL; const char *class = NULL;
const char *instance = NULL; const char *instance = NULL;
if (xcb_icccm_get_wm_class_reply(conn, cookie, &prop, NULL)) { bool has_reply = xcb_icccm_get_wm_class_reply(conn, cookie, &prop, NULL);
if (has_reply) {
class = prop.class_name; class = prop.class_name;
instance = prop.instance_name; instance = prop.instance_name;
} else { } else {
@@ -54,7 +55,7 @@ void get_window_class_instance(xcb_window_t window,
strncpy(class_instance->class, class, LENGTH(class_instance->class)); strncpy(class_instance->class, class, LENGTH(class_instance->class));
strncpy(class_instance->instance, instance, LENGTH(class_instance->instance)); strncpy(class_instance->instance, instance, LENGTH(class_instance->instance));
xcb_icccm_get_wm_class_reply_wipe(&prop); if (has_reply) xcb_icccm_get_wm_class_reply_wipe(&prop);
} }
static visual_t *find_alpha_visual() { static visual_t *find_alpha_visual() {
@@ -287,11 +288,10 @@ char *get_window_name(xcb_window_t window) {
void set_window_event_mask(xcb_window_t window, bool clear) { void set_window_event_mask(xcb_window_t window, bool clear) {
xcb_cw_t change_mask = XCB_CW_EVENT_MASK; xcb_cw_t change_mask = XCB_CW_EVENT_MASK;
uint32_t event_mask = clear ? XCB_EVENT_MASK_NO_EVENT uint32_t init_event_mask =
: XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_FOCUS_CHANGE |
XCB_EVENT_MASK_FOCUS_CHANGE | XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY;
XCB_EVENT_MASK_PROPERTY_CHANGE | uint32_t event_mask = clear ? XCB_EVENT_MASK_NO_EVENT : init_event_mask;
XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY;
xcb_params_cw_t params = {.event_mask = event_mask}; xcb_params_cw_t params = {.event_mask = event_mask};
xcb_aux_change_window_attributes(conn, window, change_mask, &params); xcb_aux_change_window_attributes(conn, window, change_mask, &params);
} }
@@ -361,3 +361,15 @@ void set_window_fullscreen_state(xcb_window_t window, bool fullscreen) {
xcb_change_property(conn, XCB_PROP_MODE_REPLACE, window, atom, xcb_change_property(conn, XCB_PROP_MODE_REPLACE, window, atom,
XCB_ATOM_WINDOW, 32, data_len, &atom); XCB_ATOM_WINDOW, 32, data_len, &atom);
} }
void ungrab_any_button(xcb_window_t window) {
xcb_ungrab_button(conn, XCB_BUTTON_INDEX_ANY, window, XCB_MOD_MASK_ANY);
}
void grab_button(xcb_window_t window, uint8_t button, uint16_t modifiers) {
uint8_t owner_events = false; /* 事件仅发送给抓取者,不发送给其他窗口 */
uint16_t event_mask =
XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE;
xcb_grab_button(conn, owner_events, window, event_mask, XCB_GRAB_MODE_SYNC,
XCB_GRAB_MODE_ASYNC, XCB_NONE, XCB_NONE, button, modifiers);
}

View File

@@ -26,6 +26,7 @@ static atom_map_t atom_map[] = {
{.atom = atom_wm_protocols, .name = "WM_PROTOCOLS"}, {.atom = atom_wm_protocols, .name = "WM_PROTOCOLS"},
{.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_active_window, .name = "_NET_ACTIVE_WINDOW"},
{.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_state, .name = "_NET_WM_STATE"},
{.atom = atom_net_wm_fullscreen, .name = "_NET_WM_STATE_FULLSCREEN"}, {.atom = atom_net_wm_fullscreen, .name = "_NET_WM_STATE_FULLSCREEN"},
@@ -110,6 +111,23 @@ xcb_atom_t get_xcb_atom(atom_t atom) {
return atom_map[atom].xcb_atom; return atom_map[atom].xcb_atom;
} }
/**
* @brief 获取 xcb atom 名称
* @returns xcb atom 对应的名称,使用后记得用 free 释放
*/
char *get_xcb_atom_name(xcb_atom_t atom) {
xcb_get_atom_name_cookie_t cookie = xcb_get_atom_name(conn, atom);
xcb_get_atom_name_reply_t *reply =
xcb_get_atom_name_reply(conn, cookie, NULL);
int length = xcb_get_atom_name_name_length(reply);
char *name_buffer = ecalloc(1, length + 1);
const char *name = xcb_get_atom_name_name(reply);
strncpy(name_buffer, name, length);
free(reply);
return name_buffer;
}
bool send_event(xcb_window_t window, atom_t atom) { bool send_event(xcb_window_t window, atom_t atom) {
if (atom < 0 || atom >= LENGTH(atom_map)) return false; if (atom < 0 || atom >= LENGTH(atom_map)) return false;
@@ -181,14 +199,16 @@ bool get_window_tag(xcb_window_t window, client_tag_info_t *client_info) {
XCB_ATOM_CARDINAL, 0, 2); XCB_ATOM_CARDINAL, 0, 2);
xcb_get_property_reply_t *reply = xcb_get_property_reply(conn, cookie, NULL); xcb_get_property_reply_t *reply = xcb_get_property_reply(conn, cookie, NULL);
int length = xcb_get_property_value_length(reply); int length = xcb_get_property_value_length(reply);
if (length >= 2) { bool result = length >= 2;
if (result) {
uint32_t *data = xcb_get_property_value(reply); uint32_t *data = xcb_get_property_value(reply);
client_info->tags = data[0]; client_info->tags = data[0];
client_info->monitor_index = data[1]; client_info->monitor_index = data[1];
return true;
} else {
return false;
} }
free(reply);
return result;
} }
void set_window_tag(xcb_window_t window, client_tag_info_t *client_info) { void set_window_tag(xcb_window_t window, client_tag_info_t *client_info) {

View File

@@ -10,6 +10,7 @@
#include <xcb/xcb_ewmh.h> #include <xcb/xcb_ewmh.h>
#include <xcb/xcb_icccm.h> #include <xcb/xcb_icccm.h>
#include <xcb/xcb_image.h> #include <xcb/xcb_image.h>
#include <xcb/xcb_keysyms.h>
#include <xcb/xinerama.h> #include <xcb/xinerama.h>
#include <xcb/xproto.h> #include <xcb/xproto.h>
@@ -288,9 +289,11 @@ void clean_xcb(void) {
focus_window(XCB_WINDOW_NONE); focus_window(XCB_WINDOW_NONE);
clean_xcb_cursor(); clean_xcb_cursor();
clean_ewmh_extension(); clean_ewmh_extension();
if (key_symbols) xcb_key_symbols_free(key_symbols);
xcb_disconnect(conn); xcb_disconnect(conn);
conn = NULL; conn = NULL;
screen = NULL; screen = NULL;
key_symbols = NULL;
} }
void grab_keybindings(const keybinding_t *keys, const size_t length) { void grab_keybindings(const keybinding_t *keys, const size_t length) {
@@ -310,6 +313,7 @@ void grab_keybindings(const keybinding_t *keys, const size_t length) {
keycode = xcb_key_symbols_get_keycode(key_symbols, keysym); keycode = xcb_key_symbols_get_keycode(key_symbols, keysym);
modifiers = keys[i].modifiers; modifiers = keys[i].modifiers;
cookie = xcb_grab_key(conn, key, root, modifiers, *keycode, mode, mode); cookie = xcb_grab_key(conn, key, root, modifiers, *keycode, mode, mode);
free(keycode);
error = xcb_request_check(conn, cookie); error = xcb_request_check(conn, cookie);
if (error) { if (error) {
clean_xcb(); clean_xcb();
@@ -333,6 +337,11 @@ bool get_pointer_position(int32_t *x, int32_t *y) {
return false; return false;
} }
void set_pointer_position(int32_t x, int32_t y) {
xcb_warp_pointer(conn, XCB_WINDOW_NONE, screen->root, 0, 0, 1, 1, x, y);
xcb_flush(conn);
}
bool xcb_check_mask_event(xcb_event_mask_t event_mask) { bool xcb_check_mask_event(xcb_event_mask_t event_mask) {
xcb_generic_event_t *event = xcb_poll_for_event(conn); xcb_generic_event_t *event = xcb_poll_for_event(conn);
if (!event) return false; if (!event) return false;