Compare commits
15 Commits
88d9dfd714
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
c6ade0144c
|
|||
|
6d9ff81f78
|
|||
|
0626a4c512
|
|||
|
4500a5adfd
|
|||
|
9315505085
|
|||
|
bec2162890
|
|||
|
b40f499e7b
|
|||
|
9dfcf62c3c
|
|||
|
55c3fa1156
|
|||
|
ac91ebbed3
|
|||
|
6719ccca2b
|
|||
|
8cccf23a32
|
|||
|
0c361a6593
|
|||
|
645083d3a7
|
|||
|
f31b18316b
|
3
Makefile
3
Makefile
@@ -28,13 +28,14 @@ wm: $(TARGET_NAME)
|
||||
DISPLAY=:3 $(TARGET)
|
||||
|
||||
debug: $(TARGET_NAME)
|
||||
DISPLAY=:3 valgrind $(TARGET)
|
||||
DISPLAY=:3 valgrind --leak-check=full $(TARGET)
|
||||
|
||||
prepare:
|
||||
@cmake -S $(SRC_DIR) -B $(BUILD_DIR)
|
||||
@cp $(BUILD_DIR)/compile_commands.json $(MAKEFILE_DIR)
|
||||
|
||||
build: prepare
|
||||
${RM} -f $(TARGET)
|
||||
@cmake --build $(BUILD_DIR)
|
||||
@cp $(BUILD_DIR)/$(TARGET_NAME) $(TARGET)
|
||||
|
||||
|
||||
21
TODO.org
Normal file
21
TODO.org
Normal 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 添加系统托盘显示
|
||||
@@ -12,6 +12,7 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
# compile flags
|
||||
add_compile_options(-g)
|
||||
add_compile_options(-Wall)
|
||||
# add_link_options(-fsanitize=address)
|
||||
|
||||
project(${APP_NAME}
|
||||
VERSION 0.0.1
|
||||
|
||||
17
src/config.h
17
src/config.h
@@ -80,11 +80,17 @@ const rule_t rules[] = {
|
||||
},
|
||||
};
|
||||
|
||||
#define MODIFIER_ANY XCB_MOD_MASK_ANY
|
||||
#define SUPER XCB_MOD_MASK_4
|
||||
#define ALT XCB_MOD_MASK_1
|
||||
#define CTRL XCB_MOD_MASK_CONTROL
|
||||
#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[] =
|
||||
"rofi -show combi -modes window,drun,run,ssh,combi,windowcd";
|
||||
const keybinding_t keys[] = {
|
||||
@@ -142,6 +148,11 @@ const keybinding_t keys[] = {
|
||||
{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 tag_bg = "#222222";
|
||||
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 uint32_t default_font_size = 10;
|
||||
const uint32_t dpi_fallback = 96;
|
||||
const uint32_t refresh_rate = 60;
|
||||
|
||||
const uint32_t bar_y_padding = 1;
|
||||
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_icon_padding = 2;
|
||||
const uint32_t border_width = 1;
|
||||
|
||||
const char *autostart_list[] = {
|
||||
"gentoo-pipewire-launcher restart",
|
||||
"picom -b --backend xrender",
|
||||
};
|
||||
|
||||
@@ -40,8 +40,15 @@ typedef enum notify_detail_t {
|
||||
notify_detail_none = 7,
|
||||
} notify_detail_t;
|
||||
|
||||
typedef enum mapping_t {
|
||||
MAPPING_MODIFIER = 0,
|
||||
MAPPING_KEYBOARD = 1,
|
||||
MAPPING_POINTER = 2
|
||||
} mapping_t;
|
||||
|
||||
typedef struct mapping_notify_event_t {
|
||||
event_type_t type;
|
||||
mapping_t request;
|
||||
} mapping_notify_event_t;
|
||||
|
||||
typedef struct key_press_event_t {
|
||||
@@ -52,6 +59,10 @@ typedef struct key_press_event_t {
|
||||
|
||||
typedef struct button_press_event_t {
|
||||
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;
|
||||
|
||||
typedef struct configure_request_event_t {
|
||||
@@ -100,6 +111,7 @@ typedef struct motion_notify_event_t {
|
||||
xcb_window_t window;
|
||||
int32_t x;
|
||||
int32_t y;
|
||||
uint32_t time; /* 时间戳(单位:ms) */
|
||||
} motion_notify_event_t;
|
||||
|
||||
typedef struct enter_notify_event_t {
|
||||
|
||||
@@ -33,6 +33,9 @@ struct monitor_t {
|
||||
client_t *clients_stack;
|
||||
client_t *selected_client;
|
||||
|
||||
int32_t pointer_x; /* 记录光标 x 坐标位置 */
|
||||
int32_t pointer_y; /* 记录光标 y 坐标位置 */
|
||||
|
||||
xcb_window_t bar_window;
|
||||
};
|
||||
|
||||
@@ -180,3 +183,20 @@ typedef struct icons_t {
|
||||
icon_res_t cpu;
|
||||
icon_res_t clock;
|
||||
} 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;
|
||||
|
||||
@@ -126,6 +126,7 @@ typedef enum atom_t {
|
||||
atom_wm_protocols,
|
||||
atom_wm_delete,
|
||||
atom_wm_take_focus,
|
||||
atom_net_active_window,
|
||||
atom_net_wm_name,
|
||||
atom_net_wm_state,
|
||||
atom_net_wm_fullscreen,
|
||||
@@ -134,6 +135,7 @@ typedef enum atom_t {
|
||||
atom_zswm_client_info, /* 自定义属性,用于存储所在 monitor 和 tags */
|
||||
} atom_t;
|
||||
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);
|
||||
typedef enum wm_state_t {
|
||||
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);
|
||||
|
||||
bool get_pointer_position(int32_t *x, int32_t *y);
|
||||
void set_pointer_position(int32_t x, int32_t y);
|
||||
|
||||
/**
|
||||
* @brief 非阻塞检查特定事件掩码(模拟 XCheckMaskEvent)
|
||||
*/
|
||||
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);
|
||||
|
||||
146
src/main.c
146
src/main.c
@@ -25,6 +25,8 @@
|
||||
|
||||
static void init_monitors(void);
|
||||
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 gboolean update_wallpaper(gpointer user_data);
|
||||
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 draw_all_monitor_bars(void);
|
||||
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 xcb_event_handler(generic_event_t *event, void *user_data);
|
||||
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 client_message(client_message_event_t *event);
|
||||
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 update_client_list(void);
|
||||
static void update_client_name(client_t *client);
|
||||
@@ -93,6 +99,7 @@ static event_adapter_t *adapter = NULL;
|
||||
static char *font_family = NULL;
|
||||
static uint32_t font_size = default_font_size;
|
||||
static uint32_t dpi = dpi_fallback;
|
||||
static uint32_t fps = refresh_rate;
|
||||
static uint32_t bar_y_pad = bar_y_padding;
|
||||
static uint32_t tag_x_pad = tag_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);
|
||||
current_monitor = monitor;
|
||||
focus(NULL);
|
||||
/* TODO: 调整鼠标光标 */
|
||||
|
||||
set_pointer_position(monitor->pointer_x, monitor->pointer_y);
|
||||
}
|
||||
|
||||
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,
|
||||
client->window);
|
||||
current_monitor = client->monitor;
|
||||
set_pointer_position(current_monitor->pointer_x,
|
||||
current_monitor->pointer_y);
|
||||
argument.ui = client->tags;
|
||||
select_tag(&argument);
|
||||
focus(client);
|
||||
@@ -228,19 +238,22 @@ void move_to_tag(const user_action_arg_t *arg) {
|
||||
arrange(current_monitor);
|
||||
}
|
||||
|
||||
static void print_monitor_info(monitor_t *monitor) {
|
||||
logger("========== mon[%u]:\n", monitor->index);
|
||||
logger("x: %d, y: %d, width: %u, height: %u, pointer x: %d, pointer y: %d\n",
|
||||
monitor->monitor_x, monitor->monitor_y, monitor->monitor_width,
|
||||
monitor->monitor_height, monitor->pointer_x, monitor->pointer_y);
|
||||
logger("mon[%u] tags: ", monitor->index);
|
||||
for (int i = 0; monitor->tags[i]; i++) {
|
||||
logger("%s%s", i > 0 ? ", " : "", monitor->tags[i]);
|
||||
}
|
||||
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) {
|
||||
logger("========== mon[%u]:\n", m->index);
|
||||
logger("x: %d, y: %d, width: %u, height: %u\n", m->monitor_x, m->monitor_y,
|
||||
m->monitor_width, m->monitor_height);
|
||||
logger("mon[%u] tags: ", m->index);
|
||||
for (int i = 0; m->tags[i]; i++) {
|
||||
logger("%s%s", i > 0 ? ", " : "", m->tags[i]);
|
||||
}
|
||||
logger("\n");
|
||||
}
|
||||
for (monitor_t *m = monitor_list; m; m = m->next) print_monitor_info(m);
|
||||
logger("======================= monitor info end =======================\n");
|
||||
}
|
||||
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->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) {
|
||||
@@ -382,6 +414,15 @@ char **get_monitor_tags(uint32_t monitor_count, uint32_t monitor_index) {
|
||||
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) {
|
||||
bar_cairo_params_t *p = prepare_wallpaper_surface_params();
|
||||
rect_size_t *screen_size = get_screen_size();
|
||||
@@ -690,6 +731,7 @@ void init_monitor_bar(void) {
|
||||
|
||||
void get_xresource_config(void) {
|
||||
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_uint32("zswm.font_size", &font_size);
|
||||
get_xresource_uint32("zswm.bar_y_padding", &bar_y_pad);
|
||||
@@ -720,6 +762,34 @@ void draw_monitor_bar(monitor_t *m) {
|
||||
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) {
|
||||
adapter = create_event_adapter();
|
||||
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) {
|
||||
record_mointor_pointer_position(event->root_x, event->root_y, event->time);
|
||||
|
||||
if (event->window != get_root_window()) return;
|
||||
|
||||
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) {
|
||||
logger("window: 0x%x state: %u, atom: %u\n", event->window, event->state,
|
||||
event->xcb_atom);
|
||||
char *atom_name = get_xcb_atom_name(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;
|
||||
|
||||
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) {
|
||||
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);
|
||||
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;
|
||||
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->old_border_width = geometry->border_width;
|
||||
c->border_width = border_px;
|
||||
free(geometry);
|
||||
|
||||
update_client_name(c);
|
||||
|
||||
logger("== manage window: 0x%x\n", window);
|
||||
@@ -939,8 +1039,10 @@ void manage_window(xcb_window_t window) {
|
||||
}
|
||||
|
||||
set_client_tag_prop(c);
|
||||
grab_buttons_for_client(c, false);
|
||||
|
||||
set_window_event_mask(window, false);
|
||||
|
||||
change_window_border_color(window, color_set->border_color.argb);
|
||||
|
||||
attach_client(c);
|
||||
@@ -957,6 +1059,18 @@ void manage_window(xcb_window_t window) {
|
||||
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) {
|
||||
monitor_t *monitor = client->monitor;
|
||||
|
||||
@@ -1049,6 +1163,8 @@ void apply_rules(client_t *client) {
|
||||
}
|
||||
if (switch_to_tag) {
|
||||
current_monitor = client->monitor;
|
||||
set_pointer_position(current_monitor->pointer_x,
|
||||
current_monitor->pointer_y);
|
||||
current_monitor->selected_tags = client->tags;
|
||||
}
|
||||
|
||||
@@ -1138,6 +1254,7 @@ void focus(client_t *client) {
|
||||
if (client->monitor != current_monitor) current_monitor = client->monitor;
|
||||
detach_stack_client(client);
|
||||
attach_stack_client(client);
|
||||
grab_buttons_for_client(client, true);
|
||||
change_window_border_color(client->window,
|
||||
color_set->active_border_color.argb);
|
||||
focus_window(client->window);
|
||||
@@ -1151,6 +1268,7 @@ void focus(client_t *client) {
|
||||
void unfocus(client_t *client, bool set_focus) {
|
||||
if (!client) return;
|
||||
|
||||
grab_buttons_for_client(client, false);
|
||||
change_window_border_color(client->window, color_set->border_color.argb);
|
||||
if (set_focus) focus_window(WINDOW_NONE);
|
||||
}
|
||||
@@ -1442,6 +1560,8 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
init_xcb_event_loop();
|
||||
|
||||
run_autostart();
|
||||
|
||||
g_main_loop_run(loop);
|
||||
|
||||
cleanup(need_restart);
|
||||
|
||||
24
src/status.c
24
src/status.c
@@ -3,6 +3,7 @@
|
||||
#include <glib.h>
|
||||
#include <math.h>
|
||||
#include <pulse/context.h>
|
||||
#include <pulse/def.h>
|
||||
#include <pulse/glib-mainloop.h>
|
||||
#include <pulse/introspect.h>
|
||||
#include <pulse/subscribe.h>
|
||||
@@ -47,6 +48,7 @@ static status_changed_notify listener = NULL;
|
||||
static bool pulse_inited = false;
|
||||
static pa_cvolume current_volume;
|
||||
static pa_context *context = NULL;
|
||||
static pa_glib_mainloop *loop = NULL;
|
||||
|
||||
static double calc_cpu_usage(cpu_stat_t curr, cpu_stat_t prev) {
|
||||
uint64_t curr_idle = curr.idle + curr.iowait;
|
||||
@@ -100,7 +102,7 @@ static bool get_cpu_usage(double *usage, GError *error) {
|
||||
|
||||
if (!inited) {
|
||||
prev_cpu_stat = stat;
|
||||
inited = false;
|
||||
inited = true;
|
||||
}
|
||||
|
||||
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) {
|
||||
context = c;
|
||||
parse_sink(info, &status.pulse);
|
||||
current_volume = info->volume;
|
||||
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,
|
||||
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;
|
||||
|
||||
if (facility != PA_SUBSCRIPTION_EVENT_SINK) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event == PA_SUBSCRIPTION_EVENT_CHANGE) {
|
||||
if (facility == PA_SUBSCRIPTION_EVENT_SINK) {
|
||||
pa_context_get_server_info(c, server_info_callback, NULL);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -327,7 +324,9 @@ static void clean_pulse() {
|
||||
pa_context_unref(context);
|
||||
context = NULL;
|
||||
}
|
||||
if (loop) pa_glib_mainloop_free(loop);
|
||||
pulse_inited = false;
|
||||
loop = NULL;
|
||||
}
|
||||
|
||||
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)) {
|
||||
pa_context_set_subscribe_callback(c, subscribe_callback, 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) {
|
||||
clean_pulse();
|
||||
init_pulse((GMainContext *)userdata);
|
||||
@@ -344,11 +343,12 @@ static void state_callback(pa_context *c, void *userdata) {
|
||||
}
|
||||
|
||||
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_mainloop_api *api = pa_glib_mainloop_get_api(loop);
|
||||
pa_context *ctx = pa_context_new(api, "ZSWM-Status-Volume");
|
||||
pa_context_connect(ctx, NULL, PA_CONTEXT_NOFAIL, NULL);
|
||||
pa_context_set_state_callback(ctx, state_callback, g_context);
|
||||
context = pa_context_new(api, "ZSWM-Status-Volume");
|
||||
pa_context_connect(context, NULL, PA_CONTEXT_NOFAIL, NULL);
|
||||
pa_context_set_state_callback(context, state_callback, g_context);
|
||||
}
|
||||
|
||||
static gboolean update_status(gpointer data) {
|
||||
|
||||
13
src/utils.c
13
src/utils.c
@@ -2,11 +2,14 @@
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "types.h"
|
||||
|
||||
double get_time() {
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
@@ -105,11 +108,11 @@ void extract_color_channel(const uint32_t rgba, double *red, double *green,
|
||||
*alpha = COLOR_SPLIT(rgba, 0);
|
||||
}
|
||||
|
||||
#define INTERSECT(x, y, w, h, m) \
|
||||
(MAX(0, MIN((x) + (w), (m)->window_x + (m)->window_width) - \
|
||||
MAX((x), (m)->window_x)) * \
|
||||
MAX(0, MIN((y) + (h), (m)->window_y + (m)->window_height) - \
|
||||
MAX((y), (m)->window_y)))
|
||||
#define INTERSECT(x, y, w, h, m) \
|
||||
(MAX(0, MIN((x) + (w), (m)->monitor_x + (int)(m)->monitor_width) - \
|
||||
MAX((x), (m)->monitor_x)) * \
|
||||
MAX(0, MIN((y) + (h), (m)->monitor_y + (int)(m)->monitor_height) - \
|
||||
MAX((y), (m)->monitor_y)))
|
||||
monitor_t *rect_to_monitor(monitor_t *monitors, int32_t x, int32_t y,
|
||||
uint32_t width, uint32_t height) {
|
||||
monitor_t *r = monitors;
|
||||
|
||||
@@ -18,11 +18,13 @@ struct event_adapter_t {
|
||||
static void convert_event(xcb_generic_event_t *xcb_event,
|
||||
generic_event_t *event) {
|
||||
uint8_t event_type = XCB_EVENT_RESPONSE_TYPE(xcb_event);
|
||||
const char *label = xcb_event_get_label(event_type);
|
||||
switch (event_type) {
|
||||
case XCB_MAPPING_NOTIFY:
|
||||
event->type = EVENT_TYPE_MAPPING_NOTIFY;
|
||||
case XCB_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;
|
||||
}
|
||||
case XCB_KEY_PRESS: {
|
||||
xcb_key_press_event_t *ev = (xcb_key_press_event_t *)xcb_event;
|
||||
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;
|
||||
break;
|
||||
}
|
||||
case XCB_BUTTON_PRESS:
|
||||
event->type = EVENT_TYPE_BUTTON_PRESS;
|
||||
case XCB_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;
|
||||
}
|
||||
case XCB_CONFIGURE_REQUEST: {
|
||||
xcb_configure_request_event_t *ev =
|
||||
(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.window = ev->window;
|
||||
event->unmap_notify.send_event = XCB_EVENT_SENT(ev);
|
||||
} break;
|
||||
break;
|
||||
}
|
||||
case XCB_DESTROY_NOTIFY: {
|
||||
xcb_destroy_notify_event_t *ev = (xcb_destroy_notify_event_t *)xcb_event;
|
||||
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.x = ev->event_x;
|
||||
event->motiion_notify.y = ev->event_y;
|
||||
event->motiion_notify.time = ev->time;
|
||||
break;
|
||||
}
|
||||
case XCB_ENTER_NOTIFY: {
|
||||
@@ -126,7 +139,8 @@ static void convert_event(xcb_generic_event_t *xcb_event,
|
||||
event->type = -1;
|
||||
return;
|
||||
}
|
||||
if (event->type != EVENT_TYPE_MOTION_NOTIFY) {
|
||||
if (event->type) {
|
||||
const char *label = xcb_event_get_label(event_type);
|
||||
logger("========================== %s ==========================\n", label);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,8 @@ void get_window_class_instance(xcb_window_t window,
|
||||
xcb_icccm_get_wm_class_reply_t prop;
|
||||
const char *class = 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;
|
||||
instance = prop.instance_name;
|
||||
} 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->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() {
|
||||
@@ -287,11 +288,10 @@ char *get_window_name(xcb_window_t window) {
|
||||
|
||||
void set_window_event_mask(xcb_window_t window, bool clear) {
|
||||
xcb_cw_t change_mask = XCB_CW_EVENT_MASK;
|
||||
uint32_t event_mask = clear ? XCB_EVENT_MASK_NO_EVENT
|
||||
: XCB_EVENT_MASK_ENTER_WINDOW |
|
||||
XCB_EVENT_MASK_FOCUS_CHANGE |
|
||||
XCB_EVENT_MASK_PROPERTY_CHANGE |
|
||||
XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY;
|
||||
uint32_t init_event_mask =
|
||||
XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_FOCUS_CHANGE |
|
||||
XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY;
|
||||
uint32_t event_mask = clear ? XCB_EVENT_MASK_NO_EVENT : init_event_mask;
|
||||
xcb_params_cw_t params = {.event_mask = event_mask};
|
||||
xcb_aux_change_window_attributes(conn, window, change_mask, ¶ms);
|
||||
}
|
||||
@@ -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_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);
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ static atom_map_t atom_map[] = {
|
||||
{.atom = atom_wm_protocols, .name = "WM_PROTOCOLS"},
|
||||
{.atom = atom_wm_delete, .name = "WM_DELETE_WINDOW"},
|
||||
{.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_state, .name = "_NET_WM_STATE"},
|
||||
{.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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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) {
|
||||
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_get_property_reply_t *reply = xcb_get_property_reply(conn, cookie, NULL);
|
||||
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);
|
||||
client_info->tags = data[0];
|
||||
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) {
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <xcb/xcb_ewmh.h>
|
||||
#include <xcb/xcb_icccm.h>
|
||||
#include <xcb/xcb_image.h>
|
||||
#include <xcb/xcb_keysyms.h>
|
||||
#include <xcb/xinerama.h>
|
||||
#include <xcb/xproto.h>
|
||||
|
||||
@@ -288,9 +289,11 @@ void clean_xcb(void) {
|
||||
focus_window(XCB_WINDOW_NONE);
|
||||
clean_xcb_cursor();
|
||||
clean_ewmh_extension();
|
||||
if (key_symbols) xcb_key_symbols_free(key_symbols);
|
||||
xcb_disconnect(conn);
|
||||
conn = NULL;
|
||||
screen = NULL;
|
||||
key_symbols = NULL;
|
||||
}
|
||||
|
||||
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);
|
||||
modifiers = keys[i].modifiers;
|
||||
cookie = xcb_grab_key(conn, key, root, modifiers, *keycode, mode, mode);
|
||||
free(keycode);
|
||||
error = xcb_request_check(conn, cookie);
|
||||
if (error) {
|
||||
clean_xcb();
|
||||
@@ -333,6 +337,11 @@ bool get_pointer_position(int32_t *x, int32_t *y) {
|
||||
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) {
|
||||
xcb_generic_event_t *event = xcb_poll_for_event(conn);
|
||||
if (!event) return false;
|
||||
|
||||
Reference in New Issue
Block a user