引入 interface/(零实现声明层)与 common/(共享 ADT 操作层),把跨模块
契约从 core 实现里抽出来,依赖方向回到单向(base ← interface ← common ←
各实现层)。docs/module-layering.org 记录完整方案与判定标准。
interface/(纯类型与多态接口声明,零 .c):
- types.h / event.h / backend.h 从 core 移入
- effect.h 新建(effect_t 从 plan.h 抽出)
common/(多实现层共用的自包含操作,.h + .c):
- event.{h,c}:event_reset / event_cleanup
- listeners.{h,c}:listeners 维护(add / cleanup);notify 留 core
- window.{h,c}:window_list + layer_props/metadata cleanup + window_classify_layer
- workspace.{h,c}:workspace_desc(从 wm_desc.h 拆出,改真实函数)
window 相关整理:
- window_layer_type_t 上浮 interface/types.h(common 的 classify 需要)
- window_classify_layer 移 common/window
- window_info_t 独立成 core/window_info.h(state/command 共享,不寄生)
- 删除 wm_desc.h,base/window_list 并入 common/window
全项目 include 路径同步更新
306 lines
7.9 KiB
C
306 lines
7.9 KiB
C
#include "bar/workspaces.h"
|
|
|
|
#include <assert.h>
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
#include <zdwm/action.h>
|
|
#include <zdwm/bar.h>
|
|
#include <zdwm/listeners.h>
|
|
#include <zdwm/types.h>
|
|
|
|
#include "base/array.h"
|
|
#include "base/memory.h"
|
|
#include "common/listeners.h"
|
|
|
|
typedef struct bar_workspace_t {
|
|
zdwm_workspace_t info;
|
|
zdwm_layout_notify_t layout;
|
|
size_t window_count;
|
|
size_t urgent_window_count;
|
|
} bar_workspace_t;
|
|
|
|
typedef struct bar_workspace_state_t {
|
|
zdwm_output_id_t output_id;
|
|
zdwm_workspace_id_t workspace_id;
|
|
bar_workspace_t *workspaces;
|
|
size_t count;
|
|
size_t capacity;
|
|
|
|
zdwm_window_t *windows;
|
|
size_t window_count;
|
|
size_t window_capacity;
|
|
|
|
bar_workspace_config_t config;
|
|
|
|
bool list_inited;
|
|
bool active_inited;
|
|
bool dirty;
|
|
} bar_workspace_state_t;
|
|
|
|
static size_t state_workspace_get_urgent_window_count(
|
|
const bar_workspace_state_t *state,
|
|
zdwm_workspace_id_t workspace_id
|
|
) {
|
|
size_t count = 0;
|
|
|
|
for (size_t i = 0; i < state->window_count; ++i) {
|
|
auto window = &state->windows[i];
|
|
if (window->workspace == workspace_id && window->urgent) {
|
|
count++;
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
static size_t state_workspace_get_window_count(
|
|
const bar_workspace_state_t *state,
|
|
zdwm_workspace_id_t workspace_id
|
|
) {
|
|
size_t count = 0;
|
|
|
|
for (size_t i = 0; i < state->window_count; ++i) {
|
|
auto window = &state->windows[i];
|
|
if (window->workspace == workspace_id && !window->skip_taskbar) {
|
|
count++;
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
static zdwm_window_t *state_get_window(
|
|
const bar_workspace_state_t *state,
|
|
zdwm_window_id_t window_id
|
|
) {
|
|
for (size_t i = 0; i < state->window_count; ++i) {
|
|
auto window = &state->windows[i];
|
|
if (window->id == window_id) return window;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
static void *
|
|
bar_workspaces_create_state(zdwm_output_id_t output_id, void *config) {
|
|
const bar_workspace_config_t *workspace_config = config;
|
|
assert(workspace_config);
|
|
|
|
auto state = p_new(bar_workspace_state_t, 1);
|
|
state->output_id = output_id;
|
|
state->workspace_id = ZDWM_WORKSPACE_ID_INVALID;
|
|
state->config = *workspace_config;
|
|
|
|
return state;
|
|
}
|
|
|
|
static void bar_workspaces_update(
|
|
zdwm_bar_item_t *item,
|
|
const zdwm_bar_cell_api_t *cell_api,
|
|
void *state
|
|
) {
|
|
auto data = (bar_workspace_state_t *)state;
|
|
if (!data->list_inited || !data->active_inited || !data->dirty) return;
|
|
|
|
/* 当前 workspace 的 layout 指示器放在最后 */
|
|
cell_api->set_cell_count(item, data->count + 1);
|
|
auto layout_index = data->count;
|
|
|
|
auto config = &data->config;
|
|
for (size_t i = 0; i < data->count; ++i) {
|
|
auto workspace = &data->workspaces[i];
|
|
auto info = &workspace->info;
|
|
|
|
workspace->window_count = state_workspace_get_window_count(state, info->id);
|
|
workspace->urgent_window_count =
|
|
state_workspace_get_urgent_window_count(state, info->id);
|
|
|
|
bool show_indicator = workspace->window_count;
|
|
cell_api->cell_set_indicator(item, i, show_indicator);
|
|
|
|
cell_api->cell_set_text(item, i, info->name);
|
|
if (info->id == data->workspace_id) {
|
|
cell_api->cell_set_bg(item, i, config->active_bg);
|
|
cell_api->cell_set_fg(item, i, config->active_fg);
|
|
|
|
cell_api->cell_set_bg(item, layout_index, config->layout_bg);
|
|
cell_api->cell_set_fg(item, layout_index, config->layout_fg);
|
|
cell_api->cell_set_text(item, layout_index, workspace->layout.symbol);
|
|
|
|
continue;
|
|
}
|
|
|
|
if (workspace->urgent_window_count > 0) {
|
|
cell_api->cell_set_bg(item, i, config->urgent_bg);
|
|
cell_api->cell_set_fg(item, i, config->urgent_fg);
|
|
continue;
|
|
}
|
|
|
|
cell_api->cell_set_bg(item, i, config->bg);
|
|
cell_api->cell_set_fg(item, i, config->fg);
|
|
}
|
|
|
|
data->dirty = false;
|
|
}
|
|
|
|
static zdwm_action_t bar_workspace_on_click(zdwm_bar_click_params_t *params) {
|
|
auto cell_index = params->cell_index;
|
|
auto data = (bar_workspace_state_t *)params->state;
|
|
|
|
if (cell_index == data->count) {
|
|
return (zdwm_action_t){
|
|
.type = ZDWM_ACTION_LAYOUT_CYCLE,
|
|
.as.layout_cycle = {.delta = 1},
|
|
};
|
|
}
|
|
|
|
auto workspace = &data->workspaces[cell_index];
|
|
if (workspace->info.id == data->workspace_id) {
|
|
return (zdwm_action_t){.type = ZDWM_ACTION_NONE};
|
|
}
|
|
|
|
return (zdwm_action_t){
|
|
.type = ZDWM_ACTION_WORKSPACE_SWITCH,
|
|
.as.switch_workspace.workspace = workspace->info.id,
|
|
};
|
|
}
|
|
|
|
static void bar_workspace_destroy_state(void *state) {
|
|
if (!state) return;
|
|
|
|
auto data = (bar_workspace_state_t *)state;
|
|
p_delete(&data->workspaces);
|
|
p_delete(&data->windows);
|
|
p_delete(&data);
|
|
}
|
|
|
|
zdwm_bar_item_type_t bar_workspace = {
|
|
.create_state = bar_workspaces_create_state,
|
|
.update = bar_workspaces_update,
|
|
.on_click = bar_workspace_on_click,
|
|
.destroy_state = bar_workspace_destroy_state,
|
|
.update_interval_ms = 0,
|
|
};
|
|
|
|
typedef struct bar_workspace_listener_user_data_t {
|
|
zdwm_bar_item_t *bar;
|
|
bar_workspace_state_t *state;
|
|
} bar_workspace_listener_user_data_t;
|
|
|
|
static void bar_workspace_list_filter(
|
|
const zdwm_workspace_t *list,
|
|
size_t count,
|
|
void *user_data
|
|
) {
|
|
auto state = (bar_workspace_state_t *)user_data;
|
|
|
|
for (size_t i = 0; i < count; ++i) {
|
|
auto workspace = &list[i];
|
|
if (workspace->output_id != state->output_id) continue;
|
|
|
|
auto item = array_push(state->workspaces, state->count, state->capacity);
|
|
p_clear(item, 1);
|
|
item->info = *workspace;
|
|
}
|
|
|
|
state->list_inited = true;
|
|
state->dirty = true;
|
|
}
|
|
|
|
static void bar_workspace_active_updated(
|
|
zdwm_output_id_t output_id,
|
|
zdwm_workspace_id_t workspace_id,
|
|
void *user_data
|
|
) {
|
|
auto state = (bar_workspace_state_t *)user_data;
|
|
if (state->output_id != output_id) return;
|
|
if (state->workspace_id == workspace_id) return;
|
|
|
|
state->workspace_id = workspace_id;
|
|
|
|
state->active_inited = true;
|
|
state->dirty = true;
|
|
}
|
|
|
|
static void
|
|
bar_workspace_layout_notify(zdwm_layout_notify_t layout, void *user_data) {
|
|
auto state = (bar_workspace_state_t *)user_data;
|
|
|
|
for (size_t i = 0; i < state->count; ++i) {
|
|
auto workspace = &state->workspaces[i];
|
|
if (workspace->info.id == layout.workspace) {
|
|
workspace->layout = layout;
|
|
state->dirty = true;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void bar_workspace_window_added(const zdwm_window_t *window, void *user_data) {
|
|
auto state = (bar_workspace_state_t *)user_data;
|
|
|
|
auto win =
|
|
array_push(state->windows, state->window_count, state->window_capacity);
|
|
*win = *window;
|
|
|
|
state->dirty = true;
|
|
}
|
|
|
|
static void bar_workspace_initial_windows(
|
|
const zdwm_window_t *list,
|
|
size_t count,
|
|
void *user_data
|
|
) {
|
|
for (size_t i = 0; i < count; ++i) {
|
|
auto window = &list[i];
|
|
bar_workspace_window_added(window, user_data);
|
|
}
|
|
}
|
|
|
|
void bar_workspace_window_updated(
|
|
const zdwm_window_t *window,
|
|
void *user_data
|
|
) {
|
|
auto state = (bar_workspace_state_t *)user_data;
|
|
|
|
auto old_window = state_get_window(state, window->id);
|
|
*old_window = *window;
|
|
state->dirty = true;
|
|
}
|
|
|
|
void bar_workspace_window_removed(zdwm_window_id_t window_id, void *user_data) {
|
|
auto state = (bar_workspace_state_t *)user_data;
|
|
|
|
bool found = false;
|
|
size_t index = 0;
|
|
|
|
for (size_t i = 0; i < state->window_count; ++i) {
|
|
auto window = &state->windows[i];
|
|
if (window->id == window_id) {
|
|
found = true;
|
|
index = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found) return;
|
|
|
|
if (array_erase(state->windows, state->window_count, index)) {
|
|
state->dirty = true;
|
|
}
|
|
}
|
|
|
|
void bar_workspace_add_listeners(listeners_t *listeners, void *state) {
|
|
#define ADD(ADD_FN, LISTENER) ADD_FN(listeners, LISTENER, state)
|
|
|
|
ADD(listeners_add_initial_workspace_listener, bar_workspace_list_filter);
|
|
ADD(listeners_add_active_workspace_listener, bar_workspace_active_updated);
|
|
ADD(listeners_add_layout_notify, bar_workspace_layout_notify);
|
|
ADD(listeners_add_initial_window_listener, bar_workspace_initial_windows);
|
|
ADD(listeners_add_window_added_listener, bar_workspace_window_added);
|
|
ADD(listeners_add_window_updated_listener, bar_workspace_window_updated);
|
|
ADD(listeners_add_window_removed_listener, bar_workspace_window_removed);
|
|
|
|
#undef ADD
|
|
}
|