Compare commits

...

9 Commits

31 changed files with 1163 additions and 760 deletions

30
.clang-tidy Normal file
View File

@@ -0,0 +1,30 @@
# -*- mode: yaml; -*-
Checks: >-
-*,
clang-analyzer-*,
-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling,
cert-err34-c,
bugprone-assert-side-effect,
bugprone-assignment-in-if-condition,
bugprone-branch-clone,
bugprone-infinite-loop,
bugprone-macro-parentheses,
bugprone-macro-repeated-side-effects,
bugprone-misplaced-operator-in-strlen-in-alloc,
bugprone-misplaced-pointer-arithmetic-in-alloc,
bugprone-not-null-terminated-result,
bugprone-posix-return,
bugprone-sizeof-expression,
bugprone-suspicious-memory-comparison,
bugprone-suspicious-memset-usage,
bugprone-suspicious-missing-comma,
bugprone-suspicious-semicolon,
bugprone-swapped-arguments,
bugprone-undefined-memory-manipulation,
bugprone-unsafe-functions,
bugprone-unused-return-value
WarningsAsErrors: ''
HeaderFilterRegex: '^(src|include|tests)/'
FormatStyle: file
SystemHeaders: false

51
.githooks/pre-commit Executable file
View File

@@ -0,0 +1,51 @@
#!/usr/bin/env bash
set -euo pipefail
repo_root="$(git rev-parse --show-toplevel)"
cd "$repo_root"
if ! command -v clang-format >/dev/null 2>&1; then
echo "pre-commit: clang-format not found in PATH" >&2
exit 1
fi
if ! git clang-format -h >/dev/null 2>&1; then
echo "pre-commit: git clang-format is not available" >&2
exit 1
fi
mapfile -d '' staged_files < <(
git diff --cached --name-only --diff-filter=ACMR -z -- \
'*.c' '*.h' '*.cc' '*.cpp' '*.cxx' '*.hh' '*.hpp' '*.hxx'
)
if ((${#staged_files[@]} == 0)); then
exit 0
fi
if git rev-parse --verify HEAD >/dev/null 2>&1; then
base_commit=HEAD
else
empty_tree="$(git hash-object -t tree /dev/null)"
base_commit="$(
printf 'pre-commit empty base\n' | \
GIT_AUTHOR_NAME=pre-commit \
GIT_AUTHOR_EMAIL=pre-commit@example.invalid \
GIT_COMMITTER_NAME=pre-commit \
GIT_COMMITTER_EMAIL=pre-commit@example.invalid \
git commit-tree "$empty_tree"
)"
fi
set +e
git clang-format --staged --binary clang-format --quiet "$base_commit" -- \
"${staged_files[@]}"
status=$?
set -e
if [[ $status -ne 0 && $status -ne 1 ]]; then
exit "$status"
fi
git add -- "${staged_files[@]}"

View File

@@ -7,6 +7,7 @@ set(CMAKE_C_EXTENSIONS ON)
set(APP_NAME "zdwm") set(APP_NAME "zdwm")
set(SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/src") set(SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/src")
set(INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include")
set(BUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}") set(BUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}")
add_compile_definitions(LOG_FILE="$ENV{HOME}/${APP_NAME}-log.txt") add_compile_definitions(LOG_FILE="$ENV{HOME}/${APP_NAME}-log.txt")
@@ -28,7 +29,7 @@ option(BUILD_TESTING "Build tests" OFF)
add_executable(${APP_NAME} add_executable(${APP_NAME}
${SOURCE_DIR}/wm.c ${SOURCE_DIR}/wm.c
${SOURCE_DIR}/utils.c ${SOURCE_DIR}/base/log.c
${SOURCE_DIR}/buffer.c ${SOURCE_DIR}/buffer.c
${SOURCE_DIR}/backtrace.c ${SOURCE_DIR}/backtrace.c
${SOURCE_DIR}/monitor.c ${SOURCE_DIR}/monitor.c
@@ -82,6 +83,7 @@ pkg_check_modules(deps REQUIRED
target_include_directories(${APP_NAME} SYSTEM target_include_directories(${APP_NAME} SYSTEM
PRIVATE ${deps_INCLUDE_DIRS} PRIVATE ${deps_INCLUDE_DIRS}
PRIVATE ${INCLUDE_DIR}
) )
target_include_directories(${APP_NAME} target_include_directories(${APP_NAME}
PRIVATE ${SOURCE_DIR} PRIVATE ${SOURCE_DIR}

View File

@@ -44,4 +44,8 @@ test:
cmake --build $(BUILD_DIR) cmake --build $(BUILD_DIR)
ctest --test-dir $(BUILD_DIR) ctest --test-dir $(BUILD_DIR)
.PHONY: clean run wm prepare build install uninstall reinstall test install-hooks:
chmod +x $(MAKEFILE_DIR).githooks/pre-commit
git -C $(MAKEFILE_DIR) config core.hooksPath $(MAKEFILE_DIR).githooks
.PHONY: clean run wm prepare build install uninstall reinstall test install-hooks

80
include/zdwm/config.h Normal file
View File

@@ -0,0 +1,80 @@
#ifndef ZDWM_CONFIG_H
#define ZDWM_CONFIG_H
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <zdwm/layout.h>
#include <zdwm/types.h>
#if defined(__cplusplus)
extern "C" {
#endif
#define ZDWM_CONFIG_ABI_VERSION 1u
typedef struct zdwm_config_builder_t zdwm_config_builder_t;
typedef struct zdwm_builtin_layouts_t {
zdwm_layout_fn tile;
zdwm_layout_fn monocle;
} zdwm_builtin_layouts_t;
typedef struct zdwm_api_t {
uint32_t abi_version;
zdwm_builtin_layouts_t builtin_layouts;
/**
* 用户调用这个函数注册布局算法
* @param builder 配置构建上下文
* @param name 布局算法名称,不能为 nullptr
* @param symbol 布局算法标志符号,用于在状态栏中显示,不能为 nullptr
* @param description 布局算法描述,可以为 nullptr
* @param fn 布局算法,可以为 nullptr ,如果为 nullptr 则代表不自动布局
* @returns 返回布局算法对应的布局 id ,如果注册失败,返回
* ZDWM_LAYOUT_ID_INVALID
*/
zdwm_layout_id_t (*register_layout)(zdwm_config_builder_t *builder,
const char *name, const char *symbol,
const char *description,
zdwm_layout_fn fn);
/**
* 用户调用这个函数添加 output 中的工作空间
* @param builder 配置构建上下文
* @param output_index 该工作空间归属的 output 索引
* @param name 工作空间名称,用于状态栏显示,不能为 nullptr
* @param layout_ids 该工作空间支持的布局算法列表
* @param layout_count 支持的布局算法总数
* @param initial_layout_id 初始布局算法 id
* @returns 返回工作空间 id 。如果注册失败,返回 ZDWM_WORKSPACE_ID_INVALID
*/
zdwm_workspace_id_t (*define_workspace)(zdwm_config_builder_t *builder,
size_t output_index, const char *name,
const zdwm_layout_id_t *layout_ids,
size_t layout_count,
zdwm_layout_id_t initial_layout_id);
} zdwm_api_t;
/**
* 用户配置入口函数类型定义
* @param api 用户自定义时可以使用的 api 接口及数据api
* 中的函数和信息只能在函数内部使用,如果用户将其保存后在函数外使用是未定义行为
* @param builder 配置构建上下文
* @param outputs 屏幕信息列表
* @param output_count 屏幕个数
* @returns 用户配置成功返回 true ,配置失败返回 false
*/
typedef bool zdwm_config_setup_fn(const zdwm_api_t *api,
zdwm_config_builder_t *builder,
const zdwm_output_info_t *outputs,
size_t output_count);
/* 用户在自己的动态链接库配置中必须实现该函数 */
extern zdwm_config_setup_fn setup;
#if defined(__cplusplus)
}
#endif
#endif /* ZDWM_CONFIG_H */

61
include/zdwm/layout.h Normal file
View File

@@ -0,0 +1,61 @@
#ifndef ZDWM_LAYOUT_H
#define ZDWM_LAYOUT_H
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <zdwm/types.h>
#if defined(__cplusplus)
extern "C" {
#endif
typedef struct zdwm_layout_ctx_t {
zdwm_workspace_id_t workspace_id;
zdwm_window_id_t focused_window_id;
zdwm_rect_t workarea;
const zdwm_window_id_t *window_ids;
size_t window_count;
} zdwm_layout_ctx_t;
typedef struct zdwm_layout_item_t {
zdwm_window_id_t window_id;
zdwm_rect_t rect;
} zdwm_layout_item_t;
typedef struct zdwm_layout_result_t {
zdwm_layout_item_t *items;
size_t item_count;
size_t item_capacity;
} zdwm_layout_result_t;
typedef bool (*zdwm_layout_fn)(const zdwm_layout_ctx_t *ctx,
zdwm_layout_result_t *out);
static inline void zdwm_layout_result_reset(zdwm_layout_result_t *result) {
if (!result) abort();
result->item_count = 0;
}
static inline void zdwm_layout_result_push(zdwm_layout_result_t *result,
zdwm_layout_item_t item) {
if (!result) abort();
if (result->item_count == result->item_capacity) {
size_t capacity = result->item_capacity ? result->item_capacity * 2u : 4u;
zdwm_layout_item_t *items =
realloc(result->items, capacity * sizeof(*result->items));
if (!items) abort();
result->items = items;
result->item_capacity = capacity;
}
result->items[result->item_count++] = item;
}
#if defined(__cplusplus)
}
#endif
#endif /* ZDWM_LAYOUT_H */

36
include/zdwm/types.h Normal file
View File

@@ -0,0 +1,36 @@
#ifndef ZDWM_TYPES_H
#define ZDWM_TYPES_H
#include <stdint.h>
#if defined(__cplusplus)
extern "C" {
#endif
typedef uint32_t zdwm_layout_id_t;
typedef uint32_t zdwm_window_id_t;
typedef uint32_t zdwm_workspace_id_t;
/* clang-format off */
#define ZDWM_LAYOUT_ID_INVALID ((zdwm_layout_id_t)UINT32_MAX)
#define ZDWM_WINDOW_ID_INVALID ((zdwm_window_id_t)0)
#define ZDWM_WORKSPACE_ID_INVALID ((zdwm_workspace_id_t)UINT32_MAX)
/* clang-format on */
typedef struct zdwm_rect_t {
int32_t x;
int32_t y;
int32_t width;
int32_t height;
} zdwm_rect_t;
typedef struct zdwm_output_info_t {
const char *name;
zdwm_rect_t geometry;
} zdwm_output_info_t;
#if defined(__cplusplus)
}
#endif
#endif /* ZDWM_TYPES_H */

View File

@@ -3,20 +3,19 @@
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include "base/memory.h"
#include "core/backend.h" #include "core/backend.h"
#include "core/types.h" #include "core/types.h"
#include "core/wm_desc.h"
#include "utils.h"
static inline int32_t right(wm_rect_t a) { return a.x + a.width; } static inline int32_t right(rect_t a) { return a.x + a.width; }
static inline int32_t bottom(wm_rect_t a) { return a.y + a.height; } static inline int32_t bottom(rect_t a) { return a.y + a.height; }
static inline bool fully_contains(wm_rect_t outer, wm_rect_t inner) { static inline bool fully_contains(rect_t outer, rect_t inner) {
return inner.x >= outer.x && inner.y >= outer.y && return inner.x >= outer.x && inner.y >= outer.y &&
right(inner) <= right(outer) && bottom(inner) <= bottom(outer); right(inner) <= right(outer) && bottom(inner) <= bottom(outer);
} }
wm_backend_detect_t *output_remove_duplication(const wm_output_info_t *output, backend_detect_t *output_remove_duplication(const output_info_t *output,
const size_t count) { const size_t count) {
if (!output || count < 1) return nullptr; if (!output || count < 1) return nullptr;
bool *remove = p_new(bool, count); bool *remove = p_new(bool, count);
@@ -42,14 +41,14 @@ wm_backend_detect_t *output_remove_duplication(const wm_output_info_t *output,
return nullptr; return nullptr;
} }
wm_output_info_t *list = p_new(wm_output_info_t, amount); output_info_t *list = p_new(output_info_t, amount);
amount = 0; amount = 0;
for (size_t i = 0; i < count; i++) { for (size_t i = 0; i < count; i++) {
if (remove[i]) continue; if (remove[i]) continue;
list[amount++] = output[i]; list[amount++] = output[i];
} }
p_delete(&remove); p_delete(&remove);
wm_backend_detect_t *detect = p_new(wm_backend_detect_t, 1); backend_detect_t *detect = p_new(backend_detect_t, 1);
detect->outputs = list; detect->outputs = list;
detect->output_count = amount; detect->output_count = amount;
return detect; return detect;

View File

@@ -3,7 +3,7 @@
#include <stddef.h> #include <stddef.h>
#include "core/backend.h" #include "core/backend.h"
#include "core/wm_desc.h" #include "core/types.h"
wm_backend_detect_t *output_remove_duplication(const wm_output_info_t *output, backend_detect_t *output_remove_duplication(const output_info_t *output,
const size_t count); const size_t count);

View File

@@ -12,17 +12,18 @@
#include <xcb/xproto.h> #include <xcb/xproto.h>
#include "backend/output_utils.h" #include "backend/output_utils.h"
#include "core/wm_desc.h" #include "base/log.h"
#include "utils.h" #include "base/memory.h"
#include "core/types.h"
struct wm_backend_t { struct backend_t {
xcb_connection_t *conn; xcb_connection_t *conn;
xcb_screen_t *screen; xcb_screen_t *screen;
int screenp; int screenp;
bool have_xfixes; bool have_xfixes;
}; };
wm_backend_t *wm_backend_create(const char *display_name) { backend_t *backend_create(const char *display_name) {
int screen_num = 0; int screen_num = 0;
xcb_connection_t *conn = xcb_connect(display_name, &screen_num); xcb_connection_t *conn = xcb_connect(display_name, &screen_num);
int xcb_conn_error = xcb_connection_has_error(conn); int xcb_conn_error = xcb_connection_has_error(conn);
@@ -46,7 +47,7 @@ wm_backend_t *wm_backend_create(const char *display_name) {
"SubstructureRedirect)"); "SubstructureRedirect)");
} }
wm_backend_t *backend = p_new(wm_backend_t, 1); backend_t *backend = p_new(backend_t, 1);
backend->conn = conn; backend->conn = conn;
backend->screen = screen; backend->screen = screen;
backend->screenp = screen_num; backend->screenp = screen_num;
@@ -71,7 +72,7 @@ wm_backend_t *wm_backend_create(const char *display_name) {
return backend; return backend;
} }
void wm_backend_destroy(wm_backend_t *backend) { void backend_destroy(backend_t *backend) {
if (!backend) return; if (!backend) return;
xcb_disconnect(backend->conn); xcb_disconnect(backend->conn);
@@ -79,8 +80,8 @@ void wm_backend_destroy(wm_backend_t *backend) {
free(backend); free(backend);
} }
static bool detect_monitor_by_randr(const wm_backend_t *backend, static bool detect_monitor_by_randr(const backend_t *backend,
wm_output_info_t **outputs, size_t *count) { output_info_t **outputs, size_t *count) {
xcb_prefetch_extension_data(backend->conn, &xcb_randr_id); xcb_prefetch_extension_data(backend->conn, &xcb_randr_id);
const xcb_query_extension_reply_t *query = const xcb_query_extension_reply_t *query =
xcb_get_extension_data(backend->conn, &xcb_randr_id); xcb_get_extension_data(backend->conn, &xcb_randr_id);
@@ -116,11 +117,11 @@ static bool detect_monitor_by_randr(const wm_backend_t *backend,
return false; return false;
} }
wm_output_info_t *output_list = p_new(wm_output_info_t, len); output_info_t *output_list = p_new(output_info_t, len);
xcb_randr_monitor_info_iterator_t iter = xcb_randr_monitor_info_iterator_t iter =
xcb_randr_get_monitors_monitors_iterator(monitors_reply); xcb_randr_get_monitors_monitors_iterator(monitors_reply);
for (int i = 0; iter.rem; xcb_randr_monitor_info_next(&iter), i++) { for (int i = 0; iter.rem; xcb_randr_monitor_info_next(&iter), i++) {
wm_output_info_t *output = &output_list[i]; output_info_t *output = &output_list[i];
output->geometry.x = iter.data->x; output->geometry.x = iter.data->x;
output->geometry.y = iter.data->y; output->geometry.y = iter.data->y;
output->geometry.width = iter.data->width; output->geometry.width = iter.data->width;
@@ -152,9 +153,8 @@ static bool detect_monitor_by_randr(const wm_backend_t *backend,
return true; return true;
} }
static bool detect_monitor_by_xinerama(const wm_backend_t *backend, static bool detect_monitor_by_xinerama(const backend_t *backend,
wm_output_info_t **outputs, output_info_t **outputs, size_t *count) {
size_t *count) {
xcb_prefetch_extension_data(backend->conn, &xcb_xinerama_id); xcb_prefetch_extension_data(backend->conn, &xcb_xinerama_id);
const xcb_query_extension_reply_t *query = const xcb_query_extension_reply_t *query =
xcb_get_extension_data(backend->conn, &xcb_xinerama_id); xcb_get_extension_data(backend->conn, &xcb_xinerama_id);
@@ -195,9 +195,9 @@ static bool detect_monitor_by_xinerama(const wm_backend_t *backend,
return false; return false;
} }
wm_output_info_t *output_list = p_new(wm_output_info_t, len); output_info_t *output_list = p_new(output_info_t, len);
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
wm_output_info_t *output = &output_list[i]; output_info_t *output = &output_list[i];
output->geometry.x = screen_info[i].x_org; output->geometry.x = screen_info[i].x_org;
output->geometry.y = screen_info[i].y_org; output->geometry.y = screen_info[i].y_org;
output->geometry.width = screen_info[i].width; output->geometry.width = screen_info[i].width;
@@ -216,35 +216,35 @@ static bool detect_monitor_by_xinerama(const wm_backend_t *backend,
return true; return true;
} }
wm_backend_detect_t *wm_backend_detect(wm_backend_t *backend) { backend_detect_t *backend_detect(backend_t *backend) {
wm_output_info_t *outputs = nullptr; output_info_t *outputs = nullptr;
size_t count = 0; size_t count = 0;
if (detect_monitor_by_randr(backend, &outputs, &count)) { if (detect_monitor_by_randr(backend, &outputs, &count)) {
wm_backend_detect_t *detect = output_remove_duplication(outputs, count); backend_detect_t *detect = output_remove_duplication(outputs, count);
p_delete(&outputs); p_delete(&outputs);
return detect; return detect;
} }
if (detect_monitor_by_xinerama(backend, &outputs, &count)) { if (detect_monitor_by_xinerama(backend, &outputs, &count)) {
wm_backend_detect_t *detect = output_remove_duplication(outputs, count); backend_detect_t *detect = output_remove_duplication(outputs, count);
p_delete(&outputs); p_delete(&outputs);
return detect; return detect;
} }
wm_output_info_t *output = p_new(wm_output_info_t, 1); output_info_t *output = p_new(output_info_t, 1);
output->geometry.x = 0; output->geometry.x = 0;
output->geometry.y = 0; output->geometry.y = 0;
output->geometry.width = backend->screen->width_in_pixels; output->geometry.width = backend->screen->width_in_pixels;
output->geometry.height = backend->screen->height_in_pixels; output->geometry.height = backend->screen->height_in_pixels;
wm_backend_detect_t *detect = p_new(wm_backend_detect_t, 1); backend_detect_t *detect = p_new(backend_detect_t, 1);
detect->outputs = output; detect->outputs = output;
detect->output_count = 1; detect->output_count = 1;
return detect; return detect;
} }
void wm_backend_detect_destroy(wm_backend_detect_t *detect) { void backend_detect_destroy(backend_detect_t *detect) {
if (!detect) return; if (!detect) return;
for (size_t i = 0; i < detect->output_count; i++) { for (size_t i = 0; i < detect->output_count; i++) {

View File

@@ -1,4 +1,4 @@
#include "utils.h" #include "base/log.h"
#include <stdarg.h> #include <stdarg.h>
#include <stdio.h> #include <stdio.h>
@@ -19,9 +19,6 @@ const char *current_time_str(void) {
return buffer; return buffer;
} }
/**
* Print error and exit with EXIT_FAILURE code.
*/
void _fatal(int line, const char *fct, const char *file, const char *fmt, ...) { void _fatal(int line, const char *fct, const char *file, const char *fmt, ...) {
va_list ap; va_list ap;
@@ -34,9 +31,6 @@ void _fatal(int line, const char *fct, const char *file, const char *fmt, ...) {
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
/**
* Print error message on stderr
*/
void _warn(int line, const char *fct, const char *file, const char *fmt, ...) { void _warn(int line, const char *fct, const char *file, const char *fmt, ...) {
va_list ap; va_list ap;

32
src/base/log.h Normal file
View File

@@ -0,0 +1,32 @@
#pragma once
#include <stdarg.h>
#include <stdio.h>
#define fatal(format, ...) \
_fatal(__LINE__, __FUNCTION__, __FILE__, format, ##__VA_ARGS__)
void _fatal(int line, const char *function, const char *file,
const char *format, ...) __attribute__((noreturn))
__attribute__((format(printf, 4, 5)));
#define warn(format, ...) \
_warn(__LINE__, __FUNCTION__, __FILE__, format, ##__VA_ARGS__)
void _warn(int line, const char *function, const char *file, const char *format,
...) __attribute__((format(printf, 4, 5)));
const char *current_time_str(void);
#if defined(RELEASE)
#define logger(format, ...)
#elif defined(LOG_FILE)
static inline void logger(const char *format, ...) {
va_list ap;
va_start(ap, format);
FILE *log_file = fopen(LOG_FILE, "a+t");
vfprintf(log_file, format, ap);
va_end(ap);
fclose(log_file);
}
#else
#define logger(format, ...) printf(format, ##__VA_ARGS__)
#endif

29
src/base/macros.h Normal file
View File

@@ -0,0 +1,29 @@
#pragma once
#include <stddef.h>
#include <sys/types.h>
#define ssizeof(foo) (ssize_t)sizeof(foo)
#define countof(foo) (ssizeof(foo) / ssizeof(foo[0]))
#ifdef __GNUC__
#define likely(expr) __builtin_expect(!!(expr), 1)
#define unlikely(expr) __builtin_expect((expr), 0)
#else
#define likely(expr) expr
#define unlikely(expr) expr
#endif
#ifdef MIN
#undef MIN
#endif
#ifndef MIN
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
#endif
#ifdef MAX
#undef MAX
#endif
#ifndef MAX
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
#endif

73
src/base/memory.h Normal file
View File

@@ -0,0 +1,73 @@
#pragma once
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include "base/macros.h"
#define p_alloc_nr(x) (((x) + 16) * 3 / 2)
#define p_new(type, count) ((type *)xmalloc(sizeof(type) * (count)))
#define p_clear(p, count) ((void)memset((p), 0, sizeof(*(p)) * (count)))
#define p_realloc(pp, count) xrealloc((void *)(pp), sizeof(**(pp)) * (count))
#define p_copy(src, count) xmemcopy((src), sizeof(*(src)) * (count))
#define p_delete(mem_p) \
do { \
void **__ptr = (void **)(mem_p); \
free(*__ptr); \
*(void **)__ptr = nullptr; \
} while (0)
static inline char *p_strdup(const char *text) {
char *r = strdup(text);
if (!r) abort();
return r;
}
static inline char *p_strdup_nullable(const char *text) {
if (!text) return nullptr;
return p_strdup(text);
}
static inline void *__attribute__((malloc)) xmalloc(ssize_t size) {
void *ptr;
if (size <= 0) return nullptr;
ptr = calloc(1, size);
if (!ptr) abort();
return ptr;
}
static inline void *xmemcopy(const void *src, size_t n) {
if (!n) return nullptr;
void *mem = xmalloc(n);
void *ret = memcpy(mem, src, n);
if (ret != mem) abort();
return ret;
}
static inline void xrealloc(void **ptr, ssize_t newsize) {
if (newsize <= 0)
p_delete(ptr);
else {
*ptr = realloc(*ptr, newsize);
if (!*ptr) abort();
}
}
/*
* Unlike strlen(), a_strlen() accepts nullptr and returns 0 in that case.
*/
static inline ssize_t a_strlen(const char *s) { return s ? strlen(s) : 0; }
static constexpr size_t INIT_CAPACITY = 4;
static inline size_t next_capacity(size_t capacity) {
if (capacity) return capacity * 2;
return INIT_CAPACITY;
}

103
src/config/loader.c Normal file
View File

@@ -0,0 +1,103 @@
#include "config/loader.h"
#include <dlfcn.h>
#include <stdlib.h>
#include <unistd.h>
#include "base/log.h"
#include "base/memory.h"
static char *config_loader_join_path(const char *base, const char *suffix) {
if (!base || !suffix) return nullptr;
size_t base_len = strlen(base);
size_t suffix_len = strlen(suffix);
char *path = p_new(char, base_len + suffix_len + 1);
memcpy(path, base, base_len);
memcpy(path + base_len, suffix, suffix_len + 1);
return path;
}
static bool config_loader_resolve_path(char **out_path, bool *out_is_explicit,
const char *override_path) {
const char *path = override_path;
if (path && *path) {
*out_path = p_strdup(path);
*out_is_explicit = true;
return true;
}
path = getenv("ZDWM_CONFIG_LIB");
if (path && *path) {
*out_path = p_strdup(path);
*out_is_explicit = true;
return true;
}
path = getenv("XDG_CONFIG_HOME");
if (path && *path) {
*out_path = config_loader_join_path(path, "/zdwm/config.so");
*out_is_explicit = false;
return *out_path != nullptr;
}
path = getenv("HOME");
if (path && *path) {
*out_path = config_loader_join_path(path, "/.config/zdwm/config.so");
*out_is_explicit = false;
return *out_path != nullptr;
}
return false;
}
bool config_loader_load(config_loader_t *loader, const char *override_path) {
if (!loader) return false;
config_loader_cleanup(loader);
char *path = nullptr;
bool is_explicit = false;
if (!config_loader_resolve_path(&path, &is_explicit, override_path)) {
return false;
}
loader->path = path;
if (access(path, F_OK) != 0) {
if (is_explicit) {
warn("config library not found: %s", path);
config_loader_cleanup(loader);
return false;
}
return true;
}
loader->handle = dlopen(path, RTLD_NOW | RTLD_LOCAL);
if (!loader->handle) {
warn("failed to load config library %s: %s", path, dlerror());
config_loader_cleanup(loader);
return false;
}
dlerror();
loader->setup = (zdwm_config_setup_fn *)dlsym(loader->handle, "setup");
const char *error = dlerror();
if (error) {
warn("failed to resolve setup from %s: %s", path, error);
config_loader_cleanup(loader);
return false;
}
return true;
}
void config_loader_cleanup(config_loader_t *loader) {
if (!loader) return;
loader->setup = nullptr;
if (loader->handle) dlclose(loader->handle);
loader->handle = nullptr;
p_delete(&loader->path);
}

12
src/config/loader.h Normal file
View File

@@ -0,0 +1,12 @@
#pragma once
#include <zdwm/config.h>
typedef struct config_loader_t {
char *path;
void *handle;
zdwm_config_setup_fn *setup;
} config_loader_t;
bool config_loader_load(config_loader_t *loader, const char *override_path);
void config_loader_cleanup(config_loader_t *loader);

View File

@@ -2,17 +2,17 @@
#include <stddef.h> #include <stddef.h>
#include "core/wm_desc.h" #include "core/types.h"
typedef struct wm_backend_t wm_backend_t; typedef struct backend_t backend_t;
typedef struct wm_backend_detect_t { typedef struct backend_detect_t {
wm_output_info_t *outputs; output_info_t *outputs;
size_t output_count; size_t output_count;
} wm_backend_detect_t; } backend_detect_t;
wm_backend_t *wm_backend_create(const char *display_name); backend_t *backend_create(const char *display_name);
void wm_backend_destroy(wm_backend_t *backend); void backend_destroy(backend_t *backend);
wm_backend_detect_t *wm_backend_detect(wm_backend_t *backend); backend_detect_t *backend_detect(backend_t *backend);
void wm_backend_detect_destroy(wm_backend_detect_t *detect); void backend_detect_destroy(backend_detect_t *detect);

View File

@@ -2,173 +2,174 @@
#include <stdint.h> #include <stdint.h>
#include "core/types.h"
#include "core/wm_desc.h" #include "core/wm_desc.h"
typedef enum wm_event_type_t { typedef enum event_type_t {
WM_EVENT_KEY_PRESS, ZDWM_EVENT_KEY_PRESS,
WM_EVENT_POINTER_BUTTON_PRESS, ZDWM_EVENT_POINTER_BUTTON_PRESS,
WM_EVENT_POINTER_BUTTON_RELEASE, ZDWM_EVENT_POINTER_BUTTON_RELEASE,
WM_EVENT_POINTER_MOTION, ZDWM_EVENT_POINTER_MOTION,
WM_EVENT_POINTER_ENTER, ZDWM_EVENT_POINTER_ENTER,
WM_EVENT_WINDOW_MAP_REQUEST, ZDWM_EVENT_WINDOW_MAP_REQUEST,
WM_EVENT_WINDOW_REMOVE, ZDWM_EVENT_WINDOW_REMOVE,
WM_EVENT_WINDOW_METADATA_CHANGED, ZDWM_EVENT_WINDOW_METADATA_CHANGED,
WM_EVENT_WINDOW_HINTS_CHANGED, ZDWM_EVENT_WINDOW_HINTS_CHANGED,
WM_EVENT_WINDOW_ACTIVATE_REQUEST, ZDWM_EVENT_WINDOW_ACTIVATE_REQUEST,
WM_EVENT_WINDOW_STATE_REQUEST, ZDWM_EVENT_WINDOW_STATE_REQUEST,
WM_EVENT_CONFIGURE_REQUEST, ZDWM_EVENT_CONFIGURE_REQUEST,
} wm_event_type_t; } event_type_t;
typedef enum wm_modifier_bit_t { typedef enum modifier_bit_t {
WM_MOD_SHIFT = 1u << 0, ZDWM_MOD_SHIFT = 1u << 0,
WM_MOD_CTRL = 1u << 1, ZDWM_MOD_CTRL = 1u << 1,
WM_MOD_ALT = 1u << 2, ZDWM_MOD_ALT = 1u << 2,
WM_MOD_SUPER = 1u << 3, ZDWM_MOD_SUPER = 1u << 3,
} wm_modifier_bit_t; } modifier_bit_t;
typedef uint32_t wm_modifier_mask_t; typedef uint32_t modifier_mask_t;
typedef uint32_t wm_keysym_t; typedef uint32_t keysym_t;
typedef struct wm_key_press_event_t { typedef struct key_press_event_t {
wm_modifier_mask_t modifiers; modifier_mask_t modifiers;
wm_keysym_t keysym; keysym_t keysym;
uint32_t keycode; uint32_t keycode;
} wm_key_press_event_t; } key_press_event_t;
typedef enum wm_button_t { typedef enum button_t {
WM_BUTTON_LEFT, ZDWM_BUTTON_LEFT,
WM_BUTTON_RIGHT, ZDWM_BUTTON_RIGHT,
WM_BUTTON_MIDDLE, ZDWM_BUTTON_MIDDLE,
} wm_button_t; } button_t;
typedef struct wm_pointer_button_event_t { typedef struct pointer_button_event_t {
wm_modifier_mask_t modifiers; modifier_mask_t modifiers;
wm_button_t button; button_t button;
wm_window_id_t window; window_id_t window;
wm_point_t root; point_t root;
wm_point_t local; point_t local;
} wm_pointer_button_event_t; } pointer_button_event_t;
typedef struct wm_pointer_motion_event_t { typedef struct pointer_motion_event_t {
wm_window_id_t window; window_id_t window;
wm_point_t root; point_t root;
wm_point_t local; point_t local;
} wm_pointer_motion_event_t; } pointer_motion_event_t;
typedef struct wm_pointer_enter_event_t { typedef struct pointer_enter_event_t {
wm_window_id_t window; window_id_t window;
wm_point_t root; point_t root;
wm_point_t local; point_t local;
} wm_pointer_enter_event_t; } pointer_enter_event_t;
typedef struct wm_window_map_request_event_t { typedef struct window_map_request_event_t {
wm_window_info_t info; window_info_t info;
wm_window_id_t transient_for; window_id_t transient_for;
bool is_dialog; bool is_dialog;
} wm_window_map_request_event_t; } window_map_request_event_t;
typedef enum wm_window_remove_reason_t { typedef enum window_remove_reason_t {
WM_WINDOW_REMOVE_WITHDRAWN, ZDWM_WINDOW_REMOVE_WITHDRAWN,
WM_WINDOW_REMOVE_DESTROY, ZDWM_WINDOW_REMOVE_DESTROY,
} wm_window_remove_reason_t; } window_remove_reason_t;
typedef struct wm_window_remove_event_t { typedef struct window_remove_event_t {
wm_window_id_t window; window_id_t window;
wm_window_remove_reason_t reason; window_remove_reason_t reason;
} wm_window_remove_event_t; } window_remove_event_t;
typedef enum wm_window_metadata_change_flags_t { typedef enum window_metadata_change_flags_t {
WM_WINDOW_METADATA_CHANGE_TITLE = 1u << 0, ZDWM_WINDOW_METADATA_CHANGE_TITLE = 1u << 0,
WM_WINDOW_METADATA_CHANGE_APP_ID = 1u << 1, ZDWM_WINDOW_METADATA_CHANGE_APP_ID = 1u << 1,
WM_WINDOW_METADATA_CHANGE_CLASS = 1u << 2, ZDWM_WINDOW_METADATA_CHANGE_CLASS = 1u << 2,
WM_WINDOW_METADATA_CHANGE_INSTANCE = 1u << 3, ZDWM_WINDOW_METADATA_CHANGE_INSTANCE = 1u << 3,
} wm_window_metadata_change_flags_t; } window_metadata_change_flags_t;
typedef struct wm_window_metadata_change_event_t { typedef struct window_metadata_change_event_t {
wm_window_id_t window; window_id_t window;
uint32_t changed_fields; uint32_t changed_fields;
const char *title; const char *title;
const char *app_id; const char *app_id;
const char *class_name; const char *class_name;
const char *instance_name; const char *instance_name;
} wm_window_metadata_change_event_t; } window_metadata_change_event_t;
typedef enum wm_window_hint_changed_flags_t { typedef enum window_hint_changed_flags_t {
WM_WINDOW_HINT_CHANGED_NONE = 0, ZDWM_WINDOW_HINT_CHANGED_NONE = 0,
WM_WINDOW_HINT_CHANGED_URGENT = 1u << 0, ZDWM_WINDOW_HINT_CHANGED_URGENT = 1u << 0,
WM_WINDOW_HINT_CHANGED_FIXED_SIZE = 1u << 1, ZDWM_WINDOW_HINT_CHANGED_FIXED_SIZE = 1u << 1,
WM_WINDOW_HINT_CHANGED_SKIP_TASKBAR = 1u << 2, ZDWM_WINDOW_HINT_CHANGED_SKIP_TASKBAR = 1u << 2,
} wm_window_hint_changed_flags_t; } window_hint_changed_flags_t;
typedef struct wm_window_hints_changed_event_t { typedef struct window_hints_changed_event_t {
wm_window_id_t window; window_id_t window;
uint32_t changed_fields; uint32_t changed_fields;
bool urgent; bool urgent;
bool fixed_size; bool fixed_size;
bool skip_taskbar; bool skip_taskbar;
} wm_window_hints_changed_event_t; } window_hints_changed_event_t;
typedef enum wm_window_activation_source_t { typedef enum window_activation_source_t {
WM_WINDOW_ACTIVATION_SOURCE_LEGACY = 0, ZDWM_WINDOW_ACTIVATION_SOURCE_LEGACY = 0,
WM_WINDOW_ACTIVATION_SOURCE_APPLICATION = 1, ZDWM_WINDOW_ACTIVATION_SOURCE_APPLICATION = 1,
WM_WINDOW_ACTIVATION_SOURCE_PAGER = 2, ZDWM_WINDOW_ACTIVATION_SOURCE_PAGER = 2,
} wm_window_activation_source_t; } window_activation_source_t;
typedef struct wm_window_activate_request_event_t { typedef struct window_activate_request_event_t {
wm_window_id_t window; window_id_t window;
wm_window_activation_source_t source; window_activation_source_t source;
} wm_window_activate_request_event_t; } window_activate_request_event_t;
typedef enum wm_window_state_request_kind_t { typedef enum window_state_request_kind_t {
WM_WINDOW_STATE_REQUEST_FULLSCREEN, ZDWM_WINDOW_STATE_REQUEST_FULLSCREEN,
WM_WINDOW_STATE_REQUEST_MAXIMIZED, ZDWM_WINDOW_STATE_REQUEST_MAXIMIZED,
WM_WINDOW_STATE_REQUEST_MINIMIZED, ZDWM_WINDOW_STATE_REQUEST_MINIMIZED,
} wm_window_state_request_kind_t; } window_state_request_kind_t;
typedef enum wm_window_state_request_action_t { typedef enum window_state_request_action_t {
WM_WINDOW_STATE_REQUEST_ACTION_ADD, ZDWM_WINDOW_STATE_REQUEST_ACTION_ADD,
WM_WINDOW_STATE_REQUEST_ACTION_REMOVE, ZDWM_WINDOW_STATE_REQUEST_ACTION_REMOVE,
WM_WINDOW_STATE_REQUEST_ACTION_TOGGLE, ZDWM_WINDOW_STATE_REQUEST_ACTION_TOGGLE,
} wm_window_state_request_action_t; } window_state_request_action_t;
typedef struct wm_window_state_request_event_t { typedef struct window_state_request_event_t {
wm_window_id_t window; window_id_t window;
wm_window_state_request_kind_t kind; window_state_request_kind_t kind;
wm_window_state_request_action_t action; window_state_request_action_t action;
} wm_window_state_request_event_t; } window_state_request_event_t;
typedef enum wm_configure_request_field_t { typedef enum configure_request_field_t {
WM_CONFIGURE_REQUEST_FIELD_NONE = 0, ZDWM_CONFIGURE_REQUEST_FIELD_NONE = 0,
WM_CONFIGURE_REQUEST_FIELD_X = 1u << 0, ZDWM_CONFIGURE_REQUEST_FIELD_X = 1u << 0,
WM_CONFIGURE_REQUEST_FIELD_Y = 1u << 1, ZDWM_CONFIGURE_REQUEST_FIELD_Y = 1u << 1,
WM_CONFIGURE_REQUEST_FIELD_WIDTH = 1u << 2, ZDWM_CONFIGURE_REQUEST_FIELD_WIDTH = 1u << 2,
WM_CONFIGURE_REQUEST_FIELD_HEIGHT = 1u << 3, ZDWM_CONFIGURE_REQUEST_FIELD_HEIGHT = 1u << 3,
} wm_configure_request_field_t; } configure_request_field_t;
typedef struct wm_configure_request_event_t { typedef struct configure_request_event_t {
wm_window_id_t window; window_id_t window;
uint32_t changed_fields; uint32_t changed_fields;
int32_t x; int32_t x;
int32_t y; int32_t y;
int32_t width; int32_t width;
int32_t height; int32_t height;
} wm_configure_request_event_t; } configure_request_event_t;
typedef struct wm_event_t { typedef struct event_t {
wm_event_type_t type; event_type_t type;
uint32_t time_ms; uint32_t time_ms;
union { union {
wm_key_press_event_t key_press; key_press_event_t key_press;
wm_pointer_button_event_t pointer_button_press; pointer_button_event_t pointer_button_press;
wm_pointer_button_event_t pointer_button_release; pointer_button_event_t pointer_button_release;
wm_pointer_motion_event_t pointer_motion; pointer_motion_event_t pointer_motion;
wm_pointer_enter_event_t pointer_enter; pointer_enter_event_t pointer_enter;
wm_window_map_request_event_t window_map_request; window_map_request_event_t window_map_request;
wm_window_remove_event_t window_remove; window_remove_event_t window_remove;
wm_window_metadata_change_event_t window_metadata_change; window_metadata_change_event_t window_metadata_change;
wm_window_hints_changed_event_t window_hints_changed; window_hints_changed_event_t window_hints_changed;
wm_window_activate_request_event_t window_activate_request; window_activate_request_event_t window_activate_request;
wm_window_state_request_event_t window_state_request; window_state_request_event_t window_state_request;
wm_configure_request_event_t configure_request; configure_request_event_t configure_request;
} data; } data;
} wm_event_t; } event_t;

View File

@@ -4,25 +4,21 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "utils.h" #include "base/memory.h"
void wm_layout_result_init(wm_layout_result_t *result) { void layout_result_init(layout_result_t *result) {
result->item_count = 0; result->item_count = 0;
result->item_capacity = INIT_CAPACITY; result->item_capacity = INIT_CAPACITY;
result->items = p_new(wm_layout_item_t, result->item_capacity); result->items = p_new(layout_item_t, result->item_capacity);
} }
void wm_layout_result_reset(wm_layout_result_t *result) { void layout_result_cleanup(layout_result_t *result) {
result->item_count = 0;
}
void wm_layout_result_cleanup(wm_layout_result_t *result) {
p_delete(&result->items); p_delete(&result->items);
result->item_count = 0; result->item_count = 0;
result->item_capacity = 0; result->item_capacity = 0;
} }
void wm_layout_result_push(wm_layout_result_t *result, wm_layout_item_t item) { void layout_result_push(layout_result_t *result, layout_item_t item) {
if (result->item_count == result->item_capacity) { if (result->item_count == result->item_capacity) {
size_t new_capacity = next_capacity(result->item_capacity); size_t new_capacity = next_capacity(result->item_capacity);
p_realloc(&result->items, new_capacity); p_realloc(&result->items, new_capacity);
@@ -32,68 +28,81 @@ void wm_layout_result_push(wm_layout_result_t *result, wm_layout_item_t item) {
result->items[result->item_count++] = item; result->items[result->item_count++] = item;
} }
void wm_layout_registry_init(wm_layout_registry_t *registry) { void layout_registry_init(layout_registry_t *registry) {
registry->slot_count = 0; registry->slot_count = 0;
registry->slot_capacity = INIT_CAPACITY; registry->slot_capacity = INIT_CAPACITY;
registry->slots = p_new(wm_layout_slot_t, registry->slot_capacity); registry->slots = p_new(layout_slot_t, registry->slot_capacity);
} }
void wm_layout_registry_cleanup(wm_layout_registry_t *registry) { void layout_registry_cleanup(layout_registry_t *registry) {
while (registry->slot_count > 0) { while (registry->slot_count > 0) {
wm_layout_slot_t *r = &registry->slots[--registry->slot_count]; layout_slot_t *r = &registry->slots[--registry->slot_count];
p_delete(&r->name); p_delete(&r->name);
p_delete(&r->symbol); p_delete(&r->symbol);
p_delete(&r->description); p_delete(&r->description);
r->fn = nullptr; r->fn = nullptr;
r->id = WM_LAYOUT_ID_INVALID; r->id = ZDWM_LAYOUT_ID_INVALID;
} }
p_delete(&registry->slots); p_delete(&registry->slots);
registry->slot_count = 0; registry->slot_count = 0;
registry->slot_capacity = 0; registry->slot_capacity = 0;
} }
size_t wm_layout_registry_count(const wm_layout_registry_t *registry) { bool layout_registry_move(layout_registry_t *src, layout_registry_t *dest) {
if (!src || !dest) return false;
dest->slots = src->slots;
dest->slot_count = src->slot_count;
dest->slot_capacity = src->slot_capacity;
src->slots = nullptr;
src->slot_count = 0;
src->slot_capacity = 0;
return true;
}
size_t layout_registry_count(const layout_registry_t *registry) {
return registry->slot_count; return registry->slot_count;
} }
const wm_layout_slot_t *wm_layout_registry_at( const layout_slot_t *layout_registry_at(const layout_registry_t *registry,
const wm_layout_registry_t *registry, size_t index) { size_t index) {
if (index >= registry->slot_count) return nullptr; if (index >= registry->slot_count) return nullptr;
return &registry->slots[index]; return &registry->slots[index];
} }
wm_layout_id_t wm_layout_register(wm_layout_registry_t *registry, layout_id_t layout_register(layout_registry_t *registry, const char *name,
const char *name, const char *symbol, const char *symbol, const char *description,
const char *description, wm_layout_fn fn) { layout_fn fn) {
if (!name || !symbol) return WM_LAYOUT_ID_INVALID; if (!name || !symbol) return ZDWM_LAYOUT_ID_INVALID;
if (registry->slot_count == registry->slot_capacity) { if (registry->slot_count == registry->slot_capacity) {
size_t new_capacity = next_capacity(registry->slot_capacity); size_t new_capacity = next_capacity(registry->slot_capacity);
p_realloc(&registry->slots, new_capacity); p_realloc(&registry->slots, new_capacity);
registry->slot_capacity = new_capacity; registry->slot_capacity = new_capacity;
} }
wm_layout_id_t id = (wm_layout_id_t)registry->slot_count; layout_id_t id = (layout_id_t)registry->slot_count;
wm_layout_slot_t *r = &registry->slots[registry->slot_count]; layout_slot_t *r = &registry->slots[registry->slot_count];
r->id = id; r->id = id;
r->name = p_strdup(name); r->name = p_strdup(name);
r->symbol = p_strdup(symbol); r->symbol = p_strdup(symbol);
r->description = description ? p_strdup(description) : nullptr; r->description = p_strdup_nullable(description);
r->fn = fn; r->fn = fn;
registry->slot_count++; registry->slot_count++;
return id; return id;
} }
wm_layout_fn wm_layout_get(const wm_layout_registry_t *registry, layout_fn layout_get(const layout_registry_t *registry, layout_id_t id) {
wm_layout_id_t id) { const layout_slot_t *layout_slot = layout_slot_get(registry, id);
const wm_layout_slot_t *layout_slot = wm_layout_slot_get(registry, id);
if (layout_slot) return layout_slot->fn; if (layout_slot) return layout_slot->fn;
return nullptr; return nullptr;
} }
const wm_layout_slot_t *wm_layout_slot_get(const wm_layout_registry_t *registry, const layout_slot_t *layout_slot_get(const layout_registry_t *registry,
wm_layout_id_t id) { layout_id_t id) {
if (id >= registry->slot_count) return nullptr; if (id >= registry->slot_count) return nullptr;
return &registry->slots[id]; return &registry->slots[id];
} }

View File

@@ -1,49 +1,17 @@
#pragma once #pragma once
#include <stddef.h> #include <stddef.h>
#include <zdwm/layout.h>
#include "core/types.h" #include "core/types.h"
typedef struct wm_layout_ctx_t { typedef zdwm_layout_ctx_t layout_ctx_t;
wm_output_id_t output_id; typedef zdwm_layout_item_t layout_item_t;
wm_workspace_id_t workspace_id; typedef zdwm_layout_result_t layout_result_t;
wm_window_id_t focused_window_id; typedef zdwm_layout_fn layout_fn;
wm_rect_t workarea;
/*
* 参与本次布局计算的窗口 ID 列表。
*
* 调用方负责在进入 layout 前完成筛选;这里不包含 floating、sticky、
* fullscreen、minimized 等不参与平铺计算的窗口。
*
* 数组顺序具有语义layout 应按该顺序解释窗口排列优先级。
*/
const wm_window_id_t *window_ids;
size_t window_count;
} wm_layout_ctx_t;
typedef struct wm_layout_item_t { typedef struct layout_slot_t {
wm_window_id_t window_id; layout_id_t id;
wm_rect_t rect; /* 平铺窗口的目标外框矩形(包含边框) */
} wm_layout_item_t;
typedef struct wm_layout_result_t {
wm_layout_item_t *items;
size_t item_count;
size_t item_capacity;
} wm_layout_result_t;
/*
* 返回 true 表示成功生成合法的布局结果。
* 返回 false 表示布局计算失败,调用方应丢弃本次结果。
*
* 该返回值不表示窗口最终几何是否发生变化;
* 是否有改动应由调用方在比较当前状态与布局结果后决定。
*/
typedef bool (*wm_layout_fn)(const wm_layout_ctx_t *ctx,
wm_layout_result_t *out);
typedef struct wm_layout_slot_t {
wm_layout_id_t id;
/* /*
* 布局名称,用于: * 布局名称,用于:
* - 配置引用 * - 配置引用
@@ -70,14 +38,14 @@ typedef struct wm_layout_slot_t {
* *
* fn == NULL 表示 floating 布局,即该布局不参与平铺计算。 * fn == NULL 表示 floating 布局,即该布局不参与平铺计算。
*/ */
wm_layout_fn fn; layout_fn fn;
} wm_layout_slot_t; } layout_slot_t;
typedef struct wm_layout_registry_t { typedef struct layout_registry_t {
wm_layout_slot_t *slots; layout_slot_t *slots;
size_t slot_count; size_t slot_count;
size_t slot_capacity; size_t slot_capacity;
} wm_layout_registry_t; } layout_registry_t;
/* /*
* 调用约束: * 调用约束:
@@ -85,11 +53,10 @@ typedef struct wm_layout_registry_t {
* - 除 init 之外,其余 result 相关接口都要求 result 已初始化 * - 除 init 之外,其余 result 相关接口都要求 result 已初始化
* - 传入空指针或未初始化对象属于调用方错误 * - 传入空指针或未初始化对象属于调用方错误
*/ */
void wm_layout_result_init(wm_layout_result_t *result); void layout_result_init(layout_result_t *result);
void wm_layout_result_reset(wm_layout_result_t *result); void layout_result_cleanup(layout_result_t *result);
void wm_layout_result_cleanup(wm_layout_result_t *result);
void wm_layout_result_push(wm_layout_result_t *result, wm_layout_item_t item); void layout_result_push(layout_result_t *result, layout_item_t item);
/* /*
* 调用约束: * 调用约束:
@@ -97,11 +64,12 @@ void wm_layout_result_push(wm_layout_result_t *result, wm_layout_item_t item);
* - 除 init 之外,其余 registry 相关接口都要求 registry 已初始化 * - 除 init 之外,其余 registry 相关接口都要求 registry 已初始化
* - 传入空指针或未初始化对象属于调用方错误 * - 传入空指针或未初始化对象属于调用方错误
*/ */
void wm_layout_registry_init(wm_layout_registry_t *registry); void layout_registry_init(layout_registry_t *registry);
void wm_layout_registry_cleanup(wm_layout_registry_t *registry); void layout_registry_cleanup(layout_registry_t *registry);
size_t wm_layout_registry_count(const wm_layout_registry_t *registry); bool layout_registry_move(layout_registry_t *src, layout_registry_t *dest);
const wm_layout_slot_t *wm_layout_registry_at( size_t layout_registry_count(const layout_registry_t *registry);
const wm_layout_registry_t *registry, size_t index); const layout_slot_t *layout_registry_at(const layout_registry_t *registry,
size_t index);
/* /*
* 注册一个布局并返回其稳定 ID。 * 注册一个布局并返回其稳定 ID。
@@ -117,13 +85,13 @@ const wm_layout_slot_t *wm_layout_registry_at(
* *
* 返回值: * 返回值:
* - 成功时返回新注册布局的 ID * - 成功时返回新注册布局的 ID
* - name 或 symbol 非法时返回 WM_LAYOUT_ID_INVALID * - name 或 symbol 非法时返回 ZDWM_LAYOUT_ID_INVALID
* *
* 注册成功后,布局 ID 与其在 registry 中的槽位索引保持一致。 * 注册成功后,布局 ID 与其在 registry 中的槽位索引保持一致。
*/ */
wm_layout_id_t wm_layout_register(wm_layout_registry_t *registry, layout_id_t layout_register(layout_registry_t *registry, const char *name,
const char *name, const char *symbol, const char *symbol, const char *description,
const char *description, wm_layout_fn fn); layout_fn fn);
/* /*
* 获取可执行的布局函数。 * 获取可执行的布局函数。
* *
@@ -132,7 +100,6 @@ wm_layout_id_t wm_layout_register(wm_layout_registry_t *registry,
* - id 对应的 slot 不存在 * - id 对应的 slot 不存在
* - slot 存在,但 fn == NULL表示 floating 布局) * - slot 存在,但 fn == NULL表示 floating 布局)
*/ */
wm_layout_fn wm_layout_get(const wm_layout_registry_t *registry, layout_fn layout_get(const layout_registry_t *registry, layout_id_t id);
wm_layout_id_t id); const layout_slot_t *layout_slot_get(const layout_registry_t *registry,
const wm_layout_slot_t *wm_layout_slot_get(const wm_layout_registry_t *registry, layout_id_t id);
wm_layout_id_t id);

View File

@@ -2,50 +2,101 @@
#include <stddef.h> #include <stddef.h>
#include "base/memory.h"
#include "core/backend.h" #include "core/backend.h"
#include "core/state.h" #include "core/state.h"
#include "core/types.h"
#include "core/wm_desc.h" #include "core/wm_desc.h"
#include "utils.h"
bool wm_runtime_init(wm_runtime_t *runtime) { static bool runtime_workspace_desc_has_valid_layouts(
p_clear(runtime, 1); const layout_registry_t *layouts, const workspace_desc_t *workspace) {
if (!layouts || !workspace) return false;
wm_backend_t *backend = wm_backend_create(nullptr); for (size_t i = 0; i < workspace->layout_count; i++) {
if (!backend) return false; if (!layout_slot_get(layouts, workspace->layout_ids[i])) 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; return true;
} }
void wm_runtime_shutdown(wm_runtime_t *runtime) { static bool runtime_init_desc_valid(const runtime_init_desc_t *desc) {
if (!desc || !desc->backend || !desc->outputs || desc->output_count == 0) {
return false;
}
if (!desc->layouts.slots || desc->layouts.slot_count == 0) return false;
if (!desc->workspaces || desc->workspace_count == 0) return false;
for (size_t i = 0; i < desc->workspace_count; i++) {
const workspace_desc_t *workspace = &desc->workspaces[i];
if (!workspace_desc_valid(workspace, desc->output_count)) return false;
if (!runtime_workspace_desc_has_valid_layouts(&desc->layouts, workspace)) {
return false;
}
}
return true;
}
static void runtime_workspace_desc_list_cleanup(workspace_desc_t **list,
size_t *count) {
if (!list || !count) return;
for (size_t i = 0; i < *count; i++) {
workspace_desc_t *workspace = &(*list)[i];
p_delete(&workspace->name);
p_delete(&workspace->layout_ids);
}
p_delete(list);
*count = 0;
}
bool runtime_init(runtime_t *runtime, runtime_init_desc_t *desc) {
if (!runtime || !runtime_init_desc_valid(desc)) return false;
p_clear(runtime, 1);
runtime->backend = desc->backend;
runtime->layouts = desc->layouts;
desc->backend = nullptr;
p_clear(&desc->layouts, 1);
state_init(&runtime->state, desc->outputs, desc->output_count,
desc->workspaces, desc->workspace_count);
runtime_workspace_desc_list_cleanup(&desc->workspaces,
&desc->workspace_count);
desc->outputs = nullptr;
desc->output_count = 0;
return true;
}
void runtime_init_desc_cleanup(runtime_init_desc_t *desc) {
if (!desc) return;
if (desc->backend) {
backend_destroy(desc->backend);
desc->backend = nullptr;
}
if (desc->layouts.slots || desc->layouts.slot_count ||
desc->layouts.slot_capacity) {
layout_registry_cleanup(&desc->layouts);
}
runtime_workspace_desc_list_cleanup(&desc->workspaces,
&desc->workspace_count);
desc->outputs = nullptr;
desc->output_count = 0;
}
void runtime_shutdown(runtime_t *runtime) {
runtime->running = false; runtime->running = false;
wm_state_cleanup(&runtime->state); if (runtime->layouts.slots || runtime->layouts.slot_count ||
wm_backend_destroy(runtime->backend); runtime->layouts.slot_capacity) {
layout_registry_cleanup(&runtime->layouts);
}
state_cleanup(&runtime->state);
backend_destroy(runtime->backend);
runtime->backend = nullptr; runtime->backend = nullptr;
} }

View File

@@ -1,15 +1,28 @@
#pragma once #pragma once
#include "core/backend.h" #include "core/backend.h"
#include "core/layout.h"
#include "core/state.h" #include "core/state.h"
typedef struct wm_runtime_t { typedef struct runtime_init_desc_t {
backend_t *backend;
output_info_t *outputs;
size_t output_count;
layout_registry_t layouts;
workspace_desc_t *workspaces;
size_t workspace_count;
} runtime_init_desc_t;
typedef struct runtime_t {
bool running; bool running;
bool will_restart; bool will_restart;
wm_state_t state; state_t state;
wm_backend_t *backend; layout_registry_t layouts;
} wm_runtime_t; backend_t *backend;
} runtime_t;
bool wm_runtime_init(wm_runtime_t *runtime); bool runtime_init(runtime_t *runtime, runtime_init_desc_t *desc);
void wm_runtime_shutdown(wm_runtime_t *runtime); void runtime_init_desc_cleanup(runtime_init_desc_t *desc);
void runtime_shutdown(runtime_t *runtime);

View File

@@ -3,63 +3,64 @@
#include <stddef.h> #include <stddef.h>
#include <string.h> #include <string.h>
#include "base/log.h"
#include "base/memory.h"
#include "core/types.h" #include "core/types.h"
#include "core/wm_desc.h" #include "core/wm_desc.h"
#include "utils.h"
void wm_state_init(wm_state_t *state, const wm_output_info_t *outputs, void state_init(state_t *state, const output_info_t *outputs,
size_t output_count, const wm_workspace_desc_t *workspaces, size_t output_count, const workspace_desc_t *workspaces,
size_t workspace_count) { size_t workspace_count) {
p_clear(state, 1); p_clear(state, 1);
state->outputs = p_new(wm_output_t, output_count); state->outputs = p_new(output_t, output_count);
for (size_t i = 0; i < output_count; i++) { for (size_t i = 0; i < output_count; i++) {
const wm_output_info_t *output_info = &outputs[i]; const output_info_t *output_info = &outputs[i];
wm_output_t *output = &state->outputs[i]; output_t *output = &state->outputs[i];
output->id = (wm_output_id_t)i; output->id = (output_id_t)i;
output->current_workspace_id = WM_WORKSPACE_ID_INVALID; output->current_workspace_id = ZDWM_WORKSPACE_ID_INVALID;
output->name = p_strdup(output_info->name); output->name = p_strdup_nullable(output_info->name);
output->geometry = output_info->geometry; output->geometry = output_info->geometry;
output->workarea = output_info->geometry; output->workarea = output_info->geometry;
} }
state->output_count = output_count; state->output_count = output_count;
state->workspaces = p_new(wm_workspace_t, workspace_count); state->workspaces = p_new(workspace_t, workspace_count);
for (size_t i = 0; i < workspace_count; i++) { for (size_t i = 0; i < workspace_count; i++) {
const wm_workspace_desc_t *workspace_desc = &workspaces[i]; const workspace_desc_t *workspace_desc = &workspaces[i];
if (!wm_workspace_desc_valid(workspace_desc, output_count)) { if (!workspace_desc_valid(workspace_desc, output_count)) {
fatal("workspace desc at index %zu invalid", i); fatal("workspace desc at index %zu invalid", i);
} }
wm_output_t *output = &state->outputs[workspace_desc->output_index]; output_t *output = &state->outputs[workspace_desc->output_index];
wm_workspace_t *workspace = &state->workspaces[i]; workspace_t *workspace = &state->workspaces[i];
workspace->id = (wm_workspace_id_t)i; workspace->id = (workspace_id_t)i;
workspace->output_id = output->id; workspace->output_id = output->id;
workspace->focused_window_id = WM_WINDOW_ID_INVALID; workspace->focused_window_id = ZDWM_WINDOW_ID_INVALID;
workspace->available_layouts = workspace->available_layouts =
p_copy(workspace_desc->layout_ids, workspace_desc->layout_count); p_copy(workspace_desc->layout_ids, workspace_desc->layout_count);
workspace->layout_id = workspace_desc->initial_layout_id; workspace->layout_id = workspace_desc->initial_layout_id;
workspace->layout_count = workspace_desc->layout_count; workspace->layout_count = workspace_desc->layout_count;
workspace->name = p_strdup(workspace_desc->name); workspace->name = p_strdup(workspace_desc->name);
if (output->current_workspace_id == WM_WORKSPACE_ID_INVALID) { if (output->current_workspace_id == ZDWM_WORKSPACE_ID_INVALID) {
output->current_workspace_id = workspace->id; output->current_workspace_id = workspace->id;
} }
} }
state->workspace_count = workspace_count; state->workspace_count = workspace_count;
for (size_t i = 0; i < state->output_count; i++) { for (size_t i = 0; i < state->output_count; i++) {
const wm_output_t *output = &state->outputs[i]; const output_t *output = &state->outputs[i];
if (output->current_workspace_id == WM_WORKSPACE_ID_INVALID) { if (output->current_workspace_id == ZDWM_WORKSPACE_ID_INVALID) {
fatal("output at index %zu has no workspace", i); fatal("output at index %zu has no workspace", i);
} }
} }
} }
void wm_state_cleanup(wm_state_t *state) { void state_cleanup(state_t *state) {
for (size_t i = 0; i < state->window_count; i++) { for (size_t i = 0; i < state->window_count; i++) {
wm_window_t *window = &state->windows[i]; window_t *window = &state->windows[i];
p_delete(&window->title); p_delete(&window->title);
p_delete(&window->app_id); p_delete(&window->app_id);
p_delete(&window->class_name); p_delete(&window->class_name);
@@ -71,7 +72,7 @@ void wm_state_cleanup(wm_state_t *state) {
state->window_capacity = 0; state->window_capacity = 0;
for (size_t i = 0; i < state->workspace_count; i++) { for (size_t i = 0; i < state->workspace_count; i++) {
wm_workspace_t *workspace = &state->workspaces[i]; workspace_t *workspace = &state->workspaces[i];
p_delete(&workspace->name); p_delete(&workspace->name);
p_delete(&workspace->available_layouts); p_delete(&workspace->available_layouts);
workspace->layout_count = 0; workspace->layout_count = 0;
@@ -80,7 +81,7 @@ void wm_state_cleanup(wm_state_t *state) {
state->workspace_count = 0; state->workspace_count = 0;
for (size_t i = 0; i < state->output_count; i++) { for (size_t i = 0; i < state->output_count; i++) {
wm_output_t *output = &state->outputs[i]; output_t *output = &state->outputs[i];
p_delete(&output->name); p_delete(&output->name);
} }
p_delete(&state->outputs); p_delete(&state->outputs);
@@ -89,44 +90,42 @@ void wm_state_cleanup(wm_state_t *state) {
p_clear(state, 1); p_clear(state, 1);
} }
const wm_workspace_t *wm_state_workspace_get(const wm_state_t *state, const workspace_t *state_workspace_get(const state_t *state,
wm_workspace_id_t id) { workspace_id_t id) {
if (id < state->workspace_count) return &state->workspaces[id]; if (id < state->workspace_count) return &state->workspaces[id];
return nullptr; return nullptr;
} }
const wm_workspace_t *wm_state_workspace_at(const wm_state_t *state, const workspace_t *state_workspace_at(const state_t *state, size_t index) {
size_t index) {
if (index < state->workspace_count) return &state->workspaces[index]; if (index < state->workspace_count) return &state->workspaces[index];
return nullptr; return nullptr;
} }
static void wm_state_workspace_adjust_focused_window( static void state_workspace_adjust_focused_window(const state_t *state,
const wm_state_t *state, wm_workspace_id_t workspace_id) { workspace_id_t workspace_id) {
wm_workspace_t *workspace = workspace_t *workspace =
(wm_workspace_t *)wm_state_workspace_get(state, workspace_id); (workspace_t *)state_workspace_get(state, workspace_id);
if (!workspace) return; if (!workspace) return;
const wm_window_t *window = const window_t *window =
wm_state_window_get(state, workspace->focused_window_id); state_window_get(state, workspace->focused_window_id);
if (window && window->workspace_id == workspace_id) return; if (window && window->workspace_id == workspace_id) return;
for (size_t i = state->window_count; i > 0; i--) { for (size_t i = state->window_count; i > 0; i--) {
wm_window_id_t window_id = state->stack_order[i - 1]; window_id_t window_id = state->stack_order[i - 1];
const wm_window_t *window = wm_state_window_get(state, window_id); const window_t *window = state_window_get(state, window_id);
if (window && window->workspace_id == workspace_id) { if (window && window->workspace_id == workspace_id) {
workspace->focused_window_id = window_id; workspace->focused_window_id = window_id;
return; return;
} }
} }
workspace->focused_window_id = WM_WINDOW_ID_INVALID; workspace->focused_window_id = ZDWM_WINDOW_ID_INVALID;
} }
bool wm_state_workspace_cycle_layout(wm_state_t *state, bool state_workspace_cycle_layout(state_t *state, workspace_id_t workspace_id) {
wm_workspace_id_t workspace_id) { workspace_t *workspace =
wm_workspace_t *workspace = (workspace_t *)state_workspace_get(state, workspace_id);
(wm_workspace_t *)wm_state_workspace_get(state, workspace_id);
if (!workspace) return false; if (!workspace) return false;
size_t next_index = 0; size_t next_index = 0;
@@ -146,11 +145,11 @@ bool wm_state_workspace_cycle_layout(wm_state_t *state,
return true; return true;
} }
bool wm_state_workspace_set_layout_by_index(wm_state_t *state, bool state_workspace_set_layout_by_index(state_t *state,
wm_workspace_id_t workspace_id, workspace_id_t workspace_id,
size_t index) { size_t index) {
wm_workspace_t *workspace = workspace_t *workspace =
(wm_workspace_t *)wm_state_workspace_get(state, workspace_id); (workspace_t *)state_workspace_get(state, workspace_id);
if (!workspace) return false; if (!workspace) return false;
if (index >= workspace->layout_count) return false; if (index >= workspace->layout_count) return false;
@@ -158,11 +157,11 @@ bool wm_state_workspace_set_layout_by_index(wm_state_t *state,
return true; return true;
} }
bool wm_state_workspace_set_layout_by_id(wm_state_t *state, bool state_workspace_set_layout_by_id(state_t *state,
wm_workspace_id_t workspace_id, workspace_id_t workspace_id,
wm_layout_id_t layout_id) { layout_id_t layout_id) {
wm_workspace_t *workspace = workspace_t *workspace =
(wm_workspace_t *)wm_state_workspace_get(state, workspace_id); (workspace_t *)state_workspace_get(state, workspace_id);
if (!workspace) return false; if (!workspace) return false;
for (size_t i = 0; i < workspace->layout_count; i++) { for (size_t i = 0; i < workspace->layout_count; i++) {
if (workspace->available_layouts[i] == layout_id) { if (workspace->available_layouts[i] == layout_id) {
@@ -174,29 +173,29 @@ bool wm_state_workspace_set_layout_by_id(wm_state_t *state,
return false; return false;
} }
void wm_state_workspace_set_focused_window(wm_state_t *state, void state_workspace_set_focused_window(state_t *state,
wm_workspace_id_t workspace_id, workspace_id_t workspace_id,
wm_window_id_t window_id) { window_id_t window_id) {
wm_workspace_t *workspace = workspace_t *workspace =
(wm_workspace_t *)wm_state_workspace_get(state, workspace_id); (workspace_t *)state_workspace_get(state, workspace_id);
if (!workspace) return; if (!workspace) return;
for (size_t i = 0; i < state->window_count; i++) { for (size_t i = 0; i < state->window_count; i++) {
wm_window_t *window = &state->windows[i]; window_t *window = &state->windows[i];
if (window->id == window_id && window->workspace_id == workspace_id) { if (window->id == window_id && window->workspace_id == workspace_id) {
workspace->focused_window_id = window_id; workspace->focused_window_id = window_id;
return; return;
} }
} }
wm_state_workspace_adjust_focused_window(state, workspace_id); state_workspace_adjust_focused_window(state, workspace_id);
} }
size_t wm_state_workspace_count(const wm_state_t *state) { size_t state_workspace_count(const state_t *state) {
return state->workspace_count; return state->workspace_count;
} }
bool wm_state_workspace_valid(const wm_state_t *state, wm_workspace_id_t id) { bool state_workspace_valid(const state_t *state, workspace_id_t id) {
return id < state->workspace_count; return id < state->workspace_count;
/* 线性扫描作为备用 */ /* 线性扫描作为备用 */
@@ -211,39 +210,35 @@ bool wm_state_workspace_valid(const wm_state_t *state, wm_workspace_id_t id) {
#endif #endif
} }
const wm_output_t *wm_state_output_get(const wm_state_t *state, const output_t *state_output_get(const state_t *state, output_id_t id) {
wm_output_id_t id) {
if (id < state->output_count) return &state->outputs[id]; if (id < state->output_count) return &state->outputs[id];
return nullptr; return nullptr;
} }
const wm_output_t *wm_state_output_at(const wm_state_t *state, size_t index) { const output_t *state_output_at(const state_t *state, size_t index) {
if (index < state->output_count) return &state->outputs[index]; if (index < state->output_count) return &state->outputs[index];
return nullptr; return nullptr;
} }
void wm_state_output_set_workarea(wm_state_t *state, wm_output_id_t output_id, void state_output_set_workarea(state_t *state, output_id_t output_id,
wm_rect_t workarea) { rect_t workarea) {
wm_output_t *output = (wm_output_t *)wm_state_output_get(state, output_id); output_t *output = (output_t *)state_output_get(state, output_id);
if (output) output->workarea = workarea; if (output) output->workarea = workarea;
} }
void wm_state_output_set_current_workspace(wm_state_t *state, void state_output_set_current_workspace(state_t *state, output_id_t output_id,
wm_output_id_t output_id, workspace_id_t workspace_id) {
wm_workspace_id_t workspace_id) { output_t *output = (output_t *)state_output_get(state, output_id);
wm_output_t *output = (wm_output_t *)wm_state_output_get(state, output_id);
if (!output) return; if (!output) return;
const wm_workspace_t *workspace = wm_state_workspace_get(state, workspace_id); const workspace_t *workspace = state_workspace_get(state, workspace_id);
if (!workspace || workspace->output_id != output_id) return; if (!workspace || workspace->output_id != output_id) return;
output->current_workspace_id = workspace_id; output->current_workspace_id = workspace_id;
} }
size_t wm_state_output_count(const wm_state_t *state) { size_t state_output_count(const state_t *state) { return state->output_count; }
return state->output_count;
}
bool wm_state_output_valid(const wm_state_t *state, wm_output_id_t id) { bool state_output_valid(const state_t *state, output_id_t id) {
return id < state->output_count; return id < state->output_count;
/* 线性扫描作为备用 */ /* 线性扫描作为备用 */
@@ -258,10 +253,9 @@ bool wm_state_output_valid(const wm_state_t *state, wm_output_id_t id) {
#endif #endif
} }
const wm_window_t *wm_state_window_add(wm_state_t *state, const window_t *state_window_add(state_t *state, const window_info_t *info) {
const wm_window_info_t *info) { window_id_t id = info->id;
wm_window_id_t id = info->id; window_t *window = (window_t *)state_window_get(state, id);
wm_window_t *window = (wm_window_t *)wm_state_window_get(state, id);
if (!window) { if (!window) {
if (state->window_count == state->window_capacity) { if (state->window_count == state->window_capacity) {
size_t capacity = next_capacity(state->window_capacity); size_t capacity = next_capacity(state->window_capacity);
@@ -272,27 +266,26 @@ const wm_window_t *wm_state_window_add(wm_state_t *state,
window = &state->windows[state->window_count]; window = &state->windows[state->window_count];
p_clear(window, 1); p_clear(window, 1);
window->id = id; window->id = id;
window->workspace_id = WM_WORKSPACE_ID_INVALID; window->workspace_id = ZDWM_WORKSPACE_ID_INVALID;
state->stack_order[state->window_count] = id; state->stack_order[state->window_count] = id;
state->window_count++; state->window_count++;
} }
wm_state_window_set_geometry_mode(state, id, info->geometry_mode); state_window_set_geometry_mode(state, id, info->geometry_mode);
wm_state_window_set_urgent(state, id, info->urgent); state_window_set_urgent(state, id, info->urgent);
wm_state_window_set_fixed_size(state, id, info->fixed_size); state_window_set_fixed_size(state, id, info->fixed_size);
wm_state_window_set_frame_rect(state, id, info->frame_rect); state_window_set_frame_rect(state, id, info->frame_rect);
wm_state_window_set_title(state, id, info->title); state_window_set_title(state, id, info->title);
wm_state_window_set_app_id(state, id, info->app_id); state_window_set_app_id(state, id, info->app_id);
wm_state_window_set_class(state, id, info->class_name); state_window_set_class(state, id, info->class_name);
wm_state_window_set_instance(state, id, info->instance_name); state_window_set_instance(state, id, info->instance_name);
wm_state_window_set_skip_taskbar(state, id, info->skip_taskbar); state_window_set_skip_taskbar(state, id, info->skip_taskbar);
return window; return window;
} }
const wm_window_t *wm_state_window_get(const wm_state_t *state, const window_t *state_window_get(const state_t *state, window_id_t id) {
wm_window_id_t id) {
for (size_t i = 0; i < state->window_count; i++) { for (size_t i = 0; i < state->window_count; i++) {
if (state->windows[i].id == id) return &state->windows[i]; if (state->windows[i].id == id) return &state->windows[i];
} }
@@ -300,13 +293,13 @@ const wm_window_t *wm_state_window_get(const wm_state_t *state,
return nullptr; return nullptr;
} }
const wm_window_t *wm_state_window_at(const wm_state_t *state, size_t index) { const window_t *state_window_at(const state_t *state, size_t index) {
if (index < state->window_count) return &state->windows[index]; if (index < state->window_count) return &state->windows[index];
return nullptr; return nullptr;
} }
void wm_state_window_remove(wm_state_t *state, wm_window_id_t id) { void state_window_remove(state_t *state, window_id_t id) {
bool matched = false; bool matched = false;
size_t index = 0; size_t index = 0;
for (size_t i = 0; i < state->window_count; i++) { for (size_t i = 0; i < state->window_count; i++) {
@@ -318,8 +311,8 @@ void wm_state_window_remove(wm_state_t *state, wm_window_id_t id) {
} }
if (!matched) return; if (!matched) return;
wm_window_t *window = &state->windows[index]; window_t *window = &state->windows[index];
wm_workspace_id_t workspace_id = window->workspace_id; workspace_id_t workspace_id = window->workspace_id;
p_delete(&window->title); p_delete(&window->title);
p_delete(&window->app_id); p_delete(&window->app_id);
p_delete(&window->class_name); p_delete(&window->class_name);
@@ -330,8 +323,8 @@ void wm_state_window_remove(wm_state_t *state, wm_window_id_t id) {
} }
window = &state->windows[state->window_count - 1]; window = &state->windows[state->window_count - 1];
p_clear(window, 1); p_clear(window, 1);
window->id = WM_WINDOW_ID_INVALID; window->id = ZDWM_WINDOW_ID_INVALID;
window->workspace_id = WM_WORKSPACE_ID_INVALID; window->workspace_id = ZDWM_WORKSPACE_ID_INVALID;
matched = false; matched = false;
for (size_t i = 0; i < state->window_count; i++) { for (size_t i = 0; i < state->window_count; i++) {
@@ -345,158 +338,154 @@ void wm_state_window_remove(wm_state_t *state, wm_window_id_t id) {
for (size_t i = index + 1; i < state->window_count; i++) { for (size_t i = index + 1; i < state->window_count; i++) {
state->stack_order[i - 1] = state->stack_order[i]; state->stack_order[i - 1] = state->stack_order[i];
} }
state->stack_order[state->window_count - 1] = WM_WINDOW_ID_INVALID; state->stack_order[state->window_count - 1] = ZDWM_WINDOW_ID_INVALID;
} }
state->window_count--; state->window_count--;
wm_workspace_t *workspace = workspace_t *workspace =
(wm_workspace_t *)wm_state_workspace_get(state, workspace_id); (workspace_t *)state_workspace_get(state, workspace_id);
if (workspace && workspace->focused_window_id == id) { if (workspace && workspace->focused_window_id == id) {
wm_state_workspace_adjust_focused_window(state, workspace_id); state_workspace_adjust_focused_window(state, workspace_id);
} }
} }
size_t wm_state_window_count(const wm_state_t *state) { size_t state_window_count(const state_t *state) { return state->window_count; }
return state->window_count;
}
void wm_state_window_set_workspace(wm_state_t *state, wm_window_id_t window_id, void state_window_set_workspace(state_t *state, window_id_t window_id,
wm_workspace_id_t workspace_id) { workspace_id_t workspace_id) {
const wm_workspace_t *workspace = wm_state_workspace_get(state, workspace_id); const workspace_t *workspace = state_workspace_get(state, workspace_id);
wm_window_t *window = (wm_window_t *)wm_state_window_get(state, window_id); window_t *window = (window_t *)state_window_get(state, window_id);
if (!workspace || !window || window->workspace_id == workspace_id) return; if (!workspace || !window || window->workspace_id == workspace_id) return;
workspace = wm_state_workspace_get(state, window->workspace_id); workspace = state_workspace_get(state, window->workspace_id);
window->workspace_id = workspace_id; window->workspace_id = workspace_id;
if (workspace && workspace->focused_window_id == window_id) { if (workspace && workspace->focused_window_id == window_id) {
wm_state_workspace_adjust_focused_window(state, workspace->id); state_workspace_adjust_focused_window(state, workspace->id);
} }
wm_state_workspace_adjust_focused_window(state, workspace_id); state_workspace_adjust_focused_window(state, workspace_id);
} }
void wm_state_window_set_geometry_mode( void state_window_set_geometry_mode(state_t *state, window_id_t window_id,
wm_state_t *state, wm_window_id_t window_id, window_geometry_mode_t geometry_mode) {
wm_window_geometry_mode_t geometry_mode) { window_t *window = (window_t *)state_window_get(state, window_id);
wm_window_t *window = (wm_window_t *)wm_state_window_get(state, window_id);
if (!window) return; if (!window) return;
window->geometry_mode = geometry_mode; window->geometry_mode = geometry_mode;
} }
void wm_state_window_set_floating(wm_state_t *state, wm_window_id_t window_id, void state_window_set_floating(state_t *state, window_id_t window_id,
bool floating) { bool floating) {
wm_window_t *window = (wm_window_t *)wm_state_window_get(state, window_id); window_t *window = (window_t *)state_window_get(state, window_id);
if (!window) return; if (!window) return;
window->floating = floating; window->floating = floating;
} }
void wm_state_window_set_sticky(wm_state_t *state, wm_window_id_t window_id, void state_window_set_sticky(state_t *state, window_id_t window_id,
bool sticky) { bool sticky) {
wm_window_t *window = (wm_window_t *)wm_state_window_get(state, window_id); window_t *window = (window_t *)state_window_get(state, window_id);
if (!window) return; if (!window) return;
window->sticky = sticky; window->sticky = sticky;
if (window->sticky) wm_state_window_set_floating(state, window_id, true); if (window->sticky) state_window_set_floating(state, window_id, true);
} }
void wm_state_window_set_urgent(wm_state_t *state, wm_window_id_t window_id, void state_window_set_urgent(state_t *state, window_id_t window_id,
bool urgent) { bool urgent) {
wm_window_t *window = (wm_window_t *)wm_state_window_get(state, window_id); window_t *window = (window_t *)state_window_get(state, window_id);
if (!window) return; if (!window) return;
window->urgent = urgent; window->urgent = urgent;
} }
void wm_state_window_set_fixed_size(wm_state_t *state, wm_window_id_t window_id, void state_window_set_fixed_size(state_t *state, window_id_t window_id,
bool fixed_size) { bool fixed_size) {
wm_window_t *window = (wm_window_t *)wm_state_window_get(state, window_id); window_t *window = (window_t *)state_window_get(state, window_id);
if (!window) return; if (!window) return;
window->fixed_size = fixed_size; window->fixed_size = fixed_size;
if (fixed_size) wm_state_window_set_floating(state, window_id, true); if (fixed_size) state_window_set_floating(state, window_id, true);
} }
void wm_state_window_set_skip_taskbar(wm_state_t *state, void state_window_set_skip_taskbar(state_t *state, window_id_t window_id,
wm_window_id_t window_id, bool skip_taskbar) {
bool skip_taskbar) { window_t *window = (window_t *)state_window_get(state, window_id);
wm_window_t *window = (wm_window_t *)wm_state_window_get(state, window_id);
if (!window) return; if (!window) return;
window->skip_taskbar = skip_taskbar; window->skip_taskbar = skip_taskbar;
} }
void wm_state_window_set_float_rect(wm_state_t *state, wm_window_id_t window_id, void state_window_set_float_rect(state_t *state, window_id_t window_id,
wm_rect_t float_rect) { rect_t float_rect) {
wm_window_t *window = (wm_window_t *)wm_state_window_get(state, window_id); window_t *window = (window_t *)state_window_get(state, window_id);
if (!window) return; if (!window) return;
window->float_rect = float_rect; window->float_rect = float_rect;
} }
void wm_state_window_set_frame_rect(wm_state_t *state, wm_window_id_t window_id, void state_window_set_frame_rect(state_t *state, window_id_t window_id,
wm_rect_t frame_rect) { rect_t frame_rect) {
wm_window_t *window = (wm_window_t *)wm_state_window_get(state, window_id); window_t *window = (window_t *)state_window_get(state, window_id);
if (!window) return; if (!window) return;
window->frame_rect = frame_rect; window->frame_rect = frame_rect;
} }
bool wm_state_window_set_title(wm_state_t *state, wm_window_id_t window_id, bool state_window_set_title(state_t *state, window_id_t window_id,
const char *title) { const char *title) {
wm_window_t *window = (wm_window_t *)wm_state_window_get(state, window_id); window_t *window = (window_t *)state_window_get(state, window_id);
if (!window) return false; if (!window) return false;
p_delete(&window->title); p_delete(&window->title);
if (title) window->title = p_strdup(title); window->title = p_strdup_nullable(title);
return true; return true;
} }
bool wm_state_window_set_app_id(wm_state_t *state, wm_window_id_t window_id, bool state_window_set_app_id(state_t *state, window_id_t window_id,
const char *app_id) { const char *app_id) {
wm_window_t *window = (wm_window_t *)wm_state_window_get(state, window_id); window_t *window = (window_t *)state_window_get(state, window_id);
if (!window) return false; if (!window) return false;
p_delete(&window->app_id); p_delete(&window->app_id);
if (app_id) window->app_id = p_strdup(app_id); window->app_id = p_strdup_nullable(app_id);
return true; return true;
} }
bool wm_state_window_set_class(wm_state_t *state, wm_window_id_t window_id, bool state_window_set_class(state_t *state, window_id_t window_id,
const char *class_name) { const char *class_name) {
wm_window_t *window = (wm_window_t *)wm_state_window_get(state, window_id); window_t *window = (window_t *)state_window_get(state, window_id);
if (!window) return false; if (!window) return false;
p_delete(&window->class_name); p_delete(&window->class_name);
if (class_name) window->class_name = p_strdup(class_name); window->class_name = p_strdup_nullable(class_name);
return true; return true;
} }
bool wm_state_window_set_instance(wm_state_t *state, wm_window_id_t window_id, bool state_window_set_instance(state_t *state, window_id_t window_id,
const char *instance_name) { const char *instance_name) {
wm_window_t *window = (wm_window_t *)wm_state_window_get(state, window_id); window_t *window = (window_t *)state_window_get(state, window_id);
if (!window) return false; if (!window) return false;
p_delete(&window->instance_name); p_delete(&window->instance_name);
if (instance_name) window->instance_name = p_strdup(instance_name); window->instance_name = p_strdup_nullable(instance_name);
return true; return true;
} }
const wm_window_id_t *wm_state_stack_order(const wm_state_t *state) { const window_id_t *state_stack_order(const state_t *state) {
return state->stack_order; return state->stack_order;
} }
wm_window_id_t wm_state_stack_at(const wm_state_t *state, size_t index) { window_id_t state_stack_at(const state_t *state, size_t index) {
if (index >= state->window_count) return WM_WINDOW_ID_INVALID; if (index >= state->window_count) return ZDWM_WINDOW_ID_INVALID;
return state->stack_order[index]; return state->stack_order[index];
} }
bool wm_state_stack_raise(wm_state_t *state, wm_window_id_t window_id) { bool state_stack_raise(state_t *state, window_id_t window_id) {
bool matched = false; bool matched = false;
size_t index = 0; size_t index = 0;
for (size_t i = 0; i < state->window_count; i++) { for (size_t i = 0; i < state->window_count; i++) {
@@ -517,7 +506,7 @@ bool wm_state_stack_raise(wm_state_t *state, wm_window_id_t window_id) {
return true; return true;
} }
bool wm_state_stack_lower(wm_state_t *state, wm_window_id_t window_id) { bool state_stack_lower(state_t *state, window_id_t window_id) {
bool matched = false; bool matched = false;
size_t index = 0; size_t index = 0;
for (size_t i = 0; i < state->window_count; i++) { for (size_t i = 0; i < state->window_count; i++) {

View File

@@ -6,19 +6,19 @@
#include "core/wm_desc.h" #include "core/wm_desc.h"
/* 核心实体定义 */ /* 核心实体定义 */
typedef struct wm_window_t { typedef struct window_t {
wm_window_id_t id; window_id_t id;
wm_workspace_id_t workspace_id; workspace_id_t workspace_id;
wm_window_geometry_mode_t geometry_mode; window_geometry_mode_t geometry_mode;
bool floating; bool floating;
bool sticky; bool sticky;
bool urgent; bool urgent;
bool fixed_size; bool fixed_size;
/* 几何信息(均为包含边框后的外框矩形) */ /* 几何信息(均为包含边框后的外框矩形) */
wm_rect_t float_rect; /* floating 模式下记忆的外框矩形 */ rect_t float_rect; /* floating 模式下记忆的外框矩形 */
wm_rect_t frame_rect; /* 当前外框矩形(由 layout 或 float_rect 解析) */ rect_t frame_rect; /* 当前外框矩形(由 layout 或 float_rect 解析) */
/* 元数据(核心算法不依赖,仅用于规则匹配和信息展示) */ /* 元数据(核心算法不依赖,仅用于规则匹配和信息展示) */
const char *title; const char *title;
@@ -27,67 +27,67 @@ typedef struct wm_window_t {
const char *instance_name; const char *instance_name;
bool skip_taskbar; bool skip_taskbar;
} wm_window_t; } window_t;
typedef struct wm_workspace_t { typedef struct workspace_t {
wm_workspace_id_t id; workspace_id_t id;
wm_output_id_t output_id; /* 固定归属某个输出 */ output_id_t output_id; /* 固定归属某个输出 */
wm_layout_id_t layout_id; /* 当前活动布局(可用布局列表中的一个) */ layout_id_t layout_id; /* 当前活动布局(可用布局列表中的一个) */
wm_window_id_t focused_window_id; window_id_t focused_window_id;
/* 当前 workspace 的可用布局列表 */ /* 当前 workspace 的可用布局列表 */
const wm_layout_id_t *available_layouts; const layout_id_t *available_layouts;
size_t layout_count; size_t layout_count;
/* 名称(核心算法不依赖) */ /* 名称(核心算法不依赖) */
const char *name; const char *name;
} wm_workspace_t; } workspace_t;
typedef struct wm_output_t { typedef struct output_t {
wm_output_id_t id; output_id_t id;
wm_workspace_id_t current_workspace_id; workspace_id_t current_workspace_id;
const char *name; const char *name;
wm_rect_t geometry; /* 输出完整几何 */ rect_t geometry; /* 输出完整几何 */
wm_rect_t workarea; /* 可用区域(排除面板等) */ rect_t workarea; /* 可用区域(排除面板等) */
} wm_output_t; } output_t;
/* 全局状态容器 */ /* 全局状态容器 */
typedef struct wm_state_t { typedef struct state_t {
/* 按 id 稳定引用的 workspace / output 集合 */ /* 按 id 稳定引用的 workspace / output 集合 */
wm_workspace_t *workspaces; workspace_t *workspaces;
size_t workspace_count; size_t workspace_count;
wm_output_t *outputs; output_t *outputs;
size_t output_count; size_t output_count;
wm_window_t *windows; window_t *windows;
size_t window_count; size_t window_count;
size_t window_capacity; size_t window_capacity;
/* 与 windows[] 一一对应的堆叠顺序视图,长度等于 window_count */ /* 与 windows[] 一一对应的堆叠顺序视图,长度等于 window_count */
wm_window_id_t *stack_order; window_id_t *stack_order;
} wm_state_t; } state_t;
/* /*
* 调用约束: * 调用约束:
* - state 必须是有效的非空指针 * - state 必须是有效的非空指针
* - wm_state_init() / wm_state_cleanup() 必须成对调用 * - state_init() / state_cleanup() 必须成对调用
* - 同一个 state 生命周期内,wm_state_init() 不允许重复调用 * - 同一个 state 生命周期内state_init() 不允许重复调用
* - 除 wm_state_init() 外,其余 state 相关接口都要求 state 已初始化 * - 除 state_init() 外,其余 state 相关接口都要求 state 已初始化
* - workspaces[i] 必须满足 wm_workspace_desc_valid(&workspaces[i], * - workspaces[i] 必须满足 workspace_desc_valid(&workspaces[i],
* output_count) * output_count)
* - 传入空指针、未初始化对象或违反前置条件属于调用方错误 * - 传入空指针、未初始化对象或违反前置条件属于调用方错误
* *
* 初始化时根据描述表构建 outputs[] 与 workspaces[]。 * 初始化时根据描述表构建 outputs[] 与 workspaces[]。
* workspace_id 由 state 按描述表顺序分配并保持稳定,不从 desc 中读取。 * workspace_id 由 state 按描述表顺序分配并保持稳定,不从 desc 中读取。
* 每个 output 的 current_workspace_id 会自动设置为首个归属到该 output 的 * 每个 output 的 current_workspace_id 会自动设置为首个归属到该 output 的
* workspace若某个 output 没有任何归属 workspacewm_state_init() 会失败。 * workspace若某个 output 没有任何归属 workspacestate_init() 会失败。
*/ */
void wm_state_init(wm_state_t *state, const wm_output_info_t *outputs, void state_init(state_t *state, const output_info_t *outputs,
size_t output_count, const wm_workspace_desc_t *workspaces, size_t output_count, const workspace_desc_t *workspaces,
size_t workspace_count); size_t workspace_count);
void wm_state_cleanup(wm_state_t *state); void state_cleanup(state_t *state);
/* /*
* state 持有的 workspace 集合接口 * state 持有的 workspace 集合接口
@@ -95,23 +95,20 @@ void wm_state_cleanup(wm_state_t *state);
* 这组接口对外只提供只读访问。 * 这组接口对外只提供只读访问。
* workspace 的运行期可变状态必须通过专门的 state 级更新接口修改。 * workspace 的运行期可变状态必须通过专门的 state 级更新接口修改。
*/ */
const wm_workspace_t *wm_state_workspace_get(const wm_state_t *state, const workspace_t *state_workspace_get(const state_t *state, workspace_id_t id);
wm_workspace_id_t id); const workspace_t *state_workspace_at(const state_t *state, size_t index);
const wm_workspace_t *wm_state_workspace_at(const wm_state_t *state, bool state_workspace_cycle_layout(state_t *state, workspace_id_t workspace_id);
size_t index); bool state_workspace_set_layout_by_index(state_t *state,
bool wm_state_workspace_cycle_layout(wm_state_t *state, workspace_id_t workspace_id,
wm_workspace_id_t workspace_id); size_t index);
bool wm_state_workspace_set_layout_by_index(wm_state_t *state, bool state_workspace_set_layout_by_id(state_t *state,
wm_workspace_id_t workspace_id, workspace_id_t workspace_id,
size_t index); layout_id_t layout_id);
bool wm_state_workspace_set_layout_by_id(wm_state_t *state, void state_workspace_set_focused_window(state_t *state,
wm_workspace_id_t workspace_id, workspace_id_t workspace_id,
wm_layout_id_t layout_id); window_id_t window_id);
void wm_state_workspace_set_focused_window(wm_state_t *state, size_t state_workspace_count(const state_t *state);
wm_workspace_id_t workspace_id, bool state_workspace_valid(const state_t *state, workspace_id_t id);
wm_window_id_t window_id);
size_t wm_state_workspace_count(const wm_state_t *state);
bool wm_state_workspace_valid(const wm_state_t *state, wm_workspace_id_t id);
/* /*
* state 持有的 output 集合接口 * state 持有的 output 集合接口
@@ -119,21 +116,19 @@ bool wm_state_workspace_valid(const wm_state_t *state, wm_workspace_id_t id);
* 这组接口对外只提供只读访问。 * 这组接口对外只提供只读访问。
* output 的运行期可变状态必须通过专门的 state 级更新接口修改。 * output 的运行期可变状态必须通过专门的 state 级更新接口修改。
*/ */
const wm_output_t *wm_state_output_get(const wm_state_t *state, const output_t *state_output_get(const state_t *state, output_id_t id);
wm_output_id_t id); const output_t *state_output_at(const state_t *state, size_t index);
const wm_output_t *wm_state_output_at(const wm_state_t *state, size_t index); void state_output_set_workarea(state_t *state, output_id_t output_id,
void wm_state_output_set_workarea(wm_state_t *state, wm_output_id_t output_id, rect_t workarea);
wm_rect_t workarea); void state_output_set_current_workspace(state_t *state, output_id_t output_id,
void wm_state_output_set_current_workspace(wm_state_t *state, workspace_id_t workspace_id);
wm_output_id_t output_id, size_t state_output_count(const state_t *state);
wm_workspace_id_t workspace_id); bool state_output_valid(const state_t *state, output_id_t id);
size_t wm_state_output_count(const wm_state_t *state);
bool wm_state_output_valid(const wm_state_t *state, wm_output_id_t id);
/* /*
* state 持有的 window 集合接口 * state 持有的 window 集合接口
* *
* 这组接口负责管理 wm_state_t.windows[] 容器本身,并保持 * 这组接口负责管理 state_t.windows[] 容器本身,并保持
* stack_order[] 与 windows[] 同步。 * stack_order[] 与 windows[] 同步。
* 对外只提供只读访问。 * 对外只提供只读访问。
* window 的运行期可变状态必须通过专门的 state 级更新接口修改。 * window 的运行期可变状态必须通过专门的 state 级更新接口修改。
@@ -142,54 +137,50 @@ bool wm_state_output_valid(const wm_state_t *state, wm_output_id_t id);
* 添加窗口,并将其追加到 stack_order[] 顶部。 * 添加窗口,并将其追加到 stack_order[] 顶部。
* *
* info 提供 backend 已探测到的窗口基础信息。 * info 提供 backend 已探测到的窗口基础信息。
* workspace 归属与其他策略相关字段由后续 wm_state_window_* 接口设置。 * workspace 归属与其他策略相关字段由后续 state_window_* 接口设置。
*/ */
const wm_window_t *wm_state_window_add(wm_state_t *state, const window_t *state_window_add(state_t *state, const window_info_t *info);
const wm_window_info_t *info); const window_t *state_window_get(const state_t *state, window_id_t id);
const wm_window_t *wm_state_window_get(const wm_state_t *state, const window_t *state_window_at(const state_t *state, size_t index);
wm_window_id_t id);
const wm_window_t *wm_state_window_at(const wm_state_t *state, size_t index);
/* 删除窗口,并同步从 stack_order[] 中移除。 */ /* 删除窗口,并同步从 stack_order[] 中移除。 */
void wm_state_window_remove(wm_state_t *state, wm_window_id_t id); void state_window_remove(state_t *state, window_id_t id);
size_t wm_state_window_count(const wm_state_t *state); size_t state_window_count(const state_t *state);
/* state 持有的单个 window 状态更新接口 */ /* state 持有的单个 window 状态更新接口 */
void wm_state_window_set_workspace(wm_state_t *state, wm_window_id_t window_id, void state_window_set_workspace(state_t *state, window_id_t window_id,
wm_workspace_id_t workspace_id); workspace_id_t workspace_id);
void wm_state_window_set_geometry_mode(wm_state_t *state, void state_window_set_geometry_mode(state_t *state, window_id_t window_id,
wm_window_id_t window_id, window_geometry_mode_t geometry_mode);
wm_window_geometry_mode_t geometry_mode); void state_window_set_floating(state_t *state, window_id_t window_id,
void wm_state_window_set_floating(wm_state_t *state, wm_window_id_t window_id, bool floating);
bool floating); void state_window_set_sticky(state_t *state, window_id_t window_id,
void wm_state_window_set_sticky(wm_state_t *state, wm_window_id_t window_id, bool sticky);
bool sticky); void state_window_set_urgent(state_t *state, window_id_t window_id,
void wm_state_window_set_urgent(wm_state_t *state, wm_window_id_t window_id, bool urgent);
bool urgent); void state_window_set_fixed_size(state_t *state, window_id_t window_id,
void wm_state_window_set_fixed_size(wm_state_t *state, wm_window_id_t window_id, bool fixed_size);
bool fixed_size); void state_window_set_skip_taskbar(state_t *state, window_id_t window_id,
void wm_state_window_set_skip_taskbar(wm_state_t *state, bool skip_taskbar);
wm_window_id_t window_id, void state_window_set_float_rect(state_t *state, window_id_t window_id,
bool skip_taskbar); rect_t float_rect);
void wm_state_window_set_float_rect(wm_state_t *state, wm_window_id_t window_id, void state_window_set_frame_rect(state_t *state, window_id_t window_id,
wm_rect_t float_rect); rect_t frame_rect);
void wm_state_window_set_frame_rect(wm_state_t *state, wm_window_id_t window_id, bool state_window_set_title(state_t *state, window_id_t window_id,
wm_rect_t frame_rect); const char *title);
bool wm_state_window_set_title(wm_state_t *state, wm_window_id_t window_id, bool state_window_set_app_id(state_t *state, window_id_t window_id,
const char *title); const char *app_id);
bool wm_state_window_set_app_id(wm_state_t *state, wm_window_id_t window_id, bool state_window_set_class(state_t *state, window_id_t window_id,
const char *app_id); const char *class_name);
bool wm_state_window_set_class(wm_state_t *state, wm_window_id_t window_id, bool state_window_set_instance(state_t *state, window_id_t window_id,
const char *class_name); const char *instance_name);
bool wm_state_window_set_instance(wm_state_t *state, wm_window_id_t window_id,
const char *instance_name);
/* /*
* state 持有的堆叠顺序接口 * state 持有的堆叠顺序接口
* *
* stack_order[] 是全局 z-order 的唯一真相,从下到上排列。 * stack_order[] 是全局 z-order 的唯一真相,从下到上排列。
* 其长度恒等于 wm_state_window_count(state),因此不提供单独的 count 接口。 * 其长度恒等于 state_window_count(state),因此不提供单独的 count 接口。
*/ */
const wm_window_id_t *wm_state_stack_order(const wm_state_t *state); const window_id_t *state_stack_order(const state_t *state);
wm_window_id_t wm_state_stack_at(const wm_state_t *state, size_t index); window_id_t state_stack_at(const state_t *state, size_t index);
bool wm_state_stack_raise(wm_state_t *state, wm_window_id_t window_id); bool state_stack_raise(state_t *state, window_id_t window_id);
bool wm_state_stack_lower(wm_state_t *state, wm_window_id_t window_id); bool state_stack_lower(state_t *state, window_id_t window_id);

View File

@@ -1,45 +1,38 @@
#pragma once #pragma once
#include <stdint.h> #include <stdint.h>
#include <zdwm/types.h>
typedef uint32_t wm_window_id_t;
typedef uint32_t wm_workspace_id_t;
typedef uint32_t wm_output_id_t;
typedef uint32_t wm_layout_id_t;
/* clang-format off */ /* clang-format off */
/* 无效 ID 标记 */ typedef zdwm_layout_id_t layout_id_t;
#define WM_WINDOW_ID_INVALID ((wm_window_id_t)0) typedef zdwm_window_id_t window_id_t;
#define WM_WORKSPACE_ID_INVALID ((wm_workspace_id_t)UINT32_MAX) typedef zdwm_workspace_id_t workspace_id_t;
#define WM_OUTPUT_ID_INVALID ((wm_output_id_t)UINT32_MAX) typedef uint32_t output_id_t;
#define WM_LAYOUT_ID_INVALID ((wm_layout_id_t)UINT32_MAX)
typedef zdwm_rect_t rect_t;
typedef zdwm_output_info_t output_info_t;
/* clang-format on */ /* clang-format on */
typedef struct wm_point_t { #define ZDWM_OUTPUT_ID_INVALID ((output_id_t)UINT32_MAX)
typedef struct point_t {
int32_t x; int32_t x;
int32_t y; int32_t y;
} wm_point_t; } point_t;
typedef struct wm_rect_t { typedef enum window_geometry_mode_t {
int32_t x; ZDWM_GEOMETRY_NORMAL,
int32_t y; ZDWM_GEOMETRY_MAXIMIZED,
int32_t width; ZDWM_GEOMETRY_FULLSCREEN,
int32_t height; ZDWM_GEOMETRY_MINIMIZED,
} wm_rect_t; } window_geometry_mode_t;
typedef enum wm_window_geometry_mode_t { typedef enum focus_direction_t {
WM_GEOMETRY_NORMAL, ZDWM_FOCUS_PREV,
WM_GEOMETRY_MAXIMIZED, ZDWM_FOCUS_NEXT,
WM_GEOMETRY_FULLSCREEN, } focus_direction_t;
WM_GEOMETRY_MINIMIZED,
} wm_window_geometry_mode_t;
typedef enum wm_focus_direction_t { typedef enum cross_output_policy_t {
WM_FOCUS_PREV, ZDWM_CROSS_OUTPUT_KEEP_WORKSPACE,
WM_FOCUS_NEXT, ZDWM_CROSS_OUTPUT_MOVE_TO_TARGET_WORKSPACE,
} wm_focus_direction_t; } cross_output_policy_t;
typedef enum wm_cross_output_policy_t {
WM_CROSS_OUTPUT_KEEP_WORKSPACE,
WM_CROSS_OUTPUT_MOVE_TO_TARGET_WORKSPACE,
} wm_cross_output_policy_t;

View File

@@ -4,46 +4,41 @@
#include "core/types.h" #include "core/types.h"
typedef struct wm_output_info_t {
char *name;
wm_rect_t geometry;
} wm_output_info_t;
/* /*
* 由 backend 提供的窗口基础信息。 * 由 backend 提供的窗口基础信息。
* *
* 这些字符串由调用方提供state 如需长期持有,应自行复制。 * 这些字符串由调用方提供state 如需长期持有,应自行复制。
*/ */
typedef struct wm_window_info_t { typedef struct window_info_t {
wm_window_id_t id; window_id_t id;
wm_rect_t frame_rect; rect_t frame_rect;
const char *title; const char *title;
const char *app_id; const char *app_id;
const char *class_name; const char *class_name;
const char *instance_name; const char *instance_name;
wm_window_geometry_mode_t geometry_mode; window_geometry_mode_t geometry_mode;
bool urgent; bool urgent;
bool fixed_size; bool fixed_size;
bool skip_taskbar; bool skip_taskbar;
} wm_window_info_t; } window_info_t;
typedef struct wm_workspace_desc_t { typedef struct workspace_desc_t {
size_t output_index; /* 对应 wm_state_init() 中 outputs[] 的索引 */ size_t output_index; /* 对应 state_init() 中 outputs[] 的索引 */
const char *name; const char *name;
const wm_layout_id_t *layout_ids; const layout_id_t *layout_ids;
size_t layout_count; size_t layout_count;
wm_layout_id_t initial_layout_id; layout_id_t initial_layout_id;
} wm_workspace_desc_t; } workspace_desc_t;
/* /*
* workspace 描述校验接口。 * workspace 描述校验接口。
* *
* 这组接口只校验描述表自身的一致性,不访问 state。 * 这组接口只校验描述表自身的一致性,不访问 state。
*/ */
static inline bool wm_workspace_desc_layouts_valid( static inline bool workspace_desc_layouts_valid(
const wm_workspace_desc_t *workspace) { const workspace_desc_t *workspace) {
if (!workspace || !workspace->layout_count || !workspace->layout_ids) { if (!workspace || !workspace->layout_count || !workspace->layout_ids) {
return false; return false;
} }
@@ -55,10 +50,10 @@ static inline bool wm_workspace_desc_layouts_valid(
return false; return false;
} }
static inline bool wm_workspace_desc_valid(const wm_workspace_desc_t *workspace, static inline bool workspace_desc_valid(const workspace_desc_t *workspace,
size_t output_count) { size_t output_count) {
if (!workspace || !workspace->name) return false; if (!workspace || !workspace->name) return false;
if (workspace->output_index >= output_count) return false; if (workspace->output_index >= output_count) return false;
return wm_workspace_desc_layouts_valid(workspace); return workspace_desc_layouts_valid(workspace);
} }

View File

@@ -1,126 +1,8 @@
#pragma once #pragma once
#include <stdarg.h> /* Compatibility umbrella header. Prefer including headers from src/base in new
#include <stddef.h> * code. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define ssizeof(foo) (ssize_t)sizeof(foo) #include "base/log.h"
#define countof(foo) (ssizeof(foo) / ssizeof(foo[0])) #include "base/macros.h"
#include "base/memory.h"
#define p_alloc_nr(x) (((x) + 16) * 3 / 2)
#define p_new(type, count) ((type *)xmalloc(sizeof(type) * (count)))
#define p_clear(p, count) ((void)memset((p), 0, sizeof(*(p)) * (count)))
#define p_realloc(pp, count) xrealloc((void *)(pp), sizeof(**(pp)) * (count))
#define p_copy(src, count) xmemcopy((src), sizeof(*(src)) * (count))
#define p_delete(mem_p) \
do { \
void **__ptr = (void **)(mem_p); \
free(*__ptr); \
*(void **)__ptr = nullptr; \
} while (0)
#ifdef __GNUC__
#define likely(expr) __builtin_expect(!!(expr), 1)
#define unlikely(expr) __builtin_expect((expr), 0)
#else
#define likely(expr) expr
#define unlikely(expr) expr
#endif
static inline char *p_strdup(const char *text) {
char *r = strdup(text);
if (!r) abort();
return r;
}
static inline void *__attribute__((malloc)) xmalloc(ssize_t size) {
void *ptr;
if (size <= 0) return nullptr;
ptr = calloc(1, size);
if (!ptr) abort();
return ptr;
}
static inline void *xmemcopy(const void *src, size_t n) {
if (!n) return nullptr;
void *mem = xmalloc(n);
void *ret = memcpy(mem, src, n);
if (ret != mem) abort();
return ret;
}
static inline void xrealloc(void **ptr, ssize_t newsize) {
if (newsize <= 0)
p_delete(ptr);
else {
*ptr = realloc(*ptr, newsize);
if (!*ptr) abort();
}
}
/**
* @brief nullptr resistant strlen.
*
* Unlike it's libc sibling, a_strlen returns a ssize_t, and supports its
* argument being nullptr.
*
* @param s the string.
* @return the string length (or 0 if s is nullptr).
*/
static inline ssize_t a_strlen(const char *s) { return s ? strlen(s) : 0; }
#define fatal(format, ...) \
_fatal(__LINE__, __FUNCTION__, __FILE__, format, ##__VA_ARGS__)
void _fatal(int line, const char *function, const char *file,
const char *format, ...) __attribute__((noreturn))
__attribute__((format(printf, 4, 5)));
#define warn(format, ...) \
_warn(__LINE__, __FUNCTION__, __FILE__, format, ##__VA_ARGS__)
void _warn(int line, const char *function, const char *file, const char *format,
...) __attribute__((format(printf, 4, 5)));
const char *current_time_str(void);
#if defined(RELEASE)
#define logger(format, ...)
#elif defined(LOG_FILE)
static inline void logger(const char *format, ...) {
va_list ap;
va_start(ap, format);
FILE *log_file = fopen(LOG_FILE, "a+t");
vfprintf(log_file, format, ap);
va_end(ap);
fclose(log_file);
}
#else
#define logger(format, ...) printf(format, ##__VA_ARGS__)
#endif
#ifdef MIN
#undef MIN
#endif
#ifndef MIN
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
#endif
#ifdef MAX
#undef MAX
#endif
#ifndef MAX
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
#endif
static constexpr size_t INIT_CAPACITY = 4;
static inline size_t next_capacity(size_t capacity) {
if (capacity) return capacity * 2;
return INIT_CAPACITY;
}

View File

@@ -5,6 +5,10 @@ add_executable(${OUTPUT_UTILS_TEST_APP_NAME}
${SOURCE_DIR}/backend/output_utils.c ${SOURCE_DIR}/backend/output_utils.c
) )
target_include_directories(${OUTPUT_UTILS_TEST_APP_NAME} SYSTEM
PRIVATE ${INCLUDE_DIR}
)
target_include_directories(${OUTPUT_UTILS_TEST_APP_NAME} target_include_directories(${OUTPUT_UTILS_TEST_APP_NAME}
PRIVATE ${SOURCE_DIR} PRIVATE ${SOURCE_DIR}
PRIVATE ${BUILD_DIR} PRIVATE ${BUILD_DIR}

View File

@@ -1,12 +1,13 @@
#include "backend/output_utils.h"
#include <assert.h> #include <assert.h>
#include <stdint.h> #include <stdint.h>
#include "backend/output_utils.h" #include "core/types.h"
#include "utils.h" #include "utils.h"
static void assert_output_geometry(const wm_output_info_t *output, int32_t x, static void assert_output_geometry(const output_info_t *output, int32_t x,
int32_t y, int32_t width, int32_t y, int32_t width, int32_t height) {
int32_t height) {
assert(output); assert(output);
assert(output->geometry.x == x); assert(output->geometry.x == x);
assert(output->geometry.y == y); assert(output->geometry.y == y);
@@ -14,7 +15,7 @@ static void assert_output_geometry(const wm_output_info_t *output, int32_t x,
assert(output->geometry.height == height); assert(output->geometry.height == height);
} }
static void destroy_detect_result(wm_backend_detect_t *detect) { static void destroy_detect_result(backend_detect_t *detect) {
assert(detect); assert(detect);
for (size_t i = 0; i < detect->output_count; i++) { for (size_t i = 0; i < detect->output_count; i++) {
p_delete(&detect->outputs[i].name); p_delete(&detect->outputs[i].name);
@@ -24,14 +25,14 @@ static void destroy_detect_result(wm_backend_detect_t *detect) {
} }
static void test_output_remove_duplication_invalid_input(void) { static void test_output_remove_duplication_invalid_input(void) {
wm_output_info_t output = {0}; output_info_t output = {0};
assert(output_remove_duplication(nullptr, 0) == nullptr); assert(output_remove_duplication(nullptr, 0) == nullptr);
assert(output_remove_duplication(&output, 0) == nullptr); assert(output_remove_duplication(&output, 0) == nullptr);
} }
static void test_output_remove_duplication_keeps_non_nested_outputs(void) { static void test_output_remove_duplication_keeps_non_nested_outputs(void) {
wm_output_info_t outputs[] = { output_info_t outputs[] = {
{ {
.geometry = {.x = 0, .y = 0, .width = 3840, .height = 1080}, .geometry = {.x = 0, .y = 0, .width = 3840, .height = 1080},
}, },
@@ -46,7 +47,7 @@ static void test_output_remove_duplication_keeps_non_nested_outputs(void) {
}, },
}; };
wm_backend_detect_t *detect = backend_detect_t *detect =
output_remove_duplication(outputs, countof(outputs)); output_remove_duplication(outputs, countof(outputs));
assert(detect); assert(detect);
assert(detect->output_count == 2); assert(detect->output_count == 2);

View File

@@ -2,7 +2,7 @@ set(TEST_APP_NAME "zdwm-tests")
add_executable(${TEST_APP_NAME} add_executable(${TEST_APP_NAME}
${CMAKE_CURRENT_SOURCE_DIR}/main.c ${CMAKE_CURRENT_SOURCE_DIR}/main.c
${SOURCE_DIR}/utils.c ${SOURCE_DIR}/base/log.c
${SOURCE_DIR}/backend/output_utils.c ${SOURCE_DIR}/backend/output_utils.c
${SOURCE_DIR}/backend/x11/backend.c ${SOURCE_DIR}/backend/x11/backend.c
${SOURCE_DIR}/core/layout.c ${SOURCE_DIR}/core/layout.c
@@ -12,6 +12,7 @@ add_executable(${TEST_APP_NAME}
target_include_directories(${TEST_APP_NAME} SYSTEM target_include_directories(${TEST_APP_NAME} SYSTEM
PRIVATE ${deps_INCLUDE_DIRS} PRIVATE ${deps_INCLUDE_DIRS}
PRIVATE ${INCLUDE_DIR}
) )
target_include_directories(${TEST_APP_NAME} target_include_directories(${TEST_APP_NAME}
PRIVATE ${SOURCE_DIR} PRIVATE ${SOURCE_DIR}

View File

@@ -1,10 +1,10 @@
#include "core/runtime.h" #include "core/runtime.h"
int main(void) { int main(void) {
wm_runtime_t runtime = {0}; runtime_t runtime = {0};
wm_runtime_init(&runtime); runtime_init(&runtime, nullptr);
wm_runtime_shutdown(&runtime); runtime_shutdown(&runtime);
return 0; return 0;
} }