Compare commits

...

9 Commits

21 changed files with 437 additions and 27 deletions

View File

@@ -337,3 +337,9 @@ void backend_detect_destroy(backend_detect_t *detect) {
p_delete(&detect->outputs);
free(detect);
}
bool backend_apply_effect(backend_t *backend, const effect_t *effects,
size_t effect_count) {
/* TODO: */
return true;
}

View File

@@ -65,7 +65,7 @@ static bool handle_map_request(backend_t *backend, event_t *event,
const xcb_map_request_event_t *xcb_event) {
xcb_window_t window = xcb_event->window;
p_clear(event, 1);
event_reset(event);
event->type = ZDWM_EVENT_WINDOW_MAP_REQUEST;
window_map_request_event_t *ev = &event->as.window_map_request;
@@ -125,7 +125,7 @@ static bool handle_map_request(backend_t *backend, event_t *event,
static bool handle_unmap_notify(backend_t *backend, event_t *event,
const xcb_unmap_notify_event_t *xcb_event) {
p_clear(event, 1);
event_reset(event);
event->type = ZDWM_EVENT_WINDOW_REMOVE;
/* TODO: */
return true;
@@ -133,7 +133,7 @@ static bool handle_unmap_notify(backend_t *backend, event_t *event,
static bool handle_destroy_notify(backend_t *backend, event_t *event,
const xcb_destroy_notify_event_t *xcb_event) {
p_clear(event, 1);
event_reset(event);
event->type = ZDWM_EVENT_WINDOW_REMOVE;
/* TODO: */
return true;
@@ -142,7 +142,7 @@ static bool handle_destroy_notify(backend_t *backend, event_t *event,
static bool handle_configure_request(
backend_t *backend, event_t *event,
const xcb_configure_request_event_t *xcb_event) {
p_clear(event, 1);
event_reset(event);
event->type = ZDWM_EVENT_CONFIGURE_REQUEST;
/* TODO: */
return true;
@@ -179,5 +179,6 @@ bool backend_next_event(backend_t *backend, event_t *event) {
p_delete(&raw_event);
if (handled) return true;
event_reset(event);
}
}

View File

@@ -104,14 +104,12 @@ static bool rule_action_valid(const rule_action_t *action,
size_t workspace_count) {
if (!action) return false;
bool flag_valid = action->switch_to_workspace || action->fullscreen ||
action->maximize || action->floating;
if (action->workspace == ZDWM_WORKSPACE_ID_INVALID) {
return flag_valid;
} else {
return action->workspace < workspace_count && flag_valid;
return action->switch_to_workspace || action->fullscreen ||
action->maximize || action->floating;
}
return action->workspace < workspace_count;
}
static bool runtime_config_add_rule(zdwm_config_builder_t *builder,

View File

@@ -3,6 +3,7 @@
#include <stddef.h>
#include "core/event.h"
#include "core/plan.h"
#include "core/types.h"
typedef struct backend_t backend_t;
@@ -18,4 +19,18 @@ void backend_destroy(backend_t *backend);
backend_detect_t *backend_detect(backend_t *backend);
void backend_detect_destroy(backend_detect_t *detect);
/**
* @brief 阻塞等待 backend 产出下一个可交给 runtime 的归一化事件
* @details
* backend 可以在内部丢弃无关的原始平台事件,直到拿到一个可路由事件或遇到
* stop/error 条件。
*
* 返回 true 时,`event` 已被完整填充;若其中包含堆内存,所有权转交给调用方,
* 调用方必须在本轮处理结束后调用 event_cleanup() 或 event_reset()。
*
* 返回 false 时,表示 backend 请求停止或遇到错误;此时 `event` 必须保持为
* 无需 cleanup 的状态,调用方可以直接退出循环。
*/
bool backend_next_event(backend_t *backend, event_t *event);
bool backend_apply_effect(backend_t *backend, const effect_t *effects,
size_t effect_count);

38
src/core/command.h Normal file
View File

@@ -0,0 +1,38 @@
#pragma once
#include "core/types.h"
#include "core/wm_desc.h"
typedef enum command_type_t {
ZDWM_COMMAND_MANAGE_WINDOW,
ZDWM_COMMAND_SWITCH_WORKSPACE
} command_type_t;
typedef struct manage_window_command_t {
workspace_id_t workspace;
bool floating;
bool switch_to_workspace;
window_info_t info;
} manage_window_command_t;
typedef struct switch_workspace_command_t {
output_id_t output;
workspace_id_t workspace;
} switch_workspace_command_t;
/**
* @brief 非 owning 的命令值对象
* @details
* command_t 自身不拥有任何堆内存;其当前所有 payload 中出现的指针字段均为借用
* 语义。
*
* 因此 command_t 支持按值浅拷贝,且不需要单独的 cleanup 接口。若接收方需要在
* 源数据生命周期之外继续持有相关内容,必须自行复制。
*/
typedef struct command_t {
command_type_t type;
union {
manage_window_command_t manage_window;
switch_workspace_command_t switch_workspace;
} as;
} command_t;

23
src/core/command_buffer.c Normal file
View File

@@ -0,0 +1,23 @@
#include "core/command_buffer.h"
#include "base/array.h"
#include "base/memory.h"
#include "core/command.h"
void command_buffer_reset(command_buffer_t *buffer) {
if (!buffer->items || !buffer->capacity) return;
p_clear(buffer->items, buffer->capacity);
buffer->count = 0;
}
void command_buffer_cleanup(command_buffer_t *buffer) {
p_delete(&buffer->items);
buffer->count = 0;
buffer->capacity = 0;
}
void command_buffer_push(command_buffer_t *buffer, const command_t *command) {
command_t *cmd = array_push(buffer->items, buffer->count, buffer->capacity);
*cmd = *command;
}

15
src/core/command_buffer.h Normal file
View File

@@ -0,0 +1,15 @@
#pragma once
#include <stddef.h>
#include "core/command.h"
typedef struct command_buffer_t {
command_t *items;
size_t count;
size_t capacity;
} command_buffer_t;
void command_buffer_reset(command_buffer_t *buffer);
void command_buffer_cleanup(command_buffer_t *buffer);
void command_buffer_push(command_buffer_t *buffer, const command_t *command);

45
src/core/event.c Normal file
View File

@@ -0,0 +1,45 @@
#include "core/event.h"
#include "base/memory.h"
static void window_layer_props_cleanup(window_layer_props_t *props) {
if (!props) return;
p_delete(&props->types);
props->type_count = 0;
p_delete(&props->states);
props->state_count = 0;
}
static void window_metadata_cleanup(window_metadata_t *metadata) {
if (!metadata) return;
p_delete(&metadata->title);
p_delete(&metadata->app_id);
p_delete(&metadata->role);
p_delete(&metadata->class_name);
p_delete(&metadata->instance_name);
}
void event_cleanup(event_t *event) {
if (!event) return;
switch (event->type) {
case ZDWM_EVENT_WINDOW_MAP_REQUEST:
window_layer_props_cleanup(&event->as.window_map_request.props);
window_metadata_cleanup(&event->as.window_map_request.metadata);
break;
case ZDWM_EVENT_WINDOW_METADATA_CHANGED:
window_metadata_cleanup(&event->as.window_metadata_change.metadata);
break;
default:
break;
}
}
void event_reset(event_t *event) {
if (!event) return;
event_cleanup(event);
p_clear(event, 1);
}

View File

@@ -178,3 +178,6 @@ typedef struct event_t {
configure_request_event_t configure_request;
} as;
} event_t;
void event_cleanup(event_t *event);
void event_reset(event_t *event);

View File

@@ -21,14 +21,15 @@ void layout_result_push(layout_result_t *result, layout_item_t item) {
}
void layout_registry_cleanup(layout_registry_t *registry) {
while (registry->slot_count > 0) {
layout_slot_t *r = &registry->slots[--registry->slot_count];
for (size_t i = 0; i < registry->slot_count; ++i) {
layout_slot_t *r = &registry->slots[i];
p_delete(&r->name);
p_delete(&r->symbol);
p_delete(&r->description);
r->fn = nullptr;
r->id = ZDWM_LAYOUT_ID_INVALID;
}
p_delete(&registry->slots);
registry->slot_count = 0;
registry->slot_capacity = 0;

22
src/core/plan.c Normal file
View File

@@ -0,0 +1,22 @@
#include "core/plan.h"
#include "base/array.h"
#include "base/memory.h"
void plan_reset(plan_t *plan) {
if (!plan->effects || !plan->capacity) return;
p_clear(plan->effects, plan->capacity);
plan->count = 0;
}
void plan_cleanup(plan_t *plan) {
p_delete(&plan->effects);
plan->count = 0;
plan->capacity = 0;
}
void plan_push_effect(plan_t *plan, const effect_t *effect) {
effect_t *eft = array_push(plan->effects, plan->count, plan->capacity);
*eft = *effect;
}

44
src/core/plan.h Normal file
View File

@@ -0,0 +1,44 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
#include "core/types.h"
typedef uint32_t dirty_mask_t;
typedef enum dirty_flags_t : dirty_mask_t {
ZDWM_DIRTY_NONE = 0,
ZDWM_DIRTY_LAYOUT = 1u << 0,
} dirty_flags_t;
typedef enum effect_type_t {
ZDWM_EFFECT_NONE,
ZDWM_EFFECT_MAP_WINDOW,
ZDWM_EFFECT_UNMAP_WINDOW,
ZDWM_EFFECT_FOCUS_WINDOW,
} effect_type_t;
typedef struct effect_only_window_id_t {
window_id_t window;
} effect_only_window_id_t;
typedef struct effect_t {
effect_type_t type;
union {
effect_only_window_id_t map;
effect_only_window_id_t unmap;
effect_only_window_id_t focus;
} as;
} effect_t;
typedef struct plan_t {
effect_t *effects;
size_t count;
size_t capacity;
dirty_mask_t dirty_flags;
} plan_t;
void plan_reset(plan_t *plan);
void plan_cleanup(plan_t *plan);
void plan_push_effect(plan_t *plan, const effect_t *effect);

View File

@@ -1,10 +1,111 @@
#include "core/policy.h"
#include "base/macros.h"
#include "core/state.h"
#include <zdwm/types.h>
layer_type_t policy_adjust_window_layer_from_transient(
const state_t *state, window_id_t transient_for, layer_type_t base_layer) {
const window_t *window = state_window_get(state, transient_for);
return MAX(window->layer, base_layer);
#include "base/macros.h"
#include "core/command.h"
#include "core/command_buffer.h"
#include "core/event.h"
#include "core/layer.h"
#include "core/rules.h"
#include "core/state.h"
#include "core/types.h"
#include "core/wm_desc.h"
static workspace_id_t derive_window_workspace(const state_t *state) {
return state->outputs[state->current_output_index].current_workspace_id;
}
static bool route_map_request(const state_t *state, const rules_t *rules,
const window_map_request_event_t *e,
command_buffer_t *out) {
if (state_window_get(state, e->window)) return false;
layer_type_t layer_type = layer_classify(&e->props);
if (e->transient_for != ZDWM_WINDOW_ID_INVALID) {
const window_t *window = state_window_get(state, e->transient_for);
if (window) layer_type = MAX(window->layer, layer_type);
}
command_t cmd = {
.type = ZDWM_COMMAND_MANAGE_WINDOW,
.as.manage_window =
{
.workspace = derive_window_workspace(state),
.info =
{
.id = e->window,
.transient_for = e->transient_for,
.frame_rect = e->rect,
.title = e->metadata.title,
.app_id = e->metadata.app_id,
.role = e->metadata.role,
.class_name = e->metadata.class_name,
.instance_name = e->metadata.instance_name,
.geometry_mode = e->geometry_mode,
.layer_type = layer_type,
.urgent = e->urgent,
.fixed_size = e->fixed_size,
.skip_taskbar = e->skip_taskbar,
},
},
};
rule_action_t action = {.workspace = ZDWM_WORKSPACE_ID_INVALID};
bool have_rule_match = rules_resolve(rules, &e->metadata, &action);
if (have_rule_match) {
manage_window_command_t *data = &cmd.as.manage_window;
if (action.workspace != ZDWM_WORKSPACE_ID_INVALID) {
data->workspace = action.workspace;
}
if (action.floating) {
data->floating = true;
}
if (action.fullscreen) {
data->info.geometry_mode = ZDWM_GEOMETRY_FULLSCREEN;
} else if (action.maximize) {
data->info.geometry_mode = ZDWM_GEOMETRY_MAXIMIZED;
}
}
command_buffer_push(out, &cmd);
if (have_rule_match && action.switch_to_workspace) {
workspace_id_t workspace_id = cmd.as.manage_window.workspace;
const workspace_t *workspace = state_workspace_get(state, workspace_id);
if (workspace) {
command_t switch_workspace_cmd = {
.type = ZDWM_COMMAND_SWITCH_WORKSPACE,
.as.switch_workspace =
{
.output = workspace->output_id,
.workspace = workspace_id,
},
};
command_buffer_push(out, &switch_workspace_cmd);
}
}
return true;
}
bool policy_route_event(const policy_context_t *ctx, command_buffer_t *out) {
switch (ctx->event->type) {
case ZDWM_EVENT_WINDOW_MAP_REQUEST:
return route_map_request(ctx->state, ctx->rules,
&ctx->event->as.window_map_request, out);
default:
return false;
}
}
bool policy_apply_command(const policy_context_t *ctx,
const command_buffer_t *command_buffer,
plan_t *plan) {
/* TODO: */
return true;
}

View File

@@ -1,8 +1,17 @@
#pragma once
#include "core/layer.h"
#include "core/command_buffer.h"
#include "core/event.h"
#include "core/plan.h"
#include "core/rules.h"
#include "core/state.h"
#include "core/types.h"
layer_type_t policy_adjust_window_layer_from_transient(
const state_t *state, window_id_t transient_for, layer_type_t base_layer);
typedef struct policy_context_t {
const state_t *state;
const rules_t *rules;
const event_t *event;
} policy_context_t;
bool policy_route_event(const policy_context_t *ctx, command_buffer_t *out);
bool policy_apply_command(const policy_context_t *ctx,
const command_buffer_t *command_buffer, plan_t *plan);

View File

@@ -1,5 +1,9 @@
#include "core/rules.h"
#include <stddef.h>
#include <string.h>
#include <zdwm/types.h>
#include "base/memory.h"
#include "core/types.h"
@@ -35,3 +39,43 @@ void rules_cleanup(rules_t *rules) {
rules->capacity = 0;
rules->count = 0;
}
static inline bool str_match(const char *pattern, const char *value) {
return !pattern || (value && strcmp(pattern, value) == 0);
}
static bool rule_match_window(const rule_match_t *match,
const window_metadata_t *meta) {
return str_match(match->app_id, meta->app_id) &&
str_match(match->role, meta->role) &&
str_match(match->class_name, meta->class_name) &&
str_match(match->instance_name, meta->instance_name);
}
static void rule_action_merge(const rule_action_t *src, rule_action_t *dest) {
if (!src || !dest) return;
if (src->workspace != ZDWM_WORKSPACE_ID_INVALID) {
dest->workspace = src->workspace;
}
dest->switch_to_workspace |= src->switch_to_workspace;
dest->fullscreen |= src->fullscreen;
dest->maximize |= src->maximize;
dest->floating |= src->floating;
}
bool rules_resolve(const rules_t *rules, const window_metadata_t *metadata,
rule_action_t *action_out) {
if (!action_out) return false;
bool matched = false;
for (size_t i = 0; i < rules->count; ++i) {
if (!rule_match_window(&rules->items[i].match, metadata)) continue;
matched = true;
rule_action_merge(&rules->items[i].action, action_out);
}
return matched;
}

View File

@@ -3,6 +3,7 @@
#include <stddef.h>
#include "core/types.h"
#include "core/window.h"
typedef struct rule_item_t {
rule_match_t match;
@@ -15,5 +16,8 @@ typedef struct rules_t {
size_t capacity;
} rules_t;
bool rules_move(rules_t *dest, rules_t *src);
bool rules_move(rules_t *src, rules_t *dest);
void rules_cleanup(rules_t *rules);
bool rules_resolve(const rules_t *rules, const window_metadata_t *metadata,
rule_action_t *action_out);

View File

@@ -4,8 +4,11 @@
#include "base/memory.h"
#include "core/backend.h"
#include "core/command_buffer.h"
#include "core/event.h"
#include "core/layout.h"
#include "core/plan.h"
#include "core/policy.h"
#include "core/rules.h"
#include "core/state.h"
#include "core/wm_desc.h"
@@ -82,6 +85,8 @@ void runtime_init_desc_cleanup(runtime_init_desc_t *desc) {
void runtime_shutdown(runtime_t *runtime) {
runtime->running = false;
plan_cleanup(&runtime->plan);
command_buffer_cleanup(&runtime->command_buffer);
layout_registry_cleanup(&runtime->layouts);
rules_cleanup(&runtime->rules);
state_cleanup(&runtime->state);
@@ -92,10 +97,29 @@ void runtime_shutdown(runtime_t *runtime) {
}
void runtime_run(runtime_t *runtime) {
for (;;) {
runtime->running = true;
backend_t *backend = runtime->backend;
command_buffer_t *command_buffer = &runtime->command_buffer;
plan_t *plan = &runtime->plan;
while (runtime->running) {
event_t event = {0};
if (!backend_next_event(runtime->backend, &event)) {
break;
}
if (!backend_next_event(backend, &event)) break;
command_buffer_reset(command_buffer);
plan_reset(plan);
policy_context_t ctx = {
.state = &runtime->state,
.rules = &runtime->rules,
.event = &event,
};
policy_route_event(&ctx, command_buffer);
policy_apply_command(&ctx, command_buffer, plan);
if (plan->count) backend_apply_effect(backend, plan->effects, plan->count);
event_cleanup(&event);
}
}

View File

@@ -3,7 +3,9 @@
#include <stddef.h>
#include "core/backend.h"
#include "core/command_buffer.h"
#include "core/layout.h"
#include "core/plan.h"
#include "core/rules.h"
#include "core/state.h"
@@ -23,6 +25,8 @@ typedef struct runtime_t {
bool running;
bool will_restart;
plan_t plan;
command_buffer_t command_buffer;
state_t state;
layout_registry_t layouts;
rules_t rules;

View File

@@ -41,8 +41,12 @@ add_executable(${RUNTIME_CONFIG_TEST_APP_NAME}
${SOURCE_DIR}/config/defaults.c
${SOURCE_DIR}/config/loader.c
${SOURCE_DIR}/config/runtime_config.c
${SOURCE_DIR}/core/command_buffer.c
${SOURCE_DIR}/core/event.c
${SOURCE_DIR}/core/layer.c
${SOURCE_DIR}/core/layout.c
${SOURCE_DIR}/core/plan.c
${SOURCE_DIR}/core/policy.c
${SOURCE_DIR}/core/rules.c
${SOURCE_DIR}/core/runtime.c
${SOURCE_DIR}/core/state.c

View File

@@ -22,6 +22,11 @@ bool backend_next_event(backend_t *backend, event_t *event) {
return false;
}
bool backend_apply_effect(backend_t *backend, const effect_t *effects,
size_t effect_count) {
return false;
}
static backend_t *test_backend_create(void) {
return calloc(1, sizeof(backend_t));
}

View File

@@ -10,8 +10,12 @@ add_executable(${TEST_APP_NAME}
${SOURCE_DIR}/backend/x11/backend.c
${SOURCE_DIR}/backend/x11/event.c
${SOURCE_DIR}/backend/x11/window.c
${SOURCE_DIR}/core/command_buffer.c
${SOURCE_DIR}/core/event.c
${SOURCE_DIR}/core/layer.c
${SOURCE_DIR}/core/layout.c
${SOURCE_DIR}/core/plan.c
${SOURCE_DIR}/core/policy.c
${SOURCE_DIR}/core/rules.c
${SOURCE_DIR}/core/runtime.c
${SOURCE_DIR}/core/state.c