Compare commits

...

5 Commits

14 changed files with 420 additions and 121 deletions

View File

@@ -3,6 +3,9 @@ cmake_minimum_required(VERSION 3.24)
set(APP_NAME "zswm") set(APP_NAME "zswm")
set(SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) set(SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
add_compile_definitions(LOG_FILE="$ENV{HOME}/zswm-log.txt")
# add_compile_definitions(RELEASE)
# generate compile_commands.json for clangd # generate compile_commands.json for clangd
set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

View File

@@ -54,7 +54,7 @@ const layout_t layouts[] = {
/* 默认 tags 规则 */ /* 默认 tags 规则 */
const rule_tag_t browser_tag[] = { const rule_tag_t browser_tag[] = {
{.monitor_amount = 1, .tags = 1 << 1}, {.monitor_index = 0, .tags = 1 << 1},
{0}, {0},
}; };
const rule_tag_t mpv_tag[] = { const rule_tag_t mpv_tag[] = {
@@ -64,7 +64,7 @@ const rule_tag_t mpv_tag[] = {
}; };
const rule_tag_t emacs_tag[] = { const rule_tag_t emacs_tag[] = {
{.monitor_amount = 1, .monitor_index = 0, .tags = 1 << 2}, {.monitor_amount = 1, .monitor_index = 0, .tags = 1 << 2},
{.monitor_amount = 2, .monitor_index = 0, .tags = 1 << 1}, {.monitor_amount = 2, .monitor_index = 1, .tags = 1 << 1},
{0}, {0},
}; };
/* 新建窗口规则 */ /* 新建窗口规则 */

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include <stdint.h> #include <stdint.h>
#include <xcb/xproto.h>
#include "types.h" #include "types.h"
@@ -21,6 +22,24 @@ typedef enum event_type_t {
EVENT_TYPE_CLIENT_MESSAGE, EVENT_TYPE_CLIENT_MESSAGE,
} event_type_t; } event_type_t;
typedef enum notify_mode_t {
notify_mode_normal = 0,
notify_mode_grab = 1,
notify_mode_ungrab = 2,
notify_mode_while_grabbed = 3
} notify_mode_t;
typedef enum notify_detail_t {
notify_detail_ancestor = 0,
notify_detail_virtual = 1,
notify_detail_inferior = 2,
notify_detail_nonlinear = 3,
notify_detail_nonlinear_virtual = 4,
notify_detail_pointer = 5,
notify_detail_pointer_root = 6,
notify_detail_none = 7,
} notify_detail_t;
typedef struct mapping_notify_event_t { typedef struct mapping_notify_event_t {
event_type_t type; event_type_t type;
} mapping_notify_event_t; } mapping_notify_event_t;
@@ -71,14 +90,24 @@ typedef struct expose_event_t {
typedef struct motion_notify_event_t { typedef struct motion_notify_event_t {
event_type_t type; event_type_t type;
xcb_window_t root;
int32_t root_x;
int32_t root_y;
xcb_window_t window;
int32_t x;
int32_t y;
} motion_notify_event_t; } motion_notify_event_t;
typedef struct enter_notify_event_t { typedef struct enter_notify_event_t {
event_type_t type; event_type_t type;
xcb_window_t window;
notify_mode_t mode;
notify_detail_t detail;
} enter_notify_event_t; } enter_notify_event_t;
typedef struct focus_in_event_t { typedef struct focus_in_event_t {
event_type_t type; event_type_t type;
xcb_window_t window;
} focus_in_event_t; } focus_in_event_t;
typedef struct property_notify_event_t { typedef struct property_notify_event_t {

View File

@@ -3,8 +3,11 @@
#include <stdbool.h> #include <stdbool.h>
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h>
#include <sys/time.h> #include <sys/time.h>
#include "types.h"
#define LENGTH(X) (sizeof(X) / sizeof(X)[0]) #define LENGTH(X) (sizeof(X) / sizeof(X)[0])
#define MIN(a, b) (((a) < (b)) ? (a) : (b)) #define MIN(a, b) (((a) < (b)) ? (a) : (b))
@@ -16,6 +19,14 @@ void *ecalloc(size_t number_of_member, size_t member_size);
bool file_exist(const char *path); bool file_exist(const char *path);
#if defined(RELEASE)
#define logger(fmt, ...)
#elif defined(LOG_FILE)
void logger(const char *fmt, ...);
#else
#define logger(fmt, ...) printf(fmt, ##__VA_ARGS__)
#endif
/** /**
* @brief 将 16 进制的颜色配置转换成 uint32 格式的 rgba数据 * @brief 将 16 进制的颜色配置转换成 uint32 格式的 rgba数据
* *
@@ -40,3 +51,6 @@ uint32_t rgba_to_argb(uint32_t rgba);
*/ */
void extract_color_channel(const uint32_t rgba, double *red, double *green, void extract_color_channel(const uint32_t rgba, double *red, double *green,
double *blue, double *alpha); double *blue, double *alpha);
monitor_t *rect_to_monitor(monitor_t *monitors, int32_t x, int32_t y,
uint32_t width, uint32_t height);

View File

@@ -48,6 +48,7 @@ void map_window(xcb_window_t window);
char *get_window_name(xcb_window_t window); char *get_window_name(xcb_window_t window);
void init_window_event_mask(xcb_window_t window); void init_window_event_mask(xcb_window_t window);
void change_window_border_color(xcb_window_t window, uint32_t argb); void change_window_border_color(xcb_window_t window, uint32_t argb);
void focus_window(xcb_window_t window);
typedef struct bar_cairo_params_t { typedef struct bar_cairo_params_t {
xcb_connection_t *conn; xcb_connection_t *conn;
@@ -95,3 +96,12 @@ void grab_keybindings(const keybinding_t *keys, const size_t length);
bool get_xresource_uint32(const char *name, uint32_t *value); bool get_xresource_uint32(const char *name, uint32_t *value);
void get_xresource_string(const char *name, char **value, const char *fallback); void get_xresource_string(const char *name, char **value, const char *fallback);
void clean_xresource(void); void clean_xresource(void);
typedef enum atom_t {
atom_wm_protocols,
atom_wm_delete,
atom_wm_take_focus,
} atom_t;
bool send_event(xcb_window_t window, atom_t atom);
bool get_pointer_position(int32_t *x, int32_t *y);

View File

@@ -18,16 +18,16 @@ void maximize_client(client_t *c) {
monitor_t *m = c->monitor; monitor_t *m = c->monitor;
c->x = m->window_x; c->x = m->window_x;
c->y = m->window_y; c->y = m->window_y;
c->width = m->window_width; c->width = m->window_width - 2 * c->border_width;
c->height = m->window_height; c->height = m->window_height - 2 * c->border_width;
} }
void fullscreen_client(client_t *c) { void fullscreen_client(client_t *c) {
monitor_t *m = c->monitor; monitor_t *m = c->monitor;
c->x = m->monitor_x; c->x = m->monitor_x;
c->y = m->monitor_y; c->y = m->monitor_y;
c->width = m->monitor_width; c->width = m->monitor_width - 2 * c->border_width;
c->height = m->monitor_height; c->height = m->monitor_height - 2 * c->border_width;
} }
void monocle(monitor_t *monitor) { void monocle(monitor_t *monitor) {

View File

@@ -24,7 +24,7 @@ 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 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); static void set_wallpaper_by_index(uint32_t index, wallpaper_config_t *config);
static wallpaper_config_t *parse_wallpaper_config(void); static wallpaper_config_t *parse_wallpaper_config(void);
static void free_wallpaper_config(wallpaper_config_t *wallpaper_config); static void free_wallpaper_config(wallpaper_config_t *wallpaper_config);
static void init_color_set(void); static void init_color_set(void);
@@ -39,6 +39,9 @@ static void key_press(key_press_event_t *event);
static void configure_request(configure_request_event_t *event); static void configure_request(configure_request_event_t *event);
static void map_request(map_request_event_t *event); static void map_request(map_request_event_t *event);
static void unmap_notify(unmap_notify_event_t *event); static void unmap_notify(unmap_notify_event_t *event);
static void motion_notify(motion_notify_event_t *event);
static void enter_notify(enter_notify_event_t *event);
static void focus_in(focus_in_event_t *event);
static void manage_window(xcb_window_t window); static void manage_window(xcb_window_t window);
static void unmanage_client(client_t *client); static void unmanage_client(client_t *client);
static void update_client_name(client_t *client); static void update_client_name(client_t *client);
@@ -52,8 +55,10 @@ static void arrange(monitor_t *monitor);
static void show_hide_client(client_t *client); static void show_hide_client(client_t *client);
static void restack(monitor_t *monitor); static void restack(monitor_t *monitor);
static void focus(client_t *client); static void focus(client_t *client);
static void unfocus(client_t *client, bool set_focus);
static void apply_monitor_layout(monitor_t *monitor); static void apply_monitor_layout(monitor_t *monitor);
static client_t *get_client_of_window(xcb_window_t window); static client_t *get_client_of_window(xcb_window_t window);
static monitor_t *get_monitor_of_window(xcb_window_t window);
static gboolean handle_xcb_event(GIOChannel *channel, GIOCondition condition, static gboolean handle_xcb_event(GIOChannel *channel, GIOCondition condition,
gpointer userdata); gpointer userdata);
static void cleanup(void); static void cleanup(void);
@@ -94,22 +99,22 @@ void select_tag(const user_action_arg_t *arg) {
/* 调试函数,开发完成后删除 */ /* 调试函数,开发完成后删除 */
static void print_monitor_list_info(monitor_t *monitor_list) { static void print_monitor_list_info(monitor_t *monitor_list) {
printf("========================= monitor info =========================\n"); logger("========================= monitor info =========================\n");
for (monitor_t *m = monitor_list; m; m = m->next) { for (monitor_t *m = monitor_list; m; m = m->next) {
printf("========== mon[%u]:\n", m->index); logger("========== mon[%u]:\n", m->index);
printf("x: %d, y: %d, width: %u, height: %u\n", m->monitor_x, m->monitor_y, logger("x: %d, y: %d, width: %u, height: %u\n", m->monitor_x, m->monitor_y,
m->monitor_width, m->monitor_height); m->monitor_width, m->monitor_height);
printf("mon[%u] tags: ", m->index); logger("mon[%u] tags: ", m->index);
for (int i = 0; m->tags[i]; i++) { for (int i = 0; m->tags[i]; i++) {
printf("%s%s", i > 0 ? ", " : "", m->tags[i]); logger("%s%s", i > 0 ? ", " : "", m->tags[i]);
} }
printf("\n"); logger("\n");
} }
printf("======================= 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) {
const char *p = strlen(prompt) ? " " : ""; const char *p = strlen(prompt) ? " " : "";
printf("%s%srgba: #%08x, argb: #%08x\n", prompt, p, color->rgba, color->argb); logger("%s%srgba: #%08x, argb: #%08x\n", prompt, p, color->rgba, color->argb);
} }
static void print_color_set(color_set_t *set) { static void print_color_set(color_set_t *set) {
print_color("== bar bg:", &set->bar_bg); print_color("== bar bg:", &set->bar_bg);
@@ -126,54 +131,54 @@ static void print_color_set(color_set_t *set) {
} }
static void print_wallpaper_config(wallpaper_config_t *config) { static void print_wallpaper_config(wallpaper_config_t *config) {
if (!config) { if (!config) {
printf("no wallpaper\n"); logger("no wallpaper\n");
return; return;
} }
printf("wallpaper count: %u\n", config->count); logger("wallpaper count: %u\n", config->count);
for (uint32_t i = 0; i < config->count; i++) { for (uint32_t i = 0; i < config->count; i++) {
printf("wallpaper[%02u]: %s\n", i, config->path_list[i]); logger("wallpaper[%02u]: %s\n", i, config->path_list[i]);
} }
} }
static void print_window_list(window_list_t *window_list) { static void print_window_list(window_list_t *window_list) {
if (!window_list) { if (!window_list) {
printf("no window\n"); logger("no window\n");
return; return;
} }
for (uint32_t i = 0; i < window_list->count; i++) { for (uint32_t i = 0; i < window_list->count; i++) {
printf("window[%u]: 0x%x\n", i, window_list->list[i]); logger("window[%u]: 0x%x\n", i, window_list->list[i]);
} }
} }
static char *bool2string(bool b) { return b ? "true" : "false"; } static char *bool2string(bool b) { return b ? "true" : "false"; }
static void print_client(client_t *c) { static void print_client(client_t *c) {
if (!c) return; if (!c) return;
printf("======= client info ========\n"); logger("======= client info ========\n");
printf("window: 0x%x, x: %d, y: %d, w: %u, h: %u, bw: %u\n", c->window, c->x, logger("window: 0x%x, x: %d, y: %d, w: %u, h: %u, bw: %u\n", c->window, c->x,
c->y, c->width, c->height, c->border_width); c->y, c->width, c->height, c->border_width);
printf("floating: %s, fullscreen: %s, maximize: %s\n", logger("floating: %s, fullscreen: %s, maximize: %s\n",
bool2string(c->floating), bool2string(c->fullscreen), bool2string(c->floating), bool2string(c->fullscreen),
bool2string(c->maximize)); bool2string(c->maximize));
printf("tags: %b, monitor selected tags: %b\n", c->tags, logger("tags: %b, monitor selected tags: %b\n", c->tags,
c->monitor->selected_tags); c->monitor->selected_tags);
printf("name: %s\n", c->name); logger("name: %s\n", c->name);
printf("===== client info end ======\n"); logger("===== client info end ======\n");
} }
static void print_rule(const rule_t *r) { static void print_rule(const rule_t *r) {
printf("==================== rule ====================\n"); logger("==================== rule ====================\n");
printf("class: %s, instance: %s, title: %s\n", r->class, r->instance, logger("class: %s, instance: %s, title: %s\n", r->class, r->instance,
r->title); r->title);
printf("floating: %s, maximize: %s, fullscreen: %s, switch to tag: %s\n", logger("floating: %s, maximize: %s, fullscreen: %s, switch to tag: %s\n",
bool2string(r->action.floating), bool2string(r->action.maximize), bool2string(r->action.floating), bool2string(r->action.maximize),
bool2string(r->action.fullscreen), bool2string(r->action.fullscreen),
bool2string(r->action.switch_to_tag)); bool2string(r->action.switch_to_tag));
for (uint32_t i = 0; r->tag && r->tag[i].monitor_amount; i++) { for (uint32_t i = 0; r->tag && r->tag[i].tags; i++) {
const rule_tag_t *t = &r->tag[i]; const rule_tag_t *t = &r->tag[i];
printf("monitor amount: %u, monitor index: %u, tags: %b\n", logger("monitor amount: %u, monitor index: %u, tags: %b\n",
t->monitor_amount, t->monitor_index, t->tags); t->monitor_amount, t->monitor_index, t->tags);
} }
printf("================== rule end ==================\n"); logger("================== rule end ==================\n");
} }
/* 调试函数结束 */ /* 调试函数结束 */
@@ -250,7 +255,7 @@ gboolean update_wallpaper(gpointer user_data) {
if (!wc) return false; if (!wc) return false;
static uint32_t index = 0; static uint32_t index = 0;
set_wallpaper_by_index(index); set_wallpaper_by_index(index, wc);
uint32_t monitor_count = 0; uint32_t monitor_count = 0;
for (monitor_t *m = monitors; m; m = m->next) monitor_count++; for (monitor_t *m = monitors; m; m = m->next) monitor_count++;
@@ -265,19 +270,19 @@ gboolean update_wallpaper(gpointer user_data) {
return true; return true;
} }
void set_wallpaper_by_index(uint32_t index) { void set_wallpaper_by_index(uint32_t index, wallpaper_config_t *config) {
if (!wallpaper_config) return; if (!config) return;
rect_size_t *screen_size = get_screen_size(); rect_size_t *screen_size = get_screen_size();
uint32_t *wallpaper = generate_wallpaper( uint32_t *wallpaper = generate_wallpaper(
wallpaper_config, monitors, index, screen_size->width, screen_size->height); config, monitors, index, screen_size->width, screen_size->height);
if (wallpaper) { if (wallpaper) {
double start = get_time(); double start = get_time();
set_wallpaper(wallpaper); set_wallpaper(wallpaper);
double end = get_time(); double end = get_time();
free(wallpaper); free(wallpaper);
printf("set wallpaper cost: %fs\n", end - start); logger("set wallpaper cost: %fs\n", end - start);
} }
free(screen_size); free(screen_size);
@@ -411,7 +416,7 @@ void init_monitor_bar(void) {
m->cr = cr; m->cr = cr;
} }
printf("font height: %u, bar height: %u\n", font_height, bar_height); logger("font height: %u, bar height: %u\n", font_height, bar_height);
} }
void get_xresource_config(void) { void get_xresource_config(void) {
@@ -422,7 +427,7 @@ void get_xresource_config(void) {
get_xresource_uint32("zswm.tag_x_padding", &tag_x_pad); get_xresource_uint32("zswm.tag_x_padding", &tag_x_pad);
get_xresource_uint32("zswm.border_width", &border_px); get_xresource_uint32("zswm.border_width", &border_px);
printf("dpi: %u, font family: %s, font size: %u\n", dpi, font_family, logger("dpi: %u, font family: %s, font size: %u\n", dpi, font_family,
font_size); font_size);
clean_xresource(); clean_xresource();
} }
@@ -457,6 +462,9 @@ void xcb_event_handler(generic_event_t *event, void *user_data) {
EVENT(EVENT_TYPE_CONFIGURE_REQUEST, configure_request); EVENT(EVENT_TYPE_CONFIGURE_REQUEST, configure_request);
EVENT(EVENT_TYPE_MAP_REQUEST, map_request); EVENT(EVENT_TYPE_MAP_REQUEST, map_request);
EVENT(EVENT_TYPE_UNMAP_NOTIFY, unmap_notify); EVENT(EVENT_TYPE_UNMAP_NOTIFY, unmap_notify);
EVENT(EVENT_TYPE_MOTION_NOTIFY, motion_notify);
EVENT(EVENT_TYPE_ENTER_NOTIFY, enter_notify);
EVENT(EVENT_TYPE_FOCUS_IN, focus_in);
#undef EVENT #undef EVENT
@@ -484,7 +492,7 @@ void configure_request(configure_request_event_t *event) {
} else { } else {
configure_window(client->window, client->x, client->y, client->width, configure_window(client->window, client->x, client->y, client->width,
client->height, client->border_width); client->height, client->border_width);
printf("configure request: %s\n", client->name); logger("configure request: %s\n", client->name);
} }
flush_xcb_connection(); flush_xcb_connection();
@@ -505,6 +513,48 @@ void unmap_notify(unmap_notify_event_t *event) {
} }
} }
void motion_notify(motion_notify_event_t *event) {
logger("motion: window: 0x%x, root: 0x%x\n", event->window, event->root);
logger("x: %d, y: %d, root_x: %d, root_y: %d\n", event->x, event->y,
event->root_x, event->root_y);
logger("root window: 0x%x\n", get_root_window());
if (event->window != get_root_window()) return;
static monitor_t *mon = NULL;
monitor_t *m = rect_to_monitor(monitors, event->root_x, event->root_y, 1, 1);
if (mon && m != mon) {
unfocus(current_monitor->selected_client, true);
current_monitor = m;
focus(NULL);
}
mon = m;
}
void enter_notify(enter_notify_event_t *event) {
if ((event->mode != notify_mode_normal ||
event->detail == notify_detail_inferior) &&
event->window != get_root_window()) {
return;
}
client_t *c = get_client_of_window(event->window);
monitor_t *m = c ? c->monitor : get_monitor_of_window(event->window);
if (m != current_monitor) {
unfocus(current_monitor->selected_client, true);
current_monitor = m;
} else if (!c || c == current_monitor->selected_client) {
return;
}
focus(c);
}
void focus_in(focus_in_event_t *event) {
if (current_monitor->selected_client &&
event->window != current_monitor->selected_client->window) {
focus_window(event->window);
send_event(event->window, atom_wm_take_focus);
}
}
void manage_window(xcb_window_t window) { void manage_window(xcb_window_t window) {
window_geometry_t *geometry = get_window_geometry(window); window_geometry_t *geometry = get_window_geometry(window);
client_t *c = ecalloc(1, sizeof(client_t)); client_t *c = ecalloc(1, sizeof(client_t));
@@ -517,8 +567,8 @@ void manage_window(xcb_window_t window) {
c->border_width = border_px; c->border_width = border_px;
update_client_name(c); update_client_name(c);
printf("== manage window: %u\n", window); logger("== manage window: %u\n", window);
printf("== x: %d, y: %d, w: %u, h: %u, bw: %u\n", c->x, c->y, c->width, logger("== x: %d, y: %d, w: %u, h: %u, bw: %u\n", c->x, c->y, c->width,
c->height, c->border_width); c->height, c->border_width);
xcb_window_t tw = get_transient_window_for(window); xcb_window_t tw = get_transient_window_for(window);
@@ -535,9 +585,12 @@ void manage_window(xcb_window_t window) {
attach_stack_client(c); attach_stack_client(c);
init_window_event_mask(window); init_window_event_mask(window);
change_window_border_color(window, color_set->border_color.argb); change_window_border_color(window, color_set->border_color.argb);
if (c->monitor == current_monitor) {
unfocus(current_monitor->selected_client, false);
}
arrange(c->monitor); arrange(c->monitor);
map_window(window); map_window(window);
draw_all_monitor_bars(); focus(NULL);
} }
void unmanage_client(client_t *client) { void unmanage_client(client_t *client) {
@@ -583,11 +636,10 @@ void apply_rules(client_t *client) {
const rule_tag_t *tag = NULL; const rule_tag_t *tag = NULL;
for (uint32_t i = 0;; i++) { for (uint32_t i = 0;; i++) {
const rule_tag_t *t = &r->tag[i]; const rule_tag_t *t = &r->tag[i];
if (!t->monitor_amount) break; if (!t->tags) break;
if (t->monitor_amount == monitor_amount) { if (!t->monitor_amount || t->monitor_amount == monitor_amount) {
tag = t; tag = t;
break;
} }
} }
if (tag) { if (tag) {
@@ -597,6 +649,7 @@ void apply_rules(client_t *client) {
uint32_t tags_mask = get_tags_mask_of_monitor(m); uint32_t tags_mask = get_tags_mask_of_monitor(m);
client->tags = tag->tags & tags_mask; client->tags = tag->tags & tags_mask;
client->monitor = m;
} }
} }
@@ -610,7 +663,7 @@ void apply_rules(client_t *client) {
} }
print_client(client); print_client(client);
printf("apply rules done\n"); logger("apply rules done\n");
} }
uint32_t get_tags_mask_of_monitor(monitor_t *monitor) { uint32_t get_tags_mask_of_monitor(monitor_t *monitor) {
@@ -660,12 +713,39 @@ void arrange(monitor_t *monitor) {
} }
} }
void restack(monitor_t *monitor) { /* TODO: */ draw_monitor_bar(monitor); } void restack(monitor_t *monitor) {
void focus(client_t *client) { draw_monitor_bar(monitor);
/* TODO: */ /* TODO: */
}
void focus(client_t *client) {
if (!client || !CLIENT_VISIBLE(client)) {
for (client = current_monitor->clients_stack;
client && !CLIENT_VISIBLE(client); client = client->stack_next);
}
if (current_monitor->selected_client &&
current_monitor->selected_client != client) {
unfocus(current_monitor->selected_client, false);
}
if (client) {
if (client->monitor != current_monitor) current_monitor = client->monitor;
detach_stack_client(client);
attach_stack_client(client);
change_window_border_color(client->window,
color_set->active_border_color.argb);
focus_window(client->window);
} else {
focus_window(WINDOW_NONE);
}
current_monitor->selected_client = client; current_monitor->selected_client = client;
draw_all_monitor_bars(); draw_all_monitor_bars();
} }
void unfocus(client_t *client, bool set_focus) {
if (!client) return;
change_window_border_color(client->window, color_set->border_color.argb);
if (set_focus) focus_window(WINDOW_NONE);
}
void show_hide_client(client_t *client) { void show_hide_client(client_t *client) {
if (!client) return; if (!client) return;
@@ -715,6 +795,22 @@ client_t *get_client_of_window(xcb_window_t window) {
return NULL; return NULL;
} }
monitor_t *get_monitor_of_window(xcb_window_t window) {
int32_t x, y;
if (window == get_root_window() && get_pointer_position(&x, &y)) {
return rect_to_monitor(monitors, x, y, 1, 1);
}
for (monitor_t *m = monitors; m; m = m->next) {
if (window == m->bar_window) return m;
}
client_t *c = get_client_of_window(window);
if (c) return c->monitor;
return current_monitor;
}
gboolean handle_xcb_event(GIOChannel *channel, GIOCondition condition, gboolean handle_xcb_event(GIOChannel *channel, GIOCondition condition,
gpointer userdata) { gpointer userdata) {
if (condition & (G_IO_HUP | G_IO_ERR)) { if (condition & (G_IO_HUP | G_IO_ERR)) {

View File

@@ -44,6 +44,17 @@ bool file_exist(const char *path) {
return access(path, R_OK) == 0; return access(path, R_OK) == 0;
} }
#if (!defined(RELEASE)) && defined(LOG_FILE)
void logger(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
FILE *log_file = fopen(LOG_FILE, "a+t");
vfprintf(log_file, fmt, ap);
va_end(ap);
fclose(log_file);
}
#endif
bool hex_color_to_rgba(const char *hex, uint32_t *rgba) { bool hex_color_to_rgba(const char *hex, uint32_t *rgba) {
if (hex[0] == '#') hex++; if (hex[0] == '#') hex++;
@@ -93,3 +104,22 @@ void extract_color_channel(const uint32_t rgba, double *red, double *green,
*blue = COLOR_SPLIT(rgba, 8); *blue = COLOR_SPLIT(rgba, 8);
*alpha = COLOR_SPLIT(rgba, 0); *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)))
monitor_t *rect_to_monitor(monitor_t *monitors, int32_t x, int32_t y,
uint32_t width, uint32_t height) {
monitor_t *r = monitors;
int32_t a, area = 0;
for (monitor_t *m = monitors; m; m = m->next) {
if ((a = INTERSECT(x, y, width, height, m)) > area) {
area = a;
r = m;
}
}
return r;
}

View File

@@ -1,7 +1,7 @@
set(LIB_XCB "zswm-xcb") set(LIB_XCB "zswm-xcb")
set(LIB_XCB_NAME ${LIB_XCB} PARENT_SCOPE) set(LIB_XCB_NAME ${LIB_XCB} PARENT_SCOPE)
add_library(${LIB_XCB} STATIC xcb.c window.c res.c cursor.c event.c) add_library(${LIB_XCB} STATIC xcb.c window.c res.c cursor.c event.c wm-extension.c)
target_include_directories(${LIB_XCB} SYSTEM target_include_directories(${LIB_XCB} SYSTEM
PRIVATE ${XCB_INCLUDE_DIRS} PRIVATE ${XCB_INCLUDE_DIRS}

View File

@@ -1,5 +1,4 @@
#include <stdint.h> #include <stdint.h>
#include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <xcb/xcb.h> #include <xcb/xcb.h>
#include <xcb/xcb_event.h> #include <xcb/xcb_event.h>
@@ -19,7 +18,7 @@ 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); const char *label = xcb_event_get_label(event_type);
printf("=========================== %s ===========================\n", label); logger("=========================== %s ===========================\n", label);
switch (event_type) { switch (event_type) {
case XCB_MAPPING_NOTIFY: case XCB_MAPPING_NOTIFY:
event->type = EVENT_TYPE_MAPPING_NOTIFY; event->type = EVENT_TYPE_MAPPING_NOTIFY;
@@ -73,15 +72,30 @@ static void convert_event(xcb_generic_event_t *xcb_event,
case XCB_EXPOSE: case XCB_EXPOSE:
event->type = EVENT_TYPE_EXPOSE; event->type = EVENT_TYPE_EXPOSE;
break; break;
case XCB_MOTION_NOTIFY: case XCB_MOTION_NOTIFY: {
event->type = EVENT_TYPE_MOTION_NOTIFY; xcb_motion_notify_event_t *ev = (xcb_motion_notify_event_t *)xcb_event;
event->motiion_notify.type = EVENT_TYPE_MOTION_NOTIFY;
event->motiion_notify.root = ev->root;
event->motiion_notify.root_x = ev->root_x;
event->motiion_notify.root_y = ev->root_y;
event->motiion_notify.window = ev->event;
event->motiion_notify.x = ev->event_x;
event->motiion_notify.y = ev->event_y;
break; break;
case XCB_ENTER_NOTIFY: }
event->type = EVENT_TYPE_ENTER_NOTIFY; case XCB_ENTER_NOTIFY: {
break; xcb_enter_notify_event_t *ev = (xcb_enter_notify_event_t *)xcb_event;
case XCB_FOCUS_IN: event->enter_notify.type = EVENT_TYPE_ENTER_NOTIFY;
event->type = EVENT_TYPE_FOCUS_IN; event->enter_notify.window = ev->event;
event->enter_notify.mode = ev->mode;
event->enter_notify.detail = ev->detail;
break; break;
}
case XCB_FOCUS_IN: {
xcb_focus_in_event_t *ev = (xcb_focus_in_event_t *)xcb_event;
event->focus_in.type = EVENT_TYPE_FOCUS_IN;
event->focus_in.window = ev->event;
} break;
case XCB_PROPERTY_NOTIFY: case XCB_PROPERTY_NOTIFY:
event->type = EVENT_TYPE_PROPERTY_NOTIFY; event->type = EVENT_TYPE_PROPERTY_NOTIFY;
break; break;
@@ -89,6 +103,7 @@ static void convert_event(xcb_generic_event_t *xcb_event,
event->type = EVENT_TYPE_CLIENT_MESSAGE; event->type = EVENT_TYPE_CLIENT_MESSAGE;
break; break;
default: default:
event->type = -1;
return; return;
} }
} }

View File

@@ -285,3 +285,19 @@ void change_window_border_color(xcb_window_t window, uint32_t argb) {
xcb_params_cw_t params = {.border_pixel = argb}; xcb_params_cw_t params = {.border_pixel = argb};
xcb_aux_change_window_attributes(conn, window, XCB_CW_BORDER_PIXEL, &params); xcb_aux_change_window_attributes(conn, window, XCB_CW_BORDER_PIXEL, &params);
} }
void focus_window(xcb_window_t window) {
xcb_window_t root = screen->root;
if (window == WINDOW_NONE) {
xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, root,
XCB_CURRENT_TIME);
xcb_delete_property(conn, root, ewmh->_NET_ACTIVE_WINDOW);
} else {
xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, window,
XCB_CURRENT_TIME);
xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root,
ewmh->_NET_ACTIVE_WINDOW, XCB_ATOM_WINDOW, 32, 1,
&window);
send_event(window, atom_wm_take_focus);
}
}

127
src/xcb/wm-extension.c Normal file
View File

@@ -0,0 +1,127 @@
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <xcb/xcb.h>
#include <xcb/xcb_icccm.h>
#include <xcb/xproto.h>
#include "app.h"
#include "utils.h"
#include "window.h"
#include "xcb-private.h"
#include "xcb.h"
xcb_ewmh_connection_t *ewmh = NULL;
static bool ewmh_init_failed = false;
static xcb_window_t wm_check_window = XCB_NONE;
typedef struct atom_name_map_t {
const atom_t atom;
const char *name;
xcb_atom_t xcb_atom;
} atom_map_t;
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"},
};
void init_icccm_extension(void) {
xcb_intern_atom_cookie_t cookie;
for (int i = 0; i < LENGTH(atom_map); i++) {
const char *name = atom_map[i].name;
cookie = xcb_intern_atom(conn, false, strlen(name), name);
xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(conn, cookie, NULL);
atom_map[i].xcb_atom = reply->atom;
free(reply);
}
}
void init_ewmh_extension(void) {
if (ewmh || ewmh_init_failed) return;
ewmh = ecalloc(1, sizeof(xcb_ewmh_connection_t));
xcb_intern_atom_cookie_t *cookie = xcb_ewmh_init_atoms(conn, ewmh);
if (!xcb_ewmh_init_atoms_replies(ewmh, cookie, NULL)) {
free(ewmh);
ewmh = NULL;
ewmh_init_failed = true;
return;
}
wm_check_window = xcb_generate_id(conn);
xcb_create_window(conn, XCB_COPY_FROM_PARENT, wm_check_window, screen->root,
0, 0, 1, 1, 0, XCB_WINDOW_CLASS_INPUT_ONLY,
screen->root_visual, XCB_NONE, NULL);
const char *class = APP_NAME "_wm_check";
set_window_class_instance(wm_check_window, class, class);
xcb_ewmh_set_wm_name(ewmh, wm_check_window, strlen(APP_NAME), APP_NAME);
xcb_ewmh_set_supporting_wm_check(ewmh, screen->root, wm_check_window);
xcb_ewmh_set_supporting_wm_check(ewmh, wm_check_window, wm_check_window);
uint8_t mode = XCB_PROP_MODE_REPLACE;
xcb_atom_t atom = XCB_ATOM_ATOM;
const xcb_atom_t state[] = {ewmh->_NET_WM_STATE_STICKY};
const xcb_atom_t supported[] = {
ewmh->_NET_ACTIVE_WINDOW,
ewmh->_NET_SUPPORTED,
ewmh->_NET_WM_NAME,
ewmh->_NET_WM_STATE,
ewmh->_NET_SUPPORTING_WM_CHECK,
ewmh->_NET_WM_STATE_FULLSCREEN,
ewmh->_NET_WM_WINDOW_TYPE,
ewmh->_NET_WM_WINDOW_TYPE_DIALOG,
ewmh->_NET_CLIENT_LIST,
};
xcb_change_property(conn, mode, wm_check_window, ewmh->_NET_WM_STATE, atom,
32, LENGTH(state), state);
xcb_change_property(conn, mode, screen->root, ewmh->_NET_SUPPORTED, atom, 32,
LENGTH(supported), supported);
xcb_delete_property(conn, screen->root, ewmh->_NET_CLIENT_LIST);
}
void clean_ewmh_extension(void) {
if (wm_check_window != XCB_NONE) {
xcb_destroy_window(conn, wm_check_window);
wm_check_window = XCB_NONE;
}
if (ewmh) {
if (!ewmh_init_failed) {
xcb_ewmh_connection_wipe(ewmh);
}
free(ewmh);
ewmh_init_failed = false;
ewmh = NULL;
}
}
bool send_event(xcb_window_t window, atom_t atom) {
if (atom < 0 || atom >= LENGTH(atom_map)) return false;
bool exist = false;
xcb_atom_t xcb_atom = atom_map[atom].xcb_atom;
xcb_get_property_cookie_t cookie =
xcb_icccm_get_wm_protocols(conn, window, xcb_atom);
xcb_icccm_get_wm_protocols_reply_t reply;
if (xcb_icccm_get_wm_protocols_reply(conn, cookie, &reply, NULL)) {
for (uint32_t i = 0; !exist && i < reply.atoms_len; i++) {
exist = reply.atoms[i] == xcb_atom;
}
xcb_icccm_get_wm_protocols_reply_wipe(&reply);
}
if (exist) {
xcb_client_message_event_t ev = {
.response_type = XCB_CLIENT_MESSAGE,
.format = 32,
.window = window,
.type = atom_map[atom_wm_protocols].xcb_atom,
.data = {.data32 = {xcb_atom, XCB_CURRENT_TIME, 0, 0, 0}},
};
xcb_send_event(conn, false, window, XCB_EVENT_MASK_NO_EVENT, (char *)&ev);
}
return exist;
}

View File

@@ -12,3 +12,7 @@ extern xcb_key_symbols_t *key_symbols;
xcb_cursor_t get_xcb_cursor(cursor_t cursor); xcb_cursor_t get_xcb_cursor(cursor_t cursor);
void clean_xcb_cursor(void); void clean_xcb_cursor(void);
void init_icccm_extension(void);
void init_ewmh_extension(void);
void clean_ewmh_extension(void);

View File

@@ -15,7 +15,6 @@
#include "app.h" #include "app.h"
#include "utils.h" #include "utils.h"
#include "window.h"
#include "xcb-private.h" #include "xcb-private.h"
xcb_connection_t *conn = NULL; xcb_connection_t *conn = NULL;
@@ -170,58 +169,13 @@ void set_wallpaper(uint32_t *wallpaper_data) {
xcb_flush(conn); xcb_flush(conn);
} }
xcb_ewmh_connection_t *ewmh = NULL;
static bool ewmh_init_failed = false;
static xcb_window_t wm_check_window = XCB_NONE;
static void init_ewmh_extension(void) {
if (ewmh || ewmh_init_failed) return;
ewmh = ecalloc(1, sizeof(xcb_ewmh_connection_t));
xcb_intern_atom_cookie_t *cookie = xcb_ewmh_init_atoms(conn, ewmh);
if (!xcb_ewmh_init_atoms_replies(ewmh, cookie, NULL)) {
free(ewmh);
ewmh = NULL;
ewmh_init_failed = true;
return;
}
wm_check_window = xcb_generate_id(conn);
xcb_create_window(conn, XCB_COPY_FROM_PARENT, wm_check_window, screen->root,
0, 0, 1, 1, 0, XCB_WINDOW_CLASS_INPUT_ONLY,
screen->root_visual, XCB_NONE, NULL);
const char *class = APP_NAME "_wm_check";
set_window_class_instance(wm_check_window, class, class);
xcb_ewmh_set_wm_name(ewmh, wm_check_window, strlen(APP_NAME), APP_NAME);
xcb_ewmh_set_supporting_wm_check(ewmh, screen->root, wm_check_window);
xcb_ewmh_set_supporting_wm_check(ewmh, wm_check_window, wm_check_window);
uint8_t mode = XCB_PROP_MODE_REPLACE;
xcb_atom_t atom = XCB_ATOM_ATOM;
const xcb_atom_t state[] = {ewmh->_NET_WM_STATE_STICKY};
const xcb_atom_t supported[] = {
ewmh->_NET_ACTIVE_WINDOW,
ewmh->_NET_SUPPORTED,
ewmh->_NET_WM_NAME,
ewmh->_NET_WM_STATE,
ewmh->_NET_SUPPORTING_WM_CHECK,
ewmh->_NET_WM_STATE_FULLSCREEN,
ewmh->_NET_WM_WINDOW_TYPE,
ewmh->_NET_WM_WINDOW_TYPE_DIALOG,
ewmh->_NET_CLIENT_LIST,
};
xcb_change_property(conn, mode, wm_check_window, ewmh->_NET_WM_STATE, atom,
32, LENGTH(state), state);
xcb_change_property(conn, mode, screen->root, ewmh->_NET_SUPPORTED, atom, 32,
LENGTH(supported), supported);
xcb_delete_property(conn, screen->root, ewmh->_NET_CLIENT_LIST);
}
/** /**
* 扫描窗口列表 * 扫描窗口列表
* @returns 若无窗口,返回 NULL否则返回窗口列表 * @returns 若无窗口,返回 NULL否则返回窗口列表
* @note: 返回的窗口列表数据使用完后使用 free_window_list 释放 * @note: 返回的窗口列表数据使用完后使用 free_window_list 释放
*/ */
window_list_t *scan_window_list(void) { window_list_t *scan_window_list(void) {
init_icccm_extension();
init_ewmh_extension(); init_ewmh_extension();
xcb_query_tree_cookie_t cookie = xcb_query_tree(conn, screen->root); xcb_query_tree_cookie_t cookie = xcb_query_tree(conn, screen->root);
@@ -278,21 +232,7 @@ void free_window_list(window_list_t *window_list) {
void clean_xcb(void) { void clean_xcb(void) {
clean_xcb_cursor(); clean_xcb_cursor();
clean_ewmh_extension();
if (wm_check_window != XCB_NONE) {
xcb_destroy_window(conn, wm_check_window);
wm_check_window = XCB_NONE;
}
if (ewmh) {
if (!ewmh_init_failed) {
xcb_ewmh_connection_wipe(ewmh);
}
free(ewmh);
ewmh_init_failed = false;
ewmh = NULL;
}
xcb_disconnect(conn); xcb_disconnect(conn);
conn = NULL; conn = NULL;
screen = NULL; screen = NULL;
@@ -322,3 +262,18 @@ void grab_keybindings(const keybinding_t *keys, const size_t length) {
} }
} }
} }
bool get_pointer_position(int32_t *x, int32_t *y) {
xcb_query_pointer_cookie_t cookie = xcb_query_pointer(conn, screen->root);
xcb_query_pointer_reply_t *reply =
xcb_query_pointer_reply(conn, cookie, NULL);
if (reply) {
*x = reply->root_x;
*y = reply->root_y;
free(reply);
return true;
}
return false;
}