#include "xcb.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #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, ¶ms); 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, ¶ms); 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(); if (key_symbols) xcb_key_symbols_free(key_symbols); xcb_disconnect(conn); conn = NULL; screen = NULL; key_symbols = NULL; } void grab_keybindings(const keybinding_t *keys, const size_t length) { 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); free(keycode); 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; } void set_pointer_position(int32_t x, int32_t y) { xcb_warp_pointer(conn, XCB_WINDOW_NONE, screen->root, 0, 0, 1, 1, x, y); xcb_flush(conn); } bool xcb_check_mask_event(xcb_event_mask_t event_mask) { xcb_generic_event_t *event = xcb_poll_for_event(conn); if (!event) return false; bool result = XCB_EVENT_RESPONSE_TYPE(event) == event_mask; free(event); return result; }