Compare commits

...

6 Commits

Author SHA1 Message Date
09d5e4b4d0 refactor(policy): 删除 action 模块,将 action -> command 解析放在 policy 模块内部
- 删除 src/core/action.c|h,将 spawn 移至 src/base/process
- policy_resolve_action 实现完整的 action 分发逻辑
- binding_table_cycle_mode 改为纯计算,返回模式 ID 而非修改状态
- 从 state 移除 state_workspace_cycle_layout / state_cycle_current_output
- 新增 ZDWM_COMMAND_QUIT 命令,通过 plan 标志驱动 runtime 退出
- set_binding_mode 现在生成 BIND_KEY effect 以同步后端按键绑定
- 扩展 action 数据类型支持 window_send_to_workspace 和 window_cycle_output
2026-05-27 01:12:04 +08:00
ff3b51fd1d feat(policy): 扩展命令并实现窗口迁移、 output/layout/binding_mode 切换功能
- command.h 新增 SEND_TO_WORKSPACE、SET_CURRENT_OUTPUT、SET_LAYOUT、SET_BINDING_MODE 四个命令类型及载荷
- policy.c 实现对应的 apply 逻辑,包含焦点转移、映射/取消映射副作用和 listener 通知
- plan.h 增加 quit/will_restart 标志,为后续优雅退出做准备
- binding.c 对重复设置同一 mode 增加短路返回
2026-05-26 13:15:54 +08:00
989a1ce6e8 feat(listeners): 调整通知结构体字段并补充文档注释 2026-05-24 17:58:29 +08:00
13fc41f141 feat(runtime): 通过 listeners 发送初始状态 2026-05-23 23:06:53 +08:00
ca9e543cc0 feat(listeners): 实现 listener 通知分发和清理功能 2026-05-23 21:09:43 +08:00
33b0f2f54f refactor(action): 重新设计 action 实现方式 2026-05-19 02:55:08 +08:00
23 changed files with 1183 additions and 540 deletions

View File

@@ -33,13 +33,13 @@ add_executable(${APP_NAME}
${SOURCE_DIR}/base/color.c
${SOURCE_DIR}/base/log.c
${SOURCE_DIR}/base/process.c
${SOURCE_DIR}/base/window_list.c
${SOURCE_DIR}/config/defaults.c
${SOURCE_DIR}/config/loader.c
${SOURCE_DIR}/config/runtime_config.c
${SOURCE_DIR}/core/action.c
${SOURCE_DIR}/core/binding.c
${SOURCE_DIR}/core/command_buffer.c
${SOURCE_DIR}/core/event.c

View File

@@ -8,50 +8,76 @@
extern "C" {
#endif
typedef union zdwm_action_arg_t {
bool b;
int32_t i;
uint32_t ui;
const char *str;
const void *ptr;
} zdwm_action_arg_t;
typedef enum zdwm_action_type_t {
ZDWM_ACTION_SPAWN,
ZDWM_ACTION_QUIT,
ZDWM_ACTION_RAISE_OR_RUN,
typedef struct zdwm_runtime_t zdwm_runtime_t;
ZDWM_ACTION_OUTPUT_CYCLE,
ZDWM_ACTION_WORKSPACE_SWITCH_SAME_OUTPUT_BY_INDEX,
ZDWM_ACTION_LAYOUT_CYCLE,
ZDWM_ACTION_BINDING_MODE_CYCLE,
typedef struct zdwm_action_api_t {
void (*spawn)(const char *command);
void (*quit)(zdwm_runtime_t *runtime, bool restart);
void (*raise_or_run)(
zdwm_runtime_t *runtime,
const char *class_name,
const char *command
);
void (*toggle_fullscreen)(zdwm_runtime_t *runtime);
void (*toggle_maximize)(zdwm_runtime_t *runtime);
void (*toggle_floating)(zdwm_runtime_t *runtime);
void (*toggle_sticky)(zdwm_runtime_t *runtime);
void (*switch_workspace)(
zdwm_runtime_t *runtime,
zdwm_workspace_id_t workspace_id
);
void (*switch_workspace_in_current_output)(
zdwm_runtime_t *runtime,
zdwm_workspace_id_t workspace_id
);
void (*switch_workspace_by_index_in_current_output)(
zdwm_runtime_t *runtime,
uint32_t index
);
void (*cycle_layout)(zdwm_runtime_t *runtime, int32_t delta);
void (*cycle_current_output)(zdwm_runtime_t *runtime, int32_t delta);
void (*focus_window)(zdwm_runtime_t *runtime, int32_t delta);
} zdwm_action_api_t;
ZDWM_ACTION_WINDOW_TOGGLE_FULLSCREEN,
ZDWM_ACTION_WINDOW_TOGGLE_MAXIMIZE,
ZDWM_ACTION_WINDOW_TOGGLE_MINIMIZE,
ZDWM_ACTION_WINDOW_TOGGLE_FLOATING,
ZDWM_ACTION_WINDOW_TOGGLE_STICKY,
typedef void zdwm_action_fn(
zdwm_runtime_t *runtime,
const zdwm_action_api_t *ctx,
const zdwm_action_arg_t *arg
);
ZDWM_ACTION_WINDOW_FOCUS_CYCLE,
ZDWM_ACTION_WINDOW_KILL,
ZDWM_ACTION_WINDOW_SEND_TO_WORKSPACE_SAME_OUTPUT_BY_INDEX,
ZDWM_ACTION_WINDOW_CYCLE_OUTPUT,
} zdwm_action_type_t;
typedef struct zdwm_action_data_spawn_t {
const char *command;
} zdwm_action_data_spawn_t;
typedef struct zdwm_action_data_quit_t {
bool restart;
} zdwm_action_data_quit_t;
typedef struct zdwm_action_data_raise_or_run_t {
const char *class_name;
const char *command;
} zdwm_action_data_raise_or_run_t;
typedef struct zdwm_action_data_delta_only_t {
int32_t delta;
} zdwm_action_data_delta_only_t;
typedef struct zdwm_action_data_index_only_t {
uint32_t index;
} zdwm_action_data_index_only_t;
typedef struct zdwm_action_data_window_send_to_workspace_t {
uint32_t index;
bool switch_workspace;
} zdwm_action_data_window_send_to_workspace_t;
typedef struct zdwm_action_data_window_cycle_output_t {
int32_t delta;
bool keep_focus;
} zdwm_action_data_window_cycle_output_t;
typedef struct zdwm_action_t {
zdwm_action_type_t type;
union {
zdwm_action_data_spawn_t spawn;
zdwm_action_data_quit_t quit;
zdwm_action_data_raise_or_run_t raise_or_run;
zdwm_action_data_delta_only_t output_cycle;
zdwm_action_data_index_only_t workspace_switch_same_output_by_index;
zdwm_action_data_delta_only_t layout_cycle;
zdwm_action_data_delta_only_t binding_mode_cycle;
zdwm_action_data_delta_only_t window_focus_cycle;
zdwm_action_data_window_send_to_workspace_t
window_send_to_workspace_same_output_by_index;
zdwm_action_data_window_cycle_output_t window_cycle_output;
} as;
} zdwm_action_t;
#if defined(__cplusplus)
}

View File

@@ -112,8 +112,7 @@ typedef struct zdwm_api_t {
zdwm_config_builder_t *builder,
zdwm_binding_mode_id_t bind_mode,
const char *key_sequence,
zdwm_action_fn action_fn,
zdwm_action_arg_t action_arg
zdwm_action_t action
);
/**

View File

@@ -30,13 +30,14 @@ typedef void zdwm_workspace_active_updated(
typedef struct zdwm_layout_notify_t {
zdwm_workspace_id_t workspace;
zdwm_layout_id_t id;
const char *symbol; /* 不持有内存 */
const char *name; /* 不持有内存 */
const char *symbol; /* 不持有内存 */
const char *description; /* 不持有内存 */
} zdwm_layout_notify_t;
typedef void zdwm_layout_notify(zdwm_layout_notify_t layout, void *user_data);
typedef struct zdwm_binding_mode_notify_t {
bool is_default_mode;
bool is_current_mode;
zdwm_binding_mode_id_t id;
const char *name; /* 不持有内存 */
} zdwm_binding_mode_notify_t;

23
src/base/process.c Normal file
View File

@@ -0,0 +1,23 @@
#include "base/process.h"
#include <paths.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
#include "base/log.h"
void spawn(const char *command) {
if (fork() == 0) {
setsid();
if (fork() == 0) {
execl(_PATH_BSHELL, _PATH_BSHELL, "-c", command, nullptr);
fatal("execl fail: %s", command);
}
exit(0);
}
wait(0);
}

3
src/base/process.h Normal file
View File

@@ -0,0 +1,3 @@
#pragma once
void spawn(const char *command);

View File

@@ -13,80 +13,12 @@
static constexpr char launcher[] =
"rofi -show combi -modes combi -combi-modes window,drun,run,ssh,windowcd";
typedef struct raise_or_run_arg_t {
const char *class_name;
const char *command;
} raise_or_run_arg_t;
typedef struct zdwm_action_data_raise_or_run_t raise_or_run_data_t;
static raise_or_run_arg_t terminal = {"XTerm", "xterm"};
static raise_or_run_arg_t editor = {"Emacs", "emacsclient -a '' -r -n"};
static raise_or_run_arg_t browser = {"firefox", "firefox-bin"};
static raise_or_run_arg_t chrome = {"Google-chrome", "google-chrome-stable"};
static void spawn(
zdwm_runtime_t *runtime,
const zdwm_action_api_t *api,
const zdwm_action_arg_t *arg
) {
api->spawn(arg->str);
}
static void quit(
zdwm_runtime_t *runtime,
const zdwm_action_api_t *api,
const zdwm_action_arg_t *arg
) {
api->quit(runtime, arg->b);
}
static void raise_or_run(
zdwm_runtime_t *runtime,
const zdwm_action_api_t *api,
const zdwm_action_arg_t *arg
) {
auto args = (raise_or_run_arg_t *)arg->ptr;
api->raise_or_run(runtime, args->class_name, args->command);
}
static void toggle_fullscreen(
zdwm_runtime_t *runtime,
const zdwm_action_api_t *api,
const zdwm_action_arg_t *arg
) {
api->toggle_fullscreen(runtime);
}
static void toggle_maximize(
zdwm_runtime_t *runtime,
const zdwm_action_api_t *api,
const zdwm_action_arg_t *arg
) {
api->toggle_maximize(runtime);
}
static void toggle_floating(
zdwm_runtime_t *runtime,
const zdwm_action_api_t *api,
const zdwm_action_arg_t *arg
) {
api->toggle_floating(runtime);
}
static void switch_workspace(
zdwm_runtime_t *runtime,
const zdwm_action_api_t *api,
const zdwm_action_arg_t *arg
) {
api->switch_workspace_by_index_in_current_output(runtime, arg->ui);
}
static void focus_window(
zdwm_runtime_t *runtime,
const zdwm_action_api_t *api,
const zdwm_action_arg_t *arg
) {
api->focus_window(runtime, arg->i);
}
static raise_or_run_data_t terminal = {"XTerm", "xterm"};
static raise_or_run_data_t editor = {"Emacs", "emacsclient -a '' -r -n"};
static raise_or_run_data_t browser = {"firefox", "firefox-bin"};
static raise_or_run_data_t chrome = {"Google-chrome", "google-chrome-stable"};
bool config_defaults_build(
const zdwm_api_t *api,
@@ -156,32 +88,63 @@ bool config_defaults_build(
auto default_mode = api->add_mode(builder, "default");
#define Alt "Mod1"
#define Super "Mod4"
#define BIND(MODE, KEY, FN, ARG) \
api->bind(builder, MODE, KEY, FN, (zdwm_action_arg_t)ARG)
#define Alt_Key "Mod1"
#define Super_key "Mod4"
#define Alt(K) Alt_Key "+" K
#define Super(K) Super_key "+" K
BIND(default_mode, Super "+r", spawn, {.str = launcher});
BIND(default_mode, Super "+Shift+q", quit, {.b = false});
BIND(default_mode, Super "+Control+r", quit, {.b = true});
#define BIND(MODE, KEY, ...) \
api->bind(builder, MODE, KEY, (zdwm_action_t)__VA_ARGS__)
#define SPAWN(MODE, KEY, COMMAND) \
BIND(MODE, KEY, {.type = ZDWM_ACTION_SPAWN, .as.spawn.command = (COMMAND)})
#define QUIT(MODE, KEY, RESTART) \
BIND(MODE, KEY, {.type = ZDWM_ACTION_QUIT, .as.quit.restart = (RESTART)})
#define RAISE_OR_RUN(MODE, KEY, ...) \
BIND( \
MODE, \
KEY, \
{ \
.type = ZDWM_ACTION_RAISE_OR_RUN, \
.as.raise_or_run = (zdwm_action_data_raise_or_run_t)__VA_ARGS__, \
} \
)
#define BIND_DEFAULT(KEY, ...) BIND(default_mode, KEY, __VA_ARGS__)
BIND(default_mode, Super "+Return", spawn, {.str = terminal.command});
BIND(default_mode, Alt "+Control+r", raise_or_run, {.ptr = &terminal});
BIND(default_mode, Super "+e", raise_or_run, {.ptr = &editor});
BIND(default_mode, Super "+q", raise_or_run, {.ptr = &browser});
BIND(default_mode, Super "+a", raise_or_run, {.ptr = &chrome});
BIND(default_mode, Super "+f", toggle_fullscreen, {0});
BIND(default_mode, Super "+m", toggle_maximize, {0});
BIND(default_mode, Super "+Control+space", toggle_floating, {0});
BIND(default_mode, Alt "+j", focus_window, {.i = 1});
BIND(default_mode, Alt "+k", focus_window, {.i = -1});
SPAWN(default_mode, Super("r"), launcher);
QUIT(default_mode, Super("Shift+q"), false);
QUIT(default_mode, Super("Control+r"), true);
SPAWN(default_mode, Super("Return"), terminal.command);
RAISE_OR_RUN(default_mode, Alt("Control+r"), terminal);
RAISE_OR_RUN(default_mode, Super("e"), editor);
RAISE_OR_RUN(default_mode, Super("q"), browser);
RAISE_OR_RUN(default_mode, Super("a"), chrome);
BIND_DEFAULT(Super("f"), {.type = ZDWM_ACTION_WINDOW_TOGGLE_FLOATING});
BIND_DEFAULT(Super("m"), {.type = ZDWM_ACTION_WINDOW_TOGGLE_MAXIMIZE});
BIND_DEFAULT(
Super("Control+space"),
{.type = ZDWM_ACTION_WINDOW_TOGGLE_FLOATING}
);
BIND_DEFAULT(
Alt("j"),
{.type = ZDWM_ACTION_WINDOW_FOCUS_CYCLE, .as.window_focus_cycle.delta = 1}
);
BIND_DEFAULT(
Alt("k"),
{.type = ZDWM_ACTION_WINDOW_FOCUS_CYCLE, .as.window_focus_cycle.delta = -1}
);
char buffer[64] = {0};
for (uint32_t i = 0; i < 9; ++i) {
snprintf(buffer, sizeof(buffer), "%s+%d", Super, i + 1);
BIND(default_mode, buffer, switch_workspace, {.ui = i});
snprintf(buffer, sizeof(buffer), "%s+%d", Super_key, i + 1);
zdwm_action_t action = {
.type = ZDWM_ACTION_WINDOW_SEND_TO_WORKSPACE_SAME_OUTPUT_BY_INDEX,
.as.workspace_switch_same_output_by_index.index = i,
};
BIND_DEFAULT(buffer, action);
}
#undef BIND_DEFAULT
#undef BIND
api->set_default_mode(builder, default_mode);

View File

@@ -41,6 +41,7 @@ void runtime_config_cleanup(runtime_init_desc_t *desc) {
}
binding_table_destroy(desc->binding_table);
desc->binding_table = nullptr;
listeners_cleanup(&desc->listeners);
workspace_desc_list_cleanup(&desc->workspaces, &desc->workspace_count);
if (desc->config_module_handle) dlclose(desc->config_module_handle);
desc->config_module_handle = nullptr;
@@ -59,6 +60,7 @@ static void config_builder_cleanup(zdwm_config_builder_t *builder) {
binding_table_destroy(builder->binding_table);
builder->binding_table = nullptr;
listeners_cleanup(&builder->listeners);
}
static layout_id_t runtime_config_register_layout(
@@ -167,10 +169,9 @@ static bool runtime_config_bind(
zdwm_config_builder_t *builder,
zdwm_binding_mode_id_t mode,
const char *key,
zdwm_action_fn fn,
zdwm_action_arg_t arg
zdwm_action_t action
) {
return binding_table_add_bind(builder->binding_table, mode, key, fn, arg);
return binding_table_add_bind(builder->binding_table, mode, key, action);
}
static bool runtime_config_set_default_mode(

View File

@@ -1,265 +0,0 @@
#include "core/action.h"
#include <paths.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
#include "base/log.h"
#include "base/macros.h"
#include "core/command.h"
#include "core/command_buffer.h"
#include "core/runtime.h"
#include "core/state.h"
#include "core/types.h"
#include "core/window.h"
void spawn(const char *command) {
if (fork() == 0) {
setsid();
if (fork() == 0) {
execl(_PATH_BSHELL, _PATH_BSHELL, "-c", command, nullptr);
fatal("execl fail: %s", command);
}
exit(0);
}
wait(0);
}
void quit(runtime_t *runtime, bool restart) {
runtime->will_restart = restart;
runtime->running = false;
}
static const window_t *
get_next_window_by_class(state_t *state, const char *class_name) {
auto output = state_output_at(state, state->current_output_index);
auto workspace = state_workspace_get(state, output->current_workspace_id);
size_t start_index = 0;
if (!window_id_invalid(workspace->focused_window_id)) {
for (size_t i = 0; i < state_window_count(state); ++i) {
if (state_window_at(state, i)->id == workspace->focused_window_id) {
start_index = i;
break;
}
}
}
for (size_t i = start_index + 1; i < state_window_count(state); ++i) {
auto window = state_window_at(state, i);
if (strcmp(window->class_name, class_name) == 0) {
return window;
}
}
for (size_t i = 0; i < MIN(start_index + 1, state_window_count(state)); ++i) {
auto window = state_window_at(state, i);
if (strcmp(window->class_name, class_name) == 0) {
return window;
}
}
return nullptr;
}
void raise_or_run(
runtime_t *runtime,
const char *class_name,
const char *command
) {
auto window = get_next_window_by_class(&runtime->state, class_name);
if (!window) {
spawn(command);
return;
}
command_t switch_workspace_cmd = {
.type = ZDWM_COMMAND_SWITCH_WORKSPACE,
.as.switch_workspace.workspace = window->workspace_id,
};
command_buffer_push(&runtime->command_buffer, &switch_workspace_cmd);
command_t focus_cmd = {
.type = ZDWM_COMMAND_FOCUS_WINDOW,
.as.focus.window = window->id,
};
command_buffer_push(&runtime->command_buffer, &focus_cmd);
}
static const window_t *get_current_focused_window(const state_t *state) {
auto output = state_output_at(state, state->current_output_index);
auto workspace = state_workspace_get(state, output->current_workspace_id);
return state_window_get(state, workspace->focused_window_id);
}
void toggle_fullscreen(runtime_t *runtime) {
auto window = get_current_focused_window(&runtime->state);
if (!window) return;
command_t window_set_fullscreen_cmd = {
.type = ZDWM_COMMAND_WINDOW_SET_FULLSCREEN,
.as.fullscreen = {
.window = window->id,
.state = !window->fullscreen,
}
};
command_buffer_push(&runtime->command_buffer, &window_set_fullscreen_cmd);
}
void toggle_maximize(runtime_t *runtime) {
auto window = get_current_focused_window(&runtime->state);
if (!window) return;
command_t window_set_maximized_cmd = {
.type = ZDWM_COMMAND_WINDOW_SET_MAXIMIZED,
.as.maximized = {
.window = window->id,
.state = !window->maximized,
}
};
command_buffer_push(&runtime->command_buffer, &window_set_maximized_cmd);
}
void toggle_floating(runtime_t *runtime) {
auto window = get_current_focused_window(&runtime->state);
if (!window) return;
command_t window_set_floating_cmd = {
.type = ZDWM_COMMAND_WINDOW_SET_FLOATING,
.as.floating = {
.window = window->id,
.state = !window->floating,
}
};
command_buffer_push(&runtime->command_buffer, &window_set_floating_cmd);
}
void toggle_sticky(runtime_t *runtime) {
auto window = get_current_focused_window(&runtime->state);
if (!window) return;
command_t window_set_sticky_cmd = {
.type = ZDWM_COMMAND_WINDOW_SET_STICKY,
.as.sticky = {
.window = window->id,
.state = !window->sticky,
}
};
command_buffer_push(&runtime->command_buffer, &window_set_sticky_cmd);
}
void switch_workspace(runtime_t *runtime, workspace_id_t workspace_id) {
auto workspace = state_workspace_get(&runtime->state, workspace_id);
if (!workspace) return;
command_t switch_workspace_cmd = {
.type = ZDWM_COMMAND_SWITCH_WORKSPACE,
.as.switch_workspace.workspace = workspace_id
};
command_buffer_push(&runtime->command_buffer, &switch_workspace_cmd);
}
void switch_workspace_in_current_output(
runtime_t *runtime,
workspace_id_t workspace_id
) {
auto state = &runtime->state;
auto output = state_output_at(state, state->current_output_index);
auto workspace = state_workspace_get(state, workspace_id);
if (workspace->output_id != output->id) return;
command_t switch_workspace_cmd = {
.type = ZDWM_COMMAND_SWITCH_WORKSPACE,
.as.switch_workspace.workspace = workspace_id
};
command_buffer_push(&runtime->command_buffer, &switch_workspace_cmd);
}
void switch_workspace_by_index_in_current_output(
runtime_t *runtime,
uint32_t index
) {
auto state = &runtime->state;
auto output = state_output_at(state, state->current_output_index);
uint32_t count = 0;
for (size_t i = 0; i < state_workspace_count(state); ++i) {
auto workspace = state_workspace_at(state, i);
if (workspace->output_id != output->id) continue;
if (count == index) {
command_t switch_workspace_cmd = {
.type = ZDWM_COMMAND_SWITCH_WORKSPACE,
.as.switch_workspace.workspace = workspace->id,
};
command_buffer_push(&runtime->command_buffer, &switch_workspace_cmd);
return;
}
count++;
}
}
void cycle_layout(runtime_t *runtime, int32_t delta) {
auto state = &runtime->state;
auto output = state_output_at(state, state->current_output_index);
auto workspace = state_workspace_get(state, output->current_workspace_id);
if (!state_workspace_cycle_layout(state, workspace->id, delta)) return;
runtime->plan.need_relayout = true;
}
void cycle_current_output(runtime_t *runtime, int32_t delta) {
auto state = &runtime->state;
state_cycle_current_output(state, delta);
}
void focus_window(runtime_t *runtime, int32_t delta) {
auto state = &runtime->state;
auto output = state_output_at(state, state->current_output_index);
auto workspace = state_workspace_get(state, output->current_workspace_id);
size_t count = state_window_count(state);
if (!count) return;
size_t indices[count];
size_t num = 0;
for (size_t i = 0; i < count; ++i) {
auto window = state_window_at(state, i);
if (window->workspace_id == workspace->id) {
indices[num++] = i;
}
}
if (!num) return;
size_t current = 0;
if (!window_id_invalid(workspace->focused_window_id)) {
for (size_t i = 0; i < num; ++i) {
auto window = state_window_at(state, indices[i]);
if (window->id == workspace->focused_window_id) {
current = i;
break;
}
}
}
int64_t next = ((int64_t)current + (int64_t)delta) % (int64_t)num;
if (next < 0) next += (int64_t)num;
auto target = state_window_at(state, indices[next]);
command_t focus_cmd = {
.type = ZDWM_COMMAND_FOCUS_WINDOW,
.as.focus.window = target->id,
};
command_buffer_push(&runtime->command_buffer, &focus_cmd);
command_t raise_cmd = {
.type = ZDWM_COMMAND_RAISE_WINDOW,
.as.raise.window = target->id,
};
command_buffer_push(&runtime->command_buffer, &raise_cmd);
}

View File

@@ -1,32 +0,0 @@
#pragma once
#include <stdint.h>
#include <zdwm/action.h>
void spawn(const char *command);
void quit(zdwm_runtime_t *runtime, bool restart);
void raise_or_run(
zdwm_runtime_t *runtime,
const char *class_name,
const char *command
);
void toggle_fullscreen(zdwm_runtime_t *runtime);
void toggle_maximize(zdwm_runtime_t *runtime);
void toggle_floating(zdwm_runtime_t *runtime);
void toggle_sticky(zdwm_runtime_t *runtime);
void switch_workspace(
zdwm_runtime_t *runtime,
zdwm_workspace_id_t workspace_id
);
void switch_workspace_in_current_output(
zdwm_runtime_t *runtime,
zdwm_workspace_id_t workspace_id
);
void switch_workspace_by_index_in_current_output(
zdwm_runtime_t *runtime,
uint32_t index
);
void cycle_layout(zdwm_runtime_t *runtime, int32_t delta);
void cycle_current_output(zdwm_runtime_t *runtime, int32_t delta);
void focus_window(zdwm_runtime_t *runtime, int32_t delta);

View File

@@ -43,8 +43,7 @@ binding_table_add_mode(binding_table_t *table, const char *mode_name) {
}
zdwm_binding_mode_id_t id = (zdwm_binding_mode_id_t)table->count;
binding_mode_t *new_mode =
array_push(table->modes, table->count, table->capacity);
auto new_mode = array_push(table->modes, table->count, table->capacity);
new_mode->name = mode_name;
new_mode->id = id;
@@ -166,8 +165,7 @@ bool binding_table_add_bind(
binding_table_t *table,
zdwm_binding_mode_id_t mode_id,
const char *key_sequence,
zdwm_action_fn fn,
zdwm_action_arg_t arg
zdwm_action_t action
) {
binding_mode_t *mode = binding_table_get_mode(table, mode_id);
if (!mode) return false;
@@ -176,12 +174,11 @@ bool binding_table_add_bind(
xkb_keysym_t keysym = XKB_KEY_NoSymbol;
if (!parse_key_sequence(key_sequence, &modifiers, &keysym)) return false;
key_binding_t *item = array_push(mode->items, mode->count, mode->capacity);
item->key_str = key_sequence;
item->modifiers = modifiers;
item->keysym = keysym;
item->fn = fn;
item->arg = arg;
auto item = array_push(mode->items, mode->count, mode->capacity);
item->key_str = key_sequence;
item->modifiers = modifiers;
item->keysym = keysym;
item->action = action;
return true;
}
@@ -228,14 +225,17 @@ bool binding_table_set_current_mode(
binding_table_t *table,
zdwm_binding_mode_id_t mode_id
) {
if (mode_id >= table->count) return false;
if (mode_id >= table->count || mode_id == table->current_mode) return false;
table->current_mode = mode_id;
return true;
}
bool binding_table_cycle_mode(binding_table_t *table, int delta) {
if (!table || !table->modes || table->count < 1) return false;
zdwm_binding_mode_id_t
binding_table_cycle_mode(const binding_table_t *table, int32_t delta) {
if (!table || !table->modes || table->count <= 1) {
return ZDWM_BINDING_MODE_ID_INVALID;
}
bool matched = false;
size_t index = (size_t)table->current_mode;
@@ -251,17 +251,13 @@ bool binding_table_cycle_mode(binding_table_t *table, int delta) {
}
}
if (!matched) return false;
/* 只有一个模式没必要切换 */
if (table->count == 1) return true;
if (!matched) return ZDWM_BINDING_MODE_ID_INVALID;
int64_t count = (int64_t)table->count;
int64_t next_index = ((int64_t)index + (int64_t)delta) % count;
if (next_index < 0) next_index += count;
table->current_mode = table->modes[next_index].id;
return true;
return table->modes[next_index].id;
}
const key_binding_t *
@@ -272,3 +268,48 @@ binding_table_get_current_bindings(binding_table_t *table, size_t *count) {
*count = mode->count;
return mode->items;
}
size_t binding_table_mode_count(const binding_table_t *table) {
if (!table) return 0;
return table->count;
}
zdwm_binding_mode_id_t binding_table_get_default_mode(
const binding_table_t *table
) {
if (!table) return ZDWM_BINDING_MODE_ID_INVALID;
return table->default_mode;
}
zdwm_binding_mode_id_t binding_table_get_current_mode(
const binding_table_t *table
) {
if (!table) return ZDWM_BINDING_MODE_ID_INVALID;
return table->current_mode;
}
static const binding_mode_t *binding_table_get_mode_const(
const binding_table_t *table,
zdwm_binding_mode_id_t mode_id
) {
if (!table || mode_id >= table->count) return nullptr;
const binding_mode_t *mode = &table->modes[mode_id];
if (mode->id == mode_id) return mode;
for (size_t i = 0; i < table->count; ++i) {
mode = &table->modes[i];
if (mode->id == mode_id) return mode;
}
return nullptr;
}
const char *binding_table_get_mode_name(
const binding_table_t *table,
zdwm_binding_mode_id_t mode_id
) {
auto mode = binding_table_get_mode_const(table, mode_id);
if (!mode) return nullptr;
return mode->name;
}

View File

@@ -1,6 +1,7 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
#include <zdwm/action.h>
#include <zdwm/types.h>
@@ -12,8 +13,7 @@ typedef struct key_binding_t {
const char *key_str;
modifier_mask_t modifiers;
keysym_t keysym;
zdwm_action_fn *fn;
zdwm_action_arg_t arg;
zdwm_action_t action;
} key_binding_t;
/**
@@ -37,8 +37,7 @@ binding_table_add_mode(binding_table_t *table, const char *mode_name);
* @param table 按键绑定表
* @param mode_id 模式 ID
* @param key_sequence 按键序列的字符串表达
* @param fn 用户行为函数
* @param arg 用户行为函数所需的参数
* @param action 用户行为描述
*
* @return 添加成功返回 true 否则返回 false
*/
@@ -46,8 +45,7 @@ bool binding_table_add_bind(
binding_table_t *table,
zdwm_binding_mode_id_t mode_id,
const char *key_sequence,
zdwm_action_fn fn,
zdwm_action_arg_t arg
zdwm_action_t action
);
/**
@@ -89,12 +87,15 @@ bool binding_table_set_current_mode(
/**
* @brief 切换按键绑定模式
*
* @details 纯计算不修改任何内容
*
* @param table 按键绑定表
* @param delta 切换变化量
*
* @return 无法切换返回 false 否则返回 true
* @return 切换后的模式 ID
*/
bool binding_table_cycle_mode(binding_table_t *table, int delta);
zdwm_binding_mode_id_t
binding_table_cycle_mode(const binding_table_t *table, int32_t delta);
/**
* @brief 获取当前按键绑定列表
@@ -110,3 +111,15 @@ bool binding_table_cycle_mode(binding_table_t *table, int delta);
*/
const key_binding_t *
binding_table_get_current_bindings(binding_table_t *table, size_t *count);
size_t binding_table_mode_count(const binding_table_t *table);
zdwm_binding_mode_id_t binding_table_get_default_mode(
const binding_table_t *table
);
zdwm_binding_mode_id_t binding_table_get_current_mode(
const binding_table_t *table
);
const char *binding_table_get_mode_name(
const binding_table_t *table,
zdwm_binding_mode_id_t mode_id
);

View File

@@ -12,12 +12,17 @@ typedef enum command_type_t {
ZDWM_COMMAND_WITHDRAW_WINDOW,
ZDWM_COMMAND_CONFIGURE_WINDOW,
ZDWM_COMMAND_CHANGE_WINDOW_STATE,
ZDWM_COMMAND_WINDOW_SEND_TO_WORKSPACE,
ZDWM_COMMAND_WINDOW_SET_FLOATING,
ZDWM_COMMAND_WINDOW_SET_STICKY,
ZDWM_COMMAND_WINDOW_SET_MINIMIZED,
ZDWM_COMMAND_WINDOW_SET_MAXIMIZED,
ZDWM_COMMAND_WINDOW_SET_FULLSCREEN,
ZDWM_COMMAND_SWITCH_WORKSPACE,
ZDWM_COMMAND_SET_CURRENT_OUTPUT,
ZDWM_COMMAND_SET_LAYOUT,
ZDWM_COMMAND_SET_BINDING_MODE,
ZDWM_COMMAND_QUIT,
} command_type_t;
typedef struct manage_window_command_t {
@@ -36,11 +41,33 @@ typedef struct window_state_change_command_t {
window_state_request_action_t action;
} window_state_change_command_t;
typedef struct window_send_to_workspace_command_t {
window_id_t window;
workspace_id_t workspace;
} window_send_to_workspace_command_t;
typedef struct window_bool_state_t {
window_id_t window;
bool state;
} window_bool_state_t;
typedef struct set_current_output_command_t {
output_id_t output;
} set_current_output_command_t;
typedef struct set_layout_command_t {
workspace_id_t workspace;
layout_id_t layout;
} set_layout_command_t;
typedef struct set_binding_mode_command_t {
zdwm_binding_mode_id_t mode;
} set_binding_mode_command_t;
typedef struct quit_command_t {
bool will_restart;
} quit_command_t;
/**
* @brief 非 owning 的命令值对象
* @details
@@ -61,11 +88,16 @@ typedef struct command_t {
only_window_data_t withdraw;
configure_data_t configure;
window_state_change_command_t state_change;
window_send_to_workspace_command_t send_to_workspace;
window_bool_state_t floating;
window_bool_state_t sticky;
window_bool_state_t minimized;
window_bool_state_t maximized;
window_bool_state_t fullscreen;
switch_workspace_command_t switch_workspace;
set_current_output_command_t current_output;
set_layout_command_t layout;
set_binding_mode_command_t binding_mode;
quit_command_t quit;
} as;
} command_t;

View File

@@ -1,6 +1,10 @@
#include "core/listeners.h"
#include "base/array.h"
#include "base/memory.h"
#include "core/binding.h"
#include "core/layout.h"
#include "core/state.h"
#define ADD_LISTENER(FIELD) \
auto list = &listeners->FIELD; \
@@ -81,3 +85,209 @@ void listeners_add_window_removed_listener(
}
#undef ADD_LISTENER
#define CLEANUP_LIST(FIELD) \
p_delete(&listeners->FIELD.items); \
listeners->FIELD.count = 0; \
listeners->FIELD.capacity = 0
void listeners_cleanup(listeners_t *listeners) {
if (!listeners) return;
CLEANUP_LIST(output_listeners);
CLEANUP_LIST(initial_workspace_listeners);
CLEANUP_LIST(active_workspace_listeners);
CLEANUP_LIST(layout_listeners);
CLEANUP_LIST(binding_mode_listeners);
CLEANUP_LIST(initial_window_listeners);
CLEANUP_LIST(window_added_listeners);
CLEANUP_LIST(window_updated_listeners);
CLEANUP_LIST(window_removed_listeners);
}
#undef CLEANUP_LIST
#define FOR_EACH_LISTENER(FIELD, BODY) \
do { \
if (!(listeners)) return; \
auto list = &(listeners)->FIELD; \
for (size_t i = 0; i < list->count; ++i) { \
auto item = &list->items[i]; \
BODY; \
} \
} while (false)
void listeners_notify_current_output(
const listeners_t *listeners,
output_id_t output_id
) {
FOR_EACH_LISTENER(output_listeners, item->fn(output_id, item->user_data));
}
void listeners_notify_initial_workspaces(
const listeners_t *listeners,
const state_t *state
) {
if (!listeners || !state) return;
size_t count = state_workspace_count(state);
auto items = count ? p_new(zdwm_workspace_t, count) : nullptr;
for (size_t i = 0; i < count; ++i) {
auto workspace = state_workspace_at(state, i);
items[i] = (zdwm_workspace_t){
.output_id = workspace->output_id,
.id = workspace->id,
.name = workspace->name,
};
}
FOR_EACH_LISTENER(
initial_workspace_listeners,
item->fn(items, count, item->user_data)
);
p_delete(&items);
}
void listeners_notify_workspace_active(
const listeners_t *listeners,
output_id_t output_id,
workspace_id_t workspace_id
) {
FOR_EACH_LISTENER(
active_workspace_listeners,
item->fn(output_id, workspace_id, item->user_data)
);
}
void listeners_notify_layout(
const listeners_t *listeners,
const layout_registry_t *layouts,
workspace_id_t workspace_id,
layout_id_t layout_id
) {
if (!listeners || !layouts) return;
auto slot = layout_slot_get(layouts, layout_id);
if (!slot) return;
zdwm_layout_notify_t notify = {
.workspace = workspace_id,
.id = layout_id,
.name = slot->name,
.symbol = slot->symbol,
.description = slot->description,
};
FOR_EACH_LISTENER(layout_listeners, item->fn(notify, item->user_data));
}
void listeners_notify_binding_mode(
const listeners_t *listeners,
const binding_table_t *binding_table
) {
if (!listeners || !binding_table) return;
auto default_mode = binding_table_get_default_mode(binding_table);
auto current_mode = binding_table_get_current_mode(binding_table);
auto name = binding_table_get_mode_name(binding_table, current_mode);
zdwm_binding_mode_notify_t notify = {
.is_default_mode = default_mode == current_mode,
.id = current_mode,
.name = name,
};
FOR_EACH_LISTENER(binding_mode_listeners, item->fn(notify, item->user_data));
}
static bool listeners_window_snapshot(
const state_t *state,
window_id_t window_id,
zdwm_window_t *out
) {
if (!state || !out || window_id_invalid(window_id)) return false;
auto window = state_window_get(state, window_id);
if (!window) return false;
auto workspace = state_workspace_get(state, window->workspace_id);
if (!workspace) return false;
*out = (zdwm_window_t){
.workspace = window->workspace_id,
.id = window->id,
.title = window->title,
.focused = workspace->focused_window_id == window->id,
.urgent = window->urgent,
.skip_taskbar = window->skip_taskbar,
};
return true;
}
void listeners_notify_initial_windows(
const listeners_t *listeners,
const state_t *state
) {
if (!listeners || !state) return;
size_t count = state_window_count(state);
zdwm_window_t *items = count ? p_new(zdwm_window_t, count) : nullptr;
for (size_t i = 0; i < count; ++i) {
auto window = state_window_at(state, i);
if (!listeners_window_snapshot(state, window->id, &items[i])) {
p_clear(&items[i], 1);
}
}
FOR_EACH_LISTENER(
initial_window_listeners,
item->fn(items, count, item->user_data)
);
p_delete(&items);
}
void listeners_notify_window_added(
const listeners_t *listeners,
const state_t *state,
window_id_t window_id
) {
if (!listeners || !state) return;
zdwm_window_t window = {0};
if (!listeners_window_snapshot(state, window_id, &window)) return;
FOR_EACH_LISTENER(window_added_listeners, item->fn(&window, item->user_data));
}
void listeners_notify_window_updated(
const listeners_t *listeners,
const state_t *state,
window_id_t window_id
) {
if (!listeners || !state) return;
zdwm_window_t window = {0};
if (!listeners_window_snapshot(state, window_id, &window)) return;
FOR_EACH_LISTENER(
window_updated_listeners,
item->fn(&window, item->user_data)
);
}
void listeners_notify_window_removed(
const listeners_t *listeners,
window_id_t window_id
) {
if (!listeners || window_id_invalid(window_id)) return;
FOR_EACH_LISTENER(
window_removed_listeners,
item->fn(window_id, item->user_data)
);
}
#undef FOR_EACH_LISTENER

View File

@@ -3,6 +3,12 @@
#include <stddef.h>
#include <zdwm/listeners.h>
#include "core/types.h"
typedef struct binding_table_t binding_table_t;
typedef struct layout_registry_t layout_registry_t;
typedef struct state_t state_t;
#define ITEM(NAME, FN_TYPE) \
typedef struct NAME##_listener_item_t { \
FN_TYPE *fn; \
@@ -87,3 +93,90 @@ void listeners_add_window_removed_listener(
zdwm_window_removed fn,
void *user_data
);
void listeners_cleanup(listeners_t *listeners);
/**
* @brief 通知当前 output 信息
*
* @param listeners 通知函数集合
* @param output_id 当前 output id
*/
void listeners_notify_current_output(
const listeners_t *listeners,
output_id_t output_id
);
/**
* @brief 通知初始化 workspace 列表
*/
void listeners_notify_initial_workspaces(
const listeners_t *listeners,
const state_t *state
);
/**
* @brief 通知当前激活的 workspace 信息
*/
void listeners_notify_workspace_active(
const listeners_t *listeners,
output_id_t output_id,
workspace_id_t workspace_id
);
/**
* @brief 通知当前布局信息
*
* @param listeners 通知函数集合
* @param layouts 布局注册系统
* @param workspace_id 当前 workspace id
* @param layout_id 当前 workspace 的当前布局 id
*/
void listeners_notify_layout(
const listeners_t *listeners,
const layout_registry_t *layouts,
workspace_id_t workspace_id,
layout_id_t layout_id
);
/**
* @brief 通知当前绑定模式信息
*/
void listeners_notify_binding_mode(
const listeners_t *listeners,
const binding_table_t *binding_table
);
/**
* @brief 通知初始窗口列表
*/
void listeners_notify_initial_windows(
const listeners_t *listeners,
const state_t *state
);
/**
* @brief 通知新增窗口
*/
void listeners_notify_window_added(
const listeners_t *listeners,
const state_t *state,
window_id_t window_id
);
/**
* @brief 通知窗口信息更新
*/
void listeners_notify_window_updated(
const listeners_t *listeners,
const state_t *state,
window_id_t window_id
);
/**
* @brief 通知移除窗口
*/
void listeners_notify_window_removed(
const listeners_t *listeners,
window_id_t window_id
);

View File

@@ -36,8 +36,11 @@ void plan_reset(plan_t *plan) {
void plan_cleanup(plan_t *plan) {
free_memory_hold_by_effects(plan->effects, plan->count);
p_delete(&plan->effects);
plan->count = 0;
plan->capacity = 0;
plan->count = 0;
plan->capacity = 0;
plan->need_relayout = false;
plan->quit = false;
plan->will_restart = false;
}
void plan_push_effect(plan_t *plan, const effect_t *effect) {

View File

@@ -77,6 +77,8 @@ typedef struct plan_t {
size_t count;
size_t capacity;
bool need_relayout;
bool quit;
bool will_restart;
} plan_t;
void plan_reset(plan_t *plan);

View File

@@ -2,9 +2,12 @@
#include <stddef.h>
#include <stdint.h>
#include <zdwm/action.h>
#include <zdwm/types.h>
#include "base/macros.h"
#include "base/memory.h"
#include "base/process.h"
#include "base/window_list.h"
#include "core/binding.h"
#include "core/command.h"
@@ -18,8 +21,408 @@
#include "core/window.h"
#include "core/wm_desc.h"
static void quit(bool restart, command_buffer_t *command_buffer) {
command_t quit_command = {
.type = ZDWM_COMMAND_QUIT,
.as.quit.will_restart = restart
};
command_buffer_push(command_buffer, &quit_command);
}
static const window_t *
get_next_window_by_class(const state_t *state, const char *class_name) {
auto output = state_output_at(state, state->current_output_index);
auto workspace = state_workspace_get(state, output->current_workspace_id);
size_t start_index = 0;
if (!window_id_invalid(workspace->focused_window_id)) {
for (size_t i = 0; i < state_window_count(state); ++i) {
if (state_window_at(state, i)->id == workspace->focused_window_id) {
start_index = i;
break;
}
}
}
for (size_t i = start_index + 1; i < state_window_count(state); ++i) {
auto window = state_window_at(state, i);
if (strcmp(window->class_name, class_name) == 0) {
return window;
}
}
for (size_t i = 0; i < MIN(start_index + 1, state_window_count(state)); ++i) {
auto window = state_window_at(state, i);
if (strcmp(window->class_name, class_name) == 0) {
return window;
}
}
return nullptr;
}
static void raise_or_run(
const state_t *state,
const zdwm_action_data_raise_or_run_t *data,
command_buffer_t *command_buffer
) {
auto window = get_next_window_by_class(state, data->class_name);
if (!window) {
spawn(data->command);
return;
}
command_t switch_workspace_cmd = {
.type = ZDWM_COMMAND_SWITCH_WORKSPACE,
.as.switch_workspace.workspace = window->workspace_id,
};
command_buffer_push(command_buffer, &switch_workspace_cmd);
command_t focus_cmd = {
.type = ZDWM_COMMAND_FOCUS_WINDOW,
.as.focus.window = window->id,
};
command_buffer_push(command_buffer, &focus_cmd);
}
static output_id_t get_cycled_output(const state_t *state, int32_t delta) {
if (state->output_count <= 1) return ZDWM_OUTPUT_ID_INVALID;
int64_t count = (int64_t)state->output_count;
int64_t current = (int64_t)state->current_output_index;
int64_t next = (current + (int64_t)delta) % count;
if (next < 0) next += count;
auto output = state_output_at(state, (size_t)next);
return output->id;
}
static void cycle_current_output(
const state_t *state,
int32_t delta,
command_buffer_t *command_buffer
) {
auto output_id = get_cycled_output(state, delta);
if (output_id == ZDWM_OUTPUT_ID_INVALID) return;
command_t cmd = {
.type = ZDWM_COMMAND_SET_CURRENT_OUTPUT,
.as.current_output.output = output_id,
};
command_buffer_push(command_buffer, &cmd);
}
static void switch_workspace_same_output_by_index(
const state_t *state,
uint32_t index,
command_buffer_t *command_buffer
) {
auto output = state_output_at(state, state->current_output_index);
uint32_t count = 0;
for (size_t i = 0; i < state_workspace_count(state); ++i) {
auto workspace = state_workspace_at(state, i);
if (workspace->output_id != output->id) continue;
if (count == index) {
command_t switch_workspace_cmd = {
.type = ZDWM_COMMAND_SWITCH_WORKSPACE,
.as.switch_workspace.workspace = workspace->id,
};
command_buffer_push(command_buffer, &switch_workspace_cmd);
return;
}
count++;
}
}
static void cycle_layout(
const state_t *state,
int32_t delta,
command_buffer_t *command_buffer
) {
auto output = state_output_at(state, state->current_output_index);
auto workspace = state_workspace_get(state, output->current_workspace_id);
int32_t index = 0;
bool matched = false;
auto layout_count = (int32_t)workspace->layout_count;
if (layout_count <= 1) return;
for (int32_t i = 0; i < layout_count; ++i) {
if (workspace->layout_id == workspace->available_layouts[i]) {
index = i;
matched = true;
break;
}
}
if (!matched) return;
auto new_index = index + delta;
new_index %= layout_count;
if (new_index < 0) new_index += layout_count;
if (new_index == index) return;
auto next_layout_id = workspace->available_layouts[new_index];
command_t set_layout_command = {
.type = ZDWM_COMMAND_SET_LAYOUT,
.as.layout = {.workspace = workspace->id, .layout = next_layout_id},
};
command_buffer_push(command_buffer, &set_layout_command);
}
static void cycle_binding_mode(
const binding_table_t *table,
int32_t delta,
command_buffer_t *command_buffer
) {
auto next_mode_id = binding_table_cycle_mode(table, delta);
if (next_mode_id == ZDWM_BINDING_MODE_ID_INVALID) return;
command_t set_binding_mode_command = {
.type = ZDWM_COMMAND_SET_BINDING_MODE,
.as.binding_mode.mode = next_mode_id,
};
command_buffer_push(command_buffer, &set_binding_mode_command);
}
static const window_t *get_current_focused_window(const state_t *state) {
auto output = state_output_at(state, state->current_output_index);
auto workspace = state_workspace_get(state, output->current_workspace_id);
return state_window_get(state, workspace->focused_window_id);
}
#define DEFUN_WINDOW_TOGGLE_FN(NAME, TYPE, DATA_FIELD, WINDOW_FIELD) \
static void NAME(const state_t *state, command_buffer_t *command_buffer) { \
auto window = get_current_focused_window(state); \
if (!window) return; \
\
command_t command = { \
.type = (TYPE), \
.as.DATA_FIELD = {.window = window->id, .state = !window->WINDOW_FIELD}, \
}; \
command_buffer_push(command_buffer, &command); \
}
DEFUN_WINDOW_TOGGLE_FN(
toggle_window_fullscreen,
ZDWM_COMMAND_WINDOW_SET_FULLSCREEN,
fullscreen,
fullscreen
)
DEFUN_WINDOW_TOGGLE_FN(
toggle_window_maximize,
ZDWM_COMMAND_WINDOW_SET_MAXIMIZED,
maximized,
maximized
)
DEFUN_WINDOW_TOGGLE_FN(
toggle_window_floating,
ZDWM_COMMAND_WINDOW_SET_FLOATING,
floating,
floating
)
DEFUN_WINDOW_TOGGLE_FN(
toggle_window_sticky,
ZDWM_COMMAND_WINDOW_SET_STICKY,
sticky,
sticky
)
#undef DEFUN_WINDOW_TOGGLE_FN
static void cycle_focused_window(
const state_t *state,
int32_t delta,
command_buffer_t *command_buffer
) {
auto output = state_output_at(state, state->current_output_index);
auto workspace = state_workspace_get(state, output->current_workspace_id);
size_t count = state_window_count(state);
if (!count) return;
size_t indices[count];
size_t num = 0;
for (size_t i = 0; i < count; ++i) {
auto window = state_window_at(state, i);
if (window->workspace_id == workspace->id) {
indices[num++] = i;
}
}
if (!num) return;
size_t current = 0;
if (!window_id_invalid(workspace->focused_window_id)) {
for (size_t i = 0; i < num; ++i) {
auto window = state_window_at(state, indices[i]);
if (window->id == workspace->focused_window_id) {
current = i;
break;
}
}
}
int64_t next = ((int64_t)current + (int64_t)delta) % (int64_t)num;
if (next < 0) next += (int64_t)num;
auto target = state_window_at(state, indices[next]);
command_t focus_cmd = {
.type = ZDWM_COMMAND_FOCUS_WINDOW,
.as.focus.window = target->id,
};
command_buffer_push(command_buffer, &focus_cmd);
command_t raise_cmd = {
.type = ZDWM_COMMAND_RAISE_WINDOW,
.as.raise.window = target->id,
};
command_buffer_push(command_buffer, &raise_cmd);
}
static void
route_key_press(const policy_context_t *ctx, const key_press_event_t *e) {
kill_window_action(const state_t *state, command_buffer_t *command_buffer) {
auto window = get_current_focused_window(state);
if (!window) return;
command_t kill_window_command = {
.type = ZDWM_COMMAND_KILL_WINDOW,
.as.kill.window = window->id,
};
command_buffer_push(command_buffer, &kill_window_command);
}
static void cycle_window_output(
const state_t *state,
const zdwm_action_data_window_cycle_output_t *data,
command_buffer_t *command_buffer
) {
auto window = get_current_focused_window(state);
if (!window) return;
auto output_id = get_cycled_output(state, data->delta);
if (output_id == ZDWM_OUTPUT_ID_INVALID) return;
if (data->keep_focus) {
command_t set_current_output_command = {
.type = ZDWM_COMMAND_SET_CURRENT_OUTPUT,
.as.current_output.output = output_id,
};
command_buffer_push(command_buffer, &set_current_output_command);
}
auto output = state_output_get(state, output_id);
auto workspace = state_workspace_get(state, output->current_workspace_id);
command_t send_window_to_workspace_command = {
.type = ZDWM_COMMAND_WINDOW_SEND_TO_WORKSPACE,
.as.send_to_workspace = {
.window = window->id,
.workspace = workspace->id,
},
};
command_buffer_push(command_buffer, &send_window_to_workspace_command);
}
static void send_window_to_workspace_same_output_by_index(
const state_t *state,
const zdwm_action_data_window_send_to_workspace_t *data,
command_buffer_t *command_buffer
) {
auto output = state_output_at(state, state->current_output_index);
auto current_workspace =
state_workspace_get(state, output->current_workspace_id);
auto target_workspace = state_workspace_at(state, (size_t)data->index);
if (current_workspace->id == target_workspace->id) return;
if (data->switch_workspace) {
command_t switch_workspace_command = {
.type = ZDWM_COMMAND_SWITCH_WORKSPACE,
.as.switch_workspace.workspace = target_workspace->id,
};
command_buffer_push(command_buffer, &switch_workspace_command);
}
command_t send_window_to_workspace_command = {
.type = ZDWM_COMMAND_WINDOW_SEND_TO_WORKSPACE,
.as.send_to_workspace = {
.window = current_workspace->focused_window_id,
.workspace = target_workspace->id,
},
};
command_buffer_push(command_buffer, &send_window_to_workspace_command);
}
static void policy_resolve_action(
const policy_context_t *ctx,
const zdwm_action_t *action,
command_buffer_t *out
) {
switch (action->type) {
case ZDWM_ACTION_SPAWN:
spawn(action->as.spawn.command);
break;
case ZDWM_ACTION_QUIT:
quit(action->as.quit.restart, out);
break;
case ZDWM_ACTION_RAISE_OR_RUN:
raise_or_run(ctx->state, &action->as.raise_or_run, out);
break;
case ZDWM_ACTION_OUTPUT_CYCLE:
cycle_current_output(ctx->state, action->as.output_cycle.delta, out);
break;
case ZDWM_ACTION_WORKSPACE_SWITCH_SAME_OUTPUT_BY_INDEX: {
auto index = action->as.workspace_switch_same_output_by_index.index;
switch_workspace_same_output_by_index(ctx->state, index, out);
} break;
case ZDWM_ACTION_LAYOUT_CYCLE:
cycle_layout(ctx->state, action->as.layout_cycle.delta, out);
break;
case ZDWM_ACTION_BINDING_MODE_CYCLE: {
auto delta = action->as.binding_mode_cycle.delta;
cycle_binding_mode(ctx->bind_table, delta, out);
} break;
case ZDWM_ACTION_WINDOW_TOGGLE_FULLSCREEN:
toggle_window_fullscreen(ctx->state, out);
break;
case ZDWM_ACTION_WINDOW_TOGGLE_MAXIMIZE:
toggle_window_maximize(ctx->state, out);
break;
case ZDWM_ACTION_WINDOW_TOGGLE_MINIMIZE:
/* TODO: 恢复需要单独的逻辑,需要考虑是否将最小化和恢复拆成两个 action */
break;
case ZDWM_ACTION_WINDOW_TOGGLE_FLOATING:
toggle_window_floating(ctx->state, out);
break;
case ZDWM_ACTION_WINDOW_TOGGLE_STICKY:
toggle_window_sticky(ctx->state, out);
break;
case ZDWM_ACTION_WINDOW_FOCUS_CYCLE:
cycle_focused_window(ctx->state, action->as.window_focus_cycle.delta, out);
break;
case ZDWM_ACTION_WINDOW_KILL:
kill_window_action(ctx->state, out);
break;
case ZDWM_ACTION_WINDOW_SEND_TO_WORKSPACE_SAME_OUTPUT_BY_INDEX:
send_window_to_workspace_same_output_by_index(
ctx->state,
&action->as.window_send_to_workspace_same_output_by_index,
out
);
break;
case ZDWM_ACTION_WINDOW_CYCLE_OUTPUT:
cycle_window_output(ctx->state, &action->as.window_cycle_output, out);
break;
}
}
static void route_key_press(
const policy_context_t *ctx,
const key_press_event_t *e,
command_buffer_t *out
) {
auto binding_table = ctx->bind_table;
size_t count = 0;
@@ -29,7 +432,7 @@ route_key_press(const policy_context_t *ctx, const key_press_event_t *e) {
for (size_t i = 0; i < count; ++i) {
auto binding = &bindings[i];
if (binding->modifiers == e->modifiers && binding->keysym == e->keysym) {
binding->fn(ctx->runtime, &ctx->action_api, &binding->arg);
policy_resolve_action(ctx, &binding->action, out);
}
}
}
@@ -264,7 +667,7 @@ void policy_route_event(
auto state = ctx->state;
switch (event->type) {
case ZDWM_EVENT_KEY_PRESS:
route_key_press(ctx, &event->as.key_press);
route_key_press(ctx, &event->as.key_press, out);
break;
case ZDWM_EVENT_POINTER_ENTER:
route_pointer_enter(state, event->as.pointer_enter.window, out);
@@ -778,6 +1181,82 @@ static void change_window_state(
}
}
static void send_window_to_workspace(
const policy_context_t *ctx,
const window_send_to_workspace_command_t *command,
plan_t *plan
) {
auto state = ctx->state;
auto window = state_window_get(state, command->window);
if (!window) return;
if (!state_workspace_valid(state, command->workspace)) return;
if (window->workspace_id == command->workspace) return;
workspace_id_t src_workspace_id = window->workspace_id;
bool src_visible = state_workspace_show(state, src_workspace_id);
bool dst_visible = state_workspace_show(state, command->workspace);
const workspace_t *src_ws = state_workspace_get(state, src_workspace_id);
window_id_t src_old_focused =
src_ws ? src_ws->focused_window_id : ZDWM_WINDOW_ID_INVALID;
state_window_set_workspace(state, command->window, command->workspace);
if (!window->sticky) {
if (src_visible) plan_push_unmap_effect(plan, command->window);
if (dst_visible) plan_push_map_effect(plan, command->window);
}
if (src_visible) {
if (src_ws && src_ws->focused_window_id != src_old_focused) {
if (!window_id_invalid(src_old_focused) &&
state_window_get(state, src_old_focused)) {
plan_push_change_border_color_effect(
plan,
src_old_focused,
&ctx->border->normal_color
);
}
if (!window_id_invalid(src_ws->focused_window_id) &&
state_window_get(state, src_ws->focused_window_id)) {
plan_push_focus_effect(plan, src_ws->focused_window_id);
plan_push_change_border_color_effect(
plan,
src_ws->focused_window_id,
&ctx->border->focused_color
);
}
}
adjust_layout_windows_border_width(
state,
ctx->border->width,
src_workspace_id
);
}
if (dst_visible) {
set_foucs_window(ctx, command->workspace, command->window, plan);
adjust_layout_windows_border_width(
state,
ctx->border->width,
command->workspace
);
}
plan->need_relayout = true;
auto listeners = ctx->listeners;
listeners_notify_window_updated(listeners, state, command->window);
if (src_ws && src_ws->focused_window_id != src_old_focused &&
!window_id_invalid(src_ws->focused_window_id)) {
listeners_notify_window_updated(
listeners,
state,
src_ws->focused_window_id
);
}
}
static void command_window_set_floating(
const policy_context_t *ctx,
const window_bool_state_t *data,
@@ -845,6 +1324,60 @@ switch_workspace(state_t *state, workspace_id_t workspace_id, plan_t *plan) {
state_set_current_output(state, output_id);
}
static void set_current_output(
const policy_context_t *ctx,
const set_current_output_command_t *command
) {
if (!state_set_current_output(ctx->state, command->output)) return;
listeners_notify_current_output(ctx->listeners, command->output);
}
static void set_layout(
const policy_context_t *ctx,
const set_layout_command_t *command,
plan_t *plan
) {
auto state = ctx->state;
auto workspace_id = command->workspace;
auto layout_id = command->layout;
if (!state_workspace_set_layout_by_id(state, workspace_id, layout_id)) return;
plan->need_relayout = true;
listeners_notify_layout(
ctx->listeners,
ctx->layouts,
workspace_id,
layout_id
);
}
static void set_binding_mode(
const policy_context_t *ctx,
zdwm_binding_mode_id_t binding_mode,
plan_t *plan
) {
if (!binding_table_set_current_mode(ctx->bind_table, binding_mode)) return;
size_t count = 0;
auto bindings = binding_table_get_current_bindings(ctx->bind_table, &count);
if (bindings && count > 0) {
auto keys = p_new(key_bind_t, count);
for (size_t i = 0; i < count; ++i) {
keys[i].keysym = bindings[i].keysym;
keys[i].modifiers = bindings[i].modifiers;
}
effect_t effect = {
.type = ZDWM_EFFECT_BIND_KEY,
.as.bind_key = {.count = count, .keys = keys},
};
plan_push_effect(plan, &effect);
}
listeners_notify_binding_mode(ctx->listeners, ctx->bind_table);
}
void policy_apply_command(
const policy_context_t *ctx,
const command_buffer_t *command_buffer,
@@ -878,6 +1411,9 @@ void policy_apply_command(
case ZDWM_COMMAND_CHANGE_WINDOW_STATE:
change_window_state(ctx, &cmd->as.state_change, plan);
break;
case ZDWM_COMMAND_WINDOW_SEND_TO_WORKSPACE:
send_window_to_workspace(ctx, &cmd->as.send_to_workspace, plan);
break;
case ZDWM_COMMAND_WINDOW_SET_FLOATING:
command_window_set_floating(ctx, &cmd->as.floating, plan);
break;
@@ -896,6 +1432,19 @@ void policy_apply_command(
case ZDWM_COMMAND_SWITCH_WORKSPACE:
switch_workspace(state, cmd->as.switch_workspace.workspace, plan);
break;
case ZDWM_COMMAND_SET_CURRENT_OUTPUT:
set_current_output(ctx, &cmd->as.current_output);
break;
case ZDWM_COMMAND_SET_LAYOUT:
set_layout(ctx, &cmd->as.layout, plan);
break;
case ZDWM_COMMAND_SET_BINDING_MODE:
set_binding_mode(ctx, cmd->as.binding_mode.mode, plan);
break;
case ZDWM_COMMAND_QUIT:
plan->quit = true;
plan->will_restart = cmd->as.quit.will_restart;
break;
}
}
}

View File

@@ -6,6 +6,7 @@
#include "core/command_buffer.h"
#include "core/event.h"
#include "core/layout.h"
#include "core/listeners.h"
#include "core/plan.h"
#include "core/rules.h"
#include "core/state.h"
@@ -15,10 +16,9 @@ typedef struct policy_context_t {
binding_table_t *bind_table;
state_t *state;
const rules_t *rules;
const listeners_t *listeners;
const border_config_t *border;
const layout_registry_t *layouts;
const zdwm_action_api_t action_api;
zdwm_runtime_t *runtime;
} policy_context_t;
/**

View File

@@ -7,12 +7,12 @@
#include "base/array.h"
#include "base/memory.h"
#include "core/action.h"
#include "core/backend.h"
#include "core/binding.h"
#include "core/command_buffer.h"
#include "core/event.h"
#include "core/layout.h"
#include "core/listeners.h"
#include "core/plan.h"
#include "core/policy.h"
#include "core/rules.h"
@@ -88,6 +88,7 @@ void runtime_init_desc_cleanup(runtime_init_desc_t *desc) {
binding_table_destroy(desc->binding_table);
desc->binding_table = nullptr;
listeners_cleanup(&desc->listeners);
if (desc->backend) {
backend_destroy(desc->backend);
@@ -116,12 +117,48 @@ void runtime_shutdown(runtime_t *runtime) {
layout_result_cleanup(&runtime->layout_result);
binding_table_destroy(runtime->binding_table);
runtime->binding_table = nullptr;
listeners_cleanup(&runtime->listeners);
backend_destroy(runtime->backend);
runtime->backend = nullptr;
if (runtime->config_module_handle) dlclose(runtime->config_module_handle);
runtime->config_module_handle = nullptr;
}
static void runtime_notify_initial_state(runtime_t *runtime) {
auto listeners = &runtime->listeners;
auto state = &runtime->state;
auto output = state_output_at(state, state->current_output_index);
if (output) listeners_notify_current_output(listeners, output->id);
listeners_notify_initial_workspaces(listeners, state);
for (size_t i = 0; i < state_output_count(state); ++i) {
auto item = state_output_at(state, i);
if (!item) continue;
listeners_notify_workspace_active(
listeners,
item->id,
item->current_workspace_id
);
}
for (size_t i = 0; i < state_workspace_count(state); ++i) {
auto workspace = state_workspace_at(state, i);
if (!workspace) continue;
listeners_notify_layout(
listeners,
&runtime->layouts,
workspace->id,
workspace->layout_id
);
}
listeners_notify_binding_mode(listeners, runtime->binding_table);
}
void runtime_setup(runtime_t *runtime) {
size_t bind_count = 0;
auto bindings =
@@ -146,6 +183,7 @@ void runtime_setup(runtime_t *runtime) {
backend_apply_effect(backend, plan->effects, plan->count);
plan_reset(plan);
runtime_notify_initial_state(runtime);
}
static const layout_result_t *runtime_layout_calc(runtime_t *runtime) {
@@ -264,27 +302,9 @@ static policy_context_t policy_context_init(runtime_t *runtime) {
.bind_table = (runtime)->binding_table,
.state = &(runtime)->state,
.rules = &(runtime)->rules,
.listeners = &runtime->listeners,
.border = &(runtime)->border,
.layouts = &(runtime)->layouts,
.runtime = (runtime),
.action_api = {
.spawn = spawn,
.quit = quit,
.raise_or_run = raise_or_run,
.toggle_fullscreen = toggle_fullscreen,
.toggle_maximize = toggle_maximize,
.toggle_floating = toggle_floating,
.toggle_sticky = toggle_sticky,
.switch_workspace = switch_workspace,
.switch_workspace_in_current_output = switch_workspace_in_current_output,
.switch_workspace_by_index_in_current_output =
switch_workspace_by_index_in_current_output,
.cycle_layout = cycle_layout,
.cycle_current_output = cycle_current_output,
.focus_window = focus_window,
},
};
return ctx;
}
@@ -294,6 +314,7 @@ void runtime_scan(runtime_t *runtime) {
if (!result) return;
policy_context_t ctx = policy_context_init(runtime);
ctx.listeners = nullptr;
auto backend = runtime->backend;
auto command_buffer = &runtime->command_buffer;
@@ -316,6 +337,8 @@ void runtime_scan(runtime_t *runtime) {
backend_scan_result_destroy(result);
result = nullptr;
listeners_notify_initial_windows(&runtime->listeners, &runtime->state);
}
void runtime_run(runtime_t *runtime) {
@@ -339,6 +362,11 @@ void runtime_run(runtime_t *runtime) {
if (plan->need_relayout) runtime_arrange(runtime);
if (plan->count) backend_apply_effect(backend, plan->effects, plan->count);
if (plan->quit) {
runtime->running = false;
runtime->will_restart = plan->will_restart;
}
event_cleanup(&event);
}
}

View File

@@ -28,7 +28,7 @@ typedef struct runtime_init_desc_t {
listeners_t listeners;
} runtime_init_desc_t;
typedef struct zdwm_runtime_t {
typedef struct runtime_t {
bool running;
bool will_restart;

View File

@@ -145,39 +145,6 @@ static void state_workspace_adjust_focused_window(
workspace->focused_window_id = ZDWM_WINDOW_ID_INVALID;
}
bool state_workspace_cycle_layout(
state_t *state,
workspace_id_t workspace_id,
int32_t delta
) {
workspace_t *workspace =
(workspace_t *)state_workspace_get(state, workspace_id);
if (!workspace) return false;
int32_t index = 0;
bool matched = false;
auto layout_count = (int32_t)workspace->layout_count;
for (int32_t i = 0; i < layout_count; i++) {
if (workspace->layout_id == workspace->available_layouts[i]) {
index = i;
matched = true;
break;
}
}
if (!matched) return false;
auto new_index = index + delta;
new_index %= layout_count;
if (new_index < 0) new_index += layout_count;
if (new_index == index) return false;
auto next_layout_id = workspace->available_layouts[new_index];
workspace->layout_id = next_layout_id;
return true;
}
bool state_workspace_set_layout_by_index(
state_t *state,
workspace_id_t workspace_id,
@@ -309,14 +276,6 @@ bool state_output_valid(const state_t *state, output_id_t id) {
#endif
}
void state_cycle_current_output(state_t *state, int delta) {
int64_t count = (int64_t)state->output_count;
int64_t current = (int64_t)state->current_output_index;
int64_t next = (current + (int64_t)delta) % count;
if (next < 0) next += count;
state->current_output_index = (size_t)next;
}
bool state_set_current_output(state_t *state, output_id_t output_id) {
for (size_t i = 0; i < state->output_count; ++i) {
auto output = &state->outputs[i];

View File

@@ -81,11 +81,6 @@ void state_cleanup(state_t *state);
*/
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,
int32_t delta
);
bool state_workspace_set_layout_by_index(
state_t *state,
workspace_id_t workspace_id,
@@ -144,7 +139,6 @@ bool state_output_set_current_workspace(
);
size_t state_output_count(const state_t *state);
bool state_output_valid(const state_t *state, output_id_t id);
void state_cycle_current_output(state_t *state, int delta);
/**
* @brief 设置当前 output
*