Compare commits

...

4 Commits

Author SHA1 Message Date
8f892633cc 处理 property_notify 事件 2025-08-24 12:44:15 +08:00
165dd0733c 处理 expose 事件 2025-08-24 11:44:56 +08:00
0039cd2c43 处理窗口销毁事件 2025-08-24 11:35:52 +08:00
bac73368e3 启动时扫描已有窗口并管理 2025-08-24 10:01:46 +08:00
7 changed files with 231 additions and 57 deletions

View File

@@ -1,7 +1,7 @@
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include <xcb/xproto.h>
#include "types.h"
@@ -78,14 +78,18 @@ typedef struct map_request_event_t {
typedef struct unmap_notify_event_t {
event_type_t type;
xcb_window_t window;
bool send_event;
} unmap_notify_event_t;
typedef struct destroy_notify_event_t {
event_type_t type;
xcb_window_t window;
} destroy_notify_event_t;
typedef struct expose_event_t {
event_type_t type;
xcb_window_t window;
uint32_t count;
} expose_event_t;
typedef struct motion_notify_event_t {
@@ -110,8 +114,15 @@ typedef struct focus_in_event_t {
xcb_window_t window;
} focus_in_event_t;
typedef enum property_state_t {
property_new_value = 0,
property_delete = 1
} property_state_t;
typedef struct property_notify_event_t {
event_type_t type;
xcb_window_t window;
uint32_t xcb_atom;
property_state_t state;
} property_notify_event_t;
typedef struct client_message_event_t {

View File

@@ -46,7 +46,7 @@ void map_window(xcb_window_t window);
* @returns 返回窗口名字符串指针,使用完后记得使用 free 释放内存
*/
char *get_window_name(xcb_window_t window);
void init_window_event_mask(xcb_window_t window);
void set_window_event_mask(xcb_window_t window, bool clear);
void change_window_border_color(xcb_window_t window, uint32_t argb);
void focus_window(xcb_window_t window);
@@ -84,8 +84,10 @@ rect_size_t *get_screen_size(void);
void set_wallpaper(uint32_t *wallpaper_data);
typedef struct window_list_t {
uint32_t count;
xcb_window_t *list;
xcb_window_t *normal_list;
xcb_window_t *transient_list;
uint32_t normal_count;
uint32_t transient_count;
} window_list_t;
window_list_t *scan_window_list(void);
void free_window_list(window_list_t *window_list);
@@ -101,7 +103,17 @@ typedef enum atom_t {
atom_wm_protocols,
atom_wm_delete,
atom_wm_take_focus,
atom_net_wm_name,
} atom_t;
xcb_atom_t get_xcb_atom(atom_t atom);
bool send_event(xcb_window_t window, atom_t atom);
typedef enum wm_state_t {
wm_state_withdrawn = 0,
wm_state_normal = 1,
wm_state_iconic = 3
} wm_state_t;
int32_t get_window_state(xcb_window_t window);
void set_window_state(xcb_window_t window, wm_state_t state);
void set_window_list(xcb_window_t *list, uint32_t length);
bool get_pointer_position(int32_t *x, int32_t *y);

View File

@@ -10,6 +10,7 @@
#include <string.h>
#include <unistd.h>
#include <wordexp.h>
#include <xcb/xproto.h>
#include "action.h"
#include "config.h"
@@ -39,11 +40,15 @@ 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 destroy_notify(destroy_notify_event_t *event);
static void expose(expose_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 property_notify(property_notify_event_t *event);
static void manage_window(xcb_window_t window);
static void unmanage_client(client_t *client);
static void unmanage_client(client_t *client, bool destroyed);
static void update_client_list(void);
static void update_client_name(client_t *client);
static void apply_rules(client_t *client);
static uint32_t get_tags_mask_of_monitor(monitor_t *monitor);
@@ -146,8 +151,11 @@ static void print_window_list(window_list_t *window_list) {
return;
}
for (uint32_t i = 0; i < window_list->count; i++) {
logger("window[%u]: 0x%x\n", i, window_list->list[i]);
for (uint32_t i = 0; i < window_list->normal_count; i++) {
logger("normal window[%u]: 0x%x\n", i, window_list->normal_list[i]);
}
for (uint32_t i = 0; i < window_list->transient_count; i++) {
logger("transient window[%u]: 0x%x\n", i, window_list->transient_list[i]);
}
}
static char *bool2string(bool b) { return b ? "true" : "false"; }
@@ -462,9 +470,12 @@ 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_DESTROY_NOTIFY, destroy_notify);
EVENT(EVENT_TYPE_EXPOSE, expose);
EVENT(EVENT_TYPE_MOTION_NOTIFY, motion_notify);
EVENT(EVENT_TYPE_ENTER_NOTIFY, enter_notify);
EVENT(EVENT_TYPE_FOCUS_IN, focus_in);
EVENT(EVENT_TYPE_PROPERTY_NOTIFY, property_notify);
#undef EVENT
@@ -509,7 +520,23 @@ void map_request(map_request_event_t *event) {
void unmap_notify(unmap_notify_event_t *event) {
client_t *c = get_client_of_window(event->window);
if (c) {
unmanage_client(c);
if (event->send_event) {
set_window_state(event->window, wm_state_withdrawn);
} else {
unmanage_client(c, false);
}
}
}
void destroy_notify(destroy_notify_event_t *event) {
client_t *c = get_client_of_window(event->window);
if (c) unmanage_client(c, true);
}
void expose(expose_event_t *event) {
monitor_t *m = NULL;
if (event->count == 0 && (m = get_monitor_of_window(event->window))) {
draw_monitor_bar(m);
}
}
@@ -555,6 +582,32 @@ void focus_in(focus_in_event_t *event) {
}
}
void property_notify(property_notify_event_t *event) {
if (event->state == property_delete) return;
client_t *c = get_client_of_window(event->window);
if (!c) return;
switch (event->xcb_atom) {
case XCB_ATOM_WM_TRANSIENT_FOR: {
xcb_window_t transient_for;
if (!c->floating &&
((transient_for = get_transient_window_for(event->window)) !=
WINDOW_NONE) &&
(c->floating = (get_client_of_window(transient_for) != NULL))) {
arrange(c->monitor);
}
break;
}
default:
break;
}
if (event->xcb_atom == XCB_ATOM_WM_NAME ||
event->xcb_atom == get_xcb_atom(atom_net_wm_name)) {
update_client_name(c);
if (c->tags & c->monitor->selected_tags) draw_monitor_bar(c->monitor);
}
}
void manage_window(xcb_window_t window) {
window_geometry_t *geometry = get_window_geometry(window);
client_t *c = ecalloc(1, sizeof(client_t));
@@ -583,7 +636,7 @@ void manage_window(xcb_window_t window) {
attach_client(c);
attach_stack_client(c);
init_window_event_mask(window);
set_window_event_mask(window, false);
change_window_border_color(window, color_set->border_color.argb);
if (c->monitor == current_monitor) {
unfocus(current_monitor->selected_client, false);
@@ -593,14 +646,41 @@ void manage_window(xcb_window_t window) {
focus(NULL);
}
void unmanage_client(client_t *client) {
void unmanage_client(client_t *client, bool destroyed) {
monitor_t *monitor = client->monitor;
detach_client(client);
detach_stack_client(client);
if (!destroyed) {
set_window_event_mask(client->window, true);
configure_window(client->window, client->x, client->y, client->width,
client->height, client->old_border_width);
set_window_state(client->window, wm_state_withdrawn);
flush_xcb_connection();
}
free(client);
focus(NULL);
update_client_list();
arrange(monitor);
flush_xcb_connection();
}
void update_client_list(void) {
uint32_t length = 0;
for (monitor_t *m = monitors; m; m = m->next) {
for (client_t *c = m->clients; c; c = c->next) length++;
}
if (!length) {
set_window_list(NULL, length);
return;
}
xcb_window_t *list = ecalloc(length, sizeof(xcb_window_t));
uint32_t i = 0;
for (monitor_t *m = monitors; m; m = m->next) {
for (client_t *c = m->clients; c; c = c->next) list[i++] = c->window;
}
set_window_list(list, length);
free(list);
}
void update_client_name(client_t *client) {
@@ -823,23 +903,12 @@ gboolean handle_xcb_event(GIOChannel *channel, GIOCondition condition,
}
void cleanup(void) {
clean_pango_context();
free_wallpaper_config(wallpaper_config);
wallpaper_config = NULL;
free(color_set);
color_set = NULL;
free(font_family);
font_family = NULL;
monitor_t *m = monitors;
client_t *c = m->clients;
while (m) {
while (c) {
client_t *tc = c->next;
free(c);
unmanage_client(c, false);
c = tc;
}
monitor_t *next_monitor = m->next;
@@ -851,6 +920,17 @@ void cleanup(void) {
}
monitors = NULL;
clean_pango_context();
free_wallpaper_config(wallpaper_config);
wallpaper_config = NULL;
free(color_set);
color_set = NULL;
free(font_family);
font_family = NULL;
destroy_event_adapter(adapter);
adapter = NULL;
@@ -877,6 +957,15 @@ int main(int argc, char *argv[]) {
print_wallpaper_config(wallpaper_config);
print_window_list(window_list);
if (window_list) {
for (uint32_t i = 0; i < window_list->normal_count; i++) {
manage_window(window_list->normal_list[i]);
}
for (uint32_t i = 0; i < window_list->transient_count; i++) {
manage_window(window_list->transient_list[i]);
}
}
free_window_list(window_list);
grab_keybindings(keys, LENGTH(keys));

View File

@@ -65,13 +65,21 @@ static void convert_event(xcb_generic_event_t *xcb_event,
xcb_unmap_notify_event_t *ev = (xcb_unmap_notify_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;
case XCB_DESTROY_NOTIFY:
event->type = EVENT_TYPE_DESTROY_NOTIFY;
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;
event->destroy_notify.window = ev->window;
break;
case XCB_EXPOSE:
event->type = EVENT_TYPE_EXPOSE;
}
case XCB_EXPOSE: {
xcb_expose_event_t *ev = (xcb_expose_event_t *)xcb_event;
event->expose.type = EVENT_TYPE_EXPOSE;
event->expose.window = ev->window;
event->expose.count = ev->count;
break;
}
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;
@@ -96,9 +104,15 @@ static void convert_event(xcb_generic_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;
case XCB_PROPERTY_NOTIFY: {
xcb_property_notify_event_t *ev =
(xcb_property_notify_event_t *)xcb_event;
event->property_notify.type = EVENT_TYPE_PROPERTY_NOTIFY;
event->property_notify.window = ev->window;
event->property_notify.xcb_atom = ev->atom;
event->property_notify.state = ev->state;
break;
}
case XCB_CLIENT_MESSAGE:
event->type = EVENT_TYPE_CLIENT_MESSAGE;
break;

View File

@@ -218,11 +218,14 @@ bool get_window_visible(xcb_window_t window) {
}
xcb_window_t get_transient_window_for(xcb_window_t window) {
xcb_window_t transient_for = XCB_NONE;
xcb_window_t transient_for = WINDOW_NONE;
xcb_get_property_cookie_t cookie =
xcb_icccm_get_wm_transient_for(conn, window);
xcb_icccm_get_wm_transient_for_reply(conn, cookie, &transient_for, NULL);
return transient_for;
if (xcb_icccm_get_wm_transient_for_reply(conn, cookie, &transient_for,
NULL)) {
return transient_for;
}
return WINDOW_NONE;
}
window_geometry_t *get_window_geometry(xcb_window_t window) {
@@ -271,13 +274,14 @@ char *get_window_name(xcb_window_t window) {
}
}
void init_window_event_mask(xcb_window_t window) {
void set_window_event_mask(xcb_window_t window, bool clear) {
xcb_cw_t change_mask = XCB_CW_EVENT_MASK;
xcb_params_cw_t params = {
.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
: XCB_EVENT_MASK_ENTER_WINDOW |
XCB_EVENT_MASK_FOCUS_CHANGE |
XCB_EVENT_MASK_PROPERTY_CHANGE |
XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY;
xcb_params_cw_t params = {.event_mask = event_mask};
xcb_aux_change_window_attributes(conn, window, change_mask, &params);
}

View File

@@ -2,6 +2,7 @@
#include <stdint.h>
#include <string.h>
#include <xcb/xcb.h>
#include <xcb/xcb_ewmh.h>
#include <xcb/xcb_icccm.h>
#include <xcb/xproto.h>
@@ -24,6 +25,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_wm_name, .name = "_NET_WM_NAME"},
};
void init_icccm_extension(void) {
@@ -96,6 +98,11 @@ void clean_ewmh_extension(void) {
}
}
xcb_atom_t get_xcb_atom(atom_t atom) {
if (atom < 0 || atom >= LENGTH(atom_map)) return XCB_ATOM_NONE;
return atom_map[atom].xcb_atom;
}
bool send_event(xcb_window_t window, atom_t atom) {
if (atom < 0 || atom >= LENGTH(atom_map)) return false;
@@ -125,3 +132,38 @@ bool send_event(xcb_window_t window, atom_t atom) {
return exist;
}
int32_t get_window_state(xcb_window_t window) {
xcb_icccm_wm_hints_t hints;
xcb_get_property_cookie_t cookie = xcb_icccm_get_wm_hints(conn, window);
if (!xcb_icccm_get_wm_hints_reply(conn, cookie, &hints, NULL)) return -1;
return hints.initial_state;
}
void set_window_state(xcb_window_t window, wm_state_t state) {
xcb_icccm_wm_hints_t hints;
xcb_icccm_wm_hints_set_none(&hints);
switch (state) {
case wm_state_withdrawn:
xcb_icccm_wm_hints_set_withdrawn(&hints);
break;
case wm_state_normal:
xcb_icccm_wm_hints_set_normal(&hints);
break;
case wm_state_iconic:
xcb_icccm_wm_hints_set_iconic(&hints);
break;
}
xcb_icccm_set_wm_hints(conn, window, &hints);
}
void set_window_list(xcb_window_t *list, uint32_t length) {
int screen_nbr = 0;
for (int i = 0; i < ewmh->nb_screens; i++) {
if (screen == ewmh->screens[i]) {
screen_nbr = i;
break;
}
}
xcb_ewmh_set_client_list(ewmh, screen_nbr, length, list);
}

View File

@@ -184,9 +184,11 @@ window_list_t *scan_window_list(void) {
xcb_window_t *list = xcb_query_tree_children(tree_reply);
int len = xcb_query_tree_children_length(tree_reply);
if (!len) return NULL;
uint32_t count = 0;
xcb_window_t temp_window_array[len];
uint32_t normal_count = 0, transient_count = 0;
xcb_window_t *normal_window_array = ecalloc(len, sizeof(xcb_window_t));
xcb_window_t *transient_window_array = ecalloc(len, sizeof(xcb_window_t));
for (int i = 0; i < len; i++) {
xcb_window_t window = list[i];
@@ -199,34 +201,34 @@ window_list_t *scan_window_list(void) {
uint8_t override_redirect = attribute_reply->override_redirect;
uint8_t map_state = attribute_reply->map_state;
free(attribute_reply);
if (override_redirect || map_state != XCB_MAP_STATE_VIEWABLE) continue;
if (!(map_state == XCB_MAP_STATE_VIEWABLE ||
get_window_state(window) == wm_state_iconic)) {
continue;
}
xcb_window_t transient_for = XCB_NONE;
xcb_get_property_cookie_t p_cookie =
xcb_icccm_get_wm_transient_for(conn, window);
xcb_icccm_get_wm_transient_for_reply(conn, p_cookie, &transient_for, NULL);
if (transient_for != XCB_NONE) continue;
temp_window_array[count] = window;
count++;
xcb_window_t transient_for = get_transient_window_for(window);
if (transient_for) {
transient_window_array[transient_count++] = window;
} else if (!override_redirect) {
normal_window_array[normal_count++] = window;
}
}
free(tree_reply);
if (!count) return NULL;
xcb_window_t *window_list = ecalloc(count, sizeof(xcb_window_t));
for (uint32_t i = 0; i < count; i++) window_list[i] = temp_window_array[i];
window_list_t *r = ecalloc(1, sizeof(window_list_t));
r->count = count;
r->list = window_list;
r->normal_list = normal_window_array;
r->transient_list = transient_window_array;
r->normal_count = normal_count;
r->transient_count = transient_count;
return r;
}
void free_window_list(window_list_t *window_list) {
if (window_list == NULL) return;
free(window_list->list);
if (window_list->normal_list) free(window_list->normal_list);
if (window_list->transient_list) free(window_list->transient_list);
free(window_list);
}