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(SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/src")
set(INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include")
set(BUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}")
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}
${SOURCE_DIR}/wm.c
${SOURCE_DIR}/utils.c
${SOURCE_DIR}/base/log.c
${SOURCE_DIR}/buffer.c
${SOURCE_DIR}/backtrace.c
${SOURCE_DIR}/monitor.c
@@ -82,6 +83,7 @@ pkg_check_modules(deps REQUIRED
target_include_directories(${APP_NAME} SYSTEM
PRIVATE ${deps_INCLUDE_DIRS}
PRIVATE ${INCLUDE_DIR}
)
target_include_directories(${APP_NAME}
PRIVATE ${SOURCE_DIR}

View File

@@ -44,4 +44,8 @@ test:
cmake --build $(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 <stdint.h>
#include "base/memory.h"
#include "core/backend.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 bottom(wm_rect_t a) { return a.y + a.height; }
static inline bool fully_contains(wm_rect_t outer, wm_rect_t inner) {
static inline int32_t right(rect_t a) { return a.x + a.width; }
static inline int32_t bottom(rect_t a) { return a.y + a.height; }
static inline bool fully_contains(rect_t outer, rect_t inner) {
return inner.x >= outer.x && inner.y >= outer.y &&
right(inner) <= right(outer) && bottom(inner) <= bottom(outer);
}
wm_backend_detect_t *output_remove_duplication(const wm_output_info_t *output,
const size_t count) {
backend_detect_t *output_remove_duplication(const output_info_t *output,
const size_t count) {
if (!output || count < 1) return nullptr;
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;
}
wm_output_info_t *list = p_new(wm_output_info_t, amount);
output_info_t *list = p_new(output_info_t, amount);
amount = 0;
for (size_t i = 0; i < count; i++) {
if (remove[i]) continue;
list[amount++] = output[i];
}
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->output_count = amount;
return detect;

View File

@@ -3,7 +3,7 @@
#include <stddef.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);

View File

@@ -12,17 +12,18 @@
#include <xcb/xproto.h>
#include "backend/output_utils.h"
#include "core/wm_desc.h"
#include "utils.h"
#include "base/log.h"
#include "base/memory.h"
#include "core/types.h"
struct wm_backend_t {
struct backend_t {
xcb_connection_t *conn;
xcb_screen_t *screen;
int screenp;
bool have_xfixes;
};
wm_backend_t *wm_backend_create(const char *display_name) {
backend_t *backend_create(const char *display_name) {
int screen_num = 0;
xcb_connection_t *conn = xcb_connect(display_name, &screen_num);
int xcb_conn_error = xcb_connection_has_error(conn);
@@ -46,7 +47,7 @@ wm_backend_t *wm_backend_create(const char *display_name) {
"SubstructureRedirect)");
}
wm_backend_t *backend = p_new(wm_backend_t, 1);
backend_t *backend = p_new(backend_t, 1);
backend->conn = conn;
backend->screen = screen;
backend->screenp = screen_num;
@@ -71,7 +72,7 @@ wm_backend_t *wm_backend_create(const char *display_name) {
return backend;
}
void wm_backend_destroy(wm_backend_t *backend) {
void backend_destroy(backend_t *backend) {
if (!backend) return;
xcb_disconnect(backend->conn);
@@ -79,8 +80,8 @@ void wm_backend_destroy(wm_backend_t *backend) {
free(backend);
}
static bool detect_monitor_by_randr(const wm_backend_t *backend,
wm_output_info_t **outputs, size_t *count) {
static bool detect_monitor_by_randr(const backend_t *backend,
output_info_t **outputs, size_t *count) {
xcb_prefetch_extension_data(backend->conn, &xcb_randr_id);
const xcb_query_extension_reply_t *query =
xcb_get_extension_data(backend->conn, &xcb_randr_id);
@@ -116,11 +117,11 @@ static bool detect_monitor_by_randr(const wm_backend_t *backend,
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_get_monitors_monitors_iterator(monitors_reply);
for (int i = 0; iter.rem; xcb_randr_monitor_info_next(&iter), i++) {
wm_output_info_t *output = &output_list[i];
output_info_t *output = &output_list[i];
output->geometry.x = iter.data->x;
output->geometry.y = iter.data->y;
output->geometry.width = iter.data->width;
@@ -152,9 +153,8 @@ static bool detect_monitor_by_randr(const wm_backend_t *backend,
return true;
}
static bool detect_monitor_by_xinerama(const wm_backend_t *backend,
wm_output_info_t **outputs,
size_t *count) {
static bool detect_monitor_by_xinerama(const backend_t *backend,
output_info_t **outputs, size_t *count) {
xcb_prefetch_extension_data(backend->conn, &xcb_xinerama_id);
const xcb_query_extension_reply_t *query =
xcb_get_extension_data(backend->conn, &xcb_xinerama_id);
@@ -195,9 +195,9 @@ static bool detect_monitor_by_xinerama(const wm_backend_t *backend,
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++) {
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.y = screen_info[i].y_org;
output->geometry.width = screen_info[i].width;
@@ -216,35 +216,35 @@ static bool detect_monitor_by_xinerama(const wm_backend_t *backend,
return true;
}
wm_backend_detect_t *wm_backend_detect(wm_backend_t *backend) {
wm_output_info_t *outputs = nullptr;
backend_detect_t *backend_detect(backend_t *backend) {
output_info_t *outputs = nullptr;
size_t count = 0;
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);
return detect;
}
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);
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.y = 0;
output->geometry.width = backend->screen->width_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->output_count = 1;
return detect;
}
void wm_backend_detect_destroy(wm_backend_detect_t *detect) {
void backend_detect_destroy(backend_detect_t *detect) {
if (!detect) return;
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 <stdio.h>
@@ -19,9 +19,6 @@ const char *current_time_str(void) {
return buffer;
}
/**
* Print error and exit with EXIT_FAILURE code.
*/
void _fatal(int line, const char *fct, const char *file, const char *fmt, ...) {
va_list ap;
@@ -34,9 +31,6 @@ void _fatal(int line, const char *fct, const char *file, const char *fmt, ...) {
exit(EXIT_FAILURE);
}
/**
* Print error message on stderr
*/
void _warn(int line, const char *fct, const char *file, const char *fmt, ...) {
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 "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 {
wm_output_info_t *outputs;
typedef struct backend_detect_t {
output_info_t *outputs;
size_t output_count;
} wm_backend_detect_t;
} backend_detect_t;
wm_backend_t *wm_backend_create(const char *display_name);
void wm_backend_destroy(wm_backend_t *backend);
backend_t *backend_create(const char *display_name);
void backend_destroy(backend_t *backend);
wm_backend_detect_t *wm_backend_detect(wm_backend_t *backend);
void wm_backend_detect_destroy(wm_backend_detect_t *detect);
backend_detect_t *backend_detect(backend_t *backend);
void backend_detect_destroy(backend_detect_t *detect);

View File

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

View File

@@ -4,25 +4,21 @@
#include <stdlib.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_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) {
result->item_count = 0;
}
void wm_layout_result_cleanup(wm_layout_result_t *result) {
void layout_result_cleanup(layout_result_t *result) {
p_delete(&result->items);
result->item_count = 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) {
size_t new_capacity = next_capacity(result->item_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;
}
void wm_layout_registry_init(wm_layout_registry_t *registry) {
void layout_registry_init(layout_registry_t *registry) {
registry->slot_count = 0;
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) {
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->symbol);
p_delete(&r->description);
r->fn = nullptr;
r->id = WM_LAYOUT_ID_INVALID;
r->id = ZDWM_LAYOUT_ID_INVALID;
}
p_delete(&registry->slots);
registry->slot_count = 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;
}
const wm_layout_slot_t *wm_layout_registry_at(
const wm_layout_registry_t *registry, size_t index) {
const layout_slot_t *layout_registry_at(const layout_registry_t *registry,
size_t index) {
if (index >= registry->slot_count) return nullptr;
return &registry->slots[index];
}
wm_layout_id_t wm_layout_register(wm_layout_registry_t *registry,
const char *name, const char *symbol,
const char *description, wm_layout_fn fn) {
if (!name || !symbol) return WM_LAYOUT_ID_INVALID;
layout_id_t layout_register(layout_registry_t *registry, const char *name,
const char *symbol, const char *description,
layout_fn fn) {
if (!name || !symbol) return ZDWM_LAYOUT_ID_INVALID;
if (registry->slot_count == registry->slot_capacity) {
size_t new_capacity = next_capacity(registry->slot_capacity);
p_realloc(&registry->slots, 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->name = p_strdup(name);
r->symbol = p_strdup(symbol);
r->description = description ? p_strdup(description) : nullptr;
r->description = p_strdup_nullable(description);
r->fn = fn;
registry->slot_count++;
return id;
}
wm_layout_fn wm_layout_get(const wm_layout_registry_t *registry,
wm_layout_id_t id) {
const wm_layout_slot_t *layout_slot = wm_layout_slot_get(registry, id);
layout_fn layout_get(const layout_registry_t *registry, layout_id_t id) {
const layout_slot_t *layout_slot = layout_slot_get(registry, id);
if (layout_slot) return layout_slot->fn;
return nullptr;
}
const wm_layout_slot_t *wm_layout_slot_get(const wm_layout_registry_t *registry,
wm_layout_id_t id) {
const layout_slot_t *layout_slot_get(const layout_registry_t *registry,
layout_id_t id) {
if (id >= registry->slot_count) return nullptr;
return &registry->slots[id];
}

View File

@@ -1,49 +1,17 @@
#pragma once
#include <stddef.h>
#include <zdwm/layout.h>
#include "core/types.h"
typedef struct wm_layout_ctx_t {
wm_output_id_t output_id;
wm_workspace_id_t workspace_id;
wm_window_id_t focused_window_id;
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 zdwm_layout_ctx_t layout_ctx_t;
typedef zdwm_layout_item_t layout_item_t;
typedef zdwm_layout_result_t layout_result_t;
typedef zdwm_layout_fn layout_fn;
typedef struct wm_layout_item_t {
wm_window_id_t window_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;
typedef struct layout_slot_t {
layout_id_t id;
/*
* 布局名称,用于:
* - 配置引用
@@ -70,14 +38,14 @@ typedef struct wm_layout_slot_t {
*
* fn == NULL 表示 floating 布局,即该布局不参与平铺计算。
*/
wm_layout_fn fn;
} wm_layout_slot_t;
layout_fn fn;
} layout_slot_t;
typedef struct wm_layout_registry_t {
wm_layout_slot_t *slots;
typedef struct layout_registry_t {
layout_slot_t *slots;
size_t slot_count;
size_t slot_capacity;
} wm_layout_registry_t;
} layout_registry_t;
/*
* 调用约束:
@@ -85,11 +53,10 @@ typedef struct wm_layout_registry_t {
* - 除 init 之外,其余 result 相关接口都要求 result 已初始化
* - 传入空指针或未初始化对象属于调用方错误
*/
void wm_layout_result_init(wm_layout_result_t *result);
void wm_layout_result_reset(wm_layout_result_t *result);
void wm_layout_result_cleanup(wm_layout_result_t *result);
void layout_result_init(layout_result_t *result);
void layout_result_cleanup(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 已初始化
* - 传入空指针或未初始化对象属于调用方错误
*/
void wm_layout_registry_init(wm_layout_registry_t *registry);
void wm_layout_registry_cleanup(wm_layout_registry_t *registry);
size_t wm_layout_registry_count(const wm_layout_registry_t *registry);
const wm_layout_slot_t *wm_layout_registry_at(
const wm_layout_registry_t *registry, size_t index);
void layout_registry_init(layout_registry_t *registry);
void layout_registry_cleanup(layout_registry_t *registry);
bool layout_registry_move(layout_registry_t *src, layout_registry_t *dest);
size_t layout_registry_count(const layout_registry_t *registry);
const layout_slot_t *layout_registry_at(const layout_registry_t *registry,
size_t index);
/*
* 注册一个布局并返回其稳定 ID。
@@ -117,13 +85,13 @@ const wm_layout_slot_t *wm_layout_registry_at(
*
* 返回值:
* - 成功时返回新注册布局的 ID
* - name 或 symbol 非法时返回 WM_LAYOUT_ID_INVALID
* - name 或 symbol 非法时返回 ZDWM_LAYOUT_ID_INVALID
*
* 注册成功后,布局 ID 与其在 registry 中的槽位索引保持一致。
*/
wm_layout_id_t wm_layout_register(wm_layout_registry_t *registry,
const char *name, const char *symbol,
const char *description, wm_layout_fn fn);
layout_id_t layout_register(layout_registry_t *registry, const char *name,
const char *symbol, const char *description,
layout_fn fn);
/*
* 获取可执行的布局函数。
*
@@ -132,7 +100,6 @@ wm_layout_id_t wm_layout_register(wm_layout_registry_t *registry,
* - id 对应的 slot 不存在
* - slot 存在,但 fn == NULL表示 floating 布局)
*/
wm_layout_fn wm_layout_get(const wm_layout_registry_t *registry,
wm_layout_id_t id);
const wm_layout_slot_t *wm_layout_slot_get(const wm_layout_registry_t *registry,
wm_layout_id_t id);
layout_fn layout_get(const layout_registry_t *registry, layout_id_t id);
const layout_slot_t *layout_slot_get(const layout_registry_t *registry,
layout_id_t id);

View File

@@ -2,50 +2,101 @@
#include <stddef.h>
#include "base/memory.h"
#include "core/backend.h"
#include "core/state.h"
#include "core/types.h"
#include "core/wm_desc.h"
#include "utils.h"
bool wm_runtime_init(wm_runtime_t *runtime) {
p_clear(runtime, 1);
static bool runtime_workspace_desc_has_valid_layouts(
const layout_registry_t *layouts, const workspace_desc_t *workspace) {
if (!layouts || !workspace) return false;
wm_backend_t *backend = wm_backend_create(nullptr);
if (!backend) return false;
runtime->backend = backend;
wm_backend_detect_t *detect = wm_backend_detect(backend);
if (!detect || detect->output_count <= 0) {
wm_backend_destroy(backend);
return false;
for (size_t i = 0; i < workspace->layout_count; i++) {
if (!layout_slot_get(layouts, workspace->layout_ids[i])) return false;
}
wm_workspace_desc_t *workspaces =
p_new(wm_workspace_desc_t, detect->output_count);
static wm_layout_id_t ids[] = {0, 1, 2};
for (size_t i = 0; i < detect->output_count; i++) {
wm_workspace_desc_t *desc = &workspaces[i];
desc->output_index = i;
desc->name = "web";
desc->layout_ids = ids;
desc->layout_count = countof(ids);
desc->initial_layout_id = 1;
}
wm_state_init(&runtime->state, detect->outputs, detect->output_count,
workspaces, detect->output_count);
p_delete(&workspaces);
wm_backend_detect_destroy(detect);
detect = nullptr;
return true;
}
void wm_runtime_shutdown(wm_runtime_t *runtime) {
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;
wm_state_cleanup(&runtime->state);
wm_backend_destroy(runtime->backend);
if (runtime->layouts.slots || runtime->layouts.slot_count ||
runtime->layouts.slot_capacity) {
layout_registry_cleanup(&runtime->layouts);
}
state_cleanup(&runtime->state);
backend_destroy(runtime->backend);
runtime->backend = nullptr;
}

View File

@@ -1,15 +1,28 @@
#pragma once
#include "core/backend.h"
#include "core/layout.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 will_restart;
wm_state_t state;
wm_backend_t *backend;
} wm_runtime_t;
state_t state;
layout_registry_t layouts;
backend_t *backend;
} runtime_t;
bool wm_runtime_init(wm_runtime_t *runtime);
void wm_runtime_shutdown(wm_runtime_t *runtime);
bool runtime_init(runtime_t *runtime, runtime_init_desc_t *desc);
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 <string.h>
#include "base/log.h"
#include "base/memory.h"
#include "core/types.h"
#include "core/wm_desc.h"
#include "utils.h"
void wm_state_init(wm_state_t *state, const wm_output_info_t *outputs,
size_t output_count, const wm_workspace_desc_t *workspaces,
size_t workspace_count) {
void state_init(state_t *state, const output_info_t *outputs,
size_t output_count, const workspace_desc_t *workspaces,
size_t workspace_count) {
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++) {
const wm_output_info_t *output_info = &outputs[i];
wm_output_t *output = &state->outputs[i];
const output_info_t *output_info = &outputs[i];
output_t *output = &state->outputs[i];
output->id = (wm_output_id_t)i;
output->current_workspace_id = WM_WORKSPACE_ID_INVALID;
output->name = p_strdup(output_info->name);
output->id = (output_id_t)i;
output->current_workspace_id = ZDWM_WORKSPACE_ID_INVALID;
output->name = p_strdup_nullable(output_info->name);
output->geometry = output_info->geometry;
output->workarea = output_info->geometry;
}
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++) {
const wm_workspace_desc_t *workspace_desc = &workspaces[i];
if (!wm_workspace_desc_valid(workspace_desc, output_count)) {
const workspace_desc_t *workspace_desc = &workspaces[i];
if (!workspace_desc_valid(workspace_desc, output_count)) {
fatal("workspace desc at index %zu invalid", i);
}
wm_output_t *output = &state->outputs[workspace_desc->output_index];
wm_workspace_t *workspace = &state->workspaces[i];
workspace->id = (wm_workspace_id_t)i;
output_t *output = &state->outputs[workspace_desc->output_index];
workspace_t *workspace = &state->workspaces[i];
workspace->id = (workspace_id_t)i;
workspace->output_id = output->id;
workspace->focused_window_id = WM_WINDOW_ID_INVALID;
workspace->focused_window_id = ZDWM_WINDOW_ID_INVALID;
workspace->available_layouts =
p_copy(workspace_desc->layout_ids, workspace_desc->layout_count);
workspace->layout_id = workspace_desc->initial_layout_id;
workspace->layout_count = workspace_desc->layout_count;
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;
}
}
state->workspace_count = workspace_count;
for (size_t i = 0; i < state->output_count; i++) {
const wm_output_t *output = &state->outputs[i];
if (output->current_workspace_id == WM_WORKSPACE_ID_INVALID) {
const output_t *output = &state->outputs[i];
if (output->current_workspace_id == ZDWM_WORKSPACE_ID_INVALID) {
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++) {
wm_window_t *window = &state->windows[i];
window_t *window = &state->windows[i];
p_delete(&window->title);
p_delete(&window->app_id);
p_delete(&window->class_name);
@@ -71,7 +72,7 @@ void wm_state_cleanup(wm_state_t *state) {
state->window_capacity = 0;
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->available_layouts);
workspace->layout_count = 0;
@@ -80,7 +81,7 @@ void wm_state_cleanup(wm_state_t *state) {
state->workspace_count = 0;
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(&state->outputs);
@@ -89,44 +90,42 @@ void wm_state_cleanup(wm_state_t *state) {
p_clear(state, 1);
}
const wm_workspace_t *wm_state_workspace_get(const wm_state_t *state,
wm_workspace_id_t id) {
const workspace_t *state_workspace_get(const state_t *state,
workspace_id_t id) {
if (id < state->workspace_count) return &state->workspaces[id];
return nullptr;
}
const wm_workspace_t *wm_state_workspace_at(const wm_state_t *state,
size_t index) {
const workspace_t *state_workspace_at(const state_t *state, size_t index) {
if (index < state->workspace_count) return &state->workspaces[index];
return nullptr;
}
static void wm_state_workspace_adjust_focused_window(
const wm_state_t *state, wm_workspace_id_t workspace_id) {
wm_workspace_t *workspace =
(wm_workspace_t *)wm_state_workspace_get(state, workspace_id);
static void state_workspace_adjust_focused_window(const state_t *state,
workspace_id_t workspace_id) {
workspace_t *workspace =
(workspace_t *)state_workspace_get(state, workspace_id);
if (!workspace) return;
const wm_window_t *window =
wm_state_window_get(state, workspace->focused_window_id);
const window_t *window =
state_window_get(state, workspace->focused_window_id);
if (window && window->workspace_id == workspace_id) return;
for (size_t i = state->window_count; i > 0; i--) {
wm_window_id_t window_id = state->stack_order[i - 1];
const wm_window_t *window = wm_state_window_get(state, window_id);
window_id_t window_id = state->stack_order[i - 1];
const window_t *window = state_window_get(state, window_id);
if (window && window->workspace_id == workspace_id) {
workspace->focused_window_id = window_id;
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,
wm_workspace_id_t workspace_id) {
wm_workspace_t *workspace =
(wm_workspace_t *)wm_state_workspace_get(state, workspace_id);
bool state_workspace_cycle_layout(state_t *state, workspace_id_t workspace_id) {
workspace_t *workspace =
(workspace_t *)state_workspace_get(state, workspace_id);
if (!workspace) return false;
size_t next_index = 0;
@@ -146,11 +145,11 @@ bool wm_state_workspace_cycle_layout(wm_state_t *state,
return true;
}
bool wm_state_workspace_set_layout_by_index(wm_state_t *state,
wm_workspace_id_t workspace_id,
size_t index) {
wm_workspace_t *workspace =
(wm_workspace_t *)wm_state_workspace_get(state, workspace_id);
bool state_workspace_set_layout_by_index(state_t *state,
workspace_id_t workspace_id,
size_t index) {
workspace_t *workspace =
(workspace_t *)state_workspace_get(state, workspace_id);
if (!workspace) 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;
}
bool wm_state_workspace_set_layout_by_id(wm_state_t *state,
wm_workspace_id_t workspace_id,
wm_layout_id_t layout_id) {
wm_workspace_t *workspace =
(wm_workspace_t *)wm_state_workspace_get(state, workspace_id);
bool state_workspace_set_layout_by_id(state_t *state,
workspace_id_t workspace_id,
layout_id_t layout_id) {
workspace_t *workspace =
(workspace_t *)state_workspace_get(state, workspace_id);
if (!workspace) return false;
for (size_t i = 0; i < workspace->layout_count; i++) {
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;
}
void wm_state_workspace_set_focused_window(wm_state_t *state,
wm_workspace_id_t workspace_id,
wm_window_id_t window_id) {
wm_workspace_t *workspace =
(wm_workspace_t *)wm_state_workspace_get(state, workspace_id);
void state_workspace_set_focused_window(state_t *state,
workspace_id_t workspace_id,
window_id_t window_id) {
workspace_t *workspace =
(workspace_t *)state_workspace_get(state, workspace_id);
if (!workspace) return;
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) {
workspace->focused_window_id = window_id;
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;
}
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;
/* 线性扫描作为备用 */
@@ -211,39 +210,35 @@ bool wm_state_workspace_valid(const wm_state_t *state, wm_workspace_id_t id) {
#endif
}
const wm_output_t *wm_state_output_get(const wm_state_t *state,
wm_output_id_t id) {
const output_t *state_output_get(const state_t *state, output_id_t id) {
if (id < state->output_count) return &state->outputs[id];
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];
return nullptr;
}
void wm_state_output_set_workarea(wm_state_t *state, wm_output_id_t output_id,
wm_rect_t workarea) {
wm_output_t *output = (wm_output_t *)wm_state_output_get(state, output_id);
void state_output_set_workarea(state_t *state, output_id_t output_id,
rect_t workarea) {
output_t *output = (output_t *)state_output_get(state, output_id);
if (output) output->workarea = workarea;
}
void wm_state_output_set_current_workspace(wm_state_t *state,
wm_output_id_t output_id,
wm_workspace_id_t workspace_id) {
wm_output_t *output = (wm_output_t *)wm_state_output_get(state, output_id);
void state_output_set_current_workspace(state_t *state, output_id_t output_id,
workspace_id_t workspace_id) {
output_t *output = (output_t *)state_output_get(state, output_id);
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;
output->current_workspace_id = workspace_id;
}
size_t wm_state_output_count(const wm_state_t *state) {
return state->output_count;
}
size_t state_output_count(const state_t *state) { 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;
/* 线性扫描作为备用 */
@@ -258,10 +253,9 @@ bool wm_state_output_valid(const wm_state_t *state, wm_output_id_t id) {
#endif
}
const wm_window_t *wm_state_window_add(wm_state_t *state,
const wm_window_info_t *info) {
wm_window_id_t id = info->id;
wm_window_t *window = (wm_window_t *)wm_state_window_get(state, id);
const window_t *state_window_add(state_t *state, const window_info_t *info) {
window_id_t id = info->id;
window_t *window = (window_t *)state_window_get(state, id);
if (!window) {
if (state->window_count == 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];
p_clear(window, 1);
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->window_count++;
}
wm_state_window_set_geometry_mode(state, id, info->geometry_mode);
wm_state_window_set_urgent(state, id, info->urgent);
wm_state_window_set_fixed_size(state, id, info->fixed_size);
wm_state_window_set_frame_rect(state, id, info->frame_rect);
wm_state_window_set_title(state, id, info->title);
wm_state_window_set_app_id(state, id, info->app_id);
wm_state_window_set_class(state, id, info->class_name);
wm_state_window_set_instance(state, id, info->instance_name);
wm_state_window_set_skip_taskbar(state, id, info->skip_taskbar);
state_window_set_geometry_mode(state, id, info->geometry_mode);
state_window_set_urgent(state, id, info->urgent);
state_window_set_fixed_size(state, id, info->fixed_size);
state_window_set_frame_rect(state, id, info->frame_rect);
state_window_set_title(state, id, info->title);
state_window_set_app_id(state, id, info->app_id);
state_window_set_class(state, id, info->class_name);
state_window_set_instance(state, id, info->instance_name);
state_window_set_skip_taskbar(state, id, info->skip_taskbar);
return window;
}
const wm_window_t *wm_state_window_get(const wm_state_t *state,
wm_window_id_t id) {
const window_t *state_window_get(const state_t *state, window_id_t id) {
for (size_t i = 0; i < state->window_count; 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;
}
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];
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;
size_t index = 0;
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;
wm_window_t *window = &state->windows[index];
wm_workspace_id_t workspace_id = window->workspace_id;
window_t *window = &state->windows[index];
workspace_id_t workspace_id = window->workspace_id;
p_delete(&window->title);
p_delete(&window->app_id);
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];
p_clear(window, 1);
window->id = WM_WINDOW_ID_INVALID;
window->workspace_id = WM_WORKSPACE_ID_INVALID;
window->id = ZDWM_WINDOW_ID_INVALID;
window->workspace_id = ZDWM_WORKSPACE_ID_INVALID;
matched = false;
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++) {
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--;
wm_workspace_t *workspace =
(wm_workspace_t *)wm_state_workspace_get(state, workspace_id);
workspace_t *workspace =
(workspace_t *)state_workspace_get(state, workspace_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) {
return state->window_count;
}
size_t state_window_count(const state_t *state) { return state->window_count; }
void wm_state_window_set_workspace(wm_state_t *state, wm_window_id_t window_id,
wm_workspace_id_t workspace_id) {
const wm_workspace_t *workspace = wm_state_workspace_get(state, workspace_id);
wm_window_t *window = (wm_window_t *)wm_state_window_get(state, window_id);
void state_window_set_workspace(state_t *state, window_id_t window_id,
workspace_id_t workspace_id) {
const workspace_t *workspace = state_workspace_get(state, workspace_id);
window_t *window = (window_t *)state_window_get(state, window_id);
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;
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(
wm_state_t *state, wm_window_id_t window_id,
wm_window_geometry_mode_t geometry_mode) {
wm_window_t *window = (wm_window_t *)wm_state_window_get(state, window_id);
void state_window_set_geometry_mode(state_t *state, window_id_t window_id,
window_geometry_mode_t geometry_mode) {
window_t *window = (window_t *)state_window_get(state, window_id);
if (!window) return;
window->geometry_mode = geometry_mode;
}
void wm_state_window_set_floating(wm_state_t *state, wm_window_id_t window_id,
bool floating) {
wm_window_t *window = (wm_window_t *)wm_state_window_get(state, window_id);
void state_window_set_floating(state_t *state, window_id_t window_id,
bool floating) {
window_t *window = (window_t *)state_window_get(state, window_id);
if (!window) return;
window->floating = floating;
}
void wm_state_window_set_sticky(wm_state_t *state, wm_window_id_t window_id,
bool sticky) {
wm_window_t *window = (wm_window_t *)wm_state_window_get(state, window_id);
void state_window_set_sticky(state_t *state, window_id_t window_id,
bool sticky) {
window_t *window = (window_t *)state_window_get(state, window_id);
if (!window) return;
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,
bool urgent) {
wm_window_t *window = (wm_window_t *)wm_state_window_get(state, window_id);
void state_window_set_urgent(state_t *state, window_id_t window_id,
bool urgent) {
window_t *window = (window_t *)state_window_get(state, window_id);
if (!window) return;
window->urgent = urgent;
}
void wm_state_window_set_fixed_size(wm_state_t *state, wm_window_id_t window_id,
bool fixed_size) {
wm_window_t *window = (wm_window_t *)wm_state_window_get(state, window_id);
void state_window_set_fixed_size(state_t *state, window_id_t window_id,
bool fixed_size) {
window_t *window = (window_t *)state_window_get(state, window_id);
if (!window) return;
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,
wm_window_id_t window_id,
bool skip_taskbar) {
wm_window_t *window = (wm_window_t *)wm_state_window_get(state, window_id);
void state_window_set_skip_taskbar(state_t *state, window_id_t window_id,
bool skip_taskbar) {
window_t *window = (window_t *)state_window_get(state, window_id);
if (!window) return;
window->skip_taskbar = skip_taskbar;
}
void wm_state_window_set_float_rect(wm_state_t *state, wm_window_id_t window_id,
wm_rect_t float_rect) {
wm_window_t *window = (wm_window_t *)wm_state_window_get(state, window_id);
void state_window_set_float_rect(state_t *state, window_id_t window_id,
rect_t float_rect) {
window_t *window = (window_t *)state_window_get(state, window_id);
if (!window) return;
window->float_rect = float_rect;
}
void wm_state_window_set_frame_rect(wm_state_t *state, wm_window_id_t window_id,
wm_rect_t frame_rect) {
wm_window_t *window = (wm_window_t *)wm_state_window_get(state, window_id);
void state_window_set_frame_rect(state_t *state, window_id_t window_id,
rect_t frame_rect) {
window_t *window = (window_t *)state_window_get(state, window_id);
if (!window) return;
window->frame_rect = frame_rect;
}
bool wm_state_window_set_title(wm_state_t *state, wm_window_id_t window_id,
const char *title) {
wm_window_t *window = (wm_window_t *)wm_state_window_get(state, window_id);
bool state_window_set_title(state_t *state, window_id_t window_id,
const char *title) {
window_t *window = (window_t *)state_window_get(state, window_id);
if (!window) return false;
p_delete(&window->title);
if (title) window->title = p_strdup(title);
window->title = p_strdup_nullable(title);
return true;
}
bool wm_state_window_set_app_id(wm_state_t *state, wm_window_id_t window_id,
const char *app_id) {
wm_window_t *window = (wm_window_t *)wm_state_window_get(state, window_id);
bool state_window_set_app_id(state_t *state, window_id_t window_id,
const char *app_id) {
window_t *window = (window_t *)state_window_get(state, window_id);
if (!window) return false;
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;
}
bool wm_state_window_set_class(wm_state_t *state, wm_window_id_t window_id,
const char *class_name) {
wm_window_t *window = (wm_window_t *)wm_state_window_get(state, window_id);
bool state_window_set_class(state_t *state, window_id_t window_id,
const char *class_name) {
window_t *window = (window_t *)state_window_get(state, window_id);
if (!window) return false;
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;
}
bool wm_state_window_set_instance(wm_state_t *state, wm_window_id_t window_id,
const char *instance_name) {
wm_window_t *window = (wm_window_t *)wm_state_window_get(state, window_id);
bool state_window_set_instance(state_t *state, window_id_t window_id,
const char *instance_name) {
window_t *window = (window_t *)state_window_get(state, window_id);
if (!window) return false;
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;
}
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;
}
wm_window_id_t wm_state_stack_at(const wm_state_t *state, size_t index) {
if (index >= state->window_count) return WM_WINDOW_ID_INVALID;
window_id_t state_stack_at(const state_t *state, size_t index) {
if (index >= state->window_count) return ZDWM_WINDOW_ID_INVALID;
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;
size_t index = 0;
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;
}
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;
size_t index = 0;
for (size_t i = 0; i < state->window_count; i++) {

View File

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

View File

@@ -4,46 +4,41 @@
#include "core/types.h"
typedef struct wm_output_info_t {
char *name;
wm_rect_t geometry;
} wm_output_info_t;
/*
* 由 backend 提供的窗口基础信息。
*
* 这些字符串由调用方提供state 如需长期持有,应自行复制。
*/
typedef struct wm_window_info_t {
wm_window_id_t id;
wm_rect_t frame_rect;
typedef struct window_info_t {
window_id_t id;
rect_t frame_rect;
const char *title;
const char *app_id;
const char *class_name;
const char *instance_name;
wm_window_geometry_mode_t geometry_mode;
window_geometry_mode_t geometry_mode;
bool urgent;
bool fixed_size;
bool skip_taskbar;
} wm_window_info_t;
} window_info_t;
typedef struct wm_workspace_desc_t {
size_t output_index; /* 对应 wm_state_init() 中 outputs[] 的索引 */
typedef struct workspace_desc_t {
size_t output_index; /* 对应 state_init() 中 outputs[] 的索引 */
const char *name;
const wm_layout_id_t *layout_ids;
const layout_id_t *layout_ids;
size_t layout_count;
wm_layout_id_t initial_layout_id;
} wm_workspace_desc_t;
layout_id_t initial_layout_id;
} workspace_desc_t;
/*
* workspace 描述校验接口。
*
* 这组接口只校验描述表自身的一致性,不访问 state。
*/
static inline bool wm_workspace_desc_layouts_valid(
const wm_workspace_desc_t *workspace) {
static inline bool workspace_desc_layouts_valid(
const workspace_desc_t *workspace) {
if (!workspace || !workspace->layout_count || !workspace->layout_ids) {
return false;
}
@@ -55,10 +50,10 @@ static inline bool wm_workspace_desc_layouts_valid(
return false;
}
static inline bool wm_workspace_desc_valid(const wm_workspace_desc_t *workspace,
size_t output_count) {
static inline bool workspace_desc_valid(const workspace_desc_t *workspace,
size_t output_count) {
if (!workspace || !workspace->name) 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
#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* Compatibility umbrella header. Prefer including headers from src/base in new
* code. */
#define ssizeof(foo) (ssize_t)sizeof(foo)
#define countof(foo) (ssizeof(foo) / ssizeof(foo[0]))
#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;
}
#include "base/log.h"
#include "base/macros.h"
#include "base/memory.h"

View File

@@ -5,6 +5,10 @@ add_executable(${OUTPUT_UTILS_TEST_APP_NAME}
${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}
PRIVATE ${SOURCE_DIR}
PRIVATE ${BUILD_DIR}

View File

@@ -1,12 +1,13 @@
#include "backend/output_utils.h"
#include <assert.h>
#include <stdint.h>
#include "backend/output_utils.h"
#include "core/types.h"
#include "utils.h"
static void assert_output_geometry(const wm_output_info_t *output, int32_t x,
int32_t y, int32_t width,
int32_t height) {
static void assert_output_geometry(const output_info_t *output, int32_t x,
int32_t y, int32_t width, int32_t height) {
assert(output);
assert(output->geometry.x == x);
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);
}
static void destroy_detect_result(wm_backend_detect_t *detect) {
static void destroy_detect_result(backend_detect_t *detect) {
assert(detect);
for (size_t i = 0; i < detect->output_count; i++) {
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) {
wm_output_info_t output = {0};
output_info_t output = {0};
assert(output_remove_duplication(nullptr, 0) == nullptr);
assert(output_remove_duplication(&output, 0) == nullptr);
}
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},
},
@@ -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));
assert(detect);
assert(detect->output_count == 2);

View File

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

View File

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