From 6ec4d8422f4ea5efca6c8e080d7546fc373df2f7 Mon Sep 17 00:00:00 2001 From: Zedhugh Chen Date: Sat, 14 Mar 2026 13:53:12 +0800 Subject: [PATCH] =?UTF-8?q?core:=20=E6=B7=BB=E5=8A=A0=20X11=20=E5=90=8E?= =?UTF-8?q?=E7=AB=AF=E5=88=9D=E5=A7=8B=E5=8C=96=E4=B8=8E=E5=B1=8F=E5=B9=95?= =?UTF-8?q?=E4=BF=A1=E6=81=AF=E6=8E=A2=E6=B5=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/x11.c | 252 +++++++++++++++++++++++++++++++++++++++++++++ src/core/backend.h | 18 ++++ src/core/runtime.c | 51 +++++++++ src/core/runtime.h | 2 + src/wm_desc.h | 6 +- 5 files changed, 326 insertions(+), 3 deletions(-) create mode 100644 src/backend/x11.c create mode 100644 src/core/backend.h create mode 100644 src/core/runtime.c diff --git a/src/backend/x11.c b/src/backend/x11.c new file mode 100644 index 0000000..9faa58b --- /dev/null +++ b/src/backend/x11.c @@ -0,0 +1,252 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core/backend.h" +#include "utils.h" +#include "wm_desc.h" + +struct wm_backend_t { + xcb_connection_t *conn; + xcb_screen_t *screen; + int screenp; + bool have_xfixes; +}; + +wm_backend_t *wm_backend_create(const char *display_name) { + int screen_num = 0; + xcb_connection_t *conn = xcb_connect(display_name, &screen_num); + int xcb_conn_error = xcb_connection_has_error(conn); + if (xcb_conn_error) { + fatal("cannot open display %s, error %d", display_name, xcb_conn_error); + } + + xcb_screen_t *screen = xcb_aux_get_screen(conn, screen_num); + if (!screen) fatal("cannot get screen info"); + xcb_window_t root = screen->root; + + uint32_t mask = XCB_CW_EVENT_MASK; + const xcb_params_cw_t params = { + .event_mask = XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, + }; + xcb_void_cookie_t cookie = + xcb_aux_change_window_attributes_checked(conn, root, mask, ¶ms); + if (xcb_request_check(conn, cookie)) { + fatal( + "another window manager is already running (cannot select " + "SubstructureRedirect)"); + } + + wm_backend_t *backend = p_new(wm_backend_t, 1); + backend->conn = conn; + backend->screen = screen; + backend->screenp = screen_num; + + xcb_prefetch_extension_data(conn, &xcb_xfixes_id); + const xcb_query_extension_reply_t *query = + xcb_get_extension_data(conn, &xcb_xfixes_id); + if (query && query->present) { + xcb_xfixes_query_version_cookie_t cookie = xcb_xfixes_query_version( + conn, XCB_XFIXES_MAJOR_VERSION, XCB_XFIXES_MINOR_VERSION); + xcb_xfixes_query_version_reply_t *reply = + xcb_xfixes_query_version_reply(conn, cookie, nullptr); + + if (reply) { + backend->have_xfixes = true; + p_delete(&reply); + } + } else { + backend->have_xfixes = false; + } + + return backend; +} + +void wm_backend_destroy(wm_backend_t *backend) { + if (!backend) return; + + xcb_disconnect(backend->conn); + backend->conn = nullptr; + free(backend); +} + +static bool detect_monitor_by_randr(const wm_backend_t *backend, + wm_output_info_t **outputs, size_t *count) { + xcb_prefetch_extension_data(backend->conn, &xcb_randr_id); + const xcb_query_extension_reply_t *query = + xcb_get_extension_data(backend->conn, &xcb_randr_id); + if (!query || !query->present) return false; + + xcb_randr_query_version_cookie_t cookie = xcb_randr_query_version( + backend->conn, XCB_RANDR_MAJOR_VERSION, XCB_RANDR_MINOR_VERSION); + xcb_randr_query_version_reply_t *reply = + xcb_randr_query_version_reply(backend->conn, cookie, nullptr); + if (!reply) return false; + + /* xcb_randr_get_monitors 接口是 1.5 引入的 */ + uint32_t major_version = reply->major_version; + uint32_t minor_version = reply->minor_version; + if (major_version < 1 || (major_version == 1 && minor_version < 5)) { + p_delete(&reply); + return false; + } + p_delete(&reply); + + xcb_randr_get_monitors_cookie_t monitors_cookie = + xcb_randr_get_monitors(backend->conn, backend->screen->root, 1); + xcb_randr_get_monitors_reply_t *monitors_reply = + xcb_randr_get_monitors_reply(backend->conn, monitors_cookie, nullptr); + if (!monitors_reply) { + warn("RandR get monitor failed"); + return false; + } + + int len = xcb_randr_get_monitors_monitors_length(monitors_reply); + if (len <= 0) { + p_delete(&monitors_reply); + return false; + } + + wm_output_info_t *output_list = p_new(wm_output_info_t, len); + xcb_randr_monitor_info_iterator_t iter = + xcb_randr_get_monitors_monitors_iterator(monitors_reply); + for (int i = 0; iter.rem; xcb_randr_monitor_info_next(&iter), i++) { + wm_output_info_t *output = &output_list[i]; + output->geometry.x = iter.data->x; + output->geometry.y = iter.data->y; + output->geometry.width = iter.data->width; + output->geometry.height = iter.data->height; + + xcb_get_atom_name_cookie_t name_cookie = + xcb_get_atom_name_unchecked(backend->conn, iter.data->name); + xcb_get_atom_name_reply_t *name_reply = + xcb_get_atom_name_reply(backend->conn, name_cookie, nullptr); + + if (name_reply) { + char *name = xcb_get_atom_name_name(name_reply); + int length = xcb_get_atom_name_name_length(name_reply); + output->name = strndup(name, length); + p_delete(&name_reply); + } + } + + p_delete(&monitors_reply); + + if (count) *count = (size_t)len; + if (outputs) { + *outputs = output_list; + } else { + for (size_t i = 0; i < (size_t)len; i++) p_delete(&output_list[i].name); + p_delete(&output_list); + } + + return true; +} + +static bool detect_monitor_by_xinerama(const wm_backend_t *backend, + wm_output_info_t **outputs, + size_t *count) { + xcb_prefetch_extension_data(backend->conn, &xcb_xinerama_id); + const xcb_query_extension_reply_t *query = + xcb_get_extension_data(backend->conn, &xcb_xinerama_id); + if (!query || !query->present) return false; + + xcb_xinerama_query_version_cookie_t cookie = xcb_xinerama_query_version( + backend->conn, XCB_XINERAMA_MAJOR_VERSION, XCB_XINERAMA_MINOR_VERSION); + xcb_xinerama_query_version_reply_t *version_reply = + xcb_xinerama_query_version_reply(backend->conn, cookie, nullptr); + if (!version_reply) return false; + p_delete(&version_reply); + + xcb_xinerama_is_active_cookie_t active_cookie = + xcb_xinerama_is_active(backend->conn); + xcb_xinerama_is_active_reply_t *active_reply = + xcb_xinerama_is_active_reply(backend->conn, active_cookie, nullptr); + bool active = active_reply && active_reply->state; + if (active_reply) p_delete(&active_reply); + if (!active) return false; + + xcb_xinerama_query_screens_cookie_t screens_cookie = + xcb_xinerama_query_screens(backend->conn); + xcb_xinerama_query_screens_reply_t *screens_reply = + xcb_xinerama_query_screens_reply(backend->conn, screens_cookie, nullptr); + + if (!screens_reply) return false; + + int len = xcb_xinerama_query_screens_screen_info_length(screens_reply); + if (len <= 0) { + p_delete(&screens_reply); + return false; + } + + xcb_xinerama_screen_info_t *screen_info = + xcb_xinerama_query_screens_screen_info(screens_reply); + if (!screen_info) { + p_delete(&screens_reply); + return false; + } + + wm_output_info_t *output_list = p_new(wm_output_info_t, len); + for (int i = 0; i < len; i++) { + wm_output_info_t *output = &output_list[i]; + output->geometry.x = screen_info[i].x_org; + output->geometry.y = screen_info[i].y_org; + output->geometry.width = screen_info[i].width; + output->geometry.height = screen_info[i].height; + } + p_delete(&screens_reply); + + if (count) *count = (size_t)len; + if (outputs) { + *outputs = output_list; + } else { + for (size_t i = 0; i < (size_t)len; i++) p_delete(&output_list[i].name); + p_delete(&output_list); + } + + return true; +} + +wm_backend_detect_t *wm_backend_detect(wm_backend_t *backend) { + wm_backend_detect_t *detect = p_new(wm_backend_detect_t, 1); + + wm_output_info_t *outputs = nullptr; + size_t count = 0; + + if (detect_monitor_by_randr(backend, &outputs, &count)) { + detect->output_count = count; + detect->outputs = outputs; + } else if (detect_monitor_by_xinerama(backend, &outputs, &count)) { + detect->output_count = count; + detect->outputs = outputs; + } else { + wm_output_info_t *output = p_new(wm_output_info_t, 1); + output->geometry.x = 0; + output->geometry.y = 0; + output->geometry.width = backend->screen->width_in_pixels; + output->geometry.height = backend->screen->height_in_pixels; + + detect->outputs = output; + detect->output_count = 1; + } + + return detect; +} + +void wm_backend_detect_destroy(wm_backend_detect_t *detect) { + if (!detect) return; + + for (size_t i = 0; i < detect->output_count; i++) { + p_delete(&detect->outputs[i].name); + } + + p_delete(&detect->outputs); + free(detect); +} diff --git a/src/core/backend.h b/src/core/backend.h new file mode 100644 index 0000000..05bdc0e --- /dev/null +++ b/src/core/backend.h @@ -0,0 +1,18 @@ +#pragma once + +#include + +#include "wm_desc.h" + +typedef struct wm_backend_t wm_backend_t; + +typedef struct wm_backend_detect_t { + wm_output_info_t *outputs; + size_t output_count; +} wm_backend_detect_t; + +wm_backend_t *wm_backend_create(const char *display_name); +void wm_backend_destroy(wm_backend_t *backend); + +wm_backend_detect_t *wm_backend_detect(wm_backend_t *backend); +void wm_backend_detect_destroy(wm_backend_detect_t *detect); diff --git a/src/core/runtime.c b/src/core/runtime.c new file mode 100644 index 0000000..0b7d81c --- /dev/null +++ b/src/core/runtime.c @@ -0,0 +1,51 @@ +#include "core/runtime.h" + +#include + +#include "core/backend.h" +#include "core/state.h" +#include "core/types.h" +#include "utils.h" +#include "wm_desc.h" + +bool wm_runtime_init(wm_runtime_t *runtime) { + p_clear(runtime, 1); + + wm_backend_t *backend = wm_backend_create(nullptr); + if (!backend) return false; + + runtime->backend = backend; + + wm_backend_detect_t *detect = wm_backend_detect(backend); + if (!detect || detect->output_count <= 0) { + wm_backend_destroy(backend); + return false; + } + + wm_workspace_desc_t *workspaces = + p_new(wm_workspace_desc_t, detect->output_count); + static wm_layout_id_t ids[] = {0, 1, 2}; + for (size_t i = 0; i < detect->output_count; i++) { + wm_workspace_desc_t *desc = &workspaces[i]; + desc->output_index = i; + desc->name = "web"; + desc->layout_ids = ids; + desc->layout_count = countof(ids); + desc->initial_layout_id = 1; + } + + wm_state_init(&runtime->state, detect->outputs, detect->output_count, + workspaces, detect->output_count); + p_delete(&workspaces); + wm_backend_detect_destroy(detect); + detect = nullptr; + + return true; +} + +void wm_runtime_shutdown(wm_runtime_t *runtime) { + runtime->running = false; + wm_state_cleanup(&runtime->state); + wm_backend_destroy(runtime->backend); + runtime->backend = nullptr; +} diff --git a/src/core/runtime.h b/src/core/runtime.h index f02e5bb..9de0fc1 100644 --- a/src/core/runtime.h +++ b/src/core/runtime.h @@ -1,5 +1,6 @@ #pragma once +#include "core/backend.h" #include "core/state.h" typedef struct wm_runtime_t { @@ -7,6 +8,7 @@ typedef struct wm_runtime_t { bool will_restart; wm_state_t state; + wm_backend_t *backend; } wm_runtime_t; bool wm_runtime_init(wm_runtime_t *runtime); diff --git a/src/wm_desc.h b/src/wm_desc.h index 566c990..69522ef 100644 --- a/src/wm_desc.h +++ b/src/wm_desc.h @@ -5,7 +5,7 @@ #include "core/types.h" typedef struct wm_output_info_t { - const char *name; + char *name; wm_rect_t geometry; } wm_output_info_t; @@ -42,8 +42,8 @@ typedef struct wm_workspace_desc_t { * * 这组接口只校验描述表自身的一致性,不访问 state。 */ -static inline bool -wm_workspace_desc_layouts_valid(const wm_workspace_desc_t *workspace) { +static inline bool wm_workspace_desc_layouts_valid( + const wm_workspace_desc_t *workspace) { if (!workspace || !workspace->layout_count || !workspace->layout_ids) { return false; }