#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; 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_LEAVE_WINDOW | XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_POINTER_MOTION | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY, }; xcb_void_cookie_t cookie = xcb_aux_change_window_attributes(conn, screen->root, mask, ¶ms); xcb_generic_error_t *error = xcb_request_check(conn, cookie); if (error) { xcb_disconnect(conn); return true; } return false; } /** * @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; } 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); } static 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_ewmh_extension(); 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); xcb_window_t *list = xcb_query_tree_children(tree_reply); int len = xcb_query_tree_children_length(tree_reply); uint32_t count = 0; xcb_window_t temp_windoow_array[len]; 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); 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; 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_windoow_array[count] = window; count++; } 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_windoow_array[i]; window_list_t *r = ecalloc(1, sizeof(window_list_t)); r->count = count; r->list = window_list; return r; } void free_window_list(window_list_t *window_list) { if (window_list == NULL) return; free(window_list->list); free(window_list); } void start_xcb_event_loop(void) { xcb_generic_event_t *event = NULL; while ((event = xcb_poll_for_event(conn)) != NULL) { uint8_t event_type = XCB_EVENT_RESPONSE_TYPE(event); const char *label = xcb_event_get_label(event_type); printf("================== %s ==================\n", label); free(event); } } void clean_xcb(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; } xcb_disconnect(conn); conn = NULL; screen = NULL; }