#include "xcb.h" #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; printf("screen number: %d\n", iter.rem); 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; } /* 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; } 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; } 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; uint8_t format = XCB_INPUT_PROPERTY_FORMAT_32_BITS; 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, format, LENGTH(state), state); xcb_change_property(conn, mode, screen->root, ewmh->_NET_SUPPORTED, atom, format, 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 *reply = xcb_query_tree_reply(conn, cookie, NULL); xcb_window_t *list = xcb_query_tree_children(reply); int len = xcb_query_tree_children_length(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 *reply = xcb_get_window_attributes_reply(conn, wa_cookie, NULL); uint8_t override_redirect = reply->override_redirect; uint8_t map_state = reply->map_state; free(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++; } 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 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); } ewmh_init_failed = false; ewmh = NULL; } xcb_disconnect(conn); conn = NULL; screen = NULL; }