Compare commits
5 Commits
dfdc90e6c1
...
7eea0ee2d3
| Author | SHA1 | Date | |
|---|---|---|---|
|
7eea0ee2d3
|
|||
|
25ef44e5f9
|
|||
|
8ffa546152
|
|||
|
90b4513fb5
|
|||
|
505e2a5bcb
|
@@ -3,6 +3,9 @@ cmake_minimum_required(VERSION 3.24)
|
||||
set(APP_NAME "zswm")
|
||||
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
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ const layout_t layouts[] = {
|
||||
|
||||
/* 默认 tags 规则 */
|
||||
const rule_tag_t browser_tag[] = {
|
||||
{.monitor_amount = 1, .tags = 1 << 1},
|
||||
{.monitor_index = 0, .tags = 1 << 1},
|
||||
{0},
|
||||
};
|
||||
const rule_tag_t mpv_tag[] = {
|
||||
@@ -64,7 +64,7 @@ const rule_tag_t mpv_tag[] = {
|
||||
};
|
||||
const rule_tag_t emacs_tag[] = {
|
||||
{.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},
|
||||
};
|
||||
/* 新建窗口规则 */
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <xcb/xproto.h>
|
||||
|
||||
#include "types.h"
|
||||
|
||||
@@ -21,6 +22,24 @@ typedef enum event_type_t {
|
||||
EVENT_TYPE_CLIENT_MESSAGE,
|
||||
} 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 {
|
||||
event_type_t type;
|
||||
} mapping_notify_event_t;
|
||||
@@ -71,14 +90,24 @@ typedef struct expose_event_t {
|
||||
|
||||
typedef struct motion_notify_event_t {
|
||||
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;
|
||||
|
||||
typedef struct enter_notify_event_t {
|
||||
event_type_t type;
|
||||
xcb_window_t window;
|
||||
notify_mode_t mode;
|
||||
notify_detail_t detail;
|
||||
} enter_notify_event_t;
|
||||
|
||||
typedef struct focus_in_event_t {
|
||||
event_type_t type;
|
||||
xcb_window_t window;
|
||||
} focus_in_event_t;
|
||||
|
||||
typedef struct property_notify_event_t {
|
||||
|
||||
@@ -3,8 +3,11 @@
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "types.h"
|
||||
|
||||
#define LENGTH(X) (sizeof(X) / sizeof(X)[0])
|
||||
|
||||
#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);
|
||||
|
||||
#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数据
|
||||
*
|
||||
@@ -40,3 +51,6 @@ uint32_t rgba_to_argb(uint32_t rgba);
|
||||
*/
|
||||
void extract_color_channel(const uint32_t rgba, double *red, double *green,
|
||||
double *blue, double *alpha);
|
||||
|
||||
monitor_t *rect_to_monitor(monitor_t *monitors, int32_t x, int32_t y,
|
||||
uint32_t width, uint32_t height);
|
||||
|
||||
@@ -48,6 +48,7 @@ void map_window(xcb_window_t window);
|
||||
char *get_window_name(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 focus_window(xcb_window_t window);
|
||||
|
||||
typedef struct bar_cairo_params_t {
|
||||
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);
|
||||
void get_xresource_string(const char *name, char **value, const char *fallback);
|
||||
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);
|
||||
|
||||
@@ -18,16 +18,16 @@ void maximize_client(client_t *c) {
|
||||
monitor_t *m = c->monitor;
|
||||
c->x = m->window_x;
|
||||
c->y = m->window_y;
|
||||
c->width = m->window_width;
|
||||
c->height = m->window_height;
|
||||
c->width = m->window_width - 2 * c->border_width;
|
||||
c->height = m->window_height - 2 * c->border_width;
|
||||
}
|
||||
|
||||
void fullscreen_client(client_t *c) {
|
||||
monitor_t *m = c->monitor;
|
||||
c->x = m->monitor_x;
|
||||
c->y = m->monitor_y;
|
||||
c->width = m->monitor_width;
|
||||
c->height = m->monitor_height;
|
||||
c->width = m->monitor_width - 2 * c->border_width;
|
||||
c->height = m->monitor_height - 2 * c->border_width;
|
||||
}
|
||||
|
||||
void monocle(monitor_t *monitor) {
|
||||
|
||||
182
src/main.c
182
src/main.c
@@ -24,7 +24,7 @@ static void init_monitors(void);
|
||||
static char **get_monitor_tags(uint32_t monitor_count, uint32_t monitor_index);
|
||||
static void init_wallpaper(void);
|
||||
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 void free_wallpaper_config(wallpaper_config_t *wallpaper_config);
|
||||
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 map_request(map_request_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 unmanage_client(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 restack(monitor_t *monitor);
|
||||
static void focus(client_t *client);
|
||||
static void unfocus(client_t *client, bool set_focus);
|
||||
static void apply_monitor_layout(monitor_t *monitor);
|
||||
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,
|
||||
gpointer userdata);
|
||||
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) {
|
||||
printf("========================= monitor info =========================\n");
|
||||
logger("========================= monitor info =========================\n");
|
||||
for (monitor_t *m = monitor_list; m; m = m->next) {
|
||||
printf("========== mon[%u]:\n", m->index);
|
||||
printf("x: %d, y: %d, width: %u, height: %u\n", m->monitor_x, m->monitor_y,
|
||||
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);
|
||||
printf("mon[%u] tags: ", m->index);
|
||||
logger("mon[%u] tags: ", m->index);
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
if (!config) {
|
||||
printf("no wallpaper\n");
|
||||
logger("no wallpaper\n");
|
||||
return;
|
||||
}
|
||||
|
||||
printf("wallpaper count: %u\n", config->count);
|
||||
logger("wallpaper count: %u\n", config->count);
|
||||
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) {
|
||||
if (!window_list) {
|
||||
printf("no window\n");
|
||||
logger("no window\n");
|
||||
return;
|
||||
}
|
||||
|
||||
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 void print_client(client_t *c) {
|
||||
if (!c) return;
|
||||
|
||||
printf("======= client info ========\n");
|
||||
printf("window: 0x%x, x: %d, y: %d, w: %u, h: %u, bw: %u\n", c->window, c->x,
|
||||
logger("======= client info ========\n");
|
||||
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);
|
||||
printf("floating: %s, fullscreen: %s, maximize: %s\n",
|
||||
logger("floating: %s, fullscreen: %s, maximize: %s\n",
|
||||
bool2string(c->floating), bool2string(c->fullscreen),
|
||||
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);
|
||||
printf("name: %s\n", c->name);
|
||||
printf("===== client info end ======\n");
|
||||
logger("name: %s\n", c->name);
|
||||
logger("===== client info end ======\n");
|
||||
}
|
||||
static void print_rule(const rule_t *r) {
|
||||
printf("==================== rule ====================\n");
|
||||
printf("class: %s, instance: %s, title: %s\n", r->class, r->instance,
|
||||
logger("==================== rule ====================\n");
|
||||
logger("class: %s, instance: %s, title: %s\n", r->class, r->instance,
|
||||
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.fullscreen),
|
||||
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];
|
||||
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);
|
||||
}
|
||||
printf("================== rule end ==================\n");
|
||||
logger("================== rule end ==================\n");
|
||||
}
|
||||
/* 调试函数结束 */
|
||||
|
||||
@@ -250,7 +255,7 @@ gboolean update_wallpaper(gpointer user_data) {
|
||||
if (!wc) return false;
|
||||
|
||||
static uint32_t index = 0;
|
||||
set_wallpaper_by_index(index);
|
||||
set_wallpaper_by_index(index, wc);
|
||||
uint32_t monitor_count = 0;
|
||||
for (monitor_t *m = monitors; m; m = m->next) monitor_count++;
|
||||
|
||||
@@ -265,19 +270,19 @@ gboolean update_wallpaper(gpointer user_data) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void set_wallpaper_by_index(uint32_t index) {
|
||||
if (!wallpaper_config) return;
|
||||
void set_wallpaper_by_index(uint32_t index, wallpaper_config_t *config) {
|
||||
if (!config) return;
|
||||
|
||||
rect_size_t *screen_size = get_screen_size();
|
||||
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) {
|
||||
double start = get_time();
|
||||
set_wallpaper(wallpaper);
|
||||
double end = get_time();
|
||||
free(wallpaper);
|
||||
printf("set wallpaper cost: %fs\n", end - start);
|
||||
logger("set wallpaper cost: %fs\n", end - start);
|
||||
}
|
||||
|
||||
free(screen_size);
|
||||
@@ -411,7 +416,7 @@ void init_monitor_bar(void) {
|
||||
|
||||
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) {
|
||||
@@ -422,7 +427,7 @@ void get_xresource_config(void) {
|
||||
get_xresource_uint32("zswm.tag_x_padding", &tag_x_pad);
|
||||
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);
|
||||
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_MAP_REQUEST, map_request);
|
||||
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
|
||||
|
||||
@@ -484,7 +492,7 @@ void configure_request(configure_request_event_t *event) {
|
||||
} else {
|
||||
configure_window(client->window, client->x, client->y, client->width,
|
||||
client->height, client->border_width);
|
||||
printf("configure request: %s\n", client->name);
|
||||
logger("configure request: %s\n", client->name);
|
||||
}
|
||||
|
||||
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) {
|
||||
window_geometry_t *geometry = get_window_geometry(window);
|
||||
client_t *c = ecalloc(1, sizeof(client_t));
|
||||
@@ -517,8 +567,8 @@ void manage_window(xcb_window_t window) {
|
||||
c->border_width = border_px;
|
||||
update_client_name(c);
|
||||
|
||||
printf("== manage window: %u\n", window);
|
||||
printf("== x: %d, y: %d, w: %u, h: %u, bw: %u\n", c->x, c->y, c->width,
|
||||
logger("== manage window: %u\n", window);
|
||||
logger("== x: %d, y: %d, w: %u, h: %u, bw: %u\n", c->x, c->y, c->width,
|
||||
c->height, c->border_width);
|
||||
|
||||
xcb_window_t tw = get_transient_window_for(window);
|
||||
@@ -535,9 +585,12 @@ void manage_window(xcb_window_t window) {
|
||||
attach_stack_client(c);
|
||||
init_window_event_mask(window);
|
||||
change_window_border_color(window, color_set->border_color.argb);
|
||||
if (c->monitor == current_monitor) {
|
||||
unfocus(current_monitor->selected_client, false);
|
||||
}
|
||||
arrange(c->monitor);
|
||||
map_window(window);
|
||||
draw_all_monitor_bars();
|
||||
focus(NULL);
|
||||
}
|
||||
|
||||
void unmanage_client(client_t *client) {
|
||||
@@ -583,11 +636,10 @@ void apply_rules(client_t *client) {
|
||||
const rule_tag_t *tag = NULL;
|
||||
for (uint32_t i = 0;; 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;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (tag) {
|
||||
@@ -597,6 +649,7 @@ void apply_rules(client_t *client) {
|
||||
|
||||
uint32_t tags_mask = get_tags_mask_of_monitor(m);
|
||||
client->tags = tag->tags & tags_mask;
|
||||
client->monitor = m;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -610,7 +663,7 @@ void apply_rules(client_t *client) {
|
||||
}
|
||||
|
||||
print_client(client);
|
||||
printf("apply rules done\n");
|
||||
logger("apply rules done\n");
|
||||
}
|
||||
|
||||
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 focus(client_t *client) {
|
||||
void restack(monitor_t *monitor) {
|
||||
draw_monitor_bar(monitor);
|
||||
/* 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;
|
||||
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) {
|
||||
if (!client) return;
|
||||
@@ -715,6 +795,22 @@ client_t *get_client_of_window(xcb_window_t window) {
|
||||
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,
|
||||
gpointer userdata) {
|
||||
if (condition & (G_IO_HUP | G_IO_ERR)) {
|
||||
|
||||
30
src/utils.c
30
src/utils.c
@@ -44,6 +44,17 @@ bool file_exist(const char *path) {
|
||||
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) {
|
||||
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);
|
||||
*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;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
set(LIB_XCB "zswm-xcb")
|
||||
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
|
||||
PRIVATE ${XCB_INCLUDE_DIRS}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <xcb/xcb.h>
|
||||
#include <xcb/xcb_event.h>
|
||||
@@ -19,7 +18,7 @@ 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);
|
||||
printf("=========================== %s ===========================\n", label);
|
||||
logger("=========================== %s ===========================\n", label);
|
||||
switch (event_type) {
|
||||
case XCB_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:
|
||||
event->type = EVENT_TYPE_EXPOSE;
|
||||
break;
|
||||
case XCB_MOTION_NOTIFY:
|
||||
event->type = EVENT_TYPE_MOTION_NOTIFY;
|
||||
case XCB_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;
|
||||
case XCB_ENTER_NOTIFY:
|
||||
event->type = EVENT_TYPE_ENTER_NOTIFY;
|
||||
break;
|
||||
case XCB_FOCUS_IN:
|
||||
event->type = EVENT_TYPE_FOCUS_IN;
|
||||
}
|
||||
case XCB_ENTER_NOTIFY: {
|
||||
xcb_enter_notify_event_t *ev = (xcb_enter_notify_event_t *)xcb_event;
|
||||
event->enter_notify.type = EVENT_TYPE_ENTER_NOTIFY;
|
||||
event->enter_notify.window = ev->event;
|
||||
event->enter_notify.mode = ev->mode;
|
||||
event->enter_notify.detail = ev->detail;
|
||||
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:
|
||||
event->type = EVENT_TYPE_PROPERTY_NOTIFY;
|
||||
break;
|
||||
@@ -89,6 +103,7 @@ static void convert_event(xcb_generic_event_t *xcb_event,
|
||||
event->type = EVENT_TYPE_CLIENT_MESSAGE;
|
||||
break;
|
||||
default:
|
||||
event->type = -1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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_aux_change_window_attributes(conn, window, XCB_CW_BORDER_PIXEL, ¶ms);
|
||||
}
|
||||
|
||||
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
127
src/xcb/wm-extension.c
Normal 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;
|
||||
}
|
||||
@@ -12,3 +12,7 @@ extern xcb_key_symbols_t *key_symbols;
|
||||
|
||||
xcb_cursor_t get_xcb_cursor(cursor_t cursor);
|
||||
void clean_xcb_cursor(void);
|
||||
|
||||
void init_icccm_extension(void);
|
||||
void init_ewmh_extension(void);
|
||||
void clean_ewmh_extension(void);
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
|
||||
#include "app.h"
|
||||
#include "utils.h"
|
||||
#include "window.h"
|
||||
#include "xcb-private.h"
|
||||
|
||||
xcb_connection_t *conn = NULL;
|
||||
@@ -170,58 +169,13 @@ void set_wallpaper(uint32_t *wallpaper_data) {
|
||||
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,否则返回窗口列表
|
||||
* @note: 返回的窗口列表数据使用完后使用 free_window_list 释放
|
||||
*/
|
||||
window_list_t *scan_window_list(void) {
|
||||
init_icccm_extension();
|
||||
init_ewmh_extension();
|
||||
|
||||
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) {
|
||||
clean_xcb_cursor();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
clean_ewmh_extension();
|
||||
xcb_disconnect(conn);
|
||||
conn = 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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user