Files
zswm/src/xcb/xcb.c

344 lines
10 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "xcb.h"
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <xcb/xcb.h>
#include <xcb/xcb_aux.h>
#include <xcb/xcb_event.h>
#include <xcb/xcb_ewmh.h>
#include <xcb/xcb_icccm.h>
#include <xcb/xcb_image.h>
#include <xcb/xinerama.h>
#include <xcb/xproto.h>
#include "app.h"
#include "utils.h"
#include "window.h"
#include "xcb-private.h"
xcb_connection_t *conn = NULL;
xcb_screen_t *screen = NULL;
xcb_key_symbols_t *key_symbols = NULL;
bool has_other_wm_running(void) {
conn = xcb_connect(NULL, NULL);
if (!conn || xcb_connection_has_error(conn)) {
die("connection:");
}
const xcb_setup_t *setup = xcb_get_setup(conn);
xcb_screen_iterator_t iter = xcb_setup_roots_iterator(setup);
screen = iter.data;
uint32_t mask = XCB_CW_EVENT_MASK;
xcb_params_cw_t params = {.event_mask = XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT};
xcb_void_cookie_t cookie =
xcb_aux_change_window_attributes_checked(conn, screen->root, mask, &params);
if (xcb_request_check(conn, cookie)) {
xcb_disconnect(conn);
return true;
}
return false;
}
void setup_xcb(void) {
uint32_t event_mask =
XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY |
XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_POINTER_MOTION |
XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_LEAVE_WINDOW |
XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_PROPERTY_CHANGE;
uint32_t mask = XCB_CW_EVENT_MASK;
xcb_params_cw_t params = {.event_mask = event_mask};
xcb_aux_change_window_attributes(conn, screen->root, mask, &params);
init_icccm_extension();
init_ewmh_extension();
}
/**
* @brief 获取 xcb 链接对应的文件描述符
*/
int get_xcb_connection_fd(void) { return xcb_get_file_descriptor(conn); }
void flush_xcb_connection(void) { xcb_flush(conn); }
/* xinerama 扩展是否激活 */
static bool xinerama_active(void) {
const char *name = "XINERAMA";
xcb_query_extension_cookie_t xinerama_cookie =
xcb_query_extension(conn, strlen(name), name);
xcb_query_extension_reply_t *xinerama_reply =
xcb_query_extension_reply(conn, xinerama_cookie, NULL);
/* 扩展不存在 */
if (!xinerama_reply || !xinerama_reply->present) {
free(xinerama_reply);
return false;
}
xcb_xinerama_is_active_cookie_t active_cookie = xcb_xinerama_is_active(conn);
xcb_xinerama_is_active_reply_t *active_reply =
xcb_xinerama_is_active_reply(conn, active_cookie, NULL);
bool active = active_reply && active_reply->state;
free(xinerama_reply);
free(active_reply);
return active;
}
static monitor_rect_t *detect_monitor_rect_by_xinerama(void) {
monitor_rect_t *list, *prev;
xcb_xinerama_query_screens_cookie_t cookie = xcb_xinerama_query_screens(conn);
xcb_xinerama_query_screens_reply_t *reply =
xcb_xinerama_query_screens_reply(conn, cookie, NULL);
xcb_xinerama_screen_info_t *screen_info =
xcb_xinerama_query_screens_screen_info(reply);
if (screen_info == NULL) {
die("no monitor finded");
}
int count = xcb_xinerama_query_screens_screen_info_length(reply);
for (int i = 0; i < count; i++) {
monitor_rect_t *m = ecalloc(1, sizeof(monitor_rect_t));
xcb_xinerama_screen_info_t info = screen_info[i];
m->x = info.x_org;
m->y = info.y_org;
m->width = info.width;
m->height = info.height;
m->next = NULL;
if (i == 0) {
list = m;
} else {
prev->next = m;
}
prev = m;
}
free(reply);
return list;
}
/**
* 探测屏幕信息
* @note: 返回值使用完后记得释放其内存
*/
monitor_rect_t *detect_monitor_rect(void) {
if (xinerama_active()) {
return detect_monitor_rect_by_xinerama();
}
if (!screen) return NULL;
monitor_rect_t *rect = ecalloc(1, sizeof(monitor_rect_t));
rect->x = 0;
rect->y = 0;
rect->width = screen->width_in_pixels;
rect->height = screen->height_in_pixels;
return rect;
}
/**
* @brief 获取整个屏幕的宽高
* @returns 屏幕大小数据的指针,使用完后记得释放对应内存
*/
rect_size_t *get_screen_size(void) {
rect_size_t *size = ecalloc(1, sizeof(rect_size_t));
size->width = screen->width_in_pixels;
size->height = screen->height_in_pixels;
return size;
}
bar_cairo_params_t *prepare_wallpaper_surface_params(void) {
uint32_t width = screen->width_in_pixels;
uint32_t height = screen->height_in_pixels;
xcb_window_t root = screen->root;
visual_t *v = get_xcb_visual(false);
static xcb_pixmap_t pixmap = XCB_PIXMAP_NONE;
if (pixmap != XCB_PIXMAP_NONE) {
xcb_kill_client(conn, pixmap);
xcb_free_pixmap(conn, pixmap);
}
pixmap = xcb_generate_id(conn);
xcb_create_pixmap(conn, v->depth, pixmap, root, width, height);
xcb_gcontext_t gc = xcb_generate_id(conn);
xcb_create_gc(conn, gc, pixmap, 0, NULL);
xcb_change_window_attributes_value_list_t value_list = {
.background_pixmap = pixmap,
};
xcb_change_window_attributes_aux(conn, root, XCB_CW_BACK_PIXMAP, &value_list);
/* 兼容合成器,不设置若开启合成器壁纸会不显示 */
uint8_t mode = XCB_PROP_MODE_REPLACE;
xcb_atom_t atom = get_xcb_atom(atom__xrootpmap_id);
xcb_change_property(conn, mode, root, atom, XCB_ATOM_PIXMAP, 32, 1, &pixmap);
atom = get_xcb_atom(atom_esetroot_pmap_id);
xcb_change_property(conn, mode, root, atom, XCB_ATOM_PIXMAP, 32, 1, &pixmap);
xcb_clear_area(conn, 0, root, 0, 0, width, height);
xcb_free_gc(conn, gc);
xcb_flush(conn);
bar_cairo_params_t *p = ecalloc(1, sizeof(bar_cairo_params_t));
p->conn = conn;
p->visual = v->visual;
p->window = pixmap;
free(v);
return p;
}
void refresh_root_window(void) {
xcb_clear_area(conn, 0, screen->root, 0, 0, 0, 0);
xcb_flush(conn);
}
void set_wallpaper(uint32_t *wallpaper_data) {
uint32_t width = screen->width_in_pixels;
uint32_t height = screen->height_in_pixels;
uint8_t depth = screen->root_depth;
xcb_window_t root = screen->root;
uint32_t data_size = width * height * sizeof(uint32_t);
uint8_t *data = (uint8_t *)wallpaper_data;
xcb_image_t *xcb_img =
xcb_image_create_native(conn, width, height, XCB_IMAGE_FORMAT_Z_PIXMAP,
depth, NULL, data_size, data);
xcb_pixmap_t pixmap = xcb_generate_id(conn);
xcb_create_pixmap(conn, depth, pixmap, root, width, height);
xcb_gcontext_t gc = xcb_generate_id(conn);
xcb_create_gc(conn, gc, pixmap, 0, NULL);
xcb_image_put(conn, pixmap, gc, xcb_img, 0, 0, 0);
xcb_change_window_attributes_value_list_t value_list = {
.background_pixmap = pixmap,
};
xcb_change_window_attributes_aux(conn, root, XCB_CW_BACK_PIXMAP, &value_list);
xcb_clear_area(conn, 0, root, 0, 0, width, height);
xcb_image_destroy(xcb_img);
xcb_free_gc(conn, gc);
xcb_flush(conn);
}
/**
* 扫描窗口列表
* @returns 若无窗口,返回 NULL否则返回窗口列表
* @note: 返回的窗口列表数据使用完后使用 free_window_list 释放
*/
window_list_t *scan_window_list(void) {
xcb_query_tree_cookie_t cookie = xcb_query_tree(conn, screen->root);
xcb_query_tree_reply_t *tree_reply = xcb_query_tree_reply(conn, cookie, NULL);
if (!tree_reply) return NULL;
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 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];
xcb_get_window_attributes_cookie_t wa_cookie =
xcb_get_window_attributes(conn, window);
xcb_get_window_attributes_reply_t *attribute_reply =
xcb_get_window_attributes_reply(conn, wa_cookie, NULL);
if (!attribute_reply) continue;
uint8_t override_redirect = attribute_reply->override_redirect;
uint8_t map_state = attribute_reply->map_state;
free(attribute_reply);
if (!(map_state == XCB_MAP_STATE_VIEWABLE ||
get_window_state(window) == wm_state_iconic)) {
continue;
}
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);
window_list_t *r = ecalloc(1, sizeof(window_list_t));
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;
if (window_list->normal_list) free(window_list->normal_list);
if (window_list->transient_list) free(window_list->transient_list);
free(window_list);
}
void clean_xcb(void) {
focus_window(XCB_WINDOW_NONE);
clean_xcb_cursor();
clean_ewmh_extension();
xcb_disconnect(conn);
conn = NULL;
screen = NULL;
}
void grab_keybindings(const keybinding_t *keys, const size_t length) {
xcb_window_t root = screen->root;
xcb_keycode_t key = XCB_GRAB_ANY;
xcb_ungrab_key(conn, key, root, XCB_MOD_MASK_ANY);
key_symbols = xcb_key_symbols_alloc(conn);
xcb_grab_mode_t mode = XCB_GRAB_MODE_ASYNC;
xcb_keycode_t *keycode = NULL;
xcb_mod_mask_t modifiers;
xcb_void_cookie_t cookie;
xcb_generic_error_t *error = NULL;
for (int i = 0; i < length; i++) {
xcb_keysym_t keysym = keys[i].key_symbol;
keycode = xcb_key_symbols_get_keycode(key_symbols, keysym);
modifiers = keys[i].modifiers;
cookie = xcb_grab_key(conn, key, root, modifiers, *keycode, mode, mode);
error = xcb_request_check(conn, cookie);
if (error) {
clean_xcb();
die("%scannot grap key", APP_NAME);
}
}
}
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;
}
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;
bool result = XCB_EVENT_RESPONSE_TYPE(event) == event_mask;
free(event);
return result;
}