Files
zswm/src/xcb/xcb.c

247 lines
7.2 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <xcb/xcb.h>
#include <xcb/xcb_aux.h>
#include <xcb/xcb_ewmh.h>
#include <xcb/xcb_icccm.h>
#include <xcb/xinerama.h>
#include <xcb/xinput.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;
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, &params);
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;
}