Compare commits
55 Commits
3be83aefdf
...
arch_refac
| Author | SHA1 | Date | |
|---|---|---|---|
|
b8ca575458
|
|||
|
6b2f0e3218
|
|||
|
d7d16f1ac8
|
|||
|
88129293a0
|
|||
|
7b7b6d807f
|
|||
|
80b091a806
|
|||
|
89f11d1e24
|
|||
|
ceb40b434d
|
|||
|
272139f34f
|
|||
|
583b1fa382
|
|||
|
612c2367ca
|
|||
|
88e589cc9e
|
|||
|
c609d15d77
|
|||
|
d47ca9cf1a
|
|||
|
e39eb245b6
|
|||
|
3c5cfd3ead
|
|||
|
e7a0945a93
|
|||
|
47bd59b806
|
|||
|
31cd35bf84
|
|||
|
3d258bdc8b
|
|||
|
9f8681d467
|
|||
|
d0120566bf
|
|||
|
6a227b0497
|
|||
|
6b75cda128
|
|||
|
27aab6f0e9
|
|||
|
eaad539a0b
|
|||
|
079f41ce8a
|
|||
|
e1113c0052
|
|||
|
bf3de7fb92
|
|||
|
8f671bd755
|
|||
|
195aeb5166
|
|||
|
af80181897
|
|||
|
42bddf5ac7
|
|||
|
29e0f1e2dc
|
|||
|
74b30a5d4b
|
|||
|
40c2c328cd
|
|||
|
71f050c2f3
|
|||
|
f355b88b82
|
|||
|
f3b1466dac
|
|||
|
b71b66921e
|
|||
|
9a1e015fc1
|
|||
|
4e7ab45864
|
|||
|
b8cd40df9f
|
|||
|
7a47f4d44d
|
|||
|
090266baa5
|
|||
|
9f9adf7e2e
|
|||
|
00df685184
|
|||
|
6d06b61134
|
|||
|
30dfb19a7b
|
|||
|
195733e008
|
|||
|
6f1421f5b5
|
|||
|
9bbdb26f06
|
|||
|
6e3e7ee937
|
|||
|
9d35d17493
|
|||
|
a4bd0b53de
|
@@ -6,12 +6,6 @@ IndentWidth: 2
|
||||
ConstructorInitializerIndentWidth: 2
|
||||
ContinuationIndentWidth: 2
|
||||
|
||||
AlignConsecutiveAssignments:
|
||||
Enabled: true
|
||||
AcrossEmptyLines: false
|
||||
AcrossComments: false
|
||||
AlignCompound: true
|
||||
PadOperators: true
|
||||
AlignConsecutiveBitFields:
|
||||
Enabled: true
|
||||
|
||||
|
||||
@@ -26,28 +26,36 @@ project(${APP_NAME}
|
||||
add_executable(${APP_NAME}
|
||||
${SOURCE_DIR}/backend/output_utils.c
|
||||
${SOURCE_DIR}/backend/x11/backend.c
|
||||
${SOURCE_DIR}/backend/x11/common.c
|
||||
${SOURCE_DIR}/backend/x11/cursor.c
|
||||
${SOURCE_DIR}/backend/x11/event.c
|
||||
${SOURCE_DIR}/backend/x11/tray.c
|
||||
${SOURCE_DIR}/backend/x11/window.c
|
||||
|
||||
${SOURCE_DIR}/bar/bar.c
|
||||
${SOURCE_DIR}/bar/binding.c
|
||||
${SOURCE_DIR}/bar/cell.c
|
||||
${SOURCE_DIR}/bar/text.c
|
||||
${SOURCE_DIR}/bar/tray.c
|
||||
${SOURCE_DIR}/bar/windows.c
|
||||
${SOURCE_DIR}/bar/workspaces.c
|
||||
|
||||
${SOURCE_DIR}/base/color.c
|
||||
${SOURCE_DIR}/base/log.c
|
||||
${SOURCE_DIR}/base/process.c
|
||||
${SOURCE_DIR}/base/time.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}/common/event.c
|
||||
${SOURCE_DIR}/common/listeners.c
|
||||
${SOURCE_DIR}/common/window.c
|
||||
${SOURCE_DIR}/common/workspace.c
|
||||
|
||||
${SOURCE_DIR}/core/binding.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/listeners.c
|
||||
@@ -75,6 +83,7 @@ find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(deps REQUIRED
|
||||
xcb
|
||||
xcb-aux
|
||||
xcb-cursor
|
||||
xcb-icccm
|
||||
xcb-keysyms
|
||||
xcb-randr
|
||||
|
||||
9
Makefile
9
Makefile
@@ -1,16 +1,11 @@
|
||||
MAKEFILE_ABS_PATH := $(abspath $(lastword $(MAKEFILE_LIST)))
|
||||
MAKEFILE_DIR := $(dir $(MAKEFILE_ABS_PATH))
|
||||
PWD_DIR := $(CURDIR)/
|
||||
BUILD_DIR := $(addprefix $(MAKEFILE_DIR),build)
|
||||
SRC_DIR := $(addprefix $(MAKEFILE_DIR),/)
|
||||
|
||||
TARGET_NAME := zdwm
|
||||
TARGET := $(addprefix $(MAKEFILE_DIR),$(TARGET_NAME))
|
||||
|
||||
ifneq ($(PWD_DIR),$(MAKEFILE_DIR))
|
||||
TARGET := $(addprefix $(MAKEFILE_DIR),$(TARGET_NAME))
|
||||
endif
|
||||
|
||||
|
||||
$(TARGET_NAME): build
|
||||
|
||||
@@ -19,10 +14,10 @@ clean:
|
||||
${RM} -r $(BUILD_DIR)
|
||||
|
||||
run:
|
||||
-$(shell Xephyr :3 -screen 1920x1080 -dpi `xrdb -get Xft.dpi` &)
|
||||
Xephyr :3 -screen 1920x1080 -dpi `xrdb -get Xft.dpi`
|
||||
|
||||
run_multiple_screen:
|
||||
-$(shell Xephyr :3 -screen 1920x1080 -screen 1920x1080 +xinerama -dpi `xrdb -get Xft.dpi` &)
|
||||
Xephyr :3 -screen 1920x1080 -screen 1920x1080 +xinerama -dpi `xrdb -get Xft.dpi`
|
||||
|
||||
wm: $(TARGET_NAME)
|
||||
DISPLAY=:3 $(TARGET)
|
||||
|
||||
136
docs/module-layering.org
Normal file
136
docs/module-layering.org
Normal file
@@ -0,0 +1,136 @@
|
||||
#+title: ZDWM 模块分层与依赖方向
|
||||
#+OPTIONS: toc:2
|
||||
|
||||
* 动机
|
||||
|
||||
随着模块增加,跨模块的"契约"和"共享数据结构"开始散落:有的进了 =core/=(和窗口管理逻辑混在一起),有的不得不靠依赖倒置塞进消费方目录(如 tray 契约放 =bar/= 导致 backend 反向依赖 bar)。结果是 core 职责不清、=bar ↔ backend= 出现交叉依赖、依赖方向不再单向。
|
||||
|
||||
本文档定义一套分层约定,把"跨模块共享的东西"从具体实现里抽出来,集中到两个目录,使依赖方向回到干净的单向。
|
||||
|
||||
* 核心标准:按"谁直接消费类型"归类
|
||||
|
||||
判断一个跨模块的东西进哪个目录,不看它"属于哪个模块",也不只看"是声明还是实现",而是看**它的类型被哪些层直接消费**:
|
||||
|
||||
- 类型被**两个或以上对等的实现层**直接消费 → 类型放 =interface/=(跨模块声明)
|
||||
- 只有**操作函数**被多个实现层共用、且这些层不该互相依赖 → 操作放 =common/=
|
||||
- 类型 + 操作都只被一个实现层(及其依赖者)用 → 整个 ADT 放 =common/=,或就近放该实现层
|
||||
|
||||
关键推论:**只需消费"类型"的层,不该被迫背上"操作函数"的依赖**。这是把类型和操作分到两个目录的根本理由。
|
||||
|
||||
* 目录的定位
|
||||
|
||||
先明确三层关系:=base/= 是**项目无关**的通用底层(任何 C 项目都能直接用),=interface/= 和 =common/= 是**项目业务相关**的通用内容。后两者依赖前者(业务依赖基础设施)。
|
||||
|
||||
** =src/base/= :项目无关的通用底层,零业务依赖
|
||||
|
||||
放任何 C 项目都能直接用的纯工具(=memory= / =array= / =macros= / =color= / =log= / =time= / =process=)。不依赖 interface/common/任何实现层——它根本不知道 ZDWM 的业务。=interface/= 和 =common/= 都可以依赖它。
|
||||
|
||||
** =src/interface/= :跨模块共享的【声明】,零 .c
|
||||
|
||||
只放类型定义、多态接口的函数声明。绝对不放假装、零实现代码。被所有实现层依赖,自身不依赖任何实现层(可依赖 =base/= 的纯类型)。
|
||||
|
||||
** =src/common/= :被多实现层共用的【操作函数】,.h + .c
|
||||
|
||||
放自包含的 ADT 操作(构造/清理/查询)。操作只依赖 =interface/= 里的类型(和 =base/= 工具),不依赖任何实现层的内部状态。被"需要这些操作的层"依赖。
|
||||
|
||||
注意:=common/= 不是"什么都往里塞的杂物间"。只有当操作被**多个不该互相依赖的实现层**共用时,才进 =common/=;只被单一实现层用的操作,就近放该层。
|
||||
|
||||
* 依赖方向
|
||||
|
||||
#+begin_example
|
||||
base/ ← 项目无关的通用底层(任何 C 项目可用),零业务依赖
|
||||
↑
|
||||
interface/ ← 零业务依赖(纯类型声明,含 backend 抽象接口);可依赖 base
|
||||
↑
|
||||
common/ ← 项目业务通用的共享 ADT 操作;依赖 interface + base
|
||||
↑
|
||||
core/ runtime/ backend/ bar/ ← 实现层,都依赖 common + interface(+ base)
|
||||
#+end_example
|
||||
|
||||
注意:没有实现层"只依赖 interface"——所有实现层都用 =common/= 的业务通用操作。interface 的纯净性约束的是 interface *内部*(含 backend 抽象接口不依赖 interface 之外的任何东西),不是约束某个实现层的依赖深度。
|
||||
|
||||
不变量:
|
||||
- =base/= 不依赖任何业务层(interface/common/实现层),是项目无关的纯工具。
|
||||
- =interface/=(含 backend 抽象接口)不依赖 =interface/= 之外的任何业务内容;可依赖 =base/= 的纯类型(业务层依赖基础设施,合理)。
|
||||
- =common/= 只依赖 =interface/= 和 =base/=,不依赖任何实现层。
|
||||
- 所有实现层(core/runtime/backend/bar)都依赖 =common/= + =interface/=(+ =base/=);它们之间不直接相互依赖跨模块契约,都经由 =interface/= 或 =common/=。
|
||||
- 因此 =bar ↔ backend= 这类对等层之间不再有交叉依赖:都通过 =interface/tray.h= 通信。
|
||||
|
||||
* 判定示例:effect / plan / event / listeners
|
||||
|
||||
用上面的标准,逐个判定现有跨模块类型:
|
||||
|
||||
** =effect_t= → interface(类型),无 .c
|
||||
|
||||
消费方:core(policy/plan 产生 =effect_t=)+ backend(执行 =effect_t=)。两个对等层直接消费类型,所以类型进 =interface/effect.h=。=effect_t= 是纯数据,构造它的活(=plan_push_*=)属于 plan 的操作,所以 effect 在 interface 里只有类型、没有 .c。
|
||||
|
||||
#+begin_src c
|
||||
/* backend 只消费展开后的 effect 数组,从不碰 plan_t */
|
||||
bool backend_apply_effect(backend_t *backend, const effect_t *effects, size_t effect_count);
|
||||
#+end_src
|
||||
|
||||
** =plan_t= → 留 core(完整 ADT:类型 + 操作)
|
||||
|
||||
消费方:只 core(policy 产生、收集 effect)和 runtime(提交后 reset/cleanup)。两者之间是 *runtime→core 的天然单向依赖*(runtime 是桥接层,本就依赖 core 的 policy/state 等)。把 =plan_t= 放 =core/plan.h= + =core/plan.c=,runtime 正向 include 即可,不破坏任何方向——所以它*不必*进 common。
|
||||
|
||||
这把 common 的门槛精确化:common 是为"对等、无天然依赖"的消费者中立化(见下文 =listeners_t=);若消费者之间已有单向依赖(如 plan 的 core+runtime),共享操作放被依赖方(core)即可,不必上浮 common。
|
||||
|
||||
** =event_t= → interface(类型)+ common(操作)
|
||||
|
||||
=event_t= 类型进 =interface/event.h= 的关键理由:interface 内的 =backend.h= 契约直接 *#include 了 event.h*——=backend_poll_event= / =backend_next_event= 的参数是 =event_t *=,且契约注释讨论 event 的所有权与 =event_cleanup= 语义。一份完整的后端契约要让读者看到 event_t 全貌(字段、联合、生命周期责任),而非甩一个前向声明。所以 event_t 必须在 interface:若放 common,=backend.h= 要么退化为前向声明(契约不完整),要么 interface 反向依赖 common(破坏零依赖)。
|
||||
|
||||
=event_reset= / =event_cleanup= 这些操作被 backend 和 runtime 共用,两者对等、不该互相依赖 → 操作落 =common/event.h= + =common/event.c=。于是 event 是"类型在 interface、操作在 common"的典型。
|
||||
|
||||
** =listeners_t= → common(类型 + 维护)+ core(notify)
|
||||
|
||||
=listeners_t= 类型被 bar(订阅)、core(发布)、runtime(生命周期)消费,但 **backend 完全不碰它**。判定关键:它的消费者(bar/config/runtime)都*既用类型又用维护操作*(=add_*= / =cleanup=),没有哪个层"只需类型、不需操作"——按核心推论(只有"只需消费类型"的层才迫使类型单独上提 interface),类型不必进 interface。与 =event_t= 的对照就在这里:=event_t= 因被 interface 内的 =backend.h= 契约需要完整定义而必须留 interface;=listeners_t= 则*不被 interface 内任何契约依赖*(=backend.h= 完全不碰它),所以能整个进 common:
|
||||
|
||||
- 类型 =listeners_t= + 维护操作 =add_*= / =cleanup=:自包含容器操作,被 bar/config/runtime 多个对等层共用 → 整个 ADT 进 =common/listeners.{h,c}=。
|
||||
- =notify_*=(触发):单消费者(只 core/runtime 发布侧调用),且依赖 core 内部(把 =state_t= 翻译成 =zdwm_window_t= 等 DTO)→ 声明 + 实现都留 =core/listeners=。
|
||||
|
||||
真正的跨层契约(回调签名 =zdwm_window_added= 等 + DTO)已在 =include/zdwm/listeners.h=(比 interface 更公开的那层),=listeners_t= 只是这些回调的容器,所以它整个待在 common 自洽,不必单独占一个 interface 头。
|
||||
|
||||
* 汇总表
|
||||
|
||||
| 东西 | 类型在哪 | 操作在哪 | 判定理由 |
|
||||
|------------------------------+-----------------------+-------------------------------------------+--------------------------------------------------------------------------|
|
||||
| =effect_t= | =interface/effect.h= | (无,构造归 plan 操作) | 类型跨模块(core 产 + backend 执行),纯数据 |
|
||||
| =plan_t= | =core/plan.h= | =core/plan.c= | 消费者 runtime→core 单向,放 core 不破坏方向,不必 common |
|
||||
| =event_t= | =interface/event.h= | =common/event.h= + =common/event.c= | 类型跨模块;操作对等共用 |
|
||||
| 后端接口(=backend_t= 等) | =interface/backend.h= | =backend/x11/*.c= | 多态接口:声明在 interface,实现在具体后端 |
|
||||
| tray 契约(=tray_t= 等) | =interface/tray.h= | =backend/x11/tray.c= | 多态接口:bar 用、backend 实现,声明在 interface 消除 =bar↔backend= 交叉 |
|
||||
| 通知协议(listeners) | =common/listeners.h= | =common/listeners.c= + =core/listeners.c= | 消费者 bar↔core 对等,必须 common 中立化;notify 依赖 core 留 core |
|
||||
| 基础类型(=window_id_t= 等) | =interface/types.h= | (按需) | 所有层共用 |
|
||||
|
||||
* 多态接口 vs 共享 ADT:.c 归属的不同
|
||||
|
||||
=interface/= 里有两类声明,它们的 .c 归属不同,务必区分:
|
||||
|
||||
- **多态接口**(=backend.h=、=tray.h=):声明一组"由谁来实现"的函数,实现依赖具体实现层(xcb / 未来 wayland)。这类 .c **必须在实现层**(=backend/x11/=、=core/=),不能在 interface——否则 interface 就"知道"了具体实现。
|
||||
- **共享 ADT 的操作**(event 的操作):实现自包含、不依赖任何实现层。这类 .c 进 =common/=。
|
||||
|
||||
换句话说:=interface/= 永远零 .c。多态接口的 .c 在实现层,共享操作的 .c 在 =common/=。
|
||||
|
||||
* 和 include/zdwm/ 的区别
|
||||
|
||||
- =include/zdwm/= 是**对外公共 API**(给用户配置、插件使用)。
|
||||
- =src/interface/= 是**内部跨模块契约**(给各子系统互相通信)。
|
||||
|
||||
两者职责不同,不合并。像 =backend.h=、=event.h=、=effect.h= 是内部的,不该进 =include/zdwm/= 暴露给外部。
|
||||
|
||||
* 迁移指引
|
||||
|
||||
把现有代码按本分层整理时,建议顺序:
|
||||
|
||||
1. 建 =src/interface/= 和 =src/common/= 目录,加入 CMakeLists。
|
||||
2. 先迁移"纯类型、零依赖"的声明进 =interface/=:=types.h=、=effect.h=(从 =core/plan.h= 抽出 =effect_t=)、=event.h= 的类型部分。
|
||||
3. 多态接口声明迁移:=backend.h= 进 =interface/=(=.c= 留原处)。=listeners= 走共享 ADT、不进 interface:=listeners_t= 类型 + =add_*= / =cleanup= 整体进 =common/listeners.{h,c}=,=notify_*= 留 =core/listeners=(详见上方判定示例)。
|
||||
4. 共享 ADT 迁移:=event.h= 操作部分 + =event.c= 进 =common/=。(=plan= 留 core:消费者 runtime→core 单向,不必上浮。)
|
||||
5. 更新全项目 include 路径(="core/types.h"= → ="interface/types.h"= 等)。
|
||||
6. 每步后编译验证,确保依赖方向单向、无循环。
|
||||
|
||||
注意边界:遇到"看起来该进 interface、但其类型依赖还在 core"的情况(如某个 event 子结构引用了 =core/window.h= 的类型),要么把那个被引用的类型也提到 =interface/=,要么承认它暂不进 interface——**进 interface 的前提是它的所有依赖都在 interface**,不能硬塞。
|
||||
|
||||
* tray 的落点
|
||||
|
||||
tray 的契约(=tray_t= / =tray_api_t=)是 bar 与 backend 之间的多态接口,按本分层落 =interface/tray.h=(声明),实现在 =backend/x11/tray.c=。这样 bar 和 backend 都只依赖 =interface/tray.h=,彻底消除 =bar ↔ backend= 交叉依赖——这正是引入这套分层的最初动机之一。详见 [[file:tray-design.org][tray-design.org]]。
|
||||
256
docs/tray-design.org
Normal file
256
docs/tray-design.org
Normal file
@@ -0,0 +1,256 @@
|
||||
#+title: ZDWM Tray 系统托盘设计
|
||||
#+OPTIONS: toc:2
|
||||
|
||||
* 背景与动机
|
||||
|
||||
ZDWM 当前没有系统托盘(system tray / systray)功能。本文档描述如何在 bar 中实现 X11 系统托盘,让外部应用(输入法、音量、蓝牙、网络等)的托盘图标显示在状态栏右侧并正常交互,同时保持现有前后端分离架构,为未来 Wayland 后端预留扩展空间。
|
||||
|
||||
* 设计目标与约束
|
||||
|
||||
** 前后端分离
|
||||
|
||||
tray 协议(=_NET_SYSTEM_TRAY= + XEMBED)必须*闭环在后端内部*。core 的 =event_t= / =policy= / =plan= 完全不感知 tray——tray 不产生 =event_t=、不进 policy 路由、不进 plan/effect。
|
||||
|
||||
这与现有架构的三条解耦通道一致:
|
||||
- =backend -> core=:后端把平台原始事件归一化为 =event_t=
|
||||
- =core -> backend=:core 产出 =effect_t=,后端执行
|
||||
- =runtime= 作为唯一桥接点:bar 模块本身拿不到 backend,所有平台访问经 runtime 中转
|
||||
|
||||
** Wayland 预留
|
||||
|
||||
bar 与 runtime 层必须后端无关、零 xcb。未来加 Wayland 后端时,只需新增一个后端实现——Wayland 用 StatusNotifierItem(SNI)over D-Bus,icon 是像素数据而非真实窗口,由前端 cairo 绘制。只要后端接口形状不变,上层(bar/runtime)零改动。
|
||||
|
||||
** tray 是 bar 的附属组件
|
||||
|
||||
没有 bar 就没有 tray。tray 的生命周期随 bar 窗口,*不*作为独立公共接口(不进 =include/zdwm/=)。tray 能力内嵌在 bar 窗口的创建/销毁里。
|
||||
|
||||
* 方案选型:嵌入式容器子窗口(A')
|
||||
|
||||
X11 系统托盘协议只要求:tray 创建一个 window 成为 =_NET_SYSTEM_TRAY_S<n>= 的 selection owner,客户端向 owner 发 =_NET_SYSTEM_TRAY_REQUEST_DOCK= 请求,tray 用 XEMBED reparent icon。协议本身*不*规定 icon reparent 到 bar 窗口还是独立窗口。
|
||||
|
||||
** 为何不用独立 top-level 窗口
|
||||
|
||||
完全独立的 top-level tray 窗口(如 stalonetray)是为*没有状态栏*的环境设计的。ZDWM 已有 bar,用独立窗口会引入"两个窗口的几何/对齐同步"问题(tray 窗口要贴 bar 右端、随 icon 数动态 resize、跟随 bar 移动),且无功能收益。
|
||||
|
||||
** 为何用专门的容器子窗口
|
||||
|
||||
虽然直接 reparent 到 bar 的 cairo 绘制窗口技术上可行(bar 已是 ARGB,子窗口叠加在 cairo 内容之上),但用一个*专门的 tray 容器子窗口*做 selection owner 更正统、更干净:
|
||||
|
||||
1. *职责分离*:bar 窗口专注 cairo 绘制,tray 容器专注 XEMBED / icon 管理。dock 请求和 =_XEMBED= 消息定向到容器,不进 bar 的 =handle_client_message=。
|
||||
2. *事件隔离*:tray 协议事件在容器窗口闭环,bar 的窗口管理事件流零污染。
|
||||
3. *视觉不变*:容器是 bar 窗口的子窗口,位于 tray region 内,icon 跟随 bar 移动/resize(X 子窗口语义),不占额外屏幕空间。
|
||||
|
||||
* 后端接口设计
|
||||
|
||||
tray 契约独立成头 =interface/tray.h= —— 落 interface 层:不放 core(tray 与 core 无关),也不放 bar(backend 也要实现它)。bar 和 backend 作为两个对等实现层,都依赖 =interface/tray.h= 来解耦——这正是分层消除 bar↔backend 交叉依赖的落点(详见 [[file:module-layering.org][module-layering.org]] 的 tray 落点)。它只依赖 =interface/types.h=(=window_id_t=),不含 =backend_t=,所以 bar include 它而不碰 =interface/backend.h= 的内部。=interface/backend.h= 反过来 include =interface/tray.h=,让 =backend_bar_window_t= 内嵌 =tray_t=。
|
||||
|
||||
#+begin_src c
|
||||
typedef void (*tray_icons_change_cb_t)(void *user_data);
|
||||
|
||||
typedef struct tray_api_t {
|
||||
/* 返回 tray 当前挂载的 bar window_id;invalid 表示无 tray 挂载 */
|
||||
window_id_t (*host_window)(void *handle);
|
||||
/* 当前 icon 数量 */
|
||||
size_t (*icon_count)(void *handle);
|
||||
/* icon 边长(= bar height),bar 据此算 cell 宽度 */
|
||||
int32_t (*icon_size)(void *handle);
|
||||
/* 把容器 + icon 摆到 region 起点 x */
|
||||
void (*place)(void *handle, int32_t x);
|
||||
/* 注册 icon 数量变化回调 */
|
||||
void (*set_listener)(void *handle,
|
||||
tray_icons_change_cb_t cb,
|
||||
void *user_data);
|
||||
} tray_api_t;
|
||||
|
||||
typedef struct tray_t {
|
||||
tray_api_t api; /* 值类型,后端总填充,一直可用 */
|
||||
void *handle; /* 指向 backend(bar 不知其类型) */
|
||||
} tray_t;
|
||||
|
||||
typedef struct backend_bar_window_t {
|
||||
window_id_t window_id;
|
||||
cairo_t *cr;
|
||||
tray_t tray;
|
||||
} backend_bar_window_t;
|
||||
|
||||
/* 扩展:加 enable_tray;icon_size 直接用 geometry.height */
|
||||
backend_bar_window_t backend_create_bar_window(
|
||||
backend_t *backend, rect_t geometry, uint32_t bg_pixel, bool enable_tray);
|
||||
#+end_src
|
||||
|
||||
** 关键设计点
|
||||
|
||||
- *api 是值类型*:后端用一个全局 =static const= vtable 填充,保证一直可用,bar 不需判 NULL。这与项目现有 =zdwm_bar_item_type_t=(纯 vtable)+ =zdwm_bar_item_t=(实例持有 api + state)的模式一致。
|
||||
- *handle 总有效*:实际指向 =backend_t*=(tray api 函数内用 =backend->conn=、=backend->tray= host),对 bar 透明(=void*=)。有无 tray 统一由 =host_window()= 返回值判断(invalid = 无),不靠 api 是否 NULL,避免"先判 api、再判 host_window"两个判断点。
|
||||
- *icon_size 来自 handle*(单一数据源):后端 configure icon 用的尺寸 = bar 查布局用的尺寸,杜绝两边各算导致 icon 错位/溢出。这比在 =tray_t= 冗余存一份快照更纯粹。
|
||||
- *生命周期随 bar/backend*:=backend_create_bar_window(enable_tray=true)= 时创建/迁移 host;=backend_destroy= 释放。无 =tray_host_create/destroy= 独立入口,体现"tray 是 bar 附属"。
|
||||
|
||||
** 挂载语义
|
||||
|
||||
X11 的 =_NET_SYSTEM_TRAY_S0= selection owner *整个 display 只能有一个*,但 =backend_create_bar_window= 是 per-output 调用的。处理方式:
|
||||
|
||||
- tray host 是后端单例(内联在 =backend_t=)。
|
||||
- 每次传 =enable_tray=true= 的 =create_bar_window= 把容器 reparent 到本次 bar 窗口,最终挂在*最后一个* true 的 output 上。
|
||||
- =host_window()= 返回当前挂载的 bar window_id,bar 据此决定哪个 output 渲染 tray。
|
||||
|
||||
** 为何 set_listener 不配 remove_listener
|
||||
|
||||
tray icon 变化的唯一消费者是 bar 的 tray item,且其生命周期 = host 生命周期(=backend_destroy= 时自然消失)。与 core 的 =listeners= 模块一致(listener 跟宿主容器走,靠容器销毁回收),不引入 =remove_listener=。
|
||||
|
||||
* bar item 框架扩展
|
||||
|
||||
tray 作为 right-side 的普通 item,复用 =zdwm_bar_item_type_t= 框架。为支持 tray 的两个本质差异(cell 无文本 + 需要在 layout 后把结果同步到后端真实窗口),对框架做最小*通用*扩展——不为 tray 特例化。
|
||||
|
||||
** cell_api 全包 cell 相关(不引入 item_api)
|
||||
|
||||
=set_cell_count= / =get_cell_count= 本质是 *cell 集合的管理*(类比 =std::vector= 的 =size()/resize()= 与 =operator[]= 同属 vector),留在 =zdwm_bar_cell_api_t=。
|
||||
|
||||
新增两个 cell 级接口:
|
||||
|
||||
#+begin_src c
|
||||
/* 固定宽度:>0 = 固定宽度,0 = 走文本测量(零值默认),<0 = 隐藏 */
|
||||
void cell_set_fixed_width(zdwm_bar_item_t *, size_t index, int32_t width);
|
||||
/* 单个 cell 的 region(layout 后的坐标) */
|
||||
bar_x_region_t cell_get_region(zdwm_bar_item_t *, size_t index);
|
||||
#+end_src
|
||||
|
||||
=bar_cell_t= 加 =int32_t fixed_width=,默认 =0=(走文本测量)。把零值留给最常见的默认行为(测量),=p_clear= 清零后新 cell 天然正确,=set_cell_count= 无需补初始化;=-1= 当"隐藏"哨兵(负数宽度本就无意义),与 =0=(测量)明确区分,没有歧义。
|
||||
|
||||
=bar_output_layout= 改一处:
|
||||
|
||||
#+begin_src c
|
||||
int32_t width;
|
||||
if (cell->fixed_width > 0) width = cell->fixed_width; /* 固定 */
|
||||
else if (cell->fixed_width == 0) width = 文本测量; /* 测量(默认)*/
|
||||
else width = 0; /* 隐藏 */
|
||||
#+end_src
|
||||
|
||||
** after_layout / after_draw 钩子
|
||||
|
||||
普通 item 是 cairo 绘制;tray 是"layout 后把 region 应用到后端真实窗口"。给 =zdwm_bar_item_type_t= 加两个*可空*通用钩子,参数打包(仿现有 =zdwm_bar_click_params_t= 先例):
|
||||
|
||||
#+begin_src c
|
||||
typedef struct zdwm_bar_item_layout_params_t {
|
||||
zdwm_bar_item_t *item;
|
||||
const zdwm_bar_cell_api_t *cell_api;
|
||||
bar_x_region_t region;
|
||||
void *state;
|
||||
} zdwm_bar_item_layout_params_t;
|
||||
|
||||
void (*after_layout)(const zdwm_bar_item_layout_params_t *); /* 可空 */
|
||||
void (*after_draw)(const zdwm_bar_item_layout_params_t *); /* 可空 */
|
||||
#+end_src
|
||||
|
||||
设计要点:
|
||||
- 钩子为 NULL 则跳过,普通 item 零影响。
|
||||
- =after_layout= / =after_draw= 比 =update= 多一个 =region=(它们在 layout 之后调用),且通过 =cell_api= 可调 =cell_get_region= 取 per-cell 位置。
|
||||
- =update= 不打包参数(3 个参数不多,保持现有 =workspaces= / =binding= / =windows= 三个 item 零改动)。
|
||||
|
||||
=bar_draw= 流程:
|
||||
|
||||
#+begin_src c
|
||||
bar_output_layout(bo, ctx);
|
||||
for each item: if (after_layout) after_layout(params); /* params 含 region */
|
||||
bar_output_draw(bo, ctx, bg);
|
||||
for each item: if (after_draw) after_draw(params);
|
||||
#+end_src
|
||||
|
||||
* bar tray item
|
||||
|
||||
新增 =src/bar/tray.c=,作为普通 right-side item(参考 [[file:../src/bar/workspaces.c][workspaces.c]] 的实现模式):
|
||||
|
||||
#+begin_src c
|
||||
zdwm_bar_item_type_t bar_tray = {
|
||||
.create_state = bar_tray_create_state,
|
||||
.update = bar_tray_update,
|
||||
.on_click = bar_tray_on_click, /* 返回 NONE:icon 真实子窗口自收点击 */
|
||||
.after_layout = bar_tray_after_layout,
|
||||
.destroy_state = bar_tray_destroy_state,
|
||||
};
|
||||
#+end_src
|
||||
|
||||
- =create_state=:拷贝 =tray_t=,调 =set_listener= 注册回调(回调置 =state->dirty = true=)。
|
||||
- =update=:=dirty= 时 =set_cell_count(icon_count)= + 循环 =cell_set_fixed_width(i, icon_size)=。
|
||||
- =after_layout=:=place(handle, params->region.start)=。
|
||||
|
||||
tray cell 不画 cairo 内容(text 为空,=bar_item_draw= 只画融入 bar 的背景色块,被真实 icon 窗口盖住)。
|
||||
|
||||
* runtime 桥接
|
||||
|
||||
=runtime_init_bar=(=src/runtime/runtime.c=)的 create 循环:
|
||||
|
||||
#+begin_src c
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
bool enable_tray = ((int32_t)i == runtime->bar.config.tray_output_index);
|
||||
auto bar_window = backend_create_bar_window(backend, bar_rect, color, enable_tray);
|
||||
...
|
||||
runtime->bar.tray = bar_window.tray; /* 每个返回都一样,存一份 */
|
||||
}
|
||||
bar_init(&runtime->bar, &runtime->listeners);
|
||||
#+end_src
|
||||
|
||||
=bar_init= 遍历 =bar_output=,对 =host_window()= 匹配自身 =window_id= 的那个 output 调 =bar_output_add_tray=(right side),其余不加。tray 生命周期随 =backend_destroy=,runtime 无特殊清理。
|
||||
|
||||
* 配置
|
||||
|
||||
=zdwm_bar_config_t=(=include/zdwm/config.h=)加:
|
||||
|
||||
#+begin_src c
|
||||
int32_t tray_output_index; /* 默认 0 = 第一个 output */
|
||||
#+end_src
|
||||
|
||||
配置 setup 中可通过 output 信息确定具体 index。
|
||||
|
||||
* 数据流(触发链)
|
||||
|
||||
#+begin_example
|
||||
icon 增删
|
||||
-> 后端 set_listener 回调 -> state->dirty = true
|
||||
-> 下个 timerfd tick -> bar_update -> bar_tray_update
|
||||
-> set_cell_count + cell_set_fixed_width -> item 标 dirty
|
||||
-> bar_draw -> bar_output_layout 重算 region(cell 数变了)
|
||||
-> tray 的 after_layout -> backend place(handle, region.start)
|
||||
-> 后端 move 容器 + configure 每个 icon 到 (region.start + i*icon_size, 0)
|
||||
#+end_example
|
||||
|
||||
bar 重绘是 timerfd(fps)驱动,非事件驱动。tray icon 是真实窗口,X server 自管绘制,不依赖 cairo 重绘。
|
||||
|
||||
* 实现范围(第一版)
|
||||
|
||||
第一版做*基础可用*:
|
||||
|
||||
- =_NET_SYSTEM_TRAY_S0= selection owner + MANAGER 广播
|
||||
- 基础 XEMBED(reparent 容器、=_XEMBED_EMBEDDED_NOTIFY=)
|
||||
- icon 显示与点击
|
||||
- icon 尺寸 = bar 高度(统一,不缩放)
|
||||
|
||||
放后续迭代:
|
||||
- 完整 XEMBED 焦点 / 激活转发
|
||||
- icon 尺寸协商与缩放
|
||||
- 客户端异常断开的清理
|
||||
|
||||
*透明背景*第一版基本白送——bar 窗口已是 ARGB visual(见 =backend_create_bar_window= 调用 =window_get_visual(backend, true)=),tray 容器作为子窗口继承。
|
||||
|
||||
* 关键文件清单
|
||||
|
||||
| 文件 | 改动 |
|
||||
|------------------------------+------------------------------------------------------------------------------------------------------------------------|
|
||||
| =interface/tray.h= | **新增** tray 契约:=tray_t= / =tray_api_t= / =tray_icons_change_cb_t=(bar 与 backend 共享,落 interface 解耦) |
|
||||
| =interface/backend.h= | include =interface/tray.h=;=backend_bar_window_t= 内嵌 =tray_t=;扩展 =backend_create_bar_window=(加 =enable_tray=) |
|
||||
| =src/backend/x11/tray.c= | *新增*:selection / MANAGER / 基础 XEMBED / icon 管理 / =place= |
|
||||
| =src/backend/x11/internal.h= | =ATOM_LIST= 追加 tray/XEMBED atoms;=backend_t= 加 =tray= 字段 |
|
||||
| =src/backend/x11/event.c= | 消化 tray 相关 client message(不产 =event_t=) |
|
||||
| =src/backend/x11/backend.c= | =backend_create_bar_window= 实现 enable_tray 分支;=backend_destroy= 释放 tray |
|
||||
| =src/bar/types.h= | =bar_cell_t= 加 =fixed_width=;=bar_t= 加 =tray= |
|
||||
| =src/bar/cell.h= / =cell.c= | =cell_api= 加 =cell_set_fixed_width= / =cell_get_region= |
|
||||
| =src/bar/bar.c= | layout 支持 =fixed_width=;=bar_draw= 调 after 钩子;=bar_init= 加 tray item;新增 layout params 类型 |
|
||||
| =src/bar/tray.c= | *新增*:tray item 实现 |
|
||||
| =src/runtime/runtime.c= | =runtime_init_bar= 传 =enable_tray=、存 =bar->tray= |
|
||||
| =include/zdwm/config.h= | =zdwm_bar_config_t= 加 =tray_output_index= |
|
||||
|
||||
* 复用现有
|
||||
|
||||
- =backend_create_bar_window=(=src/backend/x11/backend.c=):bar 窗口已是 ARGB,tray 容器继承透明。
|
||||
- =zdwm_bar_item_type_t= vtable 框架 + [[file:../src/bar/workspaces.c][workspaces.c]]:tray item 同模式落地。
|
||||
- =zdwm_bar_click_params_t=:=after_layout= / =after_draw= 的 params 打包仿此先例。
|
||||
- =cell_api= 机制:tray =update= 通过它操作 cell(与 workspaces 一致)。
|
||||
91
docs/tray-implementation-plan.org
Normal file
91
docs/tray-implementation-plan.org
Normal file
@@ -0,0 +1,91 @@
|
||||
#+title: ZDWM Tray 系统托盘实现计划
|
||||
#+OPTIONS: toc:2
|
||||
|
||||
* 背景
|
||||
|
||||
bar 当前没有系统托盘。目标是让外部应用(输入法、音量、蓝牙、网络等)的 tray icon 显示在 bar 右侧并正常交互。
|
||||
|
||||
设计文档 [[file:tray-design.org][tray-design.org]] 已把方案定死。刚完成的模块分层重构([[file:module-layering.org][module-layering.org]])为 tray 铺了路——=interface/= 层让 bar 与 backend 经契约解耦,tray 契约落 =interface/tray.h= 消除 =bar↔backend= 交叉依赖(分层的核心动机之一)。
|
||||
|
||||
* 关键决策
|
||||
|
||||
- *契约落 =interface/tray.h=*(分层后的位置,非设计文档旧版的 =src/bar/tray.h=)。
|
||||
- *tray 闭环后端*:core 的 =event_t= / =policy= / =plan= 完全不感知;tray 消息在 =event.c= 拦截后 =return false=,不进 policy 路由。
|
||||
- *tray 是 bar 附属 + 后端单例*:生命周期随 bar 窗口;=_NET_SYSTEM_TRAY_S0= selection owner 全 display 唯一,挂到最后一个 =enable_tray=true= 的 output。
|
||||
- *第一版范围*:selection owner + MANAGER 广播 + 基础 XEMBED(reparent + =_XEMBED_EMBEDDED_NOTIFY=)+ icon 显示与点击 + icon 尺寸 = bar 高度(不缩放)。焦点/激活转发、尺寸协商、客户端异常断开清理放后续迭代。
|
||||
- *tray 存 =bar_t.tray=(一份)*:单例语义,=bar_init= 时用 =host_window()= 匹配决定哪个 output 渲染。
|
||||
|
||||
* 实现步骤
|
||||
|
||||
每步可独立编译,建议每步一个提交。
|
||||
|
||||
** 步骤 1:契约层 + 调用点占位
|
||||
|
||||
签名变更与唯一调用点耦合,必须一起改才能编译。
|
||||
|
||||
- *新增 =src/interface/tray.h=*(纯声明,零 .c):=tray_icons_change_cb_t=、=tray_api_t=(=host_window= / =icon_count= / =icon_size= / =place= / =set_listener=)、=tray_t=(=api= + =handle=)。只依赖 =interface/types.h=。
|
||||
- *=src/interface/backend.h=*:=#include "interface/tray.h"=;=backend_bar_window_t= 内嵌 =tray_t tray=;=backend_create_bar_window= 加 =bool enable_tray=。
|
||||
- *=src/runtime/runtime.c=*:临时传 =enable_tray=false=。
|
||||
- *=src/backend/x11/backend.c=*:签名加参数,函数体先忽略,返回 =.tray = {0}=。
|
||||
- 验证:编译通过,bar 行为不变。
|
||||
|
||||
** 步骤 2:bar 框架扩展(零影响现有 3 个 item)
|
||||
|
||||
为 tray item 铺路;钩子可空、=fixed_width= 默认 0,现有 item 走原路径。
|
||||
|
||||
- *=include/zdwm/bar.h=*:=zdwm_bar_item_type_t= 加可空钩子 =after_layout= / =after_draw=;新增 =zdwm_bar_item_layout_params_t=(仿 =zdwm_bar_click_params_t=:=item= / =cell_api= / =region= / =state=);=zdwm_bar_cell_api_t= 加 =cell_set_fixed_width=、=cell_get_region=。
|
||||
- *=src/bar/types.h=*:=bar_cell_t= 加 =int32_t fixed_width=(默认 0 = 走文本测量,=p_clear= 后天然正确;<0 = 隐藏哨兵)。
|
||||
- *=src/bar/cell.c=*:实现 =bar_cell_set_fixed_width= / =bar_cell_get_region=,注册进 =bar_cell_api=。
|
||||
- *=src/bar/bar.c=*:=bar_output_layout= 三处宽度计算加 =fixed_width= 分支(>0 固定 / ==0 测量 / <0 隐藏);=bar_draw= 在 layout 后遍历调 =after_layout=、draw 后调 =after_draw=(NULL 跳过)。
|
||||
- 验证:编译 + 运行,*workspaces/binding/windows 三个 item 视觉与点击零变化*(关键回归点)。
|
||||
|
||||
** 步骤 3:后端协议层(核心)
|
||||
|
||||
tray 能力全部 X11 细节,"已实现但未启用"。
|
||||
|
||||
- *新增 =src/backend/x11/tray.c=*:tray host 状态 + 成为 =_NET_SYSTEM_TRAY_S0= owner + 广播 MANAGER + 容器子窗口(reparent 到当前 bar)+ 基础 XEMBED(reparent icon + =_XEMBED_EMBEDDED_NOTIFY=)+ dock 请求处理 + =place(handle,x)=(move 容器、configure 每个 icon 到 =x + i*icon_size=)+ 填充 =static const tray_api_t=(=handle= 实际是 =backend_t*=)。=icon_size= 用 bar 窗口高度(单一数据源)。
|
||||
- *=src/backend/x11/internal.h=*:=ATOM_LIST= 追加 =_NET_SYSTEM_TRAY_S0= / =_NET_SYSTEM_TRAY_OPCODE= / =_NET_SYSTEM_TRAY_MESSAGE_DATA= / =_XEMBED= / =_XEMBED_INFO= / =Manager=;=backend_t= 加 tray host 字段(单例:host_window、icon 列表、listener、enabled)。
|
||||
- *=src/backend/x11/event.c=*(=handle_client_message=):开头拦截 tray 消息(=Manager= 且 =data.data32[1]==_NET_SYSTEM_TRAY_S0=;=_NET_SYSTEM_TRAY_OPCODE= 的 =REQUEST_DOCK=),处理后 =return false=(不产 =event_t=)。
|
||||
- *=src/backend/x11/backend.c=*:=backend_create_bar_window= 的 =enable_tray=true= 分支(创建/迁移 host 容器到此 bar 窗口,返回 =tray= 填 backend 的 api + =handle=backend=);=backend_destroy= 释放 tray(selection、容器、icon 列表)。
|
||||
- *=CMakeLists.txt=*:加 =src/backend/x11/tray.c=。
|
||||
- 验证:编译通过。runtime 仍传 false,行为不变。
|
||||
|
||||
** 步骤 4:bar tray item
|
||||
|
||||
消费步骤 2 的框架 + 步骤 3 的契约,tray 作为 right-side item 接入。
|
||||
|
||||
- *新增 =src/bar/tray.c=*(参考 =workspaces.c= 模式):=bar_tray= vtable = =create_state= / =update= / =on_click= / =after_layout= / =destroy_state=。=create_state= 拷贝 =tray_t= + =set_listener= 注册回调(置 dirty);=update= 在 dirty 时 =set_cell_count(icon_count)= + 循环 =cell_set_fixed_width(i, icon_size)=;=on_click= 返回 =ACTION_NONE=(icon 真实子窗口自收点击);=after_layout= 调 =place(handle, region.start)=。cell 不画 cairo(text 空,被真实 icon 窗口盖住)。
|
||||
- *=src/bar/bar.h=*:=bar_t= 加 =tray_t tray=(一份,单例)。
|
||||
- *=src/bar/bar.c=*(=bar_init=):遍历 output,对 =bar->tray.host_window()= 匹配自身 =window_id= 的 output 调 =bar_output_add_tray=(right side),其余不加。
|
||||
- *=CMakeLists.txt=*:加 =src/bar/tray.c=。
|
||||
- 验证:编译通过。right side 仍空(runtime 未注入 tray)。
|
||||
|
||||
** 步骤 5:runtime/配置串起来(启用)
|
||||
|
||||
- *=include/zdwm/bar.h=*(=zdwm_bar_config_t=):加 =int32_t tray_output_index=(默认 0 = 第一个 output)。
|
||||
- *=src/runtime/runtime.c=*(=runtime_init_bar=):=bool enable_tray = ((int32_t)i == runtime->bar.config.tray_output_index);= 传给 =backend_create_bar_window=;=runtime->bar.tray = bar_window.tray;=(每个返回都一样,存一份)。
|
||||
- *=src/config/defaults.c=*:=tray_output_index= 默认 0。
|
||||
- 验证:编译 + 运行,tray 启用。
|
||||
|
||||
* 风险点
|
||||
|
||||
- *after 钩子对现有 item*:现有三个 item 的 vtable 不显式初始化新钩子 → NULL(C 部分初始化保证);=bar_draw= 调用前判 NULL。=fixed_width= 默认 0 走原测量路径。步骤 2 后必须确认三个 item 零变化。
|
||||
- *selection owner 单例 vs per-output create*:host 是 backend 单例;每次 =enable_tray=true= 把容器 reparent 到本次 bar 窗口,最终挂最后一个 true 的 output;=host_window()= 返回当前挂载 window_id。
|
||||
- *XEMBED 第一版只做基础*:reparent + =EMBEDDED_NOTIFY=;不做焦点/激活/缩放。某些高级 applet(要求焦点的输入法)可能受限,基础音量/网络 icon 无影响。
|
||||
- *core 零感知*:tray 消息在 =event.c= 拦截 =return false=,不进 policy/plan。icon 增删不触发 core 重排——icon 是真实 X 子窗口,X server 自管绘制,bar 重绘靠 timerfd 驱动,region 变化经 =after_layout= → =place= 同步。
|
||||
- *tray cell padding*:=fixed_width = icon_size=,确保 tray item 的 cell padding 为 0,避免 icon 间额外间距。
|
||||
- *透明背景*:bar 窗口已 ARGB(=backend.c= 用 =window_get_visual(true)=),tray 容器子窗口继承,第一版白送。
|
||||
|
||||
* 端到端验证
|
||||
|
||||
1. *每步编译*:=cmake --build build=,零 warning(项目 =-Wall=)。
|
||||
2. *步骤 5 后运行*:=./build/zdwm=。
|
||||
3. *tray icon 验证*:启动一个提供 tray icon 的 app(=volumeicon= / =nm-applet= / =fcitx5= 等,不要用 stalonetray——它会抢 selection owner)。验证:icon 显示在 bar *右侧*、尺寸 = bar 高度无溢出、多 icon 横向无重叠、点击有响应、透明背景融入 bar。
|
||||
4. *动态增删*:启动/退出 tray app,观察 icon 实时增删 + 布局重排(经 =set_listener= 回调 → dirty → 下个 timerfd tick → =update= 重算 → =after_layout= 重排),无残留空位。
|
||||
5. *回归*(每步都做,重点步骤 2):workspaces tag 切换、binding 模式、windows 标题、bar 点击命中全部不变。
|
||||
|
||||
* 关键文件
|
||||
|
||||
- *新增*:=src/interface/tray.h=(契约)、=src/backend/x11/tray.c=(协议核心)、=src/bar/tray.c=(bar item)。
|
||||
- *修改*:=src/interface/backend.h=、=src/backend/x11/{internal.h,event.c,backend.c}=、=include/zdwm/bar.h=、=src/bar/{types.h,cell.c,bar.c,bar.h}=、=src/runtime/runtime.c=、=src/config/defaults.c=、=CMakeLists.txt=。
|
||||
- *复用*:=workspaces.c=(item 模板)、=zdwm_bar_click_params_t=(layout params 打包先例)、=ATOM_LIST= 宏(atom 注册)、=window_get_visual(true)=(ARGB)、=bar_cell_api= vtable 机制。
|
||||
@@ -39,6 +39,9 @@ typedef struct zdwm_bar_config_t {
|
||||
const char *binding_mode_bg;
|
||||
const char *binding_mode_fg;
|
||||
|
||||
int32_t tray_output_index;
|
||||
|
||||
uint32_t window_padding_x;
|
||||
const char *window_bg;
|
||||
const char *window_fg;
|
||||
const char *window_focused_bg;
|
||||
@@ -47,6 +50,11 @@ typedef struct zdwm_bar_config_t {
|
||||
|
||||
typedef struct zdwm_bar_item_t zdwm_bar_item_t;
|
||||
|
||||
typedef struct zdwm_bar_x_region_t {
|
||||
int32_t start;
|
||||
int32_t end;
|
||||
} zdwm_bar_x_region_t;
|
||||
|
||||
typedef struct zdwm_bar_cell_api_t {
|
||||
size_t (*get_cell_count)(zdwm_bar_item_t *item);
|
||||
void (*set_cell_count)(zdwm_bar_item_t *item, size_t count);
|
||||
@@ -56,8 +64,31 @@ typedef struct zdwm_bar_cell_api_t {
|
||||
void (*cell_set_fg)(zdwm_bar_item_t *item, size_t index, const char *color);
|
||||
void (*cell_set_icon)(zdwm_bar_item_t *item, size_t index, zdwm_icon_t icon);
|
||||
void (*cell_set_indicator)(zdwm_bar_item_t *item, size_t index, bool show);
|
||||
void (*cell_set_fixed_width)(
|
||||
zdwm_bar_item_t *item,
|
||||
size_t index,
|
||||
int32_t width
|
||||
);
|
||||
|
||||
zdwm_bar_x_region_t (*cell_get_region)(zdwm_bar_item_t *item, size_t index);
|
||||
} zdwm_bar_cell_api_t;
|
||||
|
||||
typedef struct zdwm_bar_click_params_t {
|
||||
zdwm_bar_item_t *item;
|
||||
size_t cell_index;
|
||||
int32_t x;
|
||||
zdwm_modifier_mask_t modifiers;
|
||||
zdwm_button_t button;
|
||||
void *state;
|
||||
} zdwm_bar_click_params_t;
|
||||
|
||||
typedef struct zdwm_bar_item_hook_params_t {
|
||||
zdwm_bar_item_t *item;
|
||||
const zdwm_bar_cell_api_t *cell_api;
|
||||
zdwm_bar_x_region_t region;
|
||||
void *state;
|
||||
} zdwm_bar_item_hook_params_t;
|
||||
|
||||
typedef struct zdwm_bar_item_type_t {
|
||||
void *(*create_state)(zdwm_output_id_t output_id, void *config);
|
||||
void (*update)(
|
||||
@@ -65,12 +96,9 @@ typedef struct zdwm_bar_item_type_t {
|
||||
const zdwm_bar_cell_api_t *cells,
|
||||
void *state
|
||||
);
|
||||
zdwm_action_t (*on_click)(
|
||||
zdwm_bar_item_t *item,
|
||||
size_t cell_index,
|
||||
int32_t x,
|
||||
void *state
|
||||
);
|
||||
void (*after_layout)(const zdwm_bar_item_hook_params_t *params);
|
||||
void (*after_draw)(const zdwm_bar_item_hook_params_t *params);
|
||||
zdwm_action_t (*on_click)(zdwm_bar_click_params_t *params);
|
||||
void (*destroy_state)(void *state);
|
||||
zdwm_bar_item_t *instance;
|
||||
uint32_t update_interval_ms;
|
||||
|
||||
@@ -157,6 +157,14 @@ typedef struct zdwm_api_t {
|
||||
const char *focused
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief 设置窗口移动和调整大小时的刷新率
|
||||
*
|
||||
* @param builder 配置构建上下文
|
||||
* @param fps 刷新帧率,最小 10 帧,若设置小于 10 帧会设置为 10 帧
|
||||
*/
|
||||
void (*set_interaction_fps)(zdwm_config_builder_t *builder, uint32_t fps);
|
||||
|
||||
/** @name Listener 注册
|
||||
* @{ */
|
||||
|
||||
|
||||
@@ -18,6 +18,28 @@ typedef uint32_t zdwm_binding_mode_id_t;
|
||||
#define ZDWM_WORKSPACE_ID_INVALID ((zdwm_workspace_id_t)UINT32_MAX)
|
||||
#define ZDWM_BINDING_MODE_ID_INVALID ((zdwm_binding_mode_id_t)UINT32_MAX)
|
||||
|
||||
typedef enum zdwm_button_t {
|
||||
ZDWM_BUTTON_NONE,
|
||||
ZDWM_BUTTON_LEFT,
|
||||
ZDWM_BUTTON_RIGHT,
|
||||
ZDWM_BUTTON_MIDDLE,
|
||||
} zdwm_button_t;
|
||||
|
||||
/* clang-format off */
|
||||
typedef enum zdwm_modifier_bit_t {
|
||||
ZDWM_MOD_NONE = 0u,
|
||||
ZDWM_MOD_SHIFT = 1u << 0,
|
||||
ZDWM_MOD_CONTROL = 1u << 1,
|
||||
ZDWM_MOD_1 = 1u << 2,
|
||||
ZDWM_MOD_2 = 1u << 3,
|
||||
ZDWM_MOD_3 = 1u << 4,
|
||||
ZDWM_MOD_4 = 1u << 5,
|
||||
ZDWM_MOD_5 = 1u << 6,
|
||||
} zdwm_modifier_bit_t;
|
||||
/* clang-format on */
|
||||
|
||||
typedef uint32_t zdwm_modifier_mask_t;
|
||||
|
||||
typedef struct zdwm_rect_t {
|
||||
int32_t x;
|
||||
int32_t y;
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include "base/memory.h"
|
||||
#include "core/backend.h"
|
||||
#include "core/types.h"
|
||||
#include "interface/backend.h"
|
||||
#include "interface/types.h"
|
||||
|
||||
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; }
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "core/backend.h"
|
||||
#include "core/types.h"
|
||||
#include "interface/backend.h"
|
||||
|
||||
backend_detect_t *
|
||||
output_remove_duplication(const output_info_t *output, const size_t count);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "core/backend.h"
|
||||
#include "interface/backend.h"
|
||||
|
||||
#include <cairo-xcb.h>
|
||||
#include <cairo.h>
|
||||
@@ -18,17 +18,15 @@
|
||||
#include <xcb/xproto.h>
|
||||
|
||||
#include "backend/output_utils.h"
|
||||
#include "backend/x11/cursor.h"
|
||||
#include "backend/x11/tray.h"
|
||||
#include "backend/x11/window.h"
|
||||
#include "base/app.h"
|
||||
#include "base/array.h"
|
||||
#include "base/log.h"
|
||||
#include "base/macros.h"
|
||||
#include "base/memory.h"
|
||||
#include "base/window_list.h"
|
||||
#include "core/event.h"
|
||||
#include "core/plan.h"
|
||||
#include "core/types.h"
|
||||
#include "core/window.h"
|
||||
#include "common/window.h"
|
||||
#include "internal.h"
|
||||
|
||||
typedef struct atom_item_t {
|
||||
@@ -60,6 +58,21 @@ static void atoms_init(backend_t *backend) {
|
||||
*atom->atom = reply->atom;
|
||||
p_delete(&reply);
|
||||
}
|
||||
|
||||
#define EWMH_ITEM(name) atoms->name,
|
||||
xcb_atom_t ewmh_supported_atoms[] = {EWMH_ATOMS(EWMH_ITEM)};
|
||||
#undef EWMH_ITEM
|
||||
auto root = backend->screen->root;
|
||||
xcb_change_property(
|
||||
conn,
|
||||
XCB_PROP_MODE_REPLACE,
|
||||
root,
|
||||
atoms->_NET_SUPPORTED,
|
||||
XCB_ATOM_ATOM,
|
||||
32,
|
||||
countof(ewmh_supported_atoms),
|
||||
ewmh_supported_atoms
|
||||
);
|
||||
}
|
||||
|
||||
static void create_wm_check_window(backend_t *backend) {
|
||||
@@ -169,16 +182,21 @@ backend_t *backend_create(const char *display_name) {
|
||||
|
||||
backend->key_symbols = xcb_key_symbols_alloc(conn);
|
||||
|
||||
cursor_init(backend);
|
||||
atoms_init(backend);
|
||||
create_wm_check_window(backend);
|
||||
create_no_focus_window(backend);
|
||||
|
||||
window_change_cursor(backend, root, ZDWM_CURSOR_NORMAL);
|
||||
|
||||
return backend;
|
||||
}
|
||||
|
||||
void backend_destroy(backend_t *backend) {
|
||||
if (!backend) return;
|
||||
|
||||
tray_cleanup(backend);
|
||||
|
||||
p_delete(&backend->config_list.cfgs);
|
||||
p_clear(&backend->config_list, 1);
|
||||
|
||||
@@ -189,6 +207,8 @@ void backend_destroy(backend_t *backend) {
|
||||
xcb_key_symbols_free(backend->key_symbols);
|
||||
backend->key_symbols = nullptr;
|
||||
|
||||
cursor_cleanup(backend);
|
||||
|
||||
xcb_disconnect(backend->conn);
|
||||
backend->conn = nullptr;
|
||||
free(backend);
|
||||
@@ -501,6 +521,33 @@ backend_bind_key(backend_t *backend, const effect_bind_key_t *bind_key) {
|
||||
window_grab_keys(backend, root, bind_key->keys, bind_key->count);
|
||||
}
|
||||
|
||||
static void backend_grab_button(
|
||||
backend_t *backend,
|
||||
const effect_grab_button_t *grab_button
|
||||
) {
|
||||
auto conn = backend->conn;
|
||||
auto window = grab_button->window;
|
||||
xcb_ungrab_button(conn, XCB_BUTTON_INDEX_ANY, window, XCB_MOD_MASK_ANY);
|
||||
|
||||
auto buttons = grab_button->buttons;
|
||||
for (size_t i = 0; i < grab_button->count; ++i) {
|
||||
auto button = buttons[i].button;
|
||||
auto modifiers = buttons[i].modifiers;
|
||||
window_grab_button(backend, window, button, modifiers);
|
||||
}
|
||||
}
|
||||
|
||||
static void backend_grab_pointer(backend_t *backend, cursor_t cursor) {
|
||||
auto conn = backend->conn;
|
||||
auto root = backend->screen->root;
|
||||
auto mask = XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE |
|
||||
XCB_EVENT_MASK_POINTER_MOTION;
|
||||
auto mode = XCB_GRAB_MODE_ASYNC;
|
||||
auto xcursor = cursor_get_xcb_cursor(backend, cursor);
|
||||
auto time = XCB_TIME_CURRENT_TIME;
|
||||
xcb_grab_pointer(conn, false, root, mask, mode, mode, root, xcursor, time);
|
||||
}
|
||||
|
||||
static void backend_merge_effects(
|
||||
backend_t *backend,
|
||||
const effect_t *effects,
|
||||
@@ -511,6 +558,7 @@ static void backend_merge_effects(
|
||||
switch (e->type) {
|
||||
case ZDWM_EFFECT_MAP_WINDOW:
|
||||
window_list_push(&backend->map, e->as.map.window);
|
||||
window_list_push(&backend->configure, e->as.map.window);
|
||||
break;
|
||||
case ZDWM_EFFECT_UNMAP_WINDOW:
|
||||
window_list_push(&backend->unmap, e->as.unmap.window);
|
||||
@@ -529,6 +577,12 @@ static void backend_merge_effects(
|
||||
XCB_ICCCM_WM_STATE_WITHDRAWN
|
||||
);
|
||||
break;
|
||||
case ZDWM_EFFECT_START_MOVE_WINDOW:
|
||||
backend_grab_pointer(backend, ZDWM_CURSOR_MOVE);
|
||||
break;
|
||||
case ZDWM_EFFECT_START_RESIZE_WINDOW:
|
||||
backend_grab_pointer(backend, ZDWM_CURSOR_RESIZE);
|
||||
break;
|
||||
case ZDWM_EFFECT_MINIMIZE_WINDOW: {
|
||||
auto window = e->as.minimize.window;
|
||||
xcb_icccm_wm_state_t state = XCB_ICCCM_WM_STATE_NORMAL;
|
||||
@@ -559,6 +613,9 @@ static void backend_merge_effects(
|
||||
case ZDWM_EFFECT_CONFIGURE_WINDOW:
|
||||
merge_window_configure_params(backend, &e->as.configure);
|
||||
break;
|
||||
case ZDWM_EFFECT_CONFIGURE_NOTIFY:
|
||||
window_list_push(&backend->configure, e->as.configure_notify.window);
|
||||
break;
|
||||
case ZDWM_EFFECT_CHANGE_BORDER_COLOR: {
|
||||
xcb_change_window_attributes_value_list_t value = {
|
||||
.border_pixel = e->as.change_border_color.color->argb
|
||||
@@ -580,10 +637,56 @@ static void backend_merge_effects(
|
||||
case ZDWM_EFFECT_BIND_KEY:
|
||||
backend_bind_key(backend, &e->as.bind_key);
|
||||
break;
|
||||
case ZDWM_EFFECT_GRAB_BUTTON:
|
||||
backend_grab_button(backend, &e->as.grab_button);
|
||||
break;
|
||||
case ZDWM_EFFECT_UNGRAB_POINTER:
|
||||
xcb_ungrab_pointer(backend->conn, XCB_CURRENT_TIME);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void batch_send_configure_notify(
|
||||
xcb_connection_t *conn,
|
||||
xcb_window_t *windows,
|
||||
size_t count
|
||||
) {
|
||||
if (count == 0) return;
|
||||
|
||||
auto cookie_list = p_new(xcb_get_geometry_cookie_t, count);
|
||||
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
auto window = windows[i];
|
||||
cookie_list[i] = xcb_get_geometry(conn, window);
|
||||
}
|
||||
|
||||
uint32_t event_mask = XCB_EVENT_MASK_STRUCTURE_NOTIFY;
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
auto window = windows[i];
|
||||
auto cookie = cookie_list[i];
|
||||
auto reply = xcb_get_geometry_reply(conn, cookie, nullptr);
|
||||
if (!reply) continue;
|
||||
|
||||
xcb_configure_notify_event_t ev = {
|
||||
.response_type = XCB_CONFIGURE_NOTIFY,
|
||||
.event = window,
|
||||
.window = window,
|
||||
.above_sibling = XCB_WINDOW_NONE,
|
||||
.x = reply->x,
|
||||
.y = reply->y,
|
||||
.width = reply->width,
|
||||
.height = reply->height,
|
||||
.border_width = reply->border_width,
|
||||
.override_redirect = false,
|
||||
};
|
||||
xcb_send_event(conn, false, window, event_mask, (char *)&ev);
|
||||
p_delete(&reply);
|
||||
}
|
||||
|
||||
p_delete(&cookie_list);
|
||||
}
|
||||
|
||||
static void backend_batch_apply_effects(backend_t *backend) {
|
||||
xcb_connection_t *conn = backend->conn;
|
||||
if (backend->unmap.count) {
|
||||
@@ -613,6 +716,12 @@ static void backend_batch_apply_effects(backend_t *backend) {
|
||||
|
||||
backend_apply_window_configure_list(backend);
|
||||
|
||||
{
|
||||
auto windows = backend->configure.windows;
|
||||
auto count = backend->configure.count;
|
||||
batch_send_configure_notify(conn, windows, count);
|
||||
}
|
||||
|
||||
if (backend->update_focus) {
|
||||
backend_focus_window(backend, backend->focus_window);
|
||||
}
|
||||
@@ -628,6 +737,7 @@ bool backend_apply_effect(
|
||||
window_list_reset(&backend->unmap);
|
||||
window_list_reset(&backend->map);
|
||||
window_list_reset(&backend->kill);
|
||||
window_list_reset(&backend->configure);
|
||||
|
||||
backend_merge_effects(backend, effects, effect_count);
|
||||
backend_batch_apply_effects(backend);
|
||||
@@ -651,19 +761,48 @@ backend_scan_result_t *backend_scan_windows(backend_t *backend) {
|
||||
|
||||
auto list = xcb_query_tree_children(reply);
|
||||
|
||||
backend_scan_result_t *result = p_new(backend_scan_result_t, 1);
|
||||
auto result = p_new(backend_scan_result_t, 1);
|
||||
auto sub_result = p_new(backend_scan_result_t, 1);
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
auto slot = array_push(result->windows, result->count, result->capacity);
|
||||
if (!populate_window_event(backend, list[i], slot)) {
|
||||
for (typeof(length) i = 0; i < length; ++i) {
|
||||
auto window = list[i];
|
||||
auto wa = window_get_attributes(backend, window);
|
||||
if (!wa) continue;
|
||||
|
||||
auto override_redirect = wa->override_redirect;
|
||||
auto map_state = wa->map_state;
|
||||
p_delete(&wa);
|
||||
if (override_redirect) continue;
|
||||
|
||||
auto hints = (xcb_icccm_wm_hints_t){0};
|
||||
auto hints_getted = window_get_wm_hints(backend, window, &hints);
|
||||
if (!(map_state == XCB_MAP_STATE_VIEWABLE ||
|
||||
(hints_getted && hints.initial_state == XCB_ICCCM_WM_STATE_ICONIC))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto transient_for = window_get_transient_for(backend, window);
|
||||
auto r = transient_for == XCB_WINDOW_NONE ? result : sub_result;
|
||||
auto slot = array_push(r->windows, r->count, r->capacity);
|
||||
if (
|
||||
!populate_window_event(backend, list[i], slot) || slot->override_redirect
|
||||
) {
|
||||
window_layer_props_cleanup(&slot->props);
|
||||
window_metadata_cleanup(&slot->metadata);
|
||||
result->count--;
|
||||
r->count--;
|
||||
}
|
||||
}
|
||||
|
||||
p_delete(&reply);
|
||||
|
||||
for (size_t i = 0; i < sub_result->count; ++i) {
|
||||
auto slot = array_push(result->windows, result->count, result->capacity);
|
||||
*slot = sub_result->windows[i];
|
||||
}
|
||||
|
||||
p_clear(sub_result->windows, sub_result->count);
|
||||
backend_scan_result_destroy(sub_result);
|
||||
|
||||
if (result->count == 0) {
|
||||
backend_scan_result_destroy(result);
|
||||
result = nullptr;
|
||||
@@ -689,7 +828,8 @@ void backend_scan_result_destroy(backend_scan_result_t *result) {
|
||||
backend_bar_window_t backend_create_bar_window(
|
||||
backend_t *backend,
|
||||
rect_t geometry,
|
||||
uint32_t bg_pixel
|
||||
uint32_t bg_pixel,
|
||||
bool enable_tray
|
||||
) {
|
||||
auto conn = backend->conn;
|
||||
auto root = backend->screen->root;
|
||||
@@ -759,7 +899,24 @@ backend_bar_window_t backend_create_bar_window(
|
||||
fatal("cannot create cairo context: %s", cairo_status_to_string(statue));
|
||||
}
|
||||
|
||||
return (backend_bar_window_t){.window_id = window_id, .cr = cr};
|
||||
if (enable_tray) {
|
||||
tray_host_window_t host_window = {
|
||||
.window = window_id,
|
||||
.width = width,
|
||||
.height = height,
|
||||
.bg_pixel = bg_pixel,
|
||||
};
|
||||
tray_init(backend, host_window);
|
||||
}
|
||||
|
||||
return (backend_bar_window_t){
|
||||
.window_id = window_id,
|
||||
.cr = cr,
|
||||
.tray = {
|
||||
.api = tray_api,
|
||||
.handle = backend,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
void backend_flush(backend_t *backend) { xcb_flush(backend->conn); }
|
||||
|
||||
71
src/backend/x11/common.c
Normal file
71
src/backend/x11/common.c
Normal file
@@ -0,0 +1,71 @@
|
||||
#include "common.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <xcb/xproto.h>
|
||||
|
||||
#include "base/macros.h"
|
||||
#include "interface/types.h"
|
||||
|
||||
typedef struct modifier_map_t {
|
||||
modifier_bit_t modifier;
|
||||
xcb_mod_mask_t mask;
|
||||
} modifier_map_t;
|
||||
|
||||
static constexpr modifier_map_t modifier_map[] = {
|
||||
{ZDWM_MOD_SHIFT, XCB_MOD_MASK_SHIFT},
|
||||
{ZDWM_MOD_CONTROL, XCB_MOD_MASK_CONTROL},
|
||||
{ZDWM_MOD_1, XCB_MOD_MASK_1},
|
||||
{ZDWM_MOD_2, XCB_MOD_MASK_2},
|
||||
{ZDWM_MOD_3, XCB_MOD_MASK_3},
|
||||
{ZDWM_MOD_4, XCB_MOD_MASK_4},
|
||||
{ZDWM_MOD_5, XCB_MOD_MASK_5},
|
||||
};
|
||||
|
||||
modifier_mask_t modifiers_xcb_to_zdwm(uint16_t mask) {
|
||||
modifier_mask_t mods = ZDWM_MOD_NONE;
|
||||
for (size_t i = 0; i < countof(modifier_map); ++i) {
|
||||
auto item = &modifier_map[i];
|
||||
if (mask & item->mask) mods |= item->modifier;
|
||||
}
|
||||
return mods;
|
||||
}
|
||||
|
||||
uint16_t modifiers_zdwm_to_xcb(modifier_mask_t mask) {
|
||||
uint16_t mods = 0U;
|
||||
for (size_t i = 0; i < countof(modifier_map); ++i) {
|
||||
auto item = &modifier_map[i];
|
||||
if (mask & item->modifier) mods |= item->mask;
|
||||
}
|
||||
return mods;
|
||||
}
|
||||
|
||||
typedef struct button_map_t {
|
||||
button_t zdwm_button;
|
||||
xcb_button_index_t xcb_button;
|
||||
} button_map_t;
|
||||
|
||||
static constexpr button_map_t button_map[] = {
|
||||
{ZDWM_BUTTON_LEFT, XCB_BUTTON_INDEX_1},
|
||||
{ZDWM_BUTTON_RIGHT, XCB_BUTTON_INDEX_3},
|
||||
{ZDWM_BUTTON_MIDDLE, XCB_BUTTON_INDEX_2},
|
||||
/* TODO: 后续可以考虑加上鼠标滚轮事件 */
|
||||
};
|
||||
|
||||
button_t button_xcb_to_zdwm(xcb_button_t button) {
|
||||
for (size_t i = 0; i < countof(button_map); ++i) {
|
||||
auto item = &button_map[i];
|
||||
if (button == item->xcb_button) return item->zdwm_button;
|
||||
}
|
||||
|
||||
return ZDWM_BUTTON_NONE;
|
||||
}
|
||||
|
||||
xcb_button_t button_zdwm_to_xcb(button_t button) {
|
||||
for (size_t i = 0; i < countof(button_map); ++i) {
|
||||
auto item = &button_map[i];
|
||||
if (button == item->zdwm_button) return item->xcb_button;
|
||||
}
|
||||
|
||||
return XCB_BUTTON_INDEX_ANY;
|
||||
}
|
||||
12
src/backend/x11/common.h
Normal file
12
src/backend/x11/common.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <xcb/xproto.h>
|
||||
|
||||
#include "interface/types.h"
|
||||
|
||||
modifier_mask_t modifiers_xcb_to_zdwm(uint16_t mask);
|
||||
uint16_t modifiers_zdwm_to_xcb(modifier_mask_t mask);
|
||||
|
||||
button_t button_xcb_to_zdwm(xcb_button_t button);
|
||||
xcb_button_t button_zdwm_to_xcb(button_t button);
|
||||
51
src/backend/x11/cursor.c
Normal file
51
src/backend/x11/cursor.c
Normal file
@@ -0,0 +1,51 @@
|
||||
#include <stddef.h>
|
||||
#include <xcb/xcb_cursor.h>
|
||||
#include <xcb/xproto.h>
|
||||
|
||||
#include "base/macros.h"
|
||||
#include "base/memory.h"
|
||||
#include "internal.h"
|
||||
|
||||
static const char *const cursor_name_map[] = {
|
||||
[ZDWM_CURSOR_NORMAL] = "left_ptr",
|
||||
[ZDWM_CURSOR_MOVE] = "fleur",
|
||||
[ZDWM_CURSOR_RESIZE] = "bottom_right_corner",
|
||||
};
|
||||
|
||||
static xcb_cursor_context_t *get_xcb_cursor_context(backend_t *backend) {
|
||||
if (backend->cursor_context) return backend->cursor_context;
|
||||
|
||||
auto conn = backend->conn;
|
||||
auto screen = backend->screen;
|
||||
if (xcb_cursor_context_new(conn, screen, &backend->cursor_context) == 0) {
|
||||
return backend->cursor_context;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void cursor_init(backend_t *backend) {
|
||||
auto ctx = get_xcb_cursor_context(backend);
|
||||
if (!ctx) return;
|
||||
|
||||
for (size_t i = 0; i < countof(backend->cursors); ++i) {
|
||||
auto cursor_name = cursor_name_map[i];
|
||||
backend->cursors[i] = xcb_cursor_load_cursor(ctx, cursor_name);
|
||||
}
|
||||
}
|
||||
|
||||
void cursor_cleanup(backend_t *backend) {
|
||||
constexpr size_t count = countof(backend->cursors);
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
xcb_free_cursor(backend->conn, backend->cursors[i]);
|
||||
}
|
||||
p_clear(backend->cursors, count);
|
||||
xcb_cursor_context_free(backend->cursor_context);
|
||||
backend->cursor_context = nullptr;
|
||||
}
|
||||
|
||||
xcb_cursor_t cursor_get_xcb_cursor(backend_t *backend, cursor_t cursor) {
|
||||
if (backend->cursor_context == nullptr) return XCB_CURSOR_NONE;
|
||||
|
||||
return backend->cursors[cursor];
|
||||
}
|
||||
9
src/backend/x11/cursor.h
Normal file
9
src/backend/x11/cursor.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <xcb/xproto.h>
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
void cursor_init(backend_t *backend);
|
||||
void cursor_cleanup(backend_t *backend);
|
||||
xcb_cursor_t cursor_get_xcb_cursor(backend_t *backend, cursor_t cursor);
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "core/event.h"
|
||||
#include "common/event.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <xcb/xcb.h>
|
||||
@@ -8,12 +9,12 @@
|
||||
#include <xcb/xcb_keysyms.h>
|
||||
#include <xcb/xproto.h>
|
||||
|
||||
#include "backend/x11/common.h"
|
||||
#include "backend/x11/tray.h"
|
||||
#include "backend/x11/window.h"
|
||||
#include "base/macros.h"
|
||||
#include "base/memory.h"
|
||||
#include "core/backend.h"
|
||||
#include "core/types.h"
|
||||
#include "core/window.h"
|
||||
#include "interface/backend.h"
|
||||
#include "interface/event.h"
|
||||
#include "internal.h"
|
||||
|
||||
static window_state_t *derive_window_states(
|
||||
@@ -51,18 +52,16 @@ static bool derive_skip_taskbar(
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool minimized_from_hints(const xcb_icccm_wm_hints_t *hints) {
|
||||
return hints->initial_state == XCB_ICCCM_WM_STATE_ICONIC;
|
||||
}
|
||||
|
||||
static bool maximized_from_states(
|
||||
const atoms_t *atoms,
|
||||
const xcb_atom_t *state_atoms,
|
||||
uint32_t state_count
|
||||
) {
|
||||
for (uint32_t i = 0; i < state_count; i++) {
|
||||
if (state_atoms[i] == atoms->_NET_WM_STATE_MAXIMIZED_VERT ||
|
||||
state_atoms[i] == atoms->_NET_WM_STATE_MAXIMIZED_HORZ)
|
||||
if (
|
||||
state_atoms[i] == atoms->_NET_WM_STATE_MAXIMIZED_VERT ||
|
||||
state_atoms[i] == atoms->_NET_WM_STATE_MAXIMIZED_HORZ
|
||||
)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -91,6 +90,28 @@ static bool urgent_from_states(
|
||||
return false;
|
||||
}
|
||||
|
||||
static void parse_size_hints(
|
||||
const xcb_size_hints_t *hints,
|
||||
zdwm_size_t *min,
|
||||
zdwm_size_t *max
|
||||
) {
|
||||
assert(hints != nullptr);
|
||||
assert(min != nullptr);
|
||||
assert(max != nullptr);
|
||||
|
||||
*min = (zdwm_size_t){.width = 0, .height = 0};
|
||||
*max = (zdwm_size_t){.width = INT32_MAX, .height = INT32_MAX};
|
||||
|
||||
if (hints->flags & XCB_ICCCM_SIZE_HINT_P_MIN_SIZE) {
|
||||
min->width = hints->min_width;
|
||||
min->height = hints->min_height;
|
||||
}
|
||||
if (hints->flags & XCB_ICCCM_SIZE_HINT_P_MAX_SIZE) {
|
||||
max->width = hints->max_width;
|
||||
max->height = hints->max_height;
|
||||
}
|
||||
}
|
||||
|
||||
bool populate_window_event(
|
||||
backend_t *backend,
|
||||
xcb_window_t window,
|
||||
@@ -99,8 +120,7 @@ bool populate_window_event(
|
||||
ev->window = (window_id_t)window;
|
||||
ev->transient_for = window_get_transient_for(backend, window);
|
||||
|
||||
xcb_get_window_attributes_reply_t *wa =
|
||||
window_get_attributes(backend, window);
|
||||
auto wa = window_get_attributes(backend, window);
|
||||
if (!wa) return false;
|
||||
ev->override_redirect = (bool)wa->override_redirect;
|
||||
p_delete(&wa);
|
||||
@@ -108,10 +128,13 @@ bool populate_window_event(
|
||||
xcb_icccm_wm_hints_t wm_hints = {0};
|
||||
if (window_get_wm_hints(backend, window, &wm_hints)) {
|
||||
ev->urgent = (bool)xcb_icccm_wm_hints_get_urgency(&wm_hints);
|
||||
ev->minimized = minimized_from_hints(&wm_hints);
|
||||
ev->minimized = wm_hints.initial_state == XCB_ICCCM_WM_STATE_ICONIC;
|
||||
}
|
||||
|
||||
if (!window_get_fixed_size(backend, window, &ev->fixed_size)) return false;
|
||||
xcb_size_hints_t size_hints = {0};
|
||||
if (!window_get_size_hints(backend, window, &size_hints)) return false;
|
||||
parse_size_hints(&size_hints, &ev->min_size, &ev->max_size);
|
||||
|
||||
if (!window_get_geometry(backend, window, &ev->rect)) return false;
|
||||
|
||||
const atoms_t *atoms = &backend->atoms;
|
||||
@@ -167,6 +190,8 @@ static bool handle_map_request(
|
||||
event_t *event,
|
||||
const xcb_map_request_event_t *xcb_event
|
||||
) {
|
||||
if (tray_handle_map_request(backend, xcb_event)) return false;
|
||||
|
||||
event->type = ZDWM_EVENT_WINDOW_MAP_REQUEST;
|
||||
return populate_window_event(
|
||||
backend,
|
||||
@@ -180,6 +205,8 @@ static bool handle_unmap_notify(
|
||||
event_t *event,
|
||||
const xcb_unmap_notify_event_t *xcb_event
|
||||
) {
|
||||
if (tray_handle_unmap_notify(backend, xcb_event)) return false;
|
||||
|
||||
event->type = ZDWM_EVENT_WINDOW_REMOVE;
|
||||
event->as.window_remove.window = xcb_event->window;
|
||||
if (XCB_EVENT_SENT(xcb_event)) {
|
||||
@@ -196,6 +223,8 @@ static bool handle_destroy_notify(
|
||||
event_t *event,
|
||||
const xcb_destroy_notify_event_t *xcb_event
|
||||
) {
|
||||
if (tray_handle_destroy_notify(backend, xcb_event)) return false;
|
||||
|
||||
event->type = ZDWM_EVENT_WINDOW_REMOVE;
|
||||
event->as.window_remove.window = xcb_event->window;
|
||||
event->as.window_remove.reason = ZDWM_WINDOW_REMOVE_DESTROY;
|
||||
@@ -208,6 +237,8 @@ static bool handle_configure_request(
|
||||
event_t *event,
|
||||
const xcb_configure_request_event_t *xcb_event
|
||||
) {
|
||||
if (tray_handle_configure_request(backend, xcb_event)) return false;
|
||||
|
||||
event->type = ZDWM_EVENT_CONFIGURE_REQUEST;
|
||||
auto data = &event->as.configure_request;
|
||||
data->window = xcb_event->window;
|
||||
@@ -232,29 +263,6 @@ static bool handle_configure_request(
|
||||
return true;
|
||||
}
|
||||
|
||||
static modifier_mask_t get_modifiers(uint16_t mask) {
|
||||
typedef struct modifier_map_t {
|
||||
modifier_bit_t modifier;
|
||||
xcb_mod_mask_t mask;
|
||||
} modifier_map_t;
|
||||
static constexpr modifier_map_t modifier_map[] = {
|
||||
{ZDWM_MOD_SHIFT, XCB_MOD_MASK_SHIFT},
|
||||
{ZDWM_MOD_CONTROL, XCB_MOD_MASK_CONTROL},
|
||||
{ZDWM_MOD_1, XCB_MOD_MASK_1},
|
||||
{ZDWM_MOD_2, XCB_MOD_MASK_2},
|
||||
{ZDWM_MOD_3, XCB_MOD_MASK_3},
|
||||
{ZDWM_MOD_4, XCB_MOD_MASK_4},
|
||||
{ZDWM_MOD_5, XCB_MOD_MASK_5},
|
||||
};
|
||||
|
||||
auto mods = ZDWM_MOD_NONE;
|
||||
for (size_t i = 0; i < countof(modifier_map); ++i) {
|
||||
auto item = &modifier_map[i];
|
||||
if (mask & item->mask) mods |= item->modifier;
|
||||
}
|
||||
return mods;
|
||||
}
|
||||
|
||||
static bool handle_key_press(
|
||||
backend_t *backend,
|
||||
event_t *event,
|
||||
@@ -268,11 +276,64 @@ static bool handle_key_press(
|
||||
event->type = ZDWM_EVENT_KEY_PRESS;
|
||||
event->as.key_press.keycode = keycode;
|
||||
event->as.key_press.keysym = keysym;
|
||||
event->as.key_press.modifiers = get_modifiers(xcb_event->state);
|
||||
event->as.key_press.modifiers = modifiers_xcb_to_zdwm(xcb_event->state);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool fill_button_event(
|
||||
const xcb_button_press_event_t *xcb_event,
|
||||
pointer_button_event_t *data
|
||||
) {
|
||||
auto button = button_xcb_to_zdwm(xcb_event->detail);
|
||||
|
||||
data->modifiers = modifiers_xcb_to_zdwm(xcb_event->state);
|
||||
data->button = button;
|
||||
data->window = xcb_event->event;
|
||||
data->root = (point_t){.x = xcb_event->root_x, .y = xcb_event->root_y};
|
||||
data->local = (point_t){.x = xcb_event->event_x, .y = xcb_event->event_y};
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool handle_button_press(
|
||||
backend_t *backend,
|
||||
event_t *event,
|
||||
const xcb_button_press_event_t *xcb_event
|
||||
) {
|
||||
auto press = &event->as.pointer_button_press;
|
||||
auto handled = fill_button_event(xcb_event, press);
|
||||
if (handled) event->type = ZDWM_EVENT_POINTER_BUTTON_PRESS;
|
||||
|
||||
return handled;
|
||||
}
|
||||
|
||||
static bool handle_button_release(
|
||||
backend_t *backend,
|
||||
event_t *event,
|
||||
const xcb_button_release_event_t *xcb_event
|
||||
) {
|
||||
auto release = &event->as.pointer_button_release;
|
||||
auto handled = fill_button_event(xcb_event, release);
|
||||
if (handled) event->type = ZDWM_EVENT_POINTER_BUTTON_RELEASE;
|
||||
|
||||
return handled;
|
||||
}
|
||||
|
||||
static bool handle_motion_notify(
|
||||
backend_t *backend,
|
||||
event_t *event,
|
||||
const xcb_motion_notify_event_t *xcb_event
|
||||
) {
|
||||
event->type = ZDWM_EVENT_POINTER_MOTION;
|
||||
|
||||
auto data = &event->as.pointer_motion;
|
||||
data->window = xcb_event->event;
|
||||
data->root = (point_t){.x = xcb_event->root_x, .y = xcb_event->root_y};
|
||||
data->local = (point_t){.x = xcb_event->event_x, .y = xcb_event->event_y};
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool handle_enter_notify(
|
||||
backend_t *backend,
|
||||
event_t *event,
|
||||
@@ -289,9 +350,13 @@ static bool handle_property_notify(
|
||||
event_t *event,
|
||||
const xcb_property_notify_event_t *xcb_event
|
||||
) {
|
||||
if (tray_handle_property_notify(backend, xcb_event)) return false;
|
||||
|
||||
auto window_id = xcb_event->window;
|
||||
if (xcb_event->atom == backend->atoms.WM_NAME ||
|
||||
xcb_event->atom == backend->atoms._NET_WM_NAME) {
|
||||
if (
|
||||
xcb_event->atom == backend->atoms.WM_NAME ||
|
||||
xcb_event->atom == backend->atoms._NET_WM_NAME
|
||||
) {
|
||||
event->type = ZDWM_EVENT_WINDOW_METADATA_CHANGED;
|
||||
|
||||
auto data = &event->as.window_metadata_change;
|
||||
@@ -329,34 +394,31 @@ static bool handle_property_notify(
|
||||
|
||||
if (xcb_event->atom == XCB_ATOM_WM_HINTS) {
|
||||
xcb_icccm_wm_hints_t hints = {0};
|
||||
auto handled = window_get_wm_hints(backend, window_id, &hints);
|
||||
if (!handled) return false;
|
||||
if (!window_get_wm_hints(backend, window_id, &hints)) return false;
|
||||
|
||||
event->type = ZDWM_EVENT_WINDOW_STATE_REQUEST;
|
||||
event->type = ZDWM_EVENT_WINDOW_HINTS_CHANGED;
|
||||
|
||||
auto data = &event->as.window_state_request;
|
||||
auto data = &event->as.hints;
|
||||
data->window = window_id;
|
||||
if (hints.flags & XCB_ICCCM_WM_HINT_X_URGENCY) {
|
||||
data->type = ZDWM_WINDOW_STATE_REQUEST_URGENT;
|
||||
data->action = xcb_icccm_wm_hints_get_urgency(&hints)
|
||||
? ZDWM_WINDOW_STATE_ACTION_ADD
|
||||
: ZDWM_WINDOW_STATE_ACTION_REMOVE;
|
||||
data->changed_fields |= ZDWM_HINT_FIELD_URGENT;
|
||||
data->urgent = xcb_icccm_wm_hints_get_urgency(&hints);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (xcb_event->atom == XCB_ATOM_WM_NORMAL_HINTS) {
|
||||
bool fixed_size = false;
|
||||
if (!window_get_fixed_size(backend, window_id, &fixed_size)) return false;
|
||||
xcb_size_hints_t hints = {0};
|
||||
if (!window_get_size_hints(backend, window_id, &hints)) return false;
|
||||
|
||||
event->type = ZDWM_EVENT_WINDOW_STATE_REQUEST;
|
||||
event->type = ZDWM_EVENT_WINDOW_HINTS_CHANGED;
|
||||
|
||||
auto data = &event->as.window_state_request;
|
||||
auto data = &event->as.hints;
|
||||
data->window = window_id;
|
||||
data->type = ZDWM_WINDOW_STATE_REQUEST_FIXED_SIZE;
|
||||
data->action = fixed_size ? ZDWM_WINDOW_STATE_ACTION_ADD
|
||||
: ZDWM_WINDOW_STATE_ACTION_REMOVE;
|
||||
|
||||
data->changed_fields |= ZDWM_HINT_FIELD_SIZE;
|
||||
parse_size_hints(&hints, &data->min_size, &data->max_size);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -368,6 +430,8 @@ static bool handle_client_message(
|
||||
event_t *event,
|
||||
const xcb_client_message_event_t *xcb_event
|
||||
) {
|
||||
if (tray_handle_client_message(backend, xcb_event)) return false;
|
||||
|
||||
auto atoms = &backend->atoms;
|
||||
|
||||
if (xcb_event->type == atoms->_NET_ACTIVE_WINDOW) {
|
||||
@@ -436,7 +500,11 @@ static bool handle_client_message(
|
||||
}
|
||||
|
||||
if (urgent_from_states(atoms, properties, count)) {
|
||||
data->type = ZDWM_WINDOW_STATE_REQUEST_URGENT;
|
||||
event->type = ZDWM_EVENT_WINDOW_HINTS_CHANGED;
|
||||
event->as.hints = (typeof(event->as.hints)){
|
||||
.urgent = true,
|
||||
.changed_fields = ZDWM_HINT_FIELD_URGENT,
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -469,6 +537,9 @@ static bool handle_event(
|
||||
EVENT(XCB_DESTROY_NOTIFY, handle_destroy_notify);
|
||||
EVENT(XCB_CONFIGURE_REQUEST, handle_configure_request);
|
||||
EVENT(XCB_KEY_PRESS, handle_key_press);
|
||||
EVENT(XCB_BUTTON_PRESS, handle_button_press);
|
||||
EVENT(XCB_BUTTON_RELEASE, handle_button_release);
|
||||
EVENT(XCB_MOTION_NOTIFY, handle_motion_notify);
|
||||
EVENT(XCB_ENTER_NOTIFY, handle_enter_notify);
|
||||
EVENT(XCB_PROPERTY_NOTIFY, handle_property_notify);
|
||||
EVENT(XCB_CLIENT_MESSAGE, handle_client_message);
|
||||
@@ -492,7 +563,8 @@ bool backend_poll_event(backend_t *backend, event_t *event) {
|
||||
auto raw_event = xcb_poll_for_event(backend->conn);
|
||||
if (!raw_event) return false;
|
||||
|
||||
return handle_event(backend, raw_event, event);
|
||||
handle_event(backend, raw_event, event);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool backend_next_event(backend_t *backend, event_t *event) {
|
||||
|
||||
@@ -3,27 +3,16 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <xcb/xcb.h>
|
||||
#include <xcb/xcb_cursor.h>
|
||||
#include <xcb/xcb_keysyms.h>
|
||||
#include <xcb/xproto.h>
|
||||
|
||||
#include "base/window_list.h"
|
||||
#include "core/event.h"
|
||||
#include "common/window.h"
|
||||
#include "interface/event.h"
|
||||
|
||||
#define ATOM_LIST(X) \
|
||||
X(COMPOUND_TEXT) \
|
||||
X(UTF8_STRING) \
|
||||
\
|
||||
X(WM_WINDOW_ROLE) \
|
||||
X(WM_NAME) \
|
||||
X(WM_CHANGE_STATE) \
|
||||
#define EWMH_ATOMS(X) \
|
||||
X(_NET_WM_NAME) \
|
||||
\
|
||||
X(WM_PROTOCOLS) \
|
||||
X(WM_TAKE_FOCUS) \
|
||||
X(WM_DELETE_WINDOW) \
|
||||
X(_NET_ACTIVE_WINDOW) \
|
||||
\
|
||||
X(_NET_SUPPORTING_WM_CHECK) \
|
||||
X(_NET_WM_PID) \
|
||||
\
|
||||
X(_NET_CLIENT_LIST) \
|
||||
@@ -56,6 +45,26 @@
|
||||
X(_NET_WM_WINDOW_TYPE_DND) \
|
||||
X(_NET_WM_WINDOW_TYPE_NOTIFICATION)
|
||||
|
||||
#define ATOM_LIST(X) \
|
||||
X(MANAGER) \
|
||||
X(_NET_SYSTEM_TRAY_OPCODE) \
|
||||
X(_XEMBED) \
|
||||
\
|
||||
X(COMPOUND_TEXT) \
|
||||
X(UTF8_STRING) \
|
||||
\
|
||||
X(WM_WINDOW_ROLE) \
|
||||
X(WM_NAME) \
|
||||
X(WM_CHANGE_STATE) \
|
||||
\
|
||||
X(WM_PROTOCOLS) \
|
||||
X(WM_TAKE_FOCUS) \
|
||||
X(WM_DELETE_WINDOW) \
|
||||
\
|
||||
X(_NET_SUPPORTING_WM_CHECK) \
|
||||
X(_NET_SUPPORTED) \
|
||||
EWMH_ATOMS(X)
|
||||
|
||||
typedef struct atoms_t {
|
||||
#define DECLARATION_ATOM(name) xcb_atom_t name;
|
||||
ATOM_LIST(DECLARATION_ATOM);
|
||||
@@ -74,7 +83,16 @@ typedef struct window_configure_list_t {
|
||||
size_t capacity;
|
||||
} window_configure_list_t;
|
||||
|
||||
struct backend_t {
|
||||
typedef enum cursor_t {
|
||||
ZDWM_CURSOR_NORMAL,
|
||||
ZDWM_CURSOR_MOVE,
|
||||
ZDWM_CURSOR_RESIZE,
|
||||
ZDWM_CURSOR_COUNT,
|
||||
} cursor_t;
|
||||
|
||||
typedef struct tray_host_t tray_host_t;
|
||||
|
||||
typedef struct backend_t {
|
||||
xcb_key_symbols_t *key_symbols;
|
||||
xcb_connection_t *conn;
|
||||
xcb_screen_t *screen;
|
||||
@@ -90,11 +108,17 @@ struct backend_t {
|
||||
xcb_window_t focus_window;
|
||||
bool update_focus;
|
||||
|
||||
xcb_cursor_t cursors[ZDWM_CURSOR_COUNT];
|
||||
xcb_cursor_context_t *cursor_context;
|
||||
|
||||
window_configure_list_t config_list;
|
||||
window_list_t unmap;
|
||||
window_list_t map;
|
||||
window_list_t kill;
|
||||
};
|
||||
window_list_t configure;
|
||||
|
||||
tray_host_t *tray;
|
||||
} backend_t;
|
||||
|
||||
bool populate_window_event(
|
||||
struct backend_t *backend,
|
||||
|
||||
606
src/backend/x11/tray.c
Normal file
606
src/backend/x11/tray.c
Normal file
@@ -0,0 +1,606 @@
|
||||
#include "backend/x11/tray.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <xcb/xcb.h>
|
||||
#include <xcb/xcb_aux.h>
|
||||
#include <xcb/xproto.h>
|
||||
#include <zdwm/types.h>
|
||||
|
||||
#include "backend/x11/window.h"
|
||||
#include "base/array.h"
|
||||
#include "base/log.h"
|
||||
#include "base/macros.h"
|
||||
#include "base/memory.h"
|
||||
#include "interface/tray.h"
|
||||
#include "interface/types.h"
|
||||
#include "internal.h"
|
||||
|
||||
typedef enum tray_opcode_t {
|
||||
SYSTEM_TRAY_REQUEST_DOCK = 0,
|
||||
SYSTEM_TRAY_BEGIN_MESSAGE = 1,
|
||||
SYSTEM_TRAY_CANCEL_MESSAGE = 2,
|
||||
} tray_opcode_t;
|
||||
|
||||
typedef enum xembed_message_t {
|
||||
XEMBED_EMBEDDED_NOTIFY = 0,
|
||||
} xembed_message_t;
|
||||
|
||||
static constexpr auto XEMBED_VERSION = 0;
|
||||
|
||||
typedef struct tray_icon_t {
|
||||
xcb_window_t window;
|
||||
int32_t natural_width;
|
||||
int32_t natural_height;
|
||||
} tray_icon_t;
|
||||
|
||||
typedef struct tray_host_t {
|
||||
bool enabled;
|
||||
bool initialized;
|
||||
xcb_atom_t selection_atom;
|
||||
xcb_window_t host_window;
|
||||
xcb_window_t container;
|
||||
int32_t icon_size;
|
||||
|
||||
tray_icon_t *icons;
|
||||
size_t icon_count;
|
||||
size_t icon_capacity;
|
||||
|
||||
tray_icons_change_cb_t icon_change_cb;
|
||||
void *cb_user_data;
|
||||
} tray_host_t;
|
||||
|
||||
static tray_host_t *get_tray_host(void *handle) {
|
||||
backend_t *backend = handle;
|
||||
if (!backend) return nullptr;
|
||||
|
||||
auto tray = backend->tray;
|
||||
if (!tray || !tray->enabled) return nullptr;
|
||||
|
||||
return tray;
|
||||
}
|
||||
|
||||
static size_t tray_find_icon(tray_host_t *tray, xcb_window_t window) {
|
||||
for (size_t i = 0; i < tray->icon_count; ++i) {
|
||||
if (tray->icons[i].window == window) return i;
|
||||
}
|
||||
|
||||
return SIZE_MAX;
|
||||
}
|
||||
|
||||
static tray_icon_t *tray_get_icon(tray_host_t *tray, xcb_window_t window) {
|
||||
for (size_t i = 0; i < tray->icon_count; ++i) {
|
||||
auto icon = &tray->icons[i];
|
||||
if (icon->window == window) return icon;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static inline void tray_dispatch_callback(tray_host_t *tray) {
|
||||
assert(tray);
|
||||
if (tray->icon_change_cb) tray->icon_change_cb(tray->cb_user_data);
|
||||
}
|
||||
|
||||
static void tray_release_icon_window(backend_t *backend, tray_icon_t *icon) {
|
||||
if (!icon || icon->window == XCB_WINDOW_NONE) return;
|
||||
|
||||
auto tray = get_tray_host(backend);
|
||||
if (!tray || !tray->initialized) return;
|
||||
|
||||
auto conn = backend->conn;
|
||||
auto root = backend->screen->root;
|
||||
auto window = icon->window;
|
||||
auto mask = XCB_CW_EVENT_MASK;
|
||||
xcb_params_cw_t params = {.event_mask = XCB_EVENT_MASK_NO_EVENT};
|
||||
xcb_change_save_set(conn, XCB_SET_MODE_DELETE, window);
|
||||
xcb_aux_change_window_attributes(conn, window, mask, ¶ms);
|
||||
xcb_reparent_window(conn, window, root, 0, 0);
|
||||
}
|
||||
|
||||
static void tray_remove_icon_by_index(
|
||||
backend_t *backend,
|
||||
size_t index,
|
||||
bool release_window
|
||||
) {
|
||||
auto tray = backend->tray;
|
||||
if (index >= tray->icon_count) return;
|
||||
|
||||
auto icon = &tray->icons[index];
|
||||
if (release_window) tray_release_icon_window(backend, icon);
|
||||
|
||||
array_erase(tray->icons, tray->icon_count, index);
|
||||
tray_dispatch_callback(tray);
|
||||
}
|
||||
|
||||
static zdwm_size_t tray_get_icon_size(tray_host_t *tray, size_t index) {
|
||||
auto target_size = MAX(tray->icon_size, (int32_t)1);
|
||||
auto natural_width = tray->icons[index].natural_width;
|
||||
auto natural_height = tray->icons[index].natural_height;
|
||||
|
||||
if (natural_width == 0) natural_width = target_size;
|
||||
if (natural_height == 0) natural_height = target_size;
|
||||
|
||||
double width_scale = (double)target_size / (double)natural_width;
|
||||
double height_scale = (double)target_size / (double)natural_height;
|
||||
auto scale = MIN(width_scale, height_scale);
|
||||
zdwm_size_t size = {
|
||||
.width = (int32_t)(natural_width * scale),
|
||||
.height = (int32_t)(natural_height * scale),
|
||||
};
|
||||
return size;
|
||||
}
|
||||
|
||||
static void tray_layout_icons(backend_t *backend, int32_t start_x) {
|
||||
auto tray = get_tray_host(backend);
|
||||
if (!tray || !tray->initialized) return;
|
||||
if (tray->container == XCB_WINDOW_NONE) return;
|
||||
|
||||
auto conn = backend->conn;
|
||||
if (tray->icon_count == 0) {
|
||||
xcb_unmap_window(conn, tray->container);
|
||||
return;
|
||||
}
|
||||
|
||||
auto height = tray->icon_size;
|
||||
auto width = height * (int32_t)tray->icon_count;
|
||||
xcb_configure_window_value_list_t value_list = {
|
||||
.x = start_x,
|
||||
.y = 0,
|
||||
.width = (uint32_t)width,
|
||||
.height = (uint32_t)height,
|
||||
};
|
||||
uint16_t value_mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y |
|
||||
XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT;
|
||||
xcb_configure_window_aux(conn, tray->container, value_mask, &value_list);
|
||||
xcb_map_window(conn, tray->container);
|
||||
|
||||
for (size_t i = 0; i < tray->icon_count; ++i) {
|
||||
auto window = tray->icons[i].window;
|
||||
auto icon_size = tray_get_icon_size(tray, i);
|
||||
value_mask |= XCB_CONFIG_WINDOW_BORDER_WIDTH;
|
||||
auto offset_x = height * (int32_t)i;
|
||||
value_list = (xcb_configure_window_value_list_t){
|
||||
.x = offset_x + (icon_size.width - height) / 2,
|
||||
.y = (icon_size.height - height) / 2,
|
||||
.width = icon_size.width,
|
||||
.height = icon_size.height,
|
||||
.border_width = 0,
|
||||
};
|
||||
xcb_configure_window_aux(conn, window, value_mask, &value_list);
|
||||
xcb_map_window(conn, window);
|
||||
}
|
||||
}
|
||||
|
||||
static window_id_t tray_host_window(void *handle) {
|
||||
auto tray = get_tray_host(handle);
|
||||
if (!tray) return ZDWM_WINDOW_ID_INVALID;
|
||||
|
||||
return tray->host_window;
|
||||
}
|
||||
|
||||
static size_t tray_icon_count(void *handle) {
|
||||
auto tray = get_tray_host(handle);
|
||||
|
||||
return tray ? tray->icon_count : 0;
|
||||
}
|
||||
|
||||
static int32_t tray_icon_size(void *handle) {
|
||||
auto tray = get_tray_host(handle);
|
||||
|
||||
return tray ? tray->icon_size : 0;
|
||||
}
|
||||
|
||||
static void tray_place(void *handle, int32_t x) {
|
||||
auto tray = get_tray_host(handle);
|
||||
if (!tray) return;
|
||||
|
||||
tray_layout_icons((backend_t *)handle, x);
|
||||
}
|
||||
|
||||
static void
|
||||
tray_set_listeners(void *handle, tray_icons_change_cb_t cb, void *user_data) {
|
||||
auto tray = get_tray_host(handle);
|
||||
if (!tray) return;
|
||||
|
||||
tray->icon_change_cb = cb;
|
||||
tray->cb_user_data = user_data;
|
||||
}
|
||||
|
||||
const tray_api_t tray_api = {
|
||||
.host_window = tray_host_window,
|
||||
.icon_count = tray_icon_count,
|
||||
.icon_size = tray_icon_size,
|
||||
.place = tray_place,
|
||||
.set_listener = tray_set_listeners,
|
||||
};
|
||||
|
||||
static bool tray_intern_selection_atom(tray_host_t *tray, backend_t *backend) {
|
||||
auto conn = backend->conn;
|
||||
auto screenp = backend->screenp;
|
||||
|
||||
char selection_name[64] = {0};
|
||||
auto atom_name_format = "_NET_SYSTEM_TRAY_S%d";
|
||||
snprintf(selection_name, sizeof(selection_name), atom_name_format, screenp);
|
||||
|
||||
uint16_t name_length = strlen(selection_name);
|
||||
auto cookie = xcb_intern_atom(conn, false, name_length, selection_name);
|
||||
auto reply = xcb_intern_atom_reply(conn, cookie, nullptr);
|
||||
if (!reply) {
|
||||
warn("cannot intern tray selection atom: %s", selection_name);
|
||||
return false;
|
||||
}
|
||||
|
||||
tray->selection_atom = reply->atom;
|
||||
p_delete(&reply);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool tray_create_container_window(
|
||||
tray_host_t *tray,
|
||||
backend_t *backend,
|
||||
tray_host_window_t host_window
|
||||
) {
|
||||
auto conn = backend->conn;
|
||||
auto window_id = xcb_generate_id(conn);
|
||||
auto depth = backend->screen->root_depth;
|
||||
auto visual_id = backend->screen->root_visual;
|
||||
|
||||
static xcb_colormap_t colormap = XCB_NONE;
|
||||
if (colormap == XCB_NONE) {
|
||||
colormap = xcb_generate_id(conn);
|
||||
auto root = backend->screen->root;
|
||||
uint8_t alloc = XCB_COLORMAP_ALLOC_NONE;
|
||||
xcb_create_colormap(conn, alloc, colormap, root, visual_id);
|
||||
}
|
||||
|
||||
uint32_t value_mask = XCB_CW_BACK_PIXEL | XCB_CW_BORDER_PIXEL |
|
||||
XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK |
|
||||
XCB_CW_COLORMAP;
|
||||
xcb_create_window_value_list_t value_list = {
|
||||
.background_pixel = host_window.bg_pixel,
|
||||
.border_pixel = 0,
|
||||
.override_redirect = true,
|
||||
.event_mask =
|
||||
XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_STRUCTURE_NOTIFY,
|
||||
.colormap = colormap,
|
||||
};
|
||||
auto cookie = xcb_create_window_aux_checked(
|
||||
conn,
|
||||
depth,
|
||||
window_id,
|
||||
host_window.window,
|
||||
(int16_t)host_window.width,
|
||||
0,
|
||||
1,
|
||||
(uint16_t)MAX(1, host_window.height),
|
||||
0,
|
||||
XCB_WINDOW_CLASS_INPUT_OUTPUT,
|
||||
visual_id,
|
||||
value_mask,
|
||||
&value_list
|
||||
);
|
||||
|
||||
if (xcb_request_check(conn, cookie)) {
|
||||
warn("cannot create system tray container window: 0x%x", window_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
tray->container = window_id;
|
||||
|
||||
window_set_class_instance(conn, window_id);
|
||||
|
||||
#define TRAY_CONTAINER_WINDOW_NAME APP_NAME "_tray"
|
||||
window_set_name_static(conn, window_id, TRAY_CONTAINER_WINDOW_NAME);
|
||||
auto name = TRAY_CONTAINER_WINDOW_NAME;
|
||||
auto name_len = sizeof(TRAY_CONTAINER_WINDOW_NAME) - 1;
|
||||
#undef TRAY_CONTAINER_WINDOW_NAME
|
||||
|
||||
uint8_t mode = XCB_PROP_MODE_REPLACE;
|
||||
auto atoms = &backend->atoms;
|
||||
auto prop = atoms->_NET_WM_NAME;
|
||||
auto type = atoms->UTF8_STRING;
|
||||
xcb_change_property(conn, mode, window_id, prop, type, 8, name_len, name);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool tray_take_selection_owner(tray_host_t *tray, backend_t *backend) {
|
||||
auto conn = backend->conn;
|
||||
auto atom = tray->selection_atom;
|
||||
auto cookie = xcb_get_selection_owner(conn, atom);
|
||||
auto reply = xcb_get_selection_owner_reply(conn, cookie, nullptr);
|
||||
if (reply && reply->owner != XCB_WINDOW_NONE) {
|
||||
warn(
|
||||
"system tray selection is already owned by window: 0x%x",
|
||||
reply->owner
|
||||
);
|
||||
p_delete(&reply);
|
||||
return false;
|
||||
}
|
||||
|
||||
p_delete(&reply);
|
||||
|
||||
auto owner = tray->container;
|
||||
xcb_set_selection_owner(conn, owner, atom, XCB_CURRENT_TIME);
|
||||
|
||||
cookie = xcb_get_selection_owner(conn, atom);
|
||||
reply = xcb_get_selection_owner_reply(conn, cookie, nullptr);
|
||||
bool success = reply && reply->owner == owner;
|
||||
p_delete(&reply);
|
||||
|
||||
if (!success) warn("cannot acquire system tray selection");
|
||||
return success;
|
||||
}
|
||||
|
||||
static void tray_broadcast_manager(tray_host_t *tray, backend_t *backend) {
|
||||
auto conn = backend->conn;
|
||||
auto root = backend->screen->root;
|
||||
|
||||
xcb_client_message_event_t event = {
|
||||
.response_type = XCB_CLIENT_MESSAGE,
|
||||
.window = root,
|
||||
.format = 32,
|
||||
.type = backend->atoms.MANAGER,
|
||||
.data.data32 = {
|
||||
[0] = XCB_CURRENT_TIME,
|
||||
[1] = tray->selection_atom,
|
||||
[2] = tray->container,
|
||||
},
|
||||
};
|
||||
uint32_t event_mask = XCB_EVENT_MASK_STRUCTURE_NOTIFY;
|
||||
xcb_send_event(conn, false, root, event_mask, (char *)&event);
|
||||
}
|
||||
|
||||
void tray_init(backend_t *backend, tray_host_window_t host_window) {
|
||||
auto tray = get_tray_host(backend);
|
||||
if (!tray) {
|
||||
tray = p_new(tray_host_t, 1);
|
||||
backend->tray = tray;
|
||||
}
|
||||
|
||||
if (tray->initialized) return;
|
||||
|
||||
if (!tray_intern_selection_atom(tray, backend)) return;
|
||||
|
||||
if (!tray_create_container_window(tray, backend, host_window)) return;
|
||||
if (!tray_take_selection_owner(tray, backend)) {
|
||||
tray_cleanup(backend);
|
||||
return;
|
||||
}
|
||||
|
||||
tray->enabled = true;
|
||||
tray->initialized = true;
|
||||
tray->host_window = host_window.window;
|
||||
tray->icon_size = host_window.height;
|
||||
|
||||
tray_broadcast_manager(tray, backend);
|
||||
tray_layout_icons(backend, host_window.width);
|
||||
xcb_flush(backend->conn);
|
||||
}
|
||||
|
||||
void tray_cleanup(backend_t *backend) {
|
||||
auto tray = backend->tray;
|
||||
if (!tray) return;
|
||||
|
||||
for (size_t i = 0; i < tray->icon_count; ++i) {
|
||||
tray_release_icon_window(backend, &tray->icons[i]);
|
||||
}
|
||||
p_delete(&tray->icons);
|
||||
tray->icon_count = 0;
|
||||
tray->icon_capacity = 0;
|
||||
|
||||
auto conn = backend->conn;
|
||||
|
||||
auto atom = tray->selection_atom;
|
||||
if (atom != XCB_ATOM_NONE) {
|
||||
xcb_set_selection_owner(conn, XCB_WINDOW_NONE, atom, XCB_CURRENT_TIME);
|
||||
}
|
||||
|
||||
if (tray->container != XCB_WINDOW_NONE) {
|
||||
xcb_destroy_window(conn, tray->container);
|
||||
}
|
||||
|
||||
p_clear(backend->tray, 1);
|
||||
p_delete(&backend->tray);
|
||||
}
|
||||
|
||||
static void
|
||||
tray_select_icon_events(xcb_connection_t *conn, xcb_window_t window) {
|
||||
xcb_params_cw_t params = {
|
||||
.event_mask =
|
||||
XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_PROPERTY_CHANGE,
|
||||
};
|
||||
uint32_t mask = XCB_CW_EVENT_MASK;
|
||||
xcb_aux_change_window_attributes(conn, window, mask, ¶ms);
|
||||
xcb_clear_area(conn, false, window, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
/* reference:
|
||||
* https://specifications.freedesktop.org/xembed/latest-single/#id-1.7.4 */
|
||||
typedef struct xembed_message_data_t {
|
||||
uint32_t message; /* message opcode */
|
||||
uint32_t detail; /* message detail */
|
||||
uint32_t data1; /* message data 1 */
|
||||
uint32_t data2; /* message data 2 */
|
||||
} xembed_message_data_t;
|
||||
|
||||
static void tray_send_xembed_message(
|
||||
backend_t *backend,
|
||||
xcb_window_t window,
|
||||
xembed_message_data_t message
|
||||
) {
|
||||
auto conn = backend->conn;
|
||||
auto message_type = backend->atoms._XEMBED;
|
||||
|
||||
xcb_client_message_event_t event = {
|
||||
.response_type = XCB_CLIENT_MESSAGE,
|
||||
.format = 32,
|
||||
.window = window,
|
||||
.type = message_type,
|
||||
.data.data32 = {
|
||||
[0] = XCB_CURRENT_TIME,
|
||||
[1] = message.message,
|
||||
[2] = message.detail,
|
||||
[3] = message.data1,
|
||||
[4] = message.data2,
|
||||
},
|
||||
};
|
||||
uint32_t event_mask = XCB_EVENT_MASK_NO_EVENT;
|
||||
xcb_send_event(conn, false, window, event_mask, (char *)&event);
|
||||
}
|
||||
|
||||
static void tray_add_icon(backend_t *backend, xcb_window_t window) {
|
||||
if (window == XCB_WINDOW_NONE) return;
|
||||
|
||||
auto tray = get_tray_host(backend);
|
||||
if (!tray || !tray->initialized || tray_get_icon(tray, window)) return;
|
||||
|
||||
rect_t geometry = {0};
|
||||
if (!window_get_geometry(backend, window, &geometry)) return;
|
||||
|
||||
auto icon = array_push(tray->icons, tray->icon_count, tray->icon_capacity);
|
||||
*icon = (tray_icon_t){
|
||||
.window = window,
|
||||
.natural_width = geometry.width,
|
||||
.natural_height = geometry.height,
|
||||
};
|
||||
|
||||
auto conn = backend->conn;
|
||||
auto container = tray->container;
|
||||
xembed_message_data_t message = {
|
||||
.message = XEMBED_EMBEDDED_NOTIFY,
|
||||
.detail = 0,
|
||||
.data1 = container,
|
||||
.data2 = XEMBED_VERSION,
|
||||
};
|
||||
xcb_change_save_set(conn, XCB_SET_MODE_INSERT, window);
|
||||
xcb_reparent_window(conn, window, tray->container, 0, 0);
|
||||
tray_select_icon_events(conn, window);
|
||||
tray_send_xembed_message(backend, window, message);
|
||||
xcb_map_window(conn, window);
|
||||
tray_dispatch_callback(tray);
|
||||
}
|
||||
|
||||
bool tray_handle_client_message(
|
||||
backend_t *backend,
|
||||
const xcb_client_message_event_t *ev
|
||||
) {
|
||||
auto tray = get_tray_host(backend);
|
||||
if (!tray || !tray->enabled || !tray->initialized) return false;
|
||||
if (ev->window != tray->container && ev->window != backend->screen->root) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto opcode = ev->data.data32[1];
|
||||
switch (opcode) {
|
||||
case SYSTEM_TRAY_REQUEST_DOCK:
|
||||
auto window = ev->data.data32[2];
|
||||
tray_add_icon(backend, window);
|
||||
break;
|
||||
case SYSTEM_TRAY_BEGIN_MESSAGE:
|
||||
case SYSTEM_TRAY_CANCEL_MESSAGE:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
xcb_flush(backend->conn);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tray_handle_configure_request(
|
||||
backend_t *backend,
|
||||
const xcb_configure_request_event_t *ev
|
||||
) {
|
||||
auto tray = get_tray_host(backend);
|
||||
if (!tray || !tray->initialized) return false;
|
||||
|
||||
if (ev->window == tray->container) {
|
||||
tray_dispatch_callback(tray);
|
||||
return true;
|
||||
}
|
||||
|
||||
auto icon = tray_get_icon(tray, ev->window);
|
||||
if (!icon) return false;
|
||||
|
||||
if ((ev->value_mask & XCB_CONFIG_WINDOW_WIDTH) && ev->width > 0) {
|
||||
icon->natural_width = (int32_t)ev->width;
|
||||
}
|
||||
if ((ev->value_mask & XCB_CONFIG_WINDOW_HEIGHT) && ev->height > 0) {
|
||||
icon->natural_height = (int32_t)ev->height;
|
||||
}
|
||||
|
||||
tray_dispatch_callback(tray);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tray_handle_map_request(
|
||||
backend_t *backend,
|
||||
const xcb_map_request_event_t *ev
|
||||
) {
|
||||
auto tray = get_tray_host(backend);
|
||||
if (!tray || !tray->initialized) return false;
|
||||
|
||||
auto conn = backend->conn;
|
||||
if (ev->window == tray->container) {
|
||||
tray_dispatch_callback(tray);
|
||||
return true;
|
||||
}
|
||||
|
||||
auto icon = tray_get_icon(tray, ev->window);
|
||||
if (!icon && ev->parent != tray->container) return false;
|
||||
|
||||
xcb_map_window(conn, ev->window);
|
||||
xcb_flush(conn);
|
||||
|
||||
tray_dispatch_callback(tray);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tray_handle_property_notify(
|
||||
backend_t *backend,
|
||||
const xcb_property_notify_event_t *ev
|
||||
) {
|
||||
auto tray = get_tray_host(backend);
|
||||
if (!tray || !tray->initialized) return false;
|
||||
|
||||
if (ev->window == XCB_WINDOW_NONE) return false;
|
||||
if (ev->window == tray->container) return true;
|
||||
return tray_get_icon(tray, ev->window) != nullptr;
|
||||
}
|
||||
|
||||
bool tray_handle_unmap_notify(
|
||||
backend_t *backend,
|
||||
const xcb_unmap_notify_event_t *ev
|
||||
) {
|
||||
auto tray = get_tray_host(backend);
|
||||
if (!tray || !tray->initialized) return false;
|
||||
|
||||
auto index = tray_find_icon(tray, ev->window);
|
||||
if (index == SIZE_MAX) return ev->window == tray->container;
|
||||
|
||||
tray_remove_icon_by_index(backend, index, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tray_handle_destroy_notify(
|
||||
backend_t *backend,
|
||||
const xcb_destroy_notify_event_t *ev
|
||||
) {
|
||||
auto tray = get_tray_host(backend);
|
||||
if (!tray || !tray->initialized) return false;
|
||||
|
||||
if (ev->window == tray->container) {
|
||||
tray_cleanup(backend);
|
||||
return true;
|
||||
}
|
||||
|
||||
auto index = tray_find_icon(tray, ev->window);
|
||||
if (index == SIZE_MAX) return false;
|
||||
|
||||
tray_remove_icon_by_index(backend, index, false);
|
||||
return true;
|
||||
}
|
||||
45
src/backend/x11/tray.h
Normal file
45
src/backend/x11/tray.h
Normal file
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <xcb/xproto.h>
|
||||
|
||||
#include "interface/tray.h"
|
||||
|
||||
extern const tray_api_t tray_api;
|
||||
|
||||
typedef struct backend_t backend_t;
|
||||
|
||||
typedef struct tray_host_window_t {
|
||||
xcb_window_t window;
|
||||
int32_t width;
|
||||
int32_t height;
|
||||
uint32_t bg_pixel;
|
||||
} tray_host_window_t;
|
||||
|
||||
void tray_init(backend_t *backend, tray_host_window_t host_window);
|
||||
void tray_cleanup(backend_t *backend);
|
||||
|
||||
bool tray_handle_client_message(
|
||||
backend_t *backend,
|
||||
const xcb_client_message_event_t *ev
|
||||
);
|
||||
bool tray_handle_configure_request(
|
||||
backend_t *backend,
|
||||
const xcb_configure_request_event_t *ev
|
||||
);
|
||||
bool tray_handle_map_request(
|
||||
backend_t *backend,
|
||||
const xcb_map_request_event_t *ev
|
||||
);
|
||||
bool tray_handle_property_notify(
|
||||
backend_t *backend,
|
||||
const xcb_property_notify_event_t *ev
|
||||
);
|
||||
bool tray_handle_unmap_notify(
|
||||
backend_t *backend,
|
||||
const xcb_unmap_notify_event_t *ev
|
||||
);
|
||||
bool tray_handle_destroy_notify(
|
||||
backend_t *backend,
|
||||
const xcb_destroy_notify_event_t *ev
|
||||
);
|
||||
@@ -3,14 +3,15 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <xcb/xcb.h>
|
||||
#include <xcb/xcb_aux.h>
|
||||
#include <xcb/xcb_icccm.h>
|
||||
#include <xcb/xcb_keysyms.h>
|
||||
#include <xcb/xproto.h>
|
||||
|
||||
#include "backend/x11/common.h"
|
||||
#include "backend/x11/cursor.h"
|
||||
#include "base/macros.h"
|
||||
#include "base/memory.h"
|
||||
#include "core/backend.h"
|
||||
#include "core/types.h"
|
||||
#include "internal.h"
|
||||
|
||||
static char *window_get_text_property(
|
||||
@@ -35,10 +36,12 @@ static char *window_get_text_property(
|
||||
char *value = (char *)xcb_get_property_value(reply);
|
||||
|
||||
char *text = nullptr;
|
||||
if (length && reply->format == 8 &&
|
||||
if (
|
||||
length && reply->format == 8 &&
|
||||
(reply->type == XCB_ATOM_STRING ||
|
||||
reply->type == backend->atoms.UTF8_STRING ||
|
||||
reply->type == backend->atoms.COMPOUND_TEXT)) {
|
||||
reply->type == backend->atoms.COMPOUND_TEXT)
|
||||
) {
|
||||
text = p_strndup(value, (size_t)length);
|
||||
}
|
||||
|
||||
@@ -157,22 +160,18 @@ bool window_get_types(
|
||||
return true;
|
||||
}
|
||||
|
||||
bool window_get_fixed_size(backend_t *backend, xcb_window_t window, bool *out) {
|
||||
xcb_size_hints_t hints = {0};
|
||||
bool window_get_size_hints(
|
||||
backend_t *backend,
|
||||
xcb_window_t window,
|
||||
xcb_size_hints_t *out
|
||||
) {
|
||||
xcb_connection_t *conn = backend->conn;
|
||||
xcb_get_property_cookie_t cookie =
|
||||
xcb_icccm_get_wm_normal_hints_unchecked(conn, window);
|
||||
if (!xcb_icccm_get_wm_normal_hints_reply(conn, cookie, &hints, nullptr)) {
|
||||
if (!xcb_icccm_get_wm_normal_hints_reply(conn, cookie, out, nullptr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((hints.flags & XCB_ICCCM_SIZE_HINT_P_MIN_SIZE) &&
|
||||
(hints.flags & XCB_ICCCM_SIZE_HINT_P_MAX_SIZE)) {
|
||||
*out = hints.min_width == hints.max_width &&
|
||||
hints.min_height == hints.max_height;
|
||||
} else {
|
||||
*out = false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -451,3 +450,36 @@ visual_t *window_get_visual(backend_t *backend, bool prefer_alpha) {
|
||||
|
||||
return visual;
|
||||
}
|
||||
|
||||
void window_change_cursor(
|
||||
backend_t *backend,
|
||||
xcb_window_t window,
|
||||
cursor_t cursor
|
||||
) {
|
||||
auto conn = backend->conn;
|
||||
xcb_params_cw_t params = {.cursor = cursor_get_xcb_cursor(backend, cursor)};
|
||||
xcb_aux_change_window_attributes(conn, window, XCB_CW_CURSOR, ¶ms);
|
||||
}
|
||||
|
||||
void window_grab_button(
|
||||
backend_t *backend,
|
||||
xcb_window_t window,
|
||||
button_t button,
|
||||
modifier_mask_t modifiers
|
||||
) {
|
||||
auto xcb_button = button_zdwm_to_xcb(button);
|
||||
if (xcb_button == XCB_BUTTON_INDEX_ANY) return;
|
||||
|
||||
xcb_grab_button(
|
||||
backend->conn,
|
||||
false, /* 事件仅发送给抓取者,不发送给其他窗口 */
|
||||
window,
|
||||
XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE,
|
||||
XCB_GRAB_MODE_ASYNC,
|
||||
XCB_GRAB_MODE_SYNC,
|
||||
XCB_WINDOW_NONE,
|
||||
XCB_CURSOR_NONE,
|
||||
xcb_button,
|
||||
modifiers_zdwm_to_xcb(modifiers)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#include <xcb/xproto.h>
|
||||
|
||||
#include "base/app.h"
|
||||
#include "core/backend.h"
|
||||
#include "interface/backend.h"
|
||||
#include "internal.h"
|
||||
|
||||
typedef struct atoms_t atoms_t;
|
||||
@@ -54,7 +54,11 @@ bool window_get_types(
|
||||
window_type_t **types,
|
||||
size_t *count
|
||||
);
|
||||
bool window_get_fixed_size(backend_t *backend, xcb_window_t window, bool *out);
|
||||
bool window_get_size_hints(
|
||||
backend_t *backend,
|
||||
xcb_window_t window,
|
||||
xcb_size_hints_t *out
|
||||
);
|
||||
bool window_get_geometry(backend_t *backend, xcb_window_t window, rect_t *out);
|
||||
bool window_get_atom_array(
|
||||
backend_t *backend,
|
||||
@@ -118,3 +122,16 @@ typedef struct visual_t {
|
||||
* @returns 返回的信息使用后记得使用 free 释放
|
||||
*/
|
||||
visual_t *window_get_visual(backend_t *backend, bool prefer_alpha);
|
||||
|
||||
void window_change_cursor(
|
||||
backend_t *backend,
|
||||
xcb_window_t window,
|
||||
cursor_t cursor
|
||||
);
|
||||
|
||||
void window_grab_button(
|
||||
backend_t *backend,
|
||||
xcb_window_t window,
|
||||
button_t button,
|
||||
modifier_mask_t modifiers
|
||||
);
|
||||
|
||||
215
src/bar/bar.c
215
src/bar/bar.c
@@ -5,19 +5,22 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <zdwm/action.h>
|
||||
#include <zdwm/bar.h>
|
||||
#include <zdwm/types.h>
|
||||
|
||||
#include "bar/binding.h"
|
||||
#include "bar/cell.h"
|
||||
#include "bar/text.h"
|
||||
#include "bar/tray.h"
|
||||
#include "bar/types.h"
|
||||
#include "bar/windows.h"
|
||||
#include "bar/workspaces.h"
|
||||
#include "base/array.h"
|
||||
#include "base/color.h"
|
||||
#include "base/macros.h"
|
||||
#include "base/memory.h"
|
||||
#include "base/time.h"
|
||||
#include "core/listeners.h"
|
||||
|
||||
static zdwm_bar_item_t *bar_add_item(
|
||||
zdwm_output_id_t output_id,
|
||||
@@ -103,9 +106,39 @@ static void bar_output_add_binding(
|
||||
bar_binding_add_listeners(listeners, item->state);
|
||||
}
|
||||
|
||||
static void bar_output_add_windows(
|
||||
bar_output_t *bar_output,
|
||||
zdwm_bar_config_t *config,
|
||||
listeners_t *listeners
|
||||
) {
|
||||
auto item = &bar_output->center;
|
||||
|
||||
bar_windows_config_t window_config = {
|
||||
.cell_padding = VALUE(config->window_padding_x, config->padding_x),
|
||||
.bg = VALUE(config->window_bg, config->bg),
|
||||
.fg = VALUE(config->window_fg, config->fg),
|
||||
.focused_bg = VALUE(config->window_focused_bg, config->bg),
|
||||
.focused_fg = VALUE(config->window_focused_fg, config->fg),
|
||||
};
|
||||
item->cell_padding = window_config.cell_padding;
|
||||
|
||||
item->api = bar_windows;
|
||||
|
||||
auto create_state = item->api.create_state;
|
||||
auto output_id = bar_output->output_id;
|
||||
if (create_state) item->state = create_state(output_id, &window_config);
|
||||
|
||||
bar_windows_add_listeners(listeners, item->state);
|
||||
}
|
||||
|
||||
static inline void bar_output_add_tray(bar_output_t *bar_output, tray_t *tray) {
|
||||
bar_add_item(bar_output->output_id, &bar_output->right, bar_tray, tray);
|
||||
}
|
||||
|
||||
void bar_init(bar_t *bar, listeners_t *listeners) {
|
||||
auto c = &bar->config;
|
||||
bar->ctx = text_context_create(c->font_family, c->font_size, c->dpi);
|
||||
auto tray = &bar->tray;
|
||||
|
||||
for (size_t i = 0; i < bar->count; ++i) {
|
||||
auto bar_output = &bar->bars[i];
|
||||
@@ -113,6 +146,11 @@ void bar_init(bar_t *bar, listeners_t *listeners) {
|
||||
bar_output->height = bar->config.height;
|
||||
bar_output_add_workspace(bar_output, &bar->config, listeners);
|
||||
bar_output_add_binding(bar_output, &bar->config, listeners);
|
||||
bar_output_add_windows(bar_output, &bar->config, listeners);
|
||||
|
||||
if (tray->api.host_window(tray->handle) == bar_output->window_id) {
|
||||
bar_output_add_tray(bar_output, &bar->tray);
|
||||
}
|
||||
}
|
||||
|
||||
bar->timerfd = time_create_monotonic_timerfd_by_fps(bar->config.fps);
|
||||
@@ -184,20 +222,26 @@ static void bar_output_layout(bar_output_t *bar_output, text_context_t *ctx) {
|
||||
item->region.start = start;
|
||||
for (size_t j = 0; j < item->count; ++j) {
|
||||
auto cell = &item->cells[j];
|
||||
auto fixed_width = cell->fixed_width;
|
||||
|
||||
bar_x_region_t region = {.start = start, .end = start};
|
||||
if (fixed_width == 0) {
|
||||
int32_t width = 0;
|
||||
text_context_get_text_size(ctx, cell->text, &width, nullptr);
|
||||
|
||||
bar_x_region_t region = {
|
||||
.start = start,
|
||||
.end = start + width + item->cell_padding * 2,
|
||||
};
|
||||
region.end += width + 2 * item->cell_padding;
|
||||
} else if (fixed_width > 0) {
|
||||
region.end += fixed_width;
|
||||
}
|
||||
bar_cell_set_region(item, j, region);
|
||||
|
||||
start = region.end;
|
||||
}
|
||||
item->region.end = start;
|
||||
}
|
||||
if (left->count) {
|
||||
left->region.start = left->items[0].region.start;
|
||||
left->region.end = left->items[left->count - 1].region.end;
|
||||
}
|
||||
|
||||
auto right = &bar_output->right;
|
||||
for (size_t i = right->count; i > 0; --i) {
|
||||
@@ -208,31 +252,56 @@ static void bar_output_layout(bar_output_t *bar_output, text_context_t *ctx) {
|
||||
for (size_t j = item->count; j > 0; --j) {
|
||||
auto cell_index = j - 1;
|
||||
auto cell = &item->cells[cell_index];
|
||||
auto fixed_width = cell->fixed_width;
|
||||
|
||||
bar_x_region_t region = {.start = end, .end = end};
|
||||
if (fixed_width == 0) {
|
||||
int32_t width = 0;
|
||||
text_context_get_text_size(ctx, cell->text, &width, nullptr);
|
||||
|
||||
bar_x_region_t region = {
|
||||
.start = end - width - item->cell_padding * 2,
|
||||
.end = end,
|
||||
};
|
||||
region.start -= width + item->cell_padding * 2;
|
||||
} else if (fixed_width > 0) {
|
||||
region.start -= fixed_width;
|
||||
}
|
||||
bar_cell_set_region(item, cell_index, region);
|
||||
|
||||
end = region.start;
|
||||
}
|
||||
item->region.start = end;
|
||||
}
|
||||
if (right->count) {
|
||||
right->region.start = right->items[0].region.start;
|
||||
right->region.end = right->items[right->count - 1].region.end;
|
||||
}
|
||||
|
||||
auto center = &bar_output->center;
|
||||
if (center->count == 0) return;
|
||||
center->region = (bar_x_region_t){.start = start, .end = end};
|
||||
|
||||
auto width = (end - start) / (int32_t)center->count;
|
||||
auto center_width = end - start;
|
||||
auto center_count = center->count;
|
||||
for (size_t i = 0; i < center->count; ++i) {
|
||||
bar_x_region_t region = {
|
||||
.start = start,
|
||||
.end = start + width + center->cell_padding * 2,
|
||||
};
|
||||
auto cell = ¢er->cells[i];
|
||||
auto fixed_width = cell->fixed_width;
|
||||
if (fixed_width > 0) {
|
||||
center_width -= fixed_width;
|
||||
} else if (fixed_width < 0) {
|
||||
/* 隐藏的 cell 不占任何空间,平分宽度时不参与 */
|
||||
--center_count;
|
||||
}
|
||||
}
|
||||
|
||||
auto width = center_width / (int32_t)center_count;
|
||||
for (size_t i = 0; i < center->count; ++i) {
|
||||
auto cell = ¢er->cells[i];
|
||||
auto fixed_width = cell->fixed_width;
|
||||
|
||||
bar_x_region_t region = {.start = start, .end = start};
|
||||
if (fixed_width == 0) {
|
||||
region.end += width + center->cell_padding * 2;
|
||||
} else if (fixed_width > 0) {
|
||||
region.end += fixed_width;
|
||||
}
|
||||
start = region.end;
|
||||
bar_cell_set_region(center, i, region);
|
||||
}
|
||||
}
|
||||
@@ -281,7 +350,7 @@ static void bar_item_draw(
|
||||
.width = cell_area.width - item->cell_padding * 2,
|
||||
.height = height,
|
||||
};
|
||||
if (text_area.width > 0) {
|
||||
if (text_area.width > 0 && cell->text && cell->text[0] != '\0') {
|
||||
draw_text(cr, ctx, cell->text, &cell->fg, text_area);
|
||||
}
|
||||
|
||||
@@ -317,8 +386,25 @@ static inline bool bar_output_is_dirty(bar_output_t *bar_output) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static void bar_output_draw(bar_output_t *bar_output, text_context_t *ctx) {
|
||||
static inline void clean_cairo_context(cairo_t *cr) {
|
||||
cairo_save(cr);
|
||||
cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
|
||||
cairo_paint(cr);
|
||||
cairo_restore(cr);
|
||||
}
|
||||
|
||||
static void
|
||||
bar_output_draw(bar_output_t *bar_output, text_context_t *ctx, color_t *bg) {
|
||||
auto cr = bar_output->cr;
|
||||
clean_cairo_context(cr);
|
||||
|
||||
zdwm_rect_t output_area = {
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
.width = bar_output->width,
|
||||
.height = bar_output->height,
|
||||
};
|
||||
draw_background(cr, bg, output_area);
|
||||
|
||||
auto left = &bar_output->left;
|
||||
for (size_t i = 0; i < left->count; ++i) {
|
||||
@@ -332,6 +418,42 @@ static void bar_output_draw(bar_output_t *bar_output, text_context_t *ctx) {
|
||||
bar_item_draw(cr, &bar_output->center, ctx, bar_output->height);
|
||||
}
|
||||
|
||||
static inline void bar_item_run_hook(
|
||||
zdwm_bar_item_t *item,
|
||||
void (*hook)(const zdwm_bar_item_hook_params_t *params)
|
||||
) {
|
||||
if (!hook) return;
|
||||
|
||||
zdwm_bar_item_hook_params_t params = {
|
||||
.item = item,
|
||||
.cell_api = &bar_cell_api,
|
||||
.region = item->region,
|
||||
.state = item->state
|
||||
};
|
||||
hook(¶ms);
|
||||
}
|
||||
|
||||
static void visitor_after_layout(zdwm_bar_item_t *item) {
|
||||
bar_item_run_hook(item, item->api.after_layout);
|
||||
}
|
||||
|
||||
static void visitor_after_draw(zdwm_bar_item_t *item) {
|
||||
bar_item_run_hook(item, item->api.after_draw);
|
||||
}
|
||||
|
||||
typedef void (*bar_item_visitor_t)(zdwm_bar_item_t *item);
|
||||
|
||||
static void
|
||||
bar_output_foreach_item(bar_output_t *bar_output, bar_item_visitor_t visit) {
|
||||
for (size_t i = 0; i < bar_output->left.count; ++i) {
|
||||
visit(&bar_output->left.items[i]);
|
||||
}
|
||||
visit(&bar_output->center);
|
||||
for (size_t i = 0; i < bar_output->right.count; ++i) {
|
||||
visit(&bar_output->right.items[i]);
|
||||
}
|
||||
}
|
||||
|
||||
bool bar_draw(bar_t *bar) {
|
||||
auto ctx = bar->ctx;
|
||||
|
||||
@@ -342,9 +464,64 @@ bool bar_draw(bar_t *bar) {
|
||||
if (!bar_output_is_dirty(bar_output)) continue;
|
||||
|
||||
bar_output_layout(bar_output, ctx);
|
||||
bar_output_draw(bar_output, ctx);
|
||||
bar_output_foreach_item(bar_output, visitor_after_layout);
|
||||
bar_output_draw(bar_output, ctx, &bar->palette.bg);
|
||||
bar_output_foreach_item(bar_output, visitor_after_draw);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
static inline bool bar_item_click(
|
||||
zdwm_bar_item_t *item,
|
||||
bar_click_info_t info,
|
||||
zdwm_action_t *action
|
||||
) {
|
||||
auto x = info.x;
|
||||
if (!(item->region.start <= x && item->region.end >= x)) return false;
|
||||
|
||||
if (!item->api.on_click) return true;
|
||||
|
||||
for (size_t i = 0; i < item->count; ++i) {
|
||||
auto cell = &item->cells[i];
|
||||
if (cell->region.start <= x && cell->region.end >= x) {
|
||||
zdwm_bar_click_params_t params = {
|
||||
.item = item,
|
||||
.cell_index = i,
|
||||
.x = x,
|
||||
.modifiers = info.modifiers,
|
||||
.button = info.button,
|
||||
.state = item->state,
|
||||
};
|
||||
*action = item->api.on_click(¶ms);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
bar_side_click(bar_side_t *side, bar_click_info_t info, zdwm_action_t *action) {
|
||||
if (side->region.start > info.x || side->region.end < info.x) return false;
|
||||
|
||||
for (size_t i = 0; i < side->count; ++i) {
|
||||
if (bar_item_click(&side->items[i], info, action)) return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bar_click(bar_t *bar, bar_click_info_t info, zdwm_action_t *action) {
|
||||
for (size_t i = 0; i < bar->count; ++i) {
|
||||
auto bar_output = &bar->bars[i];
|
||||
if (bar_output->window_id != info.window) continue;
|
||||
|
||||
if (bar_side_click(&bar_output->left, info, action)) return true;
|
||||
if (bar_item_click(&bar_output->center, info, action)) return true;
|
||||
if (bar_side_click(&bar_output->right, info, action)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -3,13 +3,15 @@
|
||||
#include <cairo.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <zdwm/action.h>
|
||||
#include <zdwm/bar.h>
|
||||
#include <zdwm/types.h>
|
||||
|
||||
#include "bar/text.h"
|
||||
#include "bar/types.h"
|
||||
#include "base/color.h"
|
||||
#include "core/listeners.h"
|
||||
#include "common/listeners.h"
|
||||
#include "interface/tray.h"
|
||||
|
||||
typedef struct bar_output_t {
|
||||
cairo_t *cr;
|
||||
@@ -36,9 +38,18 @@ typedef struct bar_t {
|
||||
zdwm_bar_config_t config;
|
||||
bar_palette_t palette;
|
||||
text_context_t *ctx;
|
||||
tray_t tray;
|
||||
} bar_t;
|
||||
|
||||
void bar_init(bar_t *bar, listeners_t *listeners);
|
||||
void bar_cleanup(bar_t *bar);
|
||||
void bar_update(bar_t *bar);
|
||||
bool bar_draw(bar_t *bar);
|
||||
|
||||
typedef struct bar_click_info_t {
|
||||
zdwm_window_id_t window;
|
||||
int32_t x;
|
||||
zdwm_modifier_mask_t modifiers;
|
||||
zdwm_button_t button;
|
||||
} bar_click_info_t;
|
||||
bool bar_click(bar_t *bar, bar_click_info_t info, zdwm_action_t *action);
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#include <zdwm/types.h>
|
||||
|
||||
#include "base/memory.h"
|
||||
#include "core/listeners.h"
|
||||
#include "common/listeners.h"
|
||||
|
||||
typedef struct bar_binding_state_t {
|
||||
zdwm_binding_mode_notify_t mode;
|
||||
@@ -55,12 +55,7 @@ static void bar_binding_update(
|
||||
data->dirty = false;
|
||||
}
|
||||
|
||||
static zdwm_action_t bar_binding_on_click(
|
||||
zdwm_bar_item_t *item,
|
||||
size_t cell_index,
|
||||
int32_t x,
|
||||
void *state
|
||||
) {
|
||||
static zdwm_action_t bar_binding_on_click(zdwm_bar_click_params_t *params) {
|
||||
return (zdwm_action_t){
|
||||
.type = ZDWM_ACTION_BINDING_MODE_CYCLE,
|
||||
.as.binding_mode_cycle = {.delta = 1},
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include <stdint.h>
|
||||
#include <zdwm/bar.h>
|
||||
|
||||
#include "core/listeners.h"
|
||||
#include "common/listeners.h"
|
||||
|
||||
typedef struct bar_binding_config_t {
|
||||
bool show_default;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <zdwm/types.h>
|
||||
@@ -17,7 +18,7 @@ static void bar_cell_set_count(zdwm_bar_item_t *item, size_t count) {
|
||||
|
||||
for (size_t i = 0; i < item->count; ++i) {
|
||||
auto cell = &item->cells[i];
|
||||
p_delete(cell->text);
|
||||
p_delete(&cell->text);
|
||||
p_delete(&cell->fg_text);
|
||||
p_delete(&cell->bg_text);
|
||||
}
|
||||
@@ -51,6 +52,7 @@ bar_cell_set_bg(zdwm_bar_item_t *item, size_t index, const char *color) {
|
||||
|
||||
if (cell->bg_text && strcmp(color, cell->bg_text) == 0) return;
|
||||
|
||||
p_delete(&cell->bg_text);
|
||||
cell->bg_text = p_strdup(color);
|
||||
color_parse(color, &cell->bg);
|
||||
|
||||
@@ -65,6 +67,7 @@ bar_cell_set_fg(zdwm_bar_item_t *item, size_t index, const char *color) {
|
||||
auto cell = &item->cells[index];
|
||||
if (cell->fg_text && strcmp(color, cell->fg_text) == 0) return;
|
||||
|
||||
p_delete(&cell->fg_text);
|
||||
cell->fg_text = p_strdup(color);
|
||||
color_parse(color, &cell->fg);
|
||||
|
||||
@@ -82,13 +85,17 @@ bar_cell_set_icon(zdwm_bar_item_t *item, size_t index, zdwm_icon_t icon) {
|
||||
if (icon_ptr->type == icon.type) {
|
||||
switch (icon.type) {
|
||||
case ZDWM_ICON_TEXT:
|
||||
if (icon_ptr->as.text == icon.as.text ||
|
||||
strcmp(icon_ptr->as.text, icon.as.text) == 0) {
|
||||
if (
|
||||
icon_ptr->as.text == icon.as.text ||
|
||||
strcmp(icon_ptr->as.text, icon.as.text) == 0
|
||||
) {
|
||||
return;
|
||||
}
|
||||
case ZDWM_ICON_IMAGE:
|
||||
if (icon_ptr->as.image_path == icon.as.image_path ||
|
||||
strcmp(icon_ptr->as.image_path, icon.as.image_path) == 0) {
|
||||
if (
|
||||
icon_ptr->as.image_path == icon.as.image_path ||
|
||||
strcmp(icon_ptr->as.image_path, icon.as.image_path) == 0
|
||||
) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -113,7 +120,28 @@ bar_cell_set_indicator(zdwm_bar_item_t *item, size_t index, bool show) {
|
||||
item->dirty = true;
|
||||
}
|
||||
|
||||
zdwm_bar_cell_api_t bar_cell_api = {
|
||||
static void
|
||||
bar_cell_set_fixed_width(zdwm_bar_item_t *item, size_t index, int32_t width) {
|
||||
if (index >= item->count) return;
|
||||
|
||||
auto cell = &item->cells[index];
|
||||
if (cell->fixed_width == width) return;
|
||||
|
||||
cell->fixed_width = width;
|
||||
|
||||
cell->dirty = true;
|
||||
item->dirty = true;
|
||||
}
|
||||
|
||||
static bar_x_region_t bar_cell_get_region(zdwm_bar_item_t *item, size_t index) {
|
||||
if (index >= item->count) return (bar_x_region_t){0};
|
||||
|
||||
auto cell = &item->cells[index];
|
||||
return cell->region;
|
||||
}
|
||||
|
||||
/* clang-format off */
|
||||
const zdwm_bar_cell_api_t bar_cell_api = {
|
||||
.get_cell_count = bar_cell_get_count,
|
||||
.set_cell_count = bar_cell_set_count,
|
||||
.cell_set_text = bar_cell_set_text,
|
||||
@@ -121,7 +149,10 @@ zdwm_bar_cell_api_t bar_cell_api = {
|
||||
.cell_set_fg = bar_cell_set_fg,
|
||||
.cell_set_icon = bar_cell_set_icon,
|
||||
.cell_set_indicator = bar_cell_set_indicator,
|
||||
.cell_set_fixed_width = bar_cell_set_fixed_width,
|
||||
.cell_get_region = bar_cell_get_region,
|
||||
};
|
||||
/* clang-format on */
|
||||
|
||||
void bar_cell_set_region(
|
||||
zdwm_bar_item_t *item,
|
||||
|
||||
@@ -12,4 +12,4 @@ void bar_cell_set_region(
|
||||
bar_x_region_t region
|
||||
);
|
||||
|
||||
extern zdwm_bar_cell_api_t bar_cell_api;
|
||||
extern const zdwm_bar_cell_api_t bar_cell_api;
|
||||
|
||||
78
src/bar/tray.c
Normal file
78
src/bar/tray.c
Normal file
@@ -0,0 +1,78 @@
|
||||
#include "bar/tray.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <zdwm/bar.h>
|
||||
#include <zdwm/types.h>
|
||||
|
||||
#include "base/memory.h"
|
||||
#include "interface/tray.h"
|
||||
|
||||
typedef struct bar_tray_state_t {
|
||||
tray_t tray;
|
||||
bool dirty;
|
||||
} bar_tray_state_t;
|
||||
|
||||
static void bar_tray_icons_changed(void *user_data) {
|
||||
bar_tray_state_t *state = user_data;
|
||||
state->dirty = true;
|
||||
}
|
||||
|
||||
static void *bar_tray_create_state(zdwm_output_id_t output_id, void *config) {
|
||||
tray_t *tray = config;
|
||||
assert(tray != nullptr);
|
||||
|
||||
auto state = p_new(bar_tray_state_t, 1);
|
||||
state->tray = *tray;
|
||||
tray->api.set_listener(tray->handle, bar_tray_icons_changed, state);
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
static void bar_tray_update(
|
||||
zdwm_bar_item_t *item,
|
||||
const zdwm_bar_cell_api_t *cell_api,
|
||||
void *state
|
||||
) {
|
||||
bar_tray_state_t *data = state;
|
||||
if (!data->dirty) return;
|
||||
|
||||
auto tray_handle = data->tray.handle;
|
||||
auto tray_api = &data->tray.api;
|
||||
auto icon_count = tray_api->icon_count(tray_handle);
|
||||
auto cell_count = cell_api->get_cell_count(item);
|
||||
if (cell_count == icon_count) {
|
||||
data->dirty = false;
|
||||
return;
|
||||
}
|
||||
|
||||
cell_api->set_cell_count(item, icon_count);
|
||||
auto cell_width = tray_api->icon_size(tray_handle);
|
||||
for (size_t i = 0; i < icon_count; ++i) {
|
||||
cell_api->cell_set_fixed_width(item, i, cell_width);
|
||||
}
|
||||
|
||||
data->dirty = false;
|
||||
}
|
||||
|
||||
static void bar_tray_after_layout(const zdwm_bar_item_hook_params_t *params) {
|
||||
bar_tray_state_t *state = params->state;
|
||||
auto tray_handle = state->tray.handle;
|
||||
auto tray_api = &state->tray.api;
|
||||
tray_api->place(tray_handle, params->region.start);
|
||||
}
|
||||
|
||||
static void bar_tray_destroy_state(void *state) {
|
||||
if (!state) return;
|
||||
|
||||
bar_tray_state_t *data = state;
|
||||
p_delete(&data);
|
||||
}
|
||||
|
||||
zdwm_bar_item_type_t bar_tray = {
|
||||
.create_state = bar_tray_create_state,
|
||||
.update = bar_tray_update,
|
||||
.after_layout = bar_tray_after_layout,
|
||||
.destroy_state = bar_tray_destroy_state,
|
||||
.update_interval_ms = 0,
|
||||
};
|
||||
5
src/bar/tray.h
Normal file
5
src/bar/tray.h
Normal file
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <zdwm/bar.h>
|
||||
|
||||
extern zdwm_bar_item_type_t bar_tray;
|
||||
@@ -7,10 +7,7 @@
|
||||
|
||||
#include "base/color.h"
|
||||
|
||||
typedef struct bar_x_region_t {
|
||||
int32_t start;
|
||||
int32_t end;
|
||||
} bar_x_region_t;
|
||||
typedef struct zdwm_bar_x_region_t bar_x_region_t;
|
||||
|
||||
typedef struct bar_cell_t {
|
||||
zdwm_icon_t icon;
|
||||
@@ -20,6 +17,7 @@ typedef struct bar_cell_t {
|
||||
color_t fg;
|
||||
color_t bg;
|
||||
bar_x_region_t region;
|
||||
int32_t fixed_width;
|
||||
bool show_indicator;
|
||||
bool dirty;
|
||||
} bar_cell_t;
|
||||
|
||||
175
src/bar/windows.c
Normal file
175
src/bar/windows.c
Normal file
@@ -0,0 +1,175 @@
|
||||
#include "bar/windows.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.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_windows_state_t {
|
||||
zdwm_output_id_t output_id;
|
||||
zdwm_workspace_id_t workspace_id;
|
||||
|
||||
zdwm_window_t *windows;
|
||||
size_t count;
|
||||
size_t capacity;
|
||||
|
||||
bar_windows_config_t config;
|
||||
|
||||
bool workspace_inited;
|
||||
bool dirty;
|
||||
} bar_windows_state_t;
|
||||
|
||||
static zdwm_window_t *
|
||||
state_get_window(bar_windows_state_t *state, zdwm_window_id_t window_id) {
|
||||
for (size_t i = 0; i < state->count; ++i) {
|
||||
auto window = &state->windows[i];
|
||||
if (window->id == window_id) return window;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static void *
|
||||
bar_windows_create_state(zdwm_output_id_t output_id, void *config) {
|
||||
const bar_windows_config_t *windows_config = config;
|
||||
assert(windows_config);
|
||||
|
||||
auto state = p_new(bar_windows_state_t, 1);
|
||||
state->output_id = output_id;
|
||||
state->workspace_id = ZDWM_WORKSPACE_ID_INVALID;
|
||||
state->config = *windows_config;
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
static void bar_windows_update(
|
||||
zdwm_bar_item_t *item,
|
||||
const zdwm_bar_cell_api_t *cells,
|
||||
void *state
|
||||
) {
|
||||
bar_windows_state_t *data = state;
|
||||
if (!data->workspace_inited || !data->dirty) return;
|
||||
|
||||
zdwm_window_t *windows = nullptr;
|
||||
size_t count = 0, capacity = 0;
|
||||
for (size_t i = 0; i < data->count; ++i) {
|
||||
auto win = &data->windows[i];
|
||||
if (win->workspace != data->workspace_id || win->skip_taskbar) continue;
|
||||
|
||||
auto window = array_push(windows, count, capacity);
|
||||
*window = *win;
|
||||
}
|
||||
|
||||
cells->set_cell_count(item, count);
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
auto window = &windows[i];
|
||||
auto bg = window->focused ? data->config.focused_bg : data->config.bg;
|
||||
auto fg = window->focused ? data->config.focused_fg : data->config.fg;
|
||||
cells->cell_set_text(item, i, window->title);
|
||||
cells->cell_set_bg(item, i, bg);
|
||||
cells->cell_set_fg(item, i, fg);
|
||||
}
|
||||
p_delete(&windows);
|
||||
|
||||
data->dirty = false;
|
||||
}
|
||||
|
||||
static void bar_windows_destroy_state(void *state) {
|
||||
bar_windows_state_t *data = state;
|
||||
|
||||
p_delete(&data->windows);
|
||||
p_delete(&data);
|
||||
}
|
||||
|
||||
zdwm_bar_item_type_t bar_windows = {
|
||||
.create_state = bar_windows_create_state,
|
||||
.update = bar_windows_update,
|
||||
.destroy_state = bar_windows_destroy_state,
|
||||
.update_interval_ms = 0,
|
||||
};
|
||||
|
||||
static void bar_windows_workspace_active(
|
||||
zdwm_output_id_t output_id,
|
||||
zdwm_workspace_id_t workspace_id,
|
||||
void *user_data
|
||||
) {
|
||||
bar_windows_state_t *state = user_data;
|
||||
if (state->output_id != output_id) return;
|
||||
if (state->workspace_id == workspace_id) return;
|
||||
|
||||
state->workspace_id = workspace_id;
|
||||
|
||||
state->workspace_inited = true;
|
||||
state->dirty = true;
|
||||
}
|
||||
|
||||
static void
|
||||
bar_windows_add_window(const zdwm_window_t *window, void *user_data) {
|
||||
bar_windows_state_t *state = user_data;
|
||||
auto win = array_push(state->windows, state->count, state->capacity);
|
||||
*win = *window;
|
||||
|
||||
state->dirty = true;
|
||||
}
|
||||
|
||||
static void
|
||||
bar_windows_initial(const zdwm_window_t *list, size_t count, void *user_data) {
|
||||
bar_windows_state_t *state = user_data;
|
||||
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
auto window = &list[i];
|
||||
bar_windows_add_window(window, state);
|
||||
}
|
||||
|
||||
state->dirty = true;
|
||||
}
|
||||
|
||||
static void
|
||||
bar_windows_update_window(const zdwm_window_t *window, void *user_data) {
|
||||
bar_windows_state_t *state = user_data;
|
||||
|
||||
auto old_window = state_get_window(state, window->id);
|
||||
*old_window = *window;
|
||||
|
||||
state->dirty = true;
|
||||
}
|
||||
|
||||
static void
|
||||
bar_windows_remove_window(zdwm_window_id_t window_id, void *user_data) {
|
||||
bar_windows_state_t *state = user_data;
|
||||
|
||||
bool found = false;
|
||||
size_t index = 0;
|
||||
|
||||
for (size_t i = 0; i < state->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->count, index)) {
|
||||
state->dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
void bar_windows_add_listeners(listeners_t *listeners, void *state) {
|
||||
#define ADD(ADD_FN, LISTENER) ADD_FN(listeners, LISTENER, state)
|
||||
|
||||
ADD(listeners_add_active_workspace_listener, bar_windows_workspace_active);
|
||||
ADD(listeners_add_initial_window_listener, bar_windows_initial);
|
||||
ADD(listeners_add_window_added_listener, bar_windows_add_window);
|
||||
ADD(listeners_add_window_updated_listener, bar_windows_update_window);
|
||||
ADD(listeners_add_window_removed_listener, bar_windows_remove_window);
|
||||
|
||||
#undef ADD
|
||||
}
|
||||
18
src/bar/windows.h
Normal file
18
src/bar/windows.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <zdwm/bar.h>
|
||||
|
||||
#include "common/listeners.h"
|
||||
|
||||
typedef struct bar_windows_config_t {
|
||||
uint32_t cell_padding;
|
||||
const char *bg;
|
||||
const char *fg;
|
||||
const char *focused_bg;
|
||||
const char *focused_fg;
|
||||
} bar_windows_config_t;
|
||||
|
||||
extern zdwm_bar_item_type_t bar_windows;
|
||||
|
||||
void bar_windows_add_listeners(listeners_t *listeners, void *state);
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
#include "base/array.h"
|
||||
#include "base/memory.h"
|
||||
#include "core/listeners.h"
|
||||
#include "common/listeners.h"
|
||||
|
||||
typedef struct bar_workspace_t {
|
||||
zdwm_workspace_t info;
|
||||
@@ -143,15 +143,18 @@ static void bar_workspaces_update(
|
||||
data->dirty = false;
|
||||
}
|
||||
|
||||
static zdwm_action_t bar_workspace_on_click(
|
||||
zdwm_bar_item_t *item,
|
||||
size_t cell_index,
|
||||
int32_t x,
|
||||
void *state
|
||||
) {
|
||||
auto data = (bar_workspace_state_t *)state;
|
||||
auto workspace = &data->workspaces[cell_index];
|
||||
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};
|
||||
}
|
||||
@@ -276,6 +279,7 @@ void bar_workspace_window_removed(zdwm_window_id_t window_id, void *user_data) {
|
||||
if (window->id == window_id) {
|
||||
found = true;
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include <stdint.h>
|
||||
#include <zdwm/bar.h>
|
||||
|
||||
#include "core/listeners.h"
|
||||
#include "common/listeners.h"
|
||||
|
||||
typedef struct bar_workspace_config_t {
|
||||
uint32_t cell_padding;
|
||||
|
||||
@@ -98,11 +98,11 @@ array_erase_impl(void *items, size_t item_size, size_t *count, size_t index) {
|
||||
#define array_reserve(items, capacity, need) \
|
||||
array_reserve_impl((void **)&(items), sizeof(*(items)), &(capacity), (need))
|
||||
|
||||
/* clang-format off */
|
||||
/** @brief 为具体类型数组追加一个槽位。 */
|
||||
#define array_push(items, count, capacity) \
|
||||
( \
|
||||
typeof(items) \
|
||||
)array_push_impl((void **)&(items), sizeof(*(items)), &(count), &(capacity))
|
||||
#define array_push(items, count, capacity)\
|
||||
(typeof(items))array_push_impl((void **)&(items), sizeof(*(items)), &(count), &(capacity))
|
||||
/* clang-format on */
|
||||
|
||||
/** @brief 删除具体类型数组中指定下标的元素。 */
|
||||
#define array_erase(items, count, index) \
|
||||
|
||||
@@ -56,7 +56,7 @@ bool hex_color_to_rgba(const char *hex, uint32_t *rgba) {
|
||||
|
||||
size_t len = strlen(hex);
|
||||
char extended[9] = {0};
|
||||
uint32_t color = 0x00000000;
|
||||
uint32_t color = 0x00'00'00'00;
|
||||
|
||||
if (len == 3 || len == 4) {
|
||||
extended[0] = extended[1] = hex[0];
|
||||
@@ -76,7 +76,7 @@ bool hex_color_to_rgba(const char *hex, uint32_t *rgba) {
|
||||
return false;
|
||||
}
|
||||
|
||||
color = strtoul(extended, nullptr, 16) & 0xffffffff;
|
||||
color = strtoul(extended, nullptr, 16) & 0xff'ff'ff'ff;
|
||||
*rgba = color;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
#include "base/time.h"
|
||||
|
||||
#include <bits/time.h>
|
||||
#include <bits/types/struct_itimerspec.h>
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
#include "base/window_list.h"
|
||||
|
||||
#include <zdwm/types.h>
|
||||
|
||||
#include "base/array.h"
|
||||
#include "base/memory.h"
|
||||
|
||||
void window_list_push(
|
||||
window_list_t *window_list,
|
||||
zdwm_window_id_t window_id
|
||||
) {
|
||||
zdwm_window_id_t *window =
|
||||
array_push(window_list->windows, window_list->count, window_list->capacity);
|
||||
*window = window_id;
|
||||
}
|
||||
|
||||
void window_list_reset(window_list_t *window_list) {
|
||||
p_clear(window_list->windows, window_list->capacity);
|
||||
window_list->count = 0;
|
||||
}
|
||||
|
||||
void window_list_cleanup(window_list_t *window_list) {
|
||||
p_delete(&window_list->windows);
|
||||
window_list->count = 0;
|
||||
window_list->capacity = 0;
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
#include "core/event.h"
|
||||
#include "interface/event.h"
|
||||
|
||||
#include "base/memory.h"
|
||||
#include "core/window.h"
|
||||
#include "common/window.h"
|
||||
|
||||
void event_cleanup(event_t *event) {
|
||||
if (!event) return;
|
||||
if (!event || event->type == ZDWM_EVENT_NONE) return;
|
||||
|
||||
switch (event->type) {
|
||||
case ZDWM_EVENT_WINDOW_MAP_REQUEST:
|
||||
6
src/common/event.h
Normal file
6
src/common/event.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "interface/event.h"
|
||||
|
||||
void event_cleanup(event_t *event);
|
||||
void event_reset(event_t *event);
|
||||
107
src/common/listeners.c
Normal file
107
src/common/listeners.c
Normal file
@@ -0,0 +1,107 @@
|
||||
#include "common/listeners.h"
|
||||
|
||||
#include <zdwm/listeners.h>
|
||||
|
||||
#include "base/array.h"
|
||||
#include "base/memory.h"
|
||||
|
||||
#define ADD_LISTENER(FIELD) \
|
||||
auto list = &listeners->FIELD; \
|
||||
auto item = array_push(list->items, list->count, list->capacity); \
|
||||
item->fn = fn; \
|
||||
item->user_data = user_data
|
||||
|
||||
void listeners_add_output_listener(
|
||||
listeners_t *listeners,
|
||||
zdwm_current_output_id_listener fn,
|
||||
void *user_data
|
||||
) {
|
||||
ADD_LISTENER(output_listeners);
|
||||
}
|
||||
|
||||
void listeners_add_initial_workspace_listener(
|
||||
listeners_t *listeners,
|
||||
zdwm_initial_workspace_list fn,
|
||||
void *user_data
|
||||
) {
|
||||
ADD_LISTENER(initial_workspace_listeners);
|
||||
}
|
||||
|
||||
void listeners_add_active_workspace_listener(
|
||||
listeners_t *listeners,
|
||||
zdwm_workspace_active_updated fn,
|
||||
void *user_data
|
||||
) {
|
||||
ADD_LISTENER(active_workspace_listeners);
|
||||
}
|
||||
|
||||
void listeners_add_layout_notify(
|
||||
listeners_t *listeners,
|
||||
zdwm_layout_notify fn,
|
||||
void *user_data
|
||||
) {
|
||||
ADD_LISTENER(layout_listeners);
|
||||
}
|
||||
|
||||
void listeners_add_binding_mode_notify(
|
||||
listeners_t *listeners,
|
||||
zdwm_binding_mode_notify fn,
|
||||
void *user_data
|
||||
) {
|
||||
ADD_LISTENER(binding_mode_listeners);
|
||||
}
|
||||
|
||||
void listeners_add_initial_window_listener(
|
||||
listeners_t *listeners,
|
||||
zdwm_initial_window_list fn,
|
||||
void *user_data
|
||||
) {
|
||||
ADD_LISTENER(initial_window_listeners);
|
||||
}
|
||||
|
||||
void listeners_add_window_added_listener(
|
||||
listeners_t *listeners,
|
||||
zdwm_window_added fn,
|
||||
void *user_data
|
||||
) {
|
||||
ADD_LISTENER(window_added_listeners);
|
||||
}
|
||||
|
||||
void listeners_add_window_updated_listener(
|
||||
listeners_t *listeners,
|
||||
zdwm_window_updated fn,
|
||||
void *user_data
|
||||
) {
|
||||
ADD_LISTENER(window_updated_listeners);
|
||||
}
|
||||
|
||||
void listeners_add_window_removed_listener(
|
||||
listeners_t *listeners,
|
||||
zdwm_window_removed fn,
|
||||
void *user_data
|
||||
) {
|
||||
ADD_LISTENER(window_removed_listeners);
|
||||
}
|
||||
|
||||
#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
|
||||
94
src/common/listeners.h
Normal file
94
src/common/listeners.h
Normal file
@@ -0,0 +1,94 @@
|
||||
#pragma once
|
||||
|
||||
#include <zdwm/listeners.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; \
|
||||
void *user_data; \
|
||||
} NAME##_listener_item_t
|
||||
|
||||
#define LISTENERS(NAME, FN_TYPE) \
|
||||
ITEM(NAME, FN_TYPE); \
|
||||
typedef struct NAME##_listeners_t { \
|
||||
NAME##_listener_item_t *items; \
|
||||
size_t count; \
|
||||
size_t capacity; \
|
||||
} NAME##_listeners_t
|
||||
|
||||
LISTENERS(output, zdwm_current_output_id_listener);
|
||||
LISTENERS(initial_workspace_list, zdwm_initial_workspace_list);
|
||||
LISTENERS(workspace_active, zdwm_workspace_active_updated);
|
||||
LISTENERS(layout, zdwm_layout_notify);
|
||||
LISTENERS(binding_mode, zdwm_binding_mode_notify);
|
||||
LISTENERS(initial_window_list, zdwm_initial_window_list);
|
||||
LISTENERS(window_added, zdwm_window_added);
|
||||
LISTENERS(window_updated, zdwm_window_updated);
|
||||
LISTENERS(window_removed, zdwm_window_removed);
|
||||
|
||||
#undef ITEM
|
||||
#undef LISTENERS
|
||||
|
||||
typedef struct listeners_t {
|
||||
output_listeners_t output_listeners;
|
||||
initial_workspace_list_listeners_t initial_workspace_listeners;
|
||||
workspace_active_listeners_t active_workspace_listeners;
|
||||
layout_listeners_t layout_listeners;
|
||||
binding_mode_listeners_t binding_mode_listeners;
|
||||
initial_window_list_listeners_t initial_window_listeners;
|
||||
window_added_listeners_t window_added_listeners;
|
||||
window_updated_listeners_t window_updated_listeners;
|
||||
window_removed_listeners_t window_removed_listeners;
|
||||
} listeners_t;
|
||||
|
||||
void listeners_add_output_listener(
|
||||
listeners_t *listeners,
|
||||
zdwm_current_output_id_listener fn,
|
||||
void *user_data
|
||||
);
|
||||
void listeners_add_initial_workspace_listener(
|
||||
listeners_t *listeners,
|
||||
zdwm_initial_workspace_list fn,
|
||||
void *user_data
|
||||
);
|
||||
void listeners_add_active_workspace_listener(
|
||||
listeners_t *listeners,
|
||||
zdwm_workspace_active_updated fn,
|
||||
void *user_data
|
||||
);
|
||||
void listeners_add_layout_notify(
|
||||
listeners_t *listeners,
|
||||
zdwm_layout_notify fn,
|
||||
void *user_data
|
||||
);
|
||||
void listeners_add_binding_mode_notify(
|
||||
listeners_t *listeners,
|
||||
zdwm_binding_mode_notify fn,
|
||||
void *user_data
|
||||
);
|
||||
void listeners_add_initial_window_listener(
|
||||
listeners_t *listeners,
|
||||
zdwm_initial_window_list fn,
|
||||
void *user_data
|
||||
);
|
||||
void listeners_add_window_added_listener(
|
||||
listeners_t *listeners,
|
||||
zdwm_window_added fn,
|
||||
void *user_data
|
||||
);
|
||||
void listeners_add_window_updated_listener(
|
||||
listeners_t *listeners,
|
||||
zdwm_window_updated fn,
|
||||
void *user_data
|
||||
);
|
||||
void listeners_add_window_removed_listener(
|
||||
listeners_t *listeners,
|
||||
zdwm_window_removed fn,
|
||||
void *user_data
|
||||
);
|
||||
|
||||
void listeners_cleanup(listeners_t *listeners);
|
||||
55
src/common/window.c
Normal file
55
src/common/window.c
Normal file
@@ -0,0 +1,55 @@
|
||||
#include "common/window.h"
|
||||
|
||||
#include "base/array.h"
|
||||
#include "base/memory.h"
|
||||
#include "interface/types.h"
|
||||
|
||||
window_layer_type_t window_classify_layer(const window_layer_props_t *props) {
|
||||
for (size_t i = 0; i < props->type_count; ++i) {
|
||||
window_type_t type = props->types[i];
|
||||
if (type == ZDWM_WINDOW_TYPE_NOTIFICATION) return ZDWM_WINDOW_LAYER_OVERLAY;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < props->state_count; ++i) {
|
||||
window_state_t state = props->states[i];
|
||||
if (state == ZDWM_WINDOW_STATE_ABOVE) return ZDWM_WINDOW_LAYER_TOP;
|
||||
}
|
||||
|
||||
return ZDWM_WINDOW_LAYER_NORMAL;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 window_list_push(window_list_t *window_list, zdwm_window_id_t window_id) {
|
||||
zdwm_window_id_t *window =
|
||||
array_push(window_list->windows, window_list->count, window_list->capacity);
|
||||
*window = window_id;
|
||||
}
|
||||
|
||||
void window_list_reset(window_list_t *window_list) {
|
||||
p_clear(window_list->windows, window_list->capacity);
|
||||
window_list->count = 0;
|
||||
}
|
||||
|
||||
void window_list_cleanup(window_list_t *window_list) {
|
||||
p_delete(&window_list->windows);
|
||||
window_list->count = 0;
|
||||
window_list->capacity = 0;
|
||||
}
|
||||
@@ -1,7 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <zdwm/types.h>
|
||||
#include "interface/types.h"
|
||||
|
||||
window_layer_type_t window_classify_layer(const window_layer_props_t *props);
|
||||
void window_layer_props_cleanup(window_layer_props_t *props);
|
||||
void window_metadata_cleanup(window_metadata_t *metadata);
|
||||
|
||||
typedef struct window_list_t {
|
||||
zdwm_window_id_t *windows;
|
||||
46
src/common/workspace.c
Normal file
46
src/common/workspace.c
Normal file
@@ -0,0 +1,46 @@
|
||||
#include "common/workspace.h"
|
||||
|
||||
#include "base/memory.h"
|
||||
|
||||
void workspace_desc_cleanup(workspace_desc_t *workspace) {
|
||||
if (!workspace) return;
|
||||
|
||||
p_delete(&workspace->name);
|
||||
p_delete(&workspace->layout_ids);
|
||||
workspace->output_index = 0;
|
||||
workspace->layout_count = 0;
|
||||
workspace->initial_layout_id = ZDWM_LAYOUT_ID_INVALID;
|
||||
}
|
||||
|
||||
void 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_cleanup(&(*list)[i]);
|
||||
}
|
||||
|
||||
p_delete(list);
|
||||
*count = 0;
|
||||
}
|
||||
|
||||
bool workspace_desc_layouts_valid(const workspace_desc_t *workspace) {
|
||||
if (!workspace || !workspace->layout_count || !workspace->layout_ids) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < workspace->layout_count; i++) {
|
||||
if (workspace->layout_ids[i] == workspace->initial_layout_id) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
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 workspace_desc_layouts_valid(workspace);
|
||||
}
|
||||
21
src/common/workspace.h
Normal file
21
src/common/workspace.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "interface/types.h"
|
||||
|
||||
typedef struct workspace_desc_t {
|
||||
size_t output_index; /* 对应 state_init() 中 outputs[] 的索引 */
|
||||
const char *name;
|
||||
const layout_id_t *layout_ids;
|
||||
size_t layout_count;
|
||||
layout_id_t initial_layout_id;
|
||||
} workspace_desc_t;
|
||||
|
||||
void workspace_desc_cleanup(workspace_desc_t *workspace);
|
||||
void workspace_desc_list_cleanup(workspace_desc_t **list, size_t *count);
|
||||
bool workspace_desc_layouts_valid(const workspace_desc_t *workspace);
|
||||
bool workspace_desc_valid(
|
||||
const workspace_desc_t *workspace,
|
||||
size_t output_count
|
||||
);
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
#include "base/log.h"
|
||||
#include "base/macros.h"
|
||||
#include "core/types.h"
|
||||
#include "interface/types.h"
|
||||
|
||||
static constexpr char launcher[] =
|
||||
"rofi -show combi -modes combi -combi-modes window,drun,run,ssh,windowcd";
|
||||
@@ -52,10 +52,12 @@ bool config_defaults_build(
|
||||
);
|
||||
zdwm_layout_id_t floating_id =
|
||||
api->register_layout(builder, "floating", "><>", "floating", nullptr);
|
||||
if (fair_id == ZDWM_LAYOUT_ID_INVALID ||
|
||||
if (
|
||||
fair_id == ZDWM_LAYOUT_ID_INVALID ||
|
||||
maximize_id == ZDWM_LAYOUT_ID_INVALID ||
|
||||
fullscreen_id == ZDWM_LAYOUT_ID_INVALID ||
|
||||
floating_id == ZDWM_LAYOUT_ID_INVALID) {
|
||||
floating_id == ZDWM_LAYOUT_ID_INVALID
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -120,7 +122,7 @@ bool config_defaults_build(
|
||||
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("f"), {.type = ZDWM_ACTION_WINDOW_TOGGLE_FULLSCREEN});
|
||||
BIND_DEFAULT(Super("m"), {.type = ZDWM_ACTION_WINDOW_TOGGLE_MAXIMIZE});
|
||||
BIND_DEFAULT(
|
||||
Super("Control+space"),
|
||||
@@ -167,6 +169,7 @@ bool config_defaults_build(
|
||||
api->set_default_mode(builder, default_mode);
|
||||
api->set_initial_mode(builder, default_mode);
|
||||
api->set_border_config(builder, 2, "#1c2022", "#606060");
|
||||
api->set_interaction_fps(builder, 30);
|
||||
|
||||
zdwm_bar_config_t bar_config = {
|
||||
.height = 28,
|
||||
@@ -178,6 +181,7 @@ bool config_defaults_build(
|
||||
.dpi = 144,
|
||||
|
||||
.bg = "#222222",
|
||||
.fg = "#bbbbbb",
|
||||
|
||||
.tag_cell_padding_x = 10,
|
||||
.tag_indicator_width = 4,
|
||||
@@ -194,6 +198,9 @@ bool config_defaults_build(
|
||||
.binding_padding_x = 2,
|
||||
.binding_mode_bg = "#900000",
|
||||
.binding_mode_fg = "#ffffff",
|
||||
|
||||
.window_focused_bg = "#005577",
|
||||
.window_focused_fg = "#eeeeee",
|
||||
};
|
||||
api->set_bar_config(builder, bar_config);
|
||||
|
||||
|
||||
@@ -2,20 +2,22 @@
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <zdwm/bar.h>
|
||||
#include <zdwm/config.h>
|
||||
#include <zdwm/listeners.h>
|
||||
|
||||
#include "base/array.h"
|
||||
#include "base/color.h"
|
||||
#include "base/memory.h"
|
||||
#include "common/listeners.h"
|
||||
#include "common/workspace.h"
|
||||
#include "config/defaults.h"
|
||||
#include "config/loader.h"
|
||||
#include "core/binding.h"
|
||||
#include "core/layout.h"
|
||||
#include "core/listeners.h"
|
||||
#include "core/rules.h"
|
||||
#include "core/types.h"
|
||||
#include "core/wm_desc.h"
|
||||
#include "interface/types.h"
|
||||
#include "layouts/fair.h"
|
||||
#include "layouts/fullscreen.h"
|
||||
#include "layouts/maximize.h"
|
||||
@@ -28,6 +30,7 @@ struct zdwm_config_builder_t {
|
||||
size_t workspace_capacity;
|
||||
size_t output_count;
|
||||
border_config_t border;
|
||||
uint32_t fps;
|
||||
binding_table_t *binding_table;
|
||||
listeners_t listeners;
|
||||
zdwm_bar_config_t bar;
|
||||
@@ -36,8 +39,10 @@ struct zdwm_config_builder_t {
|
||||
void runtime_config_cleanup(runtime_init_desc_t *desc) {
|
||||
if (!desc) return;
|
||||
|
||||
if (desc->layouts.slots || desc->layouts.slot_count ||
|
||||
desc->layouts.slot_capacity) {
|
||||
if (
|
||||
desc->layouts.slots || desc->layouts.slot_count ||
|
||||
desc->layouts.slot_capacity
|
||||
) {
|
||||
layout_registry_cleanup(&desc->layouts);
|
||||
}
|
||||
binding_table_destroy(desc->binding_table);
|
||||
@@ -51,8 +56,10 @@ void runtime_config_cleanup(runtime_init_desc_t *desc) {
|
||||
static void config_builder_cleanup(zdwm_config_builder_t *builder) {
|
||||
if (!builder) return;
|
||||
|
||||
if (builder->layouts.slots || builder->layouts.slot_count ||
|
||||
builder->layouts.slot_capacity) {
|
||||
if (
|
||||
builder->layouts.slots || builder->layouts.slot_count ||
|
||||
builder->layouts.slot_capacity
|
||||
) {
|
||||
layout_registry_cleanup(&builder->layouts);
|
||||
}
|
||||
workspace_desc_list_cleanup(&builder->workspaces, &builder->workspace_count);
|
||||
@@ -200,6 +207,13 @@ static void runtime_config_set_border_config(
|
||||
color_parse(focused, &builder->border.focused_color);
|
||||
}
|
||||
|
||||
static void runtime_config_set_interaction_fps(
|
||||
zdwm_config_builder_t *builder,
|
||||
uint32_t fps
|
||||
) {
|
||||
builder->fps = fps;
|
||||
}
|
||||
|
||||
static void runtime_config_subscribe_current_output(
|
||||
zdwm_config_builder_t *builder,
|
||||
zdwm_current_output_id_listener *fn,
|
||||
@@ -300,6 +314,7 @@ static bool config_builder_finish(
|
||||
if (!layout_registry_move(&builder->layouts, &out->layouts)) return false;
|
||||
if (!rules_move(&builder->rules, &out->rules)) return false;
|
||||
out->border = builder->border;
|
||||
out->fps = builder->fps;
|
||||
out->binding_table = builder->binding_table;
|
||||
out->workspaces = builder->workspaces;
|
||||
out->workspace_count = builder->workspace_count;
|
||||
@@ -326,10 +341,10 @@ static bool runtime_config_build(
|
||||
builder.output_count = output_count;
|
||||
builder.binding_table = binding_table_create();
|
||||
|
||||
/* clang-format off */
|
||||
zdwm_api_t api = {
|
||||
.abi_version = ZDWM_CONFIG_ABI_VERSION,
|
||||
.builtin_layouts =
|
||||
{
|
||||
.builtin_layouts = {
|
||||
.fair = fair,
|
||||
.fullscreen = fullscreen,
|
||||
.maximize = maximize,
|
||||
@@ -343,20 +358,21 @@ static bool runtime_config_build(
|
||||
.set_initial_mode = runtime_config_set_initial_mode,
|
||||
.set_border_config = runtime_config_set_border_config,
|
||||
|
||||
.set_interaction_fps = runtime_config_set_interaction_fps,
|
||||
|
||||
.subscribe_current_output = runtime_config_subscribe_current_output,
|
||||
.subscribe_initial_workspace_list =
|
||||
runtime_config_subscribe_initial_workspace_list,
|
||||
.subscribe_initial_workspace_list = runtime_config_subscribe_initial_workspace_list,
|
||||
.subscribe_workspace_active = runtime_config_subscribe_workspace_active,
|
||||
.subscribe_layout = runtime_config_subscribe_layout,
|
||||
.subscribe_binding_mode = runtime_config_subscribe_binding_mode,
|
||||
.subscribe_initial_window_list =
|
||||
runtime_config_subscribe_initial_window_list,
|
||||
.subscribe_initial_window_list = runtime_config_subscribe_initial_window_list,
|
||||
.subscribe_window_added = runtime_config_subscribe_window_added,
|
||||
.subscribe_window_updated = runtime_config_subscribe_window_updated,
|
||||
.subscribe_window_removed = runtime_config_subscribe_window_removed,
|
||||
|
||||
.set_bar_config = runtime_config_set_bar_config,
|
||||
};
|
||||
/* clang-format on */
|
||||
bool ok = setup(&api, &builder, outputs, output_count) &&
|
||||
config_builder_finish(&builder, out);
|
||||
config_builder_cleanup(&builder);
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <zdwm/bar.h>
|
||||
#include <zdwm/types.h>
|
||||
|
||||
#include "core/backend.h"
|
||||
#include "common/listeners.h"
|
||||
#include "common/workspace.h"
|
||||
#include "core/binding.h"
|
||||
#include "core/layout.h"
|
||||
#include "core/listeners.h"
|
||||
#include "core/rules.h"
|
||||
#include "core/types.h"
|
||||
#include "core/wm_desc.h"
|
||||
#include "interface/backend.h"
|
||||
|
||||
typedef struct runtime_init_desc_t {
|
||||
backend_t *backend;
|
||||
@@ -20,6 +20,7 @@ typedef struct runtime_init_desc_t {
|
||||
layout_registry_t layouts;
|
||||
rules_t rules;
|
||||
border_config_t border;
|
||||
uint32_t fps;
|
||||
workspace_desc_t *workspaces;
|
||||
size_t workspace_count;
|
||||
void *config_module_handle;
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
#include "base/array.h"
|
||||
#include "base/macros.h"
|
||||
#include "base/memory.h"
|
||||
#include "core/types.h"
|
||||
#include "interface/types.h"
|
||||
|
||||
typedef struct binding_mode_t {
|
||||
zdwm_binding_mode_id_t id;
|
||||
@@ -82,8 +82,10 @@ static const mod_entry_t mod_table[] = {
|
||||
|
||||
static modifier_mask_t lookup_modifier(const char *name, size_t len) {
|
||||
for (size_t i = 0; i < countof(mod_table); ++i) {
|
||||
if (strlen(mod_table[i].name) == len &&
|
||||
strncmp(name, mod_table[i].name, len) == 0) {
|
||||
if (
|
||||
strlen(mod_table[i].name) == len &&
|
||||
strncmp(name, mod_table[i].name, len) == 0
|
||||
) {
|
||||
return mod_table[i].mask;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include <zdwm/action.h>
|
||||
#include <zdwm/types.h>
|
||||
|
||||
#include "core/types.h"
|
||||
#include "interface/types.h"
|
||||
|
||||
typedef struct binding_table_t binding_table_t;
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "core/types.h"
|
||||
#include "core/wm_desc.h"
|
||||
#include "core/window_info.h"
|
||||
#include "interface/types.h"
|
||||
|
||||
typedef enum command_type_t {
|
||||
ZDWM_COMMAND_MANAGE_WINDOW,
|
||||
@@ -11,13 +11,19 @@ typedef enum command_type_t {
|
||||
ZDWM_COMMAND_RAISE_WINDOW,
|
||||
ZDWM_COMMAND_WITHDRAW_WINDOW,
|
||||
ZDWM_COMMAND_CONFIGURE_WINDOW,
|
||||
ZDWM_COMMAND_NOTIFY_CONFIGURE,
|
||||
ZDWM_COMMAND_CHANGE_WINDOW_STATE,
|
||||
ZDWM_COMMAND_START_MOVE_WINDOW,
|
||||
ZDWM_COMMAND_STOP_MOVE_WINDOW,
|
||||
ZDWM_COMMAND_START_RESIZE_WINDOW,
|
||||
ZDWM_COMMAND_STOP_RESIZE_WINDOW,
|
||||
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_CHANGE_HINTS,
|
||||
ZDWM_COMMAND_SWITCH_WORKSPACE,
|
||||
ZDWM_COMMAND_SET_CURRENT_OUTPUT,
|
||||
ZDWM_COMMAND_SET_LAYOUT,
|
||||
@@ -42,6 +48,11 @@ typedef struct window_state_change_command_t {
|
||||
window_state_request_action_t action;
|
||||
} window_state_change_command_t;
|
||||
|
||||
typedef struct window_start_move_command_t {
|
||||
window_id_t window;
|
||||
point_t pointer_coordinate;
|
||||
} start_interaction_command_t;
|
||||
|
||||
typedef struct window_send_to_workspace_command_t {
|
||||
window_id_t window;
|
||||
workspace_id_t workspace;
|
||||
@@ -92,13 +103,17 @@ typedef struct command_t {
|
||||
only_window_data_t raise;
|
||||
only_window_data_t withdraw;
|
||||
configure_data_t configure;
|
||||
only_window_data_t notify_configure;
|
||||
window_state_change_command_t state_change;
|
||||
start_interaction_command_t move;
|
||||
start_interaction_command_t resize;
|
||||
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;
|
||||
hints_data_t hints;
|
||||
switch_workspace_command_t switch_workspace;
|
||||
set_current_output_command_t current_output;
|
||||
set_layout_command_t layout;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
#include "base/array.h"
|
||||
#include "base/memory.h"
|
||||
#include "core/types.h"
|
||||
#include "interface/types.h"
|
||||
|
||||
void layer_stack_cleanup(layer_stack_t *layer) {
|
||||
p_delete(&layer->order);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "core/types.h"
|
||||
#include "interface/types.h"
|
||||
|
||||
typedef struct layer_stack_t {
|
||||
window_id_t *order;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
#include "base/array.h"
|
||||
#include "base/memory.h"
|
||||
#include "core/types.h"
|
||||
#include "interface/types.h"
|
||||
|
||||
void layout_result_cleanup(layout_result_t *result) {
|
||||
p_delete(&result->items);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include <stddef.h>
|
||||
#include <zdwm/layout.h>
|
||||
|
||||
#include "core/types.h"
|
||||
#include "interface/types.h"
|
||||
|
||||
typedef zdwm_layout_ctx_t layout_ctx_t;
|
||||
typedef zdwm_layout_item_t layout_item_t;
|
||||
|
||||
@@ -1,111 +1,11 @@
|
||||
#include "core/listeners.h"
|
||||
|
||||
#include "base/array.h"
|
||||
#include "base/memory.h"
|
||||
#include "common/listeners.h"
|
||||
#include "core/binding.h"
|
||||
#include "core/layout.h"
|
||||
#include "core/state.h"
|
||||
|
||||
#define ADD_LISTENER(FIELD) \
|
||||
auto list = &listeners->FIELD; \
|
||||
auto item = array_push(list->items, list->count, list->capacity); \
|
||||
item->fn = fn; \
|
||||
item->user_data = user_data
|
||||
|
||||
void listeners_add_output_listener(
|
||||
listeners_t *listeners,
|
||||
zdwm_current_output_id_listener fn,
|
||||
void *user_data
|
||||
) {
|
||||
ADD_LISTENER(output_listeners);
|
||||
}
|
||||
|
||||
void listeners_add_initial_workspace_listener(
|
||||
listeners_t *listeners,
|
||||
zdwm_initial_workspace_list fn,
|
||||
void *user_data
|
||||
) {
|
||||
ADD_LISTENER(initial_workspace_listeners);
|
||||
}
|
||||
|
||||
void listeners_add_active_workspace_listener(
|
||||
listeners_t *listeners,
|
||||
zdwm_workspace_active_updated fn,
|
||||
void *user_data
|
||||
) {
|
||||
ADD_LISTENER(active_workspace_listeners);
|
||||
}
|
||||
|
||||
void listeners_add_layout_notify(
|
||||
listeners_t *listeners,
|
||||
zdwm_layout_notify fn,
|
||||
void *user_data
|
||||
) {
|
||||
ADD_LISTENER(layout_listeners);
|
||||
}
|
||||
|
||||
void listeners_add_binding_mode_notify(
|
||||
listeners_t *listeners,
|
||||
zdwm_binding_mode_notify fn,
|
||||
void *user_data
|
||||
) {
|
||||
ADD_LISTENER(binding_mode_listeners);
|
||||
}
|
||||
|
||||
void listeners_add_initial_window_listener(
|
||||
listeners_t *listeners,
|
||||
zdwm_initial_window_list fn,
|
||||
void *user_data
|
||||
) {
|
||||
ADD_LISTENER(initial_window_listeners);
|
||||
}
|
||||
|
||||
void listeners_add_window_added_listener(
|
||||
listeners_t *listeners,
|
||||
zdwm_window_added fn,
|
||||
void *user_data
|
||||
) {
|
||||
ADD_LISTENER(window_added_listeners);
|
||||
}
|
||||
|
||||
void listeners_add_window_updated_listener(
|
||||
listeners_t *listeners,
|
||||
zdwm_window_updated fn,
|
||||
void *user_data
|
||||
) {
|
||||
ADD_LISTENER(window_updated_listeners);
|
||||
}
|
||||
|
||||
void listeners_add_window_removed_listener(
|
||||
listeners_t *listeners,
|
||||
zdwm_window_removed fn,
|
||||
void *user_data
|
||||
) {
|
||||
ADD_LISTENER(window_removed_listeners);
|
||||
}
|
||||
|
||||
#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
|
||||
#include "core/window.h"
|
||||
|
||||
#define FOR_EACH_LISTENER(FIELD, BODY) \
|
||||
do { \
|
||||
@@ -218,7 +118,7 @@ static bool listeners_window_snapshot(
|
||||
*out = (zdwm_window_t){
|
||||
.workspace = window->workspace_id,
|
||||
.id = window->id,
|
||||
.title = window->title,
|
||||
.title = window_get_showing_title(window),
|
||||
.focused = workspace->focused_window_id == window->id,
|
||||
.urgent = window->urgent,
|
||||
.skip_taskbar = window->skip_taskbar,
|
||||
|
||||
@@ -3,98 +3,8 @@
|
||||
#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; \
|
||||
void *user_data; \
|
||||
} NAME##_listener_item_t
|
||||
|
||||
#define LISTENERS(NAME, FN_TYPE) \
|
||||
ITEM(NAME, FN_TYPE); \
|
||||
typedef struct NAME##_listeners_t { \
|
||||
NAME##_listener_item_t *items; \
|
||||
size_t count; \
|
||||
size_t capacity; \
|
||||
} NAME##_listeners_t
|
||||
|
||||
LISTENERS(output, zdwm_current_output_id_listener);
|
||||
LISTENERS(initial_workspace_list, zdwm_initial_workspace_list);
|
||||
LISTENERS(workspace_active, zdwm_workspace_active_updated);
|
||||
LISTENERS(layout, zdwm_layout_notify);
|
||||
LISTENERS(binding_mode, zdwm_binding_mode_notify);
|
||||
LISTENERS(initial_window_list, zdwm_initial_window_list);
|
||||
LISTENERS(window_added, zdwm_window_added);
|
||||
LISTENERS(window_updated, zdwm_window_updated);
|
||||
LISTENERS(window_removed, zdwm_window_removed);
|
||||
|
||||
#undef ITEM
|
||||
#undef LISTENERS
|
||||
|
||||
typedef struct listeners_t {
|
||||
output_listeners_t output_listeners;
|
||||
initial_workspace_list_listeners_t initial_workspace_listeners;
|
||||
workspace_active_listeners_t active_workspace_listeners;
|
||||
layout_listeners_t layout_listeners;
|
||||
binding_mode_listeners_t binding_mode_listeners;
|
||||
initial_window_list_listeners_t initial_window_listeners;
|
||||
window_added_listeners_t window_added_listeners;
|
||||
window_updated_listeners_t window_updated_listeners;
|
||||
window_removed_listeners_t window_removed_listeners;
|
||||
} listeners_t;
|
||||
|
||||
void listeners_add_output_listener(
|
||||
listeners_t *listeners,
|
||||
zdwm_current_output_id_listener fn,
|
||||
void *user_data
|
||||
);
|
||||
void listeners_add_initial_workspace_listener(
|
||||
listeners_t *listeners,
|
||||
zdwm_initial_workspace_list fn,
|
||||
void *user_data
|
||||
);
|
||||
void listeners_add_active_workspace_listener(
|
||||
listeners_t *listeners,
|
||||
zdwm_workspace_active_updated fn,
|
||||
void *user_data
|
||||
);
|
||||
void listeners_add_layout_notify(
|
||||
listeners_t *listeners,
|
||||
zdwm_layout_notify fn,
|
||||
void *user_data
|
||||
);
|
||||
void listeners_add_binding_mode_notify(
|
||||
listeners_t *listeners,
|
||||
zdwm_binding_mode_notify fn,
|
||||
void *user_data
|
||||
);
|
||||
void listeners_add_initial_window_listener(
|
||||
listeners_t *listeners,
|
||||
zdwm_initial_window_list fn,
|
||||
void *user_data
|
||||
);
|
||||
void listeners_add_window_added_listener(
|
||||
listeners_t *listeners,
|
||||
zdwm_window_added fn,
|
||||
void *user_data
|
||||
);
|
||||
void listeners_add_window_updated_listener(
|
||||
listeners_t *listeners,
|
||||
zdwm_window_updated fn,
|
||||
void *user_data
|
||||
);
|
||||
void listeners_add_window_removed_listener(
|
||||
listeners_t *listeners,
|
||||
zdwm_window_removed fn,
|
||||
void *user_data
|
||||
);
|
||||
|
||||
void listeners_cleanup(listeners_t *listeners);
|
||||
#include "common/listeners.h"
|
||||
#include "interface/types.h"
|
||||
|
||||
/**
|
||||
* @brief 通知当前 output 信息
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
|
||||
#include "base/array.h"
|
||||
#include "base/memory.h"
|
||||
#include "core/types.h"
|
||||
#include "interface/effect.h"
|
||||
#include "interface/types.h"
|
||||
|
||||
static void free_memory_hold_by_effects(effect_t *effects, size_t count) {
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
@@ -20,6 +21,8 @@ static void free_memory_hold_by_effects(effect_t *effects, size_t count) {
|
||||
case ZDWM_EFFECT_BIND_KEY:
|
||||
p_delete(&effect->as.bind_key.keys);
|
||||
break;
|
||||
case ZDWM_EFFECT_GRAB_BUTTON:
|
||||
p_delete(&effect->as.grab_button.buttons);
|
||||
default:
|
||||
}
|
||||
}
|
||||
@@ -98,6 +101,36 @@ void plan_push_withdraw_effect(plan_t *plan, window_id_t window_id) {
|
||||
plan_push_effect(plan, &effect);
|
||||
}
|
||||
|
||||
void plan_push_move_effect(plan_t *plan, window_id_t window_id) {
|
||||
if (window_id_invalid(window_id)) return;
|
||||
|
||||
effect_t effect = {
|
||||
.type = ZDWM_EFFECT_START_MOVE_WINDOW,
|
||||
.as.move.window = window_id,
|
||||
};
|
||||
plan_push_effect(plan, &effect);
|
||||
}
|
||||
|
||||
void plan_push_resize_effect(plan_t *plan, window_id_t window_id) {
|
||||
if (window_id_invalid(window_id)) return;
|
||||
|
||||
effect_t effect = {
|
||||
.type = ZDWM_EFFECT_START_RESIZE_WINDOW,
|
||||
.as.resize.window = window_id,
|
||||
};
|
||||
plan_push_effect(plan, &effect);
|
||||
}
|
||||
|
||||
void plan_push_configure_notify_effect(plan_t *plan, window_id_t window_id) {
|
||||
if (window_id_invalid(window_id)) return;
|
||||
|
||||
effect_t effect = {
|
||||
.type = ZDWM_EFFECT_CONFIGURE_NOTIFY,
|
||||
.as.configure_notify.window = window_id,
|
||||
};
|
||||
plan_push_effect(plan, &effect);
|
||||
}
|
||||
|
||||
void plan_push_fullscreen_effect(
|
||||
plan_t *plan,
|
||||
window_id_t window_id,
|
||||
@@ -165,3 +198,8 @@ void plan_push_change_border_color_effect(
|
||||
};
|
||||
plan_push_effect(plan, &effect);
|
||||
}
|
||||
|
||||
void plan_push_ungrab_pointer(plan_t *plan) {
|
||||
effect_t effect = {.type = ZDWM_EFFECT_UNGRAB_POINTER};
|
||||
plan_push_effect(plan, &effect);
|
||||
}
|
||||
|
||||
@@ -1,76 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "base/color.h"
|
||||
#include "core/types.h"
|
||||
|
||||
typedef enum effect_type_t {
|
||||
ZDWM_EFFECT_MAP_WINDOW = 1,
|
||||
ZDWM_EFFECT_UNMAP_WINDOW,
|
||||
ZDWM_EFFECT_FOCUS_WINDOW,
|
||||
ZDWM_EFFECT_KILL_WINDOW,
|
||||
ZDWM_EFFECT_WITHDRAW_WINDOW,
|
||||
ZDWM_EFFECT_MINIMIZE_WINDOW,
|
||||
ZDWM_EFFECT_MAXIMIZE_WINDOW,
|
||||
ZDWM_EFFECT_FULLSCREEN_WINDOW,
|
||||
ZDWM_EFFECT_CONFIGURE_WINDOW,
|
||||
ZDWM_EFFECT_CHANGE_BORDER_COLOR,
|
||||
ZDWM_EFFECT_CHANGE_WINDOW_LIST,
|
||||
ZDWM_EFFECT_RESTACK_WINDOWS,
|
||||
ZDWM_EFFECT_BIND_KEY,
|
||||
} effect_type_t;
|
||||
|
||||
typedef struct effect_move_window_t {
|
||||
window_id_t window;
|
||||
point_t left_top_point;
|
||||
} effect_move_window_t;
|
||||
|
||||
typedef struct effect_resize_window_t {
|
||||
window_id_t window;
|
||||
int32_t width;
|
||||
int32_t height;
|
||||
} effect_resize_window_t;
|
||||
|
||||
typedef struct effect_change_border_color_t {
|
||||
window_id_t window;
|
||||
const color_t *color;
|
||||
} effect_change_border_color_t;
|
||||
|
||||
typedef struct effect_window_list_t {
|
||||
const window_id_t *windows;
|
||||
size_t count;
|
||||
} effect_window_list_t;
|
||||
|
||||
typedef struct effect_bind_key_t {
|
||||
const key_bind_t *keys;
|
||||
size_t count;
|
||||
} effect_bind_key_t;
|
||||
|
||||
typedef struct effect_bool_window_t {
|
||||
window_id_t window;
|
||||
bool value;
|
||||
} effect_bool_window_t;
|
||||
|
||||
typedef struct effect_t {
|
||||
effect_type_t type;
|
||||
union {
|
||||
only_window_data_t map;
|
||||
only_window_data_t unmap;
|
||||
only_window_data_t focus;
|
||||
only_window_data_t kill;
|
||||
only_window_data_t withdraw;
|
||||
effect_bool_window_t minimize;
|
||||
effect_bool_window_t maximize;
|
||||
effect_bool_window_t fullscreen;
|
||||
configure_data_t configure;
|
||||
effect_change_border_color_t change_border_color;
|
||||
effect_window_list_t change_window_list;
|
||||
effect_window_list_t restack_windows;
|
||||
effect_bind_key_t bind_key;
|
||||
} as;
|
||||
} effect_t;
|
||||
#include "interface/effect.h"
|
||||
#include "interface/types.h"
|
||||
|
||||
typedef struct plan_t {
|
||||
effect_t *effects;
|
||||
@@ -90,6 +24,9 @@ void plan_push_unmap_effect(plan_t *plan, window_id_t window_id);
|
||||
void plan_push_focus_effect(plan_t *plan, window_id_t window_id);
|
||||
void plan_push_kill_effect(plan_t *plan, window_id_t window_id);
|
||||
void plan_push_withdraw_effect(plan_t *plan, window_id_t window_id);
|
||||
void plan_push_move_effect(plan_t *plan, window_id_t window_id);
|
||||
void plan_push_resize_effect(plan_t *plan, window_id_t window_id);
|
||||
void plan_push_configure_notify_effect(plan_t *plan, window_id_t window_id);
|
||||
void plan_push_fullscreen_effect(
|
||||
plan_t *plan,
|
||||
window_id_t window_id,
|
||||
@@ -102,3 +39,4 @@ void plan_push_change_border_color_effect(
|
||||
window_id_t window_id,
|
||||
const color_t *color
|
||||
);
|
||||
void plan_push_ungrab_pointer(plan_t *plan);
|
||||
|
||||
@@ -2,25 +2,27 @@
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.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 "base/time.h"
|
||||
#include "common/listeners.h"
|
||||
#include "common/window.h"
|
||||
#include "core/binding.h"
|
||||
#include "core/command.h"
|
||||
#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"
|
||||
#include "core/types.h"
|
||||
#include "core/window.h"
|
||||
#include "core/wm_desc.h"
|
||||
#include "interface/event.h"
|
||||
#include "interface/types.h"
|
||||
|
||||
static void quit(bool restart, command_buffer_t *command_buffer) {
|
||||
command_t quit_command = {
|
||||
@@ -369,7 +371,7 @@ static void toggle_bar_visibility(
|
||||
command_buffer_push(command_buffer, &set_bar_visibility_command);
|
||||
}
|
||||
|
||||
static void policy_resolve_action(
|
||||
void policy_resolve_action(
|
||||
const policy_context_t *ctx,
|
||||
const zdwm_action_t *action,
|
||||
command_buffer_t *out
|
||||
@@ -459,6 +461,130 @@ static void route_key_press(
|
||||
}
|
||||
}
|
||||
|
||||
static void route_pointer_press(
|
||||
const policy_context_t *ctx,
|
||||
const pointer_button_event_t *data,
|
||||
command_buffer_t *out
|
||||
) {
|
||||
auto window_id = data->window;
|
||||
auto window = state_window_get(ctx->state, window_id);
|
||||
if (!window) return;
|
||||
|
||||
/* TODO: 后续改为配置 */
|
||||
if (data->button == ZDWM_BUTTON_LEFT && data->modifiers == ZDWM_MOD_4) {
|
||||
command_t start_move_cmd = {
|
||||
.type = ZDWM_COMMAND_START_MOVE_WINDOW,
|
||||
.as.move = {
|
||||
.window = window_id,
|
||||
.pointer_coordinate = data->root,
|
||||
},
|
||||
};
|
||||
command_buffer_push(out, &start_move_cmd);
|
||||
}
|
||||
if (data->button == ZDWM_BUTTON_RIGHT && data->modifiers == ZDWM_MOD_4) {
|
||||
command_t start_resize_cmd = {
|
||||
.type = ZDWM_COMMAND_START_RESIZE_WINDOW,
|
||||
.as.resize = {
|
||||
.window = window_id,
|
||||
.pointer_coordinate = data->root,
|
||||
}
|
||||
};
|
||||
command_buffer_push(out, &start_resize_cmd);
|
||||
}
|
||||
}
|
||||
|
||||
static void route_pointer_release(
|
||||
const policy_context_t *ctx,
|
||||
const pointer_button_event_t *data,
|
||||
command_buffer_t *out
|
||||
) {
|
||||
switch (ctx->interaction->mode) {
|
||||
case ZDWM_WINDOW_INTERACTION_NONE:
|
||||
break;
|
||||
case ZDWM_WINDOW_INTERACTION_MOVE: {
|
||||
command_t stop_move_cmd = {.type = ZDWM_COMMAND_STOP_MOVE_WINDOW};
|
||||
command_buffer_push(out, &stop_move_cmd);
|
||||
} break;
|
||||
case ZDWM_WINDOW_INTERACTION_RESIZE: {
|
||||
command_t stop_resize_cmd = {.type = ZDWM_COMMAND_STOP_RESIZE_WINDOW};
|
||||
command_buffer_push(out, &stop_resize_cmd);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
static void route_pointer_motion(
|
||||
const policy_context_t *ctx,
|
||||
const pointer_motion_event_t *data,
|
||||
command_buffer_t *out
|
||||
) {
|
||||
auto interaction = ctx->interaction;
|
||||
auto window_id = interaction->window;
|
||||
|
||||
switch (interaction->mode) {
|
||||
case ZDWM_WINDOW_INTERACTION_NONE:
|
||||
break;
|
||||
case ZDWM_WINDOW_INTERACTION_MOVE: {
|
||||
auto time = interaction->last_change_time;
|
||||
auto now = time_monotonic_ms();
|
||||
if (now - time < 1000 / ctx->fps) break;
|
||||
|
||||
auto rect = interaction->origin_rect;
|
||||
auto start = interaction->start_coordinate;
|
||||
auto end = data->root;
|
||||
|
||||
command_t configure = {
|
||||
.type = ZDWM_COMMAND_CONFIGURE_WINDOW,
|
||||
.as.configure = {
|
||||
.window = interaction->window,
|
||||
.changed_fields = ZDWM_CONFIGURE_FIELD_X | ZDWM_CONFIGURE_FIELD_Y,
|
||||
.x = rect.x + (end.x - start.x),
|
||||
.y = rect.y + (end.y - start.y),
|
||||
},
|
||||
};
|
||||
command_buffer_push(out, &configure);
|
||||
|
||||
interaction->last_change_time = now;
|
||||
} break;
|
||||
case ZDWM_WINDOW_INTERACTION_RESIZE: {
|
||||
auto time = interaction->last_change_time;
|
||||
auto now = time_monotonic_ms();
|
||||
if (now - time < 1000 / ctx->fps) break;
|
||||
|
||||
auto window = state_window_get(ctx->state, window_id);
|
||||
if (!window_can_resize(window)) break;
|
||||
|
||||
auto rect = interaction->origin_rect;
|
||||
auto start = interaction->start_coordinate;
|
||||
auto end = data->root;
|
||||
|
||||
auto min_size = window->min_size;
|
||||
auto max_size = window->max_size;
|
||||
|
||||
auto width = rect.width + (end.x - start.x);
|
||||
auto height = rect.height + (end.y - start.y);
|
||||
width = MAX(width, min_size.width);
|
||||
height = MAX(height, min_size.height);
|
||||
width = MIN(width, max_size.width);
|
||||
height = MIN(height, max_size.height);
|
||||
|
||||
if (!window_need_resize(window, width, height)) break;
|
||||
|
||||
command_t configure_cmd = {
|
||||
.type = ZDWM_COMMAND_CONFIGURE_WINDOW,
|
||||
.as.configure = {
|
||||
.window = interaction->window,
|
||||
.changed_fields =
|
||||
ZDWM_CONFIGURE_FIELD_WIDTH | ZDWM_CONFIGURE_FIELD_HEIGHT,
|
||||
.width = width,
|
||||
.height = height,
|
||||
}
|
||||
};
|
||||
command_buffer_push(out, &configure_cmd);
|
||||
interaction->last_change_time = now;
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
route_pointer_enter(state_t *state, window_id_t window, command_buffer_t *out) {
|
||||
add_focus_window_command(out, window);
|
||||
@@ -483,6 +609,7 @@ static void route_map_request(
|
||||
if (window) layer_type = MAX(window->layer, layer_type);
|
||||
}
|
||||
|
||||
/* clang-format off */
|
||||
command_t manage_window_cmd = {
|
||||
.type = ZDWM_COMMAND_MANAGE_WINDOW,
|
||||
.as.manage_window = {
|
||||
@@ -503,11 +630,13 @@ static void route_map_request(
|
||||
.maximized = e->maximized,
|
||||
.minimized = e->minimized,
|
||||
.urgent = e->urgent,
|
||||
.fixed_size = e->fixed_size,
|
||||
.skip_taskbar = e->skip_taskbar,
|
||||
.min_size = e->min_size,
|
||||
.max_size = e->max_size,
|
||||
},
|
||||
},
|
||||
};
|
||||
/* clang-format on */
|
||||
|
||||
rule_action_t action = {.workspace = ZDWM_WORKSPACE_ID_INVALID};
|
||||
bool have_rule_match = rules_resolve(rules, &e->metadata, &action);
|
||||
@@ -558,9 +687,11 @@ static void route_window_remove(
|
||||
}
|
||||
|
||||
static void route_window_metadata_changed(
|
||||
state_t *state,
|
||||
const policy_context_t *ctx,
|
||||
const window_metadata_change_event_t *e
|
||||
) {
|
||||
auto state = ctx->state;
|
||||
|
||||
auto window = state_window_get(state, e->window);
|
||||
if (!window) return;
|
||||
|
||||
@@ -582,6 +713,22 @@ static void route_window_metadata_changed(
|
||||
if (e->changed_fields & ZDWM_WINDOW_METADATA_CHANGE_INSTANCE) {
|
||||
state_window_set_instance(state, window_id, metadata->instance_name);
|
||||
}
|
||||
listeners_notify_window_updated(ctx->listeners, state, window_id);
|
||||
}
|
||||
|
||||
static void route_window_hints_changed(
|
||||
state_t *state,
|
||||
const hints_data_t *data,
|
||||
command_buffer_t *out
|
||||
) {
|
||||
auto window = (window_t *)state_window_get(state, data->window);
|
||||
if (!window) return;
|
||||
|
||||
command_t change_hints_cmd = {
|
||||
.type = ZDWM_COMMAND_CHANGE_HINTS,
|
||||
.as.hints = *data,
|
||||
};
|
||||
command_buffer_push(out, &change_hints_cmd);
|
||||
}
|
||||
|
||||
static void route_window_activate_request(
|
||||
@@ -589,23 +736,15 @@ static void route_window_activate_request(
|
||||
const window_activate_request_event_t *e,
|
||||
command_buffer_t *out
|
||||
) {
|
||||
auto window = state_window_get(state, e->window);
|
||||
auto window = (window_t *)state_window_get(state, e->window);
|
||||
if (!window) return;
|
||||
|
||||
switch (e->source) {
|
||||
case ZDWM_WINDOW_ACTIVATION_SOURCE_LEGACY:
|
||||
break;
|
||||
case ZDWM_WINDOW_ACTIVATION_SOURCE_APPLICATION: {
|
||||
command_t change_window_state_cmd = {
|
||||
.type = ZDWM_COMMAND_CHANGE_WINDOW_STATE,
|
||||
.as.state_change = {
|
||||
.type = ZDWM_WINDOW_STATE_REQUEST_URGENT,
|
||||
.window = window->id,
|
||||
.action = ZDWM_WINDOW_STATE_ACTION_ADD,
|
||||
}
|
||||
};
|
||||
command_buffer_push(out, &change_window_state_cmd);
|
||||
} break;
|
||||
case ZDWM_WINDOW_ACTIVATION_SOURCE_APPLICATION:
|
||||
window->urgent = true;
|
||||
break;
|
||||
case ZDWM_WINDOW_ACTIVATION_SOURCE_PAGER:
|
||||
add_switch_workspace_command(out, window->workspace_id);
|
||||
add_focus_window_command(out, window->id);
|
||||
@@ -632,6 +771,15 @@ static void route_window_state_request(
|
||||
command_buffer_push(out, &change_window_state_cmd);
|
||||
}
|
||||
|
||||
static void
|
||||
add_notify_configure_command(command_buffer_t *out, window_id_t window) {
|
||||
command_t notify_cmd = {
|
||||
.type = ZDWM_COMMAND_NOTIFY_CONFIGURE,
|
||||
.as.notify_configure = {.window = window},
|
||||
};
|
||||
command_buffer_push(out, ¬ify_cmd);
|
||||
}
|
||||
|
||||
static void route_configure_request(
|
||||
state_t *state,
|
||||
const configure_data_t *data,
|
||||
@@ -649,6 +797,7 @@ static void route_configure_request(
|
||||
}
|
||||
|
||||
if (!state_workspace_show(state, window->workspace_id)) return;
|
||||
add_notify_configure_command(out, data->window);
|
||||
if (window_need_layout(window)) return;
|
||||
auto workspace = state_workspace_get(state, window->workspace_id);
|
||||
if (layout_get(layouts, workspace->layout_id)) return;
|
||||
@@ -670,9 +819,20 @@ void policy_route_event(
|
||||
) {
|
||||
auto state = ctx->state;
|
||||
switch (event->type) {
|
||||
case ZDWM_EVENT_NONE:
|
||||
break;
|
||||
case ZDWM_EVENT_KEY_PRESS:
|
||||
route_key_press(ctx, &event->as.key_press, out);
|
||||
break;
|
||||
case ZDWM_EVENT_POINTER_BUTTON_PRESS:
|
||||
route_pointer_press(ctx, &event->as.pointer_button_press, out);
|
||||
break;
|
||||
case ZDWM_EVENT_POINTER_BUTTON_RELEASE:
|
||||
route_pointer_release(ctx, &event->as.pointer_button_release, out);
|
||||
break;
|
||||
case ZDWM_EVENT_POINTER_MOTION:
|
||||
route_pointer_motion(ctx, &event->as.pointer_motion, out);
|
||||
break;
|
||||
case ZDWM_EVENT_POINTER_ENTER:
|
||||
route_pointer_enter(state, event->as.pointer_enter.window, out);
|
||||
break;
|
||||
@@ -683,7 +843,10 @@ void policy_route_event(
|
||||
route_window_remove(state, &event->as.window_remove, out);
|
||||
break;
|
||||
case ZDWM_EVENT_WINDOW_METADATA_CHANGED:
|
||||
route_window_metadata_changed(state, &event->as.window_metadata_change);
|
||||
route_window_metadata_changed(ctx, &event->as.window_metadata_change);
|
||||
break;
|
||||
case ZDWM_EVENT_WINDOW_HINTS_CHANGED:
|
||||
route_window_hints_changed(state, &event->as.hints, out);
|
||||
break;
|
||||
case ZDWM_EVENT_WINDOW_ACTIVATE_REQUEST: {
|
||||
auto data = &event->as.window_activate_request;
|
||||
@@ -696,7 +859,6 @@ void policy_route_event(
|
||||
auto data = &event->as.configure_request;
|
||||
route_configure_request(state, data, ctx->layouts, out);
|
||||
} break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
@@ -727,9 +889,6 @@ static void set_foucs_window(
|
||||
auto workspace = state_workspace_get(state, workspace_id);
|
||||
if (!workspace) return;
|
||||
|
||||
auto old_focused_window_id = workspace->focused_window_id;
|
||||
if (window_id == old_focused_window_id) return;
|
||||
|
||||
state_workspace_set_focused_window(state, workspace_id, window_id);
|
||||
plan_push_focus_effect(plan, window_id);
|
||||
|
||||
@@ -737,9 +896,12 @@ static void set_foucs_window(
|
||||
auto color = &border->focused_color;
|
||||
plan_push_change_border_color_effect(plan, window_id, color);
|
||||
}
|
||||
if (state_window_get(state, old_focused_window_id)) {
|
||||
for (size_t i = 0; i < state_window_count(state); ++i) {
|
||||
auto window = state_window_at(state, i);
|
||||
if (window->workspace_id != workspace_id) continue;
|
||||
if (workspace->focused_window_id == window->id) continue;
|
||||
auto color = &border->normal_color;
|
||||
plan_push_change_border_color_effect(plan, old_focused_window_id, color);
|
||||
plan_push_change_border_color_effect(plan, window->id, color);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -759,6 +921,24 @@ static void push_window_list_effect(const state_t *state, plan_t *plan) {
|
||||
plan_push_effect(plan, &effect);
|
||||
}
|
||||
|
||||
static void plan_push_grab_button_effect(plan_t *plan, window_id_t window) {
|
||||
/* TODO: 后续会改成配置 */
|
||||
static const grab_button_t buttons[] = {
|
||||
[0] = {.modifiers = ZDWM_MOD_4, .button = ZDWM_BUTTON_LEFT},
|
||||
[1] = {.modifiers = ZDWM_MOD_4, .button = ZDWM_BUTTON_RIGHT},
|
||||
};
|
||||
|
||||
constexpr const size_t count = countof(buttons);
|
||||
auto btns = p_new(grab_button_t, count);
|
||||
memcpy(btns, buttons, sizeof(buttons));
|
||||
|
||||
effect_t grab_button_effect = {
|
||||
.type = ZDWM_EFFECT_GRAB_BUTTON,
|
||||
.as.grab_button = {.window = window, .buttons = btns, .count = count},
|
||||
};
|
||||
plan_push_effect(plan, &grab_button_effect);
|
||||
}
|
||||
|
||||
static void manage_window(
|
||||
const policy_context_t *ctx,
|
||||
const manage_window_command_t *command,
|
||||
@@ -768,14 +948,25 @@ static void manage_window(
|
||||
auto window = state_window_add(state, &command->info);
|
||||
auto window_id = window->id;
|
||||
auto workspace_id = command->workspace;
|
||||
auto workspace = state_workspace_get(state, workspace_id);
|
||||
auto old_focused_window = workspace->focused_window_id;
|
||||
state_window_set_workspace(state, window_id, workspace_id);
|
||||
state_window_set_floating(state, window_id, command->floating);
|
||||
set_foucs_window(ctx, workspace_id, window_id, plan);
|
||||
plan_push_grab_button_effect(plan, window_id);
|
||||
push_window_list_effect(state, plan);
|
||||
listeners_notify_window_updated(ctx->listeners, state, old_focused_window);
|
||||
listeners_notify_window_added(ctx->listeners, state, window_id);
|
||||
|
||||
if (window_should_fix_size(window)) {
|
||||
state_window_set_floating(state, window_id, true);
|
||||
} else {
|
||||
state_window_set_floating(state, window_id, command->floating);
|
||||
}
|
||||
|
||||
auto need_layout = window_need_layout(window);
|
||||
if (need_layout) {
|
||||
auto layout_func = layout_get(ctx->layouts, workspace->layout_id);
|
||||
|
||||
if (need_layout && layout_func) {
|
||||
adjust_layout_windows_border_width(state, ctx->border->width, workspace_id);
|
||||
} else {
|
||||
state_window_set_border_width(state, window_id, ctx->border->width);
|
||||
@@ -786,12 +977,8 @@ static void manage_window(
|
||||
plan_push_map_effect(plan, window_id);
|
||||
plan_push_focus_effect(plan, window_id);
|
||||
|
||||
if (window_need_layout(window)) {
|
||||
plan->need_relayout = true;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
effect_t configure_effect = {
|
||||
.type = ZDWM_EFFECT_CONFIGURE_WINDOW,
|
||||
.as.configure = {
|
||||
@@ -857,8 +1044,10 @@ unmanage_window(const policy_context_t *ctx, window_id_t window, plan_t *plan) {
|
||||
|
||||
if (output->current_workspace_id != workspace_id) return;
|
||||
|
||||
if (old_focused_window != workspace->focused_window_id) {
|
||||
plan_push_focus_effect(plan, workspace->focused_window_id);
|
||||
auto focused_window_id = workspace->focused_window_id;
|
||||
if (old_focused_window != focused_window_id) {
|
||||
plan_push_focus_effect(plan, focused_window_id);
|
||||
listeners_notify_window_updated(ctx->listeners, state, focused_window_id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -877,6 +1066,7 @@ focus_window(const policy_context_t *ctx, window_id_t window, plan_t *plan) {
|
||||
|
||||
plan_push_focus_effect(plan, workspace->focused_window_id);
|
||||
listeners_notify_window_updated(ctx->listeners, state, window);
|
||||
listeners_notify_window_updated(ctx->listeners, state, old_focused_window);
|
||||
}
|
||||
|
||||
static void kill_window(state_t *state, window_id_t window, plan_t *plan) {
|
||||
@@ -1160,12 +1350,6 @@ static void change_window_state(
|
||||
case ZDWM_WINDOW_STATE_REQUEST_SKIP_TASKBAR:
|
||||
value = !window->skip_taskbar;
|
||||
break;
|
||||
case ZDWM_WINDOW_STATE_REQUEST_URGENT:
|
||||
value = !window->urgent;
|
||||
break;
|
||||
case ZDWM_WINDOW_STATE_REQUEST_FIXED_SIZE:
|
||||
value = !window->fixed_size;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1183,17 +1367,56 @@ static void change_window_state(
|
||||
case ZDWM_WINDOW_STATE_REQUEST_SKIP_TASKBAR:
|
||||
state_window_set_skip_taskbar(state, window->id, value);
|
||||
break;
|
||||
case ZDWM_WINDOW_STATE_REQUEST_URGENT:
|
||||
state_window_set_urgent(state, window->id, value);
|
||||
break;
|
||||
case ZDWM_WINDOW_STATE_REQUEST_FIXED_SIZE:
|
||||
state_window_set_fixed_size(state, window->id, value);
|
||||
break;
|
||||
}
|
||||
|
||||
listeners_notify_window_updated(ctx->listeners, state, command->window);
|
||||
}
|
||||
|
||||
static void start_window_move(
|
||||
const policy_context_t *ctx,
|
||||
const start_interaction_command_t *command,
|
||||
plan_t *plan
|
||||
) {
|
||||
auto window_id = command->window;
|
||||
auto window = state_window_get(ctx->state, window_id);
|
||||
if (!window) return;
|
||||
|
||||
plan_push_move_effect(plan, window_id);
|
||||
|
||||
*ctx->interaction = (window_interaction_state_t){
|
||||
.mode = ZDWM_WINDOW_INTERACTION_MOVE,
|
||||
.window = window_id,
|
||||
.start_coordinate = command->pointer_coordinate,
|
||||
.origin_rect = window->frame_rect,
|
||||
};
|
||||
}
|
||||
|
||||
static void start_window_resize(
|
||||
const policy_context_t *ctx,
|
||||
const start_interaction_command_t *command,
|
||||
plan_t *plan
|
||||
) {
|
||||
auto window_id = command->window;
|
||||
auto window = state_window_get(ctx->state, window_id);
|
||||
if (!window) return;
|
||||
|
||||
auto frame_rect = window->frame_rect;
|
||||
|
||||
point_t pointer_position = {
|
||||
.x = frame_rect.x + frame_rect.width,
|
||||
.y = frame_rect.y + frame_rect.height,
|
||||
};
|
||||
|
||||
plan_push_resize_effect(plan, window_id);
|
||||
|
||||
*ctx->interaction = (window_interaction_state_t){
|
||||
.mode = ZDWM_WINDOW_INTERACTION_RESIZE,
|
||||
.window = window_id,
|
||||
.start_coordinate = pointer_position,
|
||||
.origin_rect = frame_rect,
|
||||
};
|
||||
}
|
||||
|
||||
static void send_window_to_workspace(
|
||||
const policy_context_t *ctx,
|
||||
const window_send_to_workspace_command_t *command,
|
||||
@@ -1222,16 +1445,20 @@ static void send_window_to_workspace(
|
||||
|
||||
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)) {
|
||||
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)) {
|
||||
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,
|
||||
@@ -1260,8 +1487,10 @@ static void send_window_to_workspace(
|
||||
|
||||
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)) {
|
||||
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,
|
||||
@@ -1308,12 +1537,20 @@ static void command_window_set_maximized(
|
||||
maximize_window(ctx, data->window, data->state, plan);
|
||||
}
|
||||
|
||||
static void command_window_set_fullscreen(
|
||||
const policy_context_t *ctx,
|
||||
const window_bool_state_t *data,
|
||||
plan_t *plan
|
||||
) {
|
||||
fullscreen_window(ctx, data->window, data->state, plan);
|
||||
static void
|
||||
command_change_hints(state_t *state, hints_data_t *data, plan_t *plan) {
|
||||
auto window = (window_t *)state_window_get(state, data->window);
|
||||
if (data->changed_fields & ZDWM_HINT_FIELD_URGENT) {
|
||||
window->urgent = data->urgent;
|
||||
}
|
||||
if (data->changed_fields & ZDWM_HINT_FIELD_SIZE) {
|
||||
window->max_size = data->max_size;
|
||||
window->min_size = data->min_size;
|
||||
|
||||
auto floating = window->floating;
|
||||
if (window_should_fix_size(window)) window_set_floating(window, true);
|
||||
if (floating != window->floating) plan->need_relayout = true;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void change_current_output(
|
||||
@@ -1338,12 +1575,14 @@ static void switch_workspace(
|
||||
auto output_id = workspace->output_id;
|
||||
workspace_id_t old_workspace = ZDWM_WORKSPACE_ID_INVALID;
|
||||
|
||||
if (state_output_set_current_workspace(
|
||||
if (
|
||||
state_output_set_current_workspace(
|
||||
state,
|
||||
output_id,
|
||||
workspace_id,
|
||||
&old_workspace
|
||||
)) {
|
||||
)
|
||||
) {
|
||||
plan->need_relayout = true;
|
||||
add_switch_workspace_effects(state, old_workspace, workspace_id, plan);
|
||||
listeners_notify_workspace_active(listeners, output_id, workspace_id);
|
||||
@@ -1463,9 +1702,23 @@ void policy_apply_command(
|
||||
case ZDWM_COMMAND_CONFIGURE_WINDOW:
|
||||
configure_window(state, &cmd->as.configure, plan);
|
||||
break;
|
||||
case ZDWM_COMMAND_NOTIFY_CONFIGURE:
|
||||
plan_push_configure_notify_effect(plan, cmd->as.notify_configure.window);
|
||||
break;
|
||||
case ZDWM_COMMAND_CHANGE_WINDOW_STATE:
|
||||
change_window_state(ctx, &cmd->as.state_change, plan);
|
||||
break;
|
||||
case ZDWM_COMMAND_START_MOVE_WINDOW:
|
||||
start_window_move(ctx, &cmd->as.move, plan);
|
||||
break;
|
||||
case ZDWM_COMMAND_START_RESIZE_WINDOW:
|
||||
start_window_resize(ctx, &cmd->as.resize, plan);
|
||||
break;
|
||||
case ZDWM_COMMAND_STOP_MOVE_WINDOW:
|
||||
case ZDWM_COMMAND_STOP_RESIZE_WINDOW:
|
||||
plan_push_ungrab_pointer(plan);
|
||||
p_clear(ctx->interaction, 1);
|
||||
break;
|
||||
case ZDWM_COMMAND_WINDOW_SEND_TO_WORKSPACE:
|
||||
send_window_to_workspace(ctx, &cmd->as.send_to_workspace, plan);
|
||||
break;
|
||||
@@ -1481,8 +1734,13 @@ void policy_apply_command(
|
||||
case ZDWM_COMMAND_WINDOW_SET_MAXIMIZED:
|
||||
command_window_set_maximized(ctx, &cmd->as.maximized, plan);
|
||||
break;
|
||||
case ZDWM_COMMAND_WINDOW_SET_FULLSCREEN:
|
||||
command_window_set_fullscreen(ctx, &cmd->as.fullscreen, plan);
|
||||
case ZDWM_COMMAND_WINDOW_SET_FULLSCREEN: {
|
||||
auto window = cmd->as.fullscreen.window;
|
||||
auto state = cmd->as.fullscreen.state;
|
||||
fullscreen_window(ctx, window, state, plan);
|
||||
} break;
|
||||
case ZDWM_COMMAND_CHANGE_HINTS:
|
||||
command_change_hints(state, &cmd->as.hints, plan);
|
||||
break;
|
||||
case ZDWM_COMMAND_SWITCH_WORKSPACE:
|
||||
switch_workspace(ctx, cmd->as.switch_workspace.workspace, plan);
|
||||
|
||||
@@ -3,16 +3,29 @@
|
||||
#include <stdint.h>
|
||||
#include <zdwm/action.h>
|
||||
|
||||
#include "base/window_list.h"
|
||||
#include "common/listeners.h"
|
||||
#include "common/window.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/rules.h"
|
||||
#include "core/state.h"
|
||||
#include "core/types.h"
|
||||
#include "interface/event.h"
|
||||
|
||||
typedef enum window_interaction_mode_t {
|
||||
ZDWM_WINDOW_INTERACTION_NONE,
|
||||
ZDWM_WINDOW_INTERACTION_MOVE,
|
||||
ZDWM_WINDOW_INTERACTION_RESIZE,
|
||||
} window_interaction_mode_t;
|
||||
|
||||
typedef struct window_interaction_state_t {
|
||||
window_interaction_mode_t mode;
|
||||
window_id_t window;
|
||||
point_t start_coordinate;
|
||||
rect_t origin_rect;
|
||||
uint64_t last_change_time;
|
||||
} window_interaction_state_t;
|
||||
|
||||
typedef struct policy_bar_t {
|
||||
bool *visible;
|
||||
@@ -22,6 +35,8 @@ typedef struct policy_bar_t {
|
||||
} policy_bar_t;
|
||||
|
||||
typedef struct policy_context_t {
|
||||
uint32_t fps;
|
||||
window_interaction_state_t *interaction;
|
||||
binding_table_t *bind_table;
|
||||
state_t *state;
|
||||
const rules_t *rules;
|
||||
@@ -31,6 +46,12 @@ typedef struct policy_context_t {
|
||||
const policy_bar_t bar;
|
||||
} policy_context_t;
|
||||
|
||||
void policy_resolve_action(
|
||||
const policy_context_t *ctx,
|
||||
const zdwm_action_t *action,
|
||||
command_buffer_t *out
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief 事件路由:将运行时事件翻译为语义命令
|
||||
*
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include <zdwm/types.h>
|
||||
|
||||
#include "base/memory.h"
|
||||
#include "core/types.h"
|
||||
#include "interface/types.h"
|
||||
|
||||
bool rules_move(rules_t *src, rules_t *dest) {
|
||||
if (!dest || !src) return false;
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "core/types.h"
|
||||
#include "core/window.h"
|
||||
#include "interface/types.h"
|
||||
|
||||
typedef struct rule_item_t {
|
||||
rule_match_t match;
|
||||
|
||||
@@ -8,11 +8,12 @@
|
||||
#include "base/array.h"
|
||||
#include "base/log.h"
|
||||
#include "base/memory.h"
|
||||
#include "base/window_list.h"
|
||||
#include "common/window.h"
|
||||
#include "common/workspace.h"
|
||||
#include "core/layer.h"
|
||||
#include "core/types.h"
|
||||
#include "core/window.h"
|
||||
#include "core/wm_desc.h"
|
||||
#include "core/window_info.h"
|
||||
#include "interface/types.h"
|
||||
|
||||
void state_init(
|
||||
state_t *state,
|
||||
@@ -328,9 +329,9 @@ const window_t *state_window_add(state_t *state, const window_info_t *info) {
|
||||
window_set_maximized(window, info->maximized);
|
||||
window_set_minimized(window, info->minimized);
|
||||
window_set_urgent(window, info->urgent);
|
||||
window_set_fixed_size(window, info->fixed_size);
|
||||
window_set_frame_rect(window, info->frame_rect);
|
||||
window_set_float_rect(window, info->frame_rect);
|
||||
window_set_size_hint(window, info->min_size, info->max_size);
|
||||
window_set_title(window, info->title);
|
||||
window_set_app_id(window, info->app_id);
|
||||
window_set_role(window, info->role);
|
||||
@@ -469,24 +470,6 @@ void state_window_set_sticky(
|
||||
if (window) window_set_sticky(window, sticky);
|
||||
}
|
||||
|
||||
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) window_set_urgent(window, urgent);
|
||||
}
|
||||
|
||||
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) window_set_fixed_size(window, fixed_size);
|
||||
}
|
||||
|
||||
void state_window_set_border_width(
|
||||
state_t *state,
|
||||
window_id_t window_id,
|
||||
@@ -502,7 +485,7 @@ void state_window_set_skip_taskbar(
|
||||
bool skip_taskbar
|
||||
) {
|
||||
window_t *window = (window_t *)state_window_get(state, window_id);
|
||||
if (window) window_set_skip_taskbar(window, skip_taskbar);
|
||||
if (window) window->skip_taskbar = skip_taskbar;
|
||||
}
|
||||
|
||||
void state_window_set_float_rect(
|
||||
|
||||
@@ -3,11 +3,12 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "base/window_list.h"
|
||||
#include "common/window.h"
|
||||
#include "common/workspace.h"
|
||||
#include "core/layer.h"
|
||||
#include "core/types.h"
|
||||
#include "core/window.h"
|
||||
#include "core/wm_desc.h"
|
||||
#include "core/window_info.h"
|
||||
#include "interface/types.h"
|
||||
|
||||
typedef struct workspace_t {
|
||||
workspace_id_t id;
|
||||
@@ -224,16 +225,6 @@ void state_window_set_sticky(
|
||||
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_border_width(
|
||||
state_t *state,
|
||||
window_id_t window_id,
|
||||
|
||||
111
src/core/types.h
111
src/core/types.h
@@ -1,111 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <zdwm/rules.h>
|
||||
#include <zdwm/types.h>
|
||||
|
||||
#include "base/color.h"
|
||||
|
||||
/* clang-format off */
|
||||
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 zdwm_output_id_t output_id_t;
|
||||
|
||||
typedef zdwm_rect_t rect_t;
|
||||
typedef zdwm_output_info_t output_info_t;
|
||||
|
||||
typedef zdwm_rule_match_t rule_match_t;
|
||||
typedef zdwm_rule_action_t rule_action_t;
|
||||
/* clang-format on */
|
||||
|
||||
typedef uint32_t modifier_mask_t;
|
||||
typedef uint32_t keysym_t;
|
||||
|
||||
#define ZDWM_OUTPUT_ID_INVALID ((output_id_t)UINT32_MAX)
|
||||
|
||||
typedef struct point_t {
|
||||
int32_t x;
|
||||
int32_t y;
|
||||
} point_t;
|
||||
|
||||
typedef enum focus_direction_t {
|
||||
ZDWM_FOCUS_PREV,
|
||||
ZDWM_FOCUS_NEXT,
|
||||
} focus_direction_t;
|
||||
|
||||
typedef enum cross_output_policy_t {
|
||||
ZDWM_CROSS_OUTPUT_KEEP_WORKSPACE,
|
||||
ZDWM_CROSS_OUTPUT_MOVE_TO_TARGET_WORKSPACE,
|
||||
} cross_output_policy_t;
|
||||
|
||||
typedef enum modifier_bit_t {
|
||||
ZDWM_MOD_NONE = 0u,
|
||||
ZDWM_MOD_SHIFT = 1u << 0,
|
||||
ZDWM_MOD_CONTROL = 1u << 1,
|
||||
ZDWM_MOD_1 = 1u << 2,
|
||||
ZDWM_MOD_2 = 1u << 3,
|
||||
ZDWM_MOD_3 = 1u << 4,
|
||||
ZDWM_MOD_4 = 1u << 5,
|
||||
ZDWM_MOD_5 = 1u << 6,
|
||||
} modifier_bit_t;
|
||||
|
||||
typedef struct key_bind_t {
|
||||
modifier_mask_t modifiers;
|
||||
keysym_t keysym;
|
||||
} key_bind_t;
|
||||
|
||||
typedef enum window_state_request_type_t {
|
||||
ZDWM_WINDOW_STATE_REQUEST_FULLSCREEN,
|
||||
ZDWM_WINDOW_STATE_REQUEST_MAXIMIZED,
|
||||
ZDWM_WINDOW_STATE_REQUEST_MINIMIZED,
|
||||
ZDWM_WINDOW_STATE_REQUEST_SKIP_TASKBAR,
|
||||
ZDWM_WINDOW_STATE_REQUEST_URGENT,
|
||||
ZDWM_WINDOW_STATE_REQUEST_FIXED_SIZE,
|
||||
} window_state_request_type_t;
|
||||
|
||||
typedef enum window_state_request_action_t {
|
||||
ZDWM_WINDOW_STATE_ACTION_ADD,
|
||||
ZDWM_WINDOW_STATE_ACTION_REMOVE,
|
||||
ZDWM_WINDOW_STATE_ACTION_TOGGLE,
|
||||
} window_state_request_action_t;
|
||||
|
||||
typedef enum configure_field_t {
|
||||
ZDWM_CONFIGURE_FIELD_X = 1u << 0,
|
||||
ZDWM_CONFIGURE_FIELD_Y = 1u << 1,
|
||||
ZDWM_CONFIGURE_FIELD_WIDTH = 1u << 2,
|
||||
ZDWM_CONFIGURE_FIELD_HEIGHT = 1u << 3,
|
||||
ZDWM_CONFIGURE_FIELD_BORDER_WIDTH = 1u << 4,
|
||||
ZDWM_CONFIGURE_FIELD_SIBLING = 1u << 5,
|
||||
ZDWM_CONFIGURE_FIELD_STACK_MODE = 1u << 6,
|
||||
} configure_field_t;
|
||||
|
||||
typedef struct configure_data_t {
|
||||
window_id_t window;
|
||||
uint32_t changed_fields;
|
||||
int32_t x;
|
||||
int32_t y;
|
||||
int32_t width;
|
||||
int32_t height;
|
||||
uint32_t border_width;
|
||||
window_id_t sibling;
|
||||
uint32_t stack_mode;
|
||||
} configure_data_t;
|
||||
|
||||
typedef struct border_config_t {
|
||||
uint32_t width;
|
||||
color_t normal_color;
|
||||
color_t focused_color;
|
||||
} border_config_t;
|
||||
|
||||
typedef struct only_window_data_t {
|
||||
window_id_t window;
|
||||
} only_window_data_t;
|
||||
|
||||
static inline bool window_id_invalid(window_id_t window_id) {
|
||||
return window_id == ZDWM_WINDOW_ID_INVALID;
|
||||
}
|
||||
|
||||
static inline bool workspace_id_invalid(workspace_id_t workspace_id) {
|
||||
return workspace_id == ZDWM_WORKSPACE_ID_INVALID;
|
||||
}
|
||||
@@ -1,40 +1,7 @@
|
||||
#include "core/window.h"
|
||||
|
||||
#include "base/memory.h"
|
||||
#include "core/types.h"
|
||||
|
||||
window_layer_type_t window_classify_layer(const window_layer_props_t *props) {
|
||||
for (size_t i = 0; i < props->type_count; ++i) {
|
||||
window_type_t type = props->types[i];
|
||||
if (type == ZDWM_WINDOW_TYPE_NOTIFICATION) return ZDWM_WINDOW_LAYER_OVERLAY;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < props->state_count; ++i) {
|
||||
window_state_t state = props->states[i];
|
||||
if (state == ZDWM_WINDOW_STATE_ABOVE) return ZDWM_WINDOW_LAYER_TOP;
|
||||
}
|
||||
|
||||
return ZDWM_WINDOW_LAYER_NORMAL;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
#include "interface/types.h"
|
||||
|
||||
void window_set_fullscreen(window_t *window, bool fullscreen) {
|
||||
window->fullscreen = fullscreen;
|
||||
@@ -49,7 +16,7 @@ void window_set_minimized(window_t *window, bool minimized) {
|
||||
}
|
||||
|
||||
void window_set_floating(window_t *window, bool floating) {
|
||||
if (!floating && (window->fixed_size || window->sticky)) return;
|
||||
if (!floating && (window_should_fix_size(window) || window->sticky)) return;
|
||||
|
||||
if (floating == window->floating) return;
|
||||
|
||||
@@ -67,11 +34,6 @@ void window_set_urgent(window_t *window, bool urgent) {
|
||||
window->urgent = urgent;
|
||||
}
|
||||
|
||||
void window_set_fixed_size(window_t *window, bool fixed_size) {
|
||||
window->fixed_size = fixed_size;
|
||||
if (fixed_size) window_set_floating(window, true);
|
||||
}
|
||||
|
||||
void window_set_skip_taskbar(window_t *window, bool skip_taskbar) {
|
||||
window->skip_taskbar = skip_taskbar;
|
||||
}
|
||||
@@ -84,6 +46,18 @@ void window_set_frame_rect(window_t *window, rect_t rect) {
|
||||
window->frame_rect = rect;
|
||||
}
|
||||
|
||||
void window_set_size_hint(window_t *window, zdwm_size_t min, zdwm_size_t max) {
|
||||
window->min_size = min;
|
||||
window->max_size = max;
|
||||
if (!window_should_fix_size(window)) return;
|
||||
|
||||
auto rect = window->frame_rect;
|
||||
rect.width = min.width;
|
||||
rect.height = min.height;
|
||||
window_set_frame_rect(window, rect);
|
||||
window_set_floating(window, true);
|
||||
}
|
||||
|
||||
void window_set_title(window_t *window, const char *title) {
|
||||
p_delete(&window->title);
|
||||
window->title = p_strdup_nullable(title);
|
||||
@@ -158,3 +132,24 @@ bool window_should_has_border(const window_t *window) {
|
||||
if (window->fullscreen || window->maximized) return false;
|
||||
return window->floating;
|
||||
}
|
||||
|
||||
bool window_should_fix_size(const window_t *window) {
|
||||
auto max = window->max_size;
|
||||
auto min = window->min_size;
|
||||
return max.width == min.width && max.height == min.height;
|
||||
}
|
||||
|
||||
bool window_can_resize(const window_t *window) {
|
||||
if (window->fullscreen || window->maximized) return false;
|
||||
if (window_should_fix_size(window)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const char *window_get_showing_title(const window_t *window) {
|
||||
if (window->title) return window->title;
|
||||
if (window->instance_name) return window->instance_name;
|
||||
if (window->class_name) return window->class_name;
|
||||
if (window->app_id) return window->app_id;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -3,66 +3,7 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "core/types.h"
|
||||
|
||||
/* Internal semantic window types mapped from backend-specific protocols. */
|
||||
typedef enum window_type_t {
|
||||
ZDWM_WINDOW_TYPE_NORMAL = 0,
|
||||
ZDWM_WINDOW_TYPE_DESKTOP,
|
||||
ZDWM_WINDOW_TYPE_DOCK,
|
||||
ZDWM_WINDOW_TYPE_TOOLBAR,
|
||||
ZDWM_WINDOW_TYPE_DIALOG,
|
||||
ZDWM_WINDOW_TYPE_UTILITY,
|
||||
ZDWM_WINDOW_TYPE_SPLASH,
|
||||
ZDWM_WINDOW_TYPE_MENU,
|
||||
ZDWM_WINDOW_TYPE_DROPDOWN_MENU,
|
||||
ZDWM_WINDOW_TYPE_POPUP_MENU,
|
||||
ZDWM_WINDOW_TYPE_TOOLTIP,
|
||||
ZDWM_WINDOW_TYPE_COMBO,
|
||||
ZDWM_WINDOW_TYPE_DND,
|
||||
ZDWM_WINDOW_TYPE_NOTIFICATION,
|
||||
} window_type_t;
|
||||
|
||||
/* Internal semantic window states mapped from backend-specific protocols. */
|
||||
typedef enum window_state_t {
|
||||
ZDWM_WINDOW_STATE_ABOVE = 0,
|
||||
ZDWM_WINDOW_STATE_FULLSCREEN,
|
||||
ZDWM_WINDOW_STATE_MODAL,
|
||||
ZDWM_WINDOW_STATE_STICKY,
|
||||
} window_state_t;
|
||||
|
||||
/* 层级从低到高 */
|
||||
typedef enum window_layer_type_t {
|
||||
ZDWM_WINDOW_LAYER_DESKTOP = 0,
|
||||
ZDWM_WINDOW_LAYER_NORMAL,
|
||||
ZDWM_WINDOW_LAYER_TOP,
|
||||
ZDWM_WINDOW_LAYER_OVERLAY,
|
||||
ZDWM_WINDOW_LAYER_COUNT,
|
||||
} window_layer_type_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_ROLE = 1u << 2,
|
||||
ZDWM_WINDOW_METADATA_CHANGE_CLASS = 1u << 3,
|
||||
ZDWM_WINDOW_METADATA_CHANGE_INSTANCE = 1u << 4,
|
||||
} window_metadata_change_flags_t;
|
||||
|
||||
typedef struct window_layer_props_t {
|
||||
window_type_t *types;
|
||||
size_t type_count;
|
||||
|
||||
window_state_t *states;
|
||||
size_t state_count;
|
||||
} window_layer_props_t;
|
||||
|
||||
typedef struct window_metadata_t {
|
||||
char *title;
|
||||
char *app_id;
|
||||
char *role;
|
||||
char *class_name;
|
||||
char *instance_name;
|
||||
} window_metadata_t;
|
||||
#include "interface/types.h"
|
||||
|
||||
typedef struct window_t {
|
||||
window_id_t id;
|
||||
@@ -76,9 +17,11 @@ typedef struct window_t {
|
||||
bool floating;
|
||||
bool sticky;
|
||||
bool urgent;
|
||||
bool fixed_size;
|
||||
bool skip_taskbar;
|
||||
|
||||
zdwm_size_t min_size;
|
||||
zdwm_size_t max_size;
|
||||
|
||||
/* 几何信息(均为包含边框后的外框矩形) */
|
||||
rect_t float_rect; /* floating 模式下记忆的外框矩形 */
|
||||
rect_t frame_rect; /* 当前外框矩形(由 layout 或 float_rect 解析) */
|
||||
@@ -93,21 +36,16 @@ typedef struct window_t {
|
||||
uint32_t border_width;
|
||||
} window_t;
|
||||
|
||||
window_layer_type_t window_classify_layer(const window_layer_props_t *props);
|
||||
|
||||
void window_layer_props_cleanup(window_layer_props_t *props);
|
||||
void window_metadata_cleanup(window_metadata_t *metadata);
|
||||
|
||||
void window_set_fullscreen(window_t *window, bool fullscreen);
|
||||
void window_set_maximized(window_t *window, bool maximized);
|
||||
void window_set_minimized(window_t *window, bool minimized);
|
||||
void window_set_floating(window_t *window, bool floating);
|
||||
void window_set_sticky(window_t *window, bool sticky);
|
||||
void window_set_urgent(window_t *window, bool urgent);
|
||||
void window_set_fixed_size(window_t *window, bool fixed_size);
|
||||
void window_set_skip_taskbar(window_t *window, bool skip_taskbar);
|
||||
void window_set_float_rect(window_t *window, rect_t rect);
|
||||
void window_set_frame_rect(window_t *window, rect_t rect);
|
||||
void window_set_size_hint(window_t *window, zdwm_size_t min, zdwm_size_t max);
|
||||
void window_set_title(window_t *window, const char *title);
|
||||
void window_set_app_id(window_t *window, const char *app_id);
|
||||
void window_set_role(window_t *window, const char *role);
|
||||
@@ -127,3 +65,6 @@ bool window_need_layout(const window_t *window);
|
||||
bool window_need_move(const window_t *window, int32_t x, int32_t y);
|
||||
bool window_need_resize(const window_t *window, int32_t width, int32_t height);
|
||||
bool window_should_has_border(const window_t *window);
|
||||
bool window_should_fix_size(const window_t *window);
|
||||
bool window_can_resize(const window_t *window);
|
||||
const char *window_get_showing_title(const window_t *window);
|
||||
|
||||
30
src/core/window_info.h
Normal file
30
src/core/window_info.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include "interface/types.h"
|
||||
|
||||
/*
|
||||
* 由 backend 提供的窗口基础信息。
|
||||
*
|
||||
* 这些字符串由调用方提供;state 如需长期持有,应自行复制。
|
||||
*/
|
||||
typedef struct window_info_t {
|
||||
window_id_t id;
|
||||
window_id_t transient_for;
|
||||
rect_t frame_rect;
|
||||
|
||||
const char *title;
|
||||
const char *app_id;
|
||||
const char *role;
|
||||
const char *class_name;
|
||||
const char *instance_name;
|
||||
|
||||
window_layer_type_t layer_type;
|
||||
bool fullscreen;
|
||||
bool maximized;
|
||||
bool minimized;
|
||||
bool urgent;
|
||||
bool skip_taskbar;
|
||||
|
||||
zdwm_size_t min_size;
|
||||
zdwm_size_t max_size;
|
||||
} window_info_t;
|
||||
@@ -1,89 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "base/memory.h"
|
||||
#include "core/types.h"
|
||||
#include "core/window.h"
|
||||
|
||||
/*
|
||||
* 由 backend 提供的窗口基础信息。
|
||||
*
|
||||
* 这些字符串由调用方提供;state 如需长期持有,应自行复制。
|
||||
*/
|
||||
typedef struct window_info_t {
|
||||
window_id_t id;
|
||||
window_id_t transient_for;
|
||||
rect_t frame_rect;
|
||||
|
||||
const char *title;
|
||||
const char *app_id;
|
||||
const char *role;
|
||||
const char *class_name;
|
||||
const char *instance_name;
|
||||
|
||||
window_layer_type_t layer_type;
|
||||
bool fullscreen;
|
||||
bool maximized;
|
||||
bool minimized;
|
||||
bool urgent;
|
||||
bool fixed_size;
|
||||
bool skip_taskbar;
|
||||
} window_info_t;
|
||||
|
||||
typedef struct workspace_desc_t {
|
||||
size_t output_index; /* 对应 state_init() 中 outputs[] 的索引 */
|
||||
const char *name;
|
||||
const layout_id_t *layout_ids;
|
||||
size_t layout_count;
|
||||
layout_id_t initial_layout_id;
|
||||
} workspace_desc_t;
|
||||
|
||||
static inline void workspace_desc_cleanup(workspace_desc_t *workspace) {
|
||||
if (!workspace) return;
|
||||
|
||||
p_delete(&workspace->name);
|
||||
p_delete(&workspace->layout_ids);
|
||||
workspace->output_index = 0;
|
||||
workspace->layout_count = 0;
|
||||
workspace->initial_layout_id = ZDWM_LAYOUT_ID_INVALID;
|
||||
}
|
||||
|
||||
static inline void
|
||||
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_cleanup(&(*list)[i]);
|
||||
}
|
||||
|
||||
p_delete(list);
|
||||
*count = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* workspace 描述校验接口。
|
||||
*
|
||||
* 这组接口只校验描述表自身的一致性,不访问 state。
|
||||
*/
|
||||
static inline bool workspace_desc_layouts_valid(
|
||||
const workspace_desc_t *workspace
|
||||
) {
|
||||
if (!workspace || !workspace->layout_count || !workspace->layout_ids) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < workspace->layout_count; i++) {
|
||||
if (workspace->layout_ids[i] == workspace->initial_layout_id) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
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 workspace_desc_layouts_valid(workspace);
|
||||
}
|
||||
@@ -3,9 +3,10 @@
|
||||
#include <cairo.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "core/event.h"
|
||||
#include "core/plan.h"
|
||||
#include "core/types.h"
|
||||
#include "interface/effect.h"
|
||||
#include "interface/event.h"
|
||||
#include "interface/tray.h"
|
||||
#include "interface/types.h"
|
||||
|
||||
typedef struct backend_t backend_t;
|
||||
|
||||
@@ -21,6 +22,16 @@ backend_detect_t *backend_detect(backend_t *backend);
|
||||
void backend_detect_destroy(backend_detect_t *detect);
|
||||
|
||||
int backend_get_fd(backend_t *backend);
|
||||
|
||||
/**
|
||||
* @brief 非阻塞地从 backend 中读取下一个归一化的事件
|
||||
*
|
||||
* @details
|
||||
* 1. 函数立即返回
|
||||
* 2. 只要取到了事件,函数就返回 true ,事件填充到 event 参数中
|
||||
*
|
||||
* @returns 如果还有下一个事件待读取,返回 true ,否则返回 false
|
||||
*/
|
||||
bool backend_poll_event(backend_t *backend, event_t *event);
|
||||
|
||||
/**
|
||||
@@ -59,11 +70,13 @@ void backend_scan_result_destroy(backend_scan_result_t *result);
|
||||
typedef struct backend_bar_window_t {
|
||||
window_id_t window_id;
|
||||
cairo_t *cr;
|
||||
tray_t tray;
|
||||
} backend_bar_window_t;
|
||||
|
||||
backend_bar_window_t backend_create_bar_window(
|
||||
backend_t *backend,
|
||||
rect_t geometry,
|
||||
uint32_t bg_pixel
|
||||
uint32_t bg_pixel,
|
||||
bool enable_tray
|
||||
);
|
||||
void backend_flush(backend_t *backend);
|
||||
97
src/interface/effect.h
Normal file
97
src/interface/effect.h
Normal file
@@ -0,0 +1,97 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "base/color.h"
|
||||
#include "interface/types.h"
|
||||
|
||||
typedef enum effect_type_t {
|
||||
ZDWM_EFFECT_MAP_WINDOW = 1,
|
||||
ZDWM_EFFECT_UNMAP_WINDOW,
|
||||
ZDWM_EFFECT_FOCUS_WINDOW,
|
||||
ZDWM_EFFECT_KILL_WINDOW,
|
||||
ZDWM_EFFECT_WITHDRAW_WINDOW,
|
||||
ZDWM_EFFECT_START_MOVE_WINDOW,
|
||||
ZDWM_EFFECT_START_RESIZE_WINDOW,
|
||||
ZDWM_EFFECT_MINIMIZE_WINDOW,
|
||||
ZDWM_EFFECT_MAXIMIZE_WINDOW,
|
||||
ZDWM_EFFECT_FULLSCREEN_WINDOW,
|
||||
ZDWM_EFFECT_CONFIGURE_WINDOW,
|
||||
ZDWM_EFFECT_CONFIGURE_NOTIFY,
|
||||
ZDWM_EFFECT_CHANGE_BORDER_COLOR,
|
||||
ZDWM_EFFECT_CHANGE_WINDOW_LIST,
|
||||
ZDWM_EFFECT_RESTACK_WINDOWS,
|
||||
ZDWM_EFFECT_BIND_KEY,
|
||||
ZDWM_EFFECT_GRAB_BUTTON,
|
||||
ZDWM_EFFECT_UNGRAB_POINTER,
|
||||
} effect_type_t;
|
||||
|
||||
typedef struct effect_move_window_t {
|
||||
window_id_t window;
|
||||
point_t left_top_point;
|
||||
} effect_move_window_t;
|
||||
|
||||
typedef struct effect_resize_window_t {
|
||||
window_id_t window;
|
||||
int32_t width;
|
||||
int32_t height;
|
||||
} effect_resize_window_t;
|
||||
|
||||
typedef struct effect_change_border_color_t {
|
||||
window_id_t window;
|
||||
const color_t *color;
|
||||
} effect_change_border_color_t;
|
||||
|
||||
typedef struct effect_window_list_t {
|
||||
const window_id_t *windows;
|
||||
size_t count;
|
||||
} effect_window_list_t;
|
||||
|
||||
typedef struct key_bind_t {
|
||||
modifier_mask_t modifiers;
|
||||
keysym_t keysym;
|
||||
} key_bind_t;
|
||||
|
||||
typedef struct effect_bind_key_t {
|
||||
const key_bind_t *keys;
|
||||
size_t count;
|
||||
} effect_bind_key_t;
|
||||
|
||||
typedef struct grab_button_t {
|
||||
modifier_mask_t modifiers;
|
||||
button_t button;
|
||||
} grab_button_t;
|
||||
|
||||
typedef struct effect_grab_button_t {
|
||||
window_id_t window;
|
||||
const grab_button_t *buttons;
|
||||
size_t count;
|
||||
} effect_grab_button_t;
|
||||
|
||||
typedef struct effect_bool_window_t {
|
||||
window_id_t window;
|
||||
bool value;
|
||||
} effect_bool_window_t;
|
||||
|
||||
typedef struct effect_t {
|
||||
effect_type_t type;
|
||||
union {
|
||||
only_window_data_t map;
|
||||
only_window_data_t unmap;
|
||||
only_window_data_t focus;
|
||||
only_window_data_t kill;
|
||||
only_window_data_t withdraw;
|
||||
only_window_data_t move;
|
||||
only_window_data_t resize;
|
||||
effect_bool_window_t minimize;
|
||||
effect_bool_window_t maximize;
|
||||
effect_bool_window_t fullscreen;
|
||||
configure_data_t configure;
|
||||
only_window_data_t configure_notify;
|
||||
effect_change_border_color_t change_border_color;
|
||||
effect_window_list_t change_window_list;
|
||||
effect_window_list_t restack_windows;
|
||||
effect_bind_key_t bind_key;
|
||||
effect_grab_button_t grab_button;
|
||||
} as;
|
||||
} effect_t;
|
||||
@@ -2,10 +2,10 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "core/types.h"
|
||||
#include "core/window.h"
|
||||
#include "interface/types.h"
|
||||
|
||||
typedef enum event_type_t {
|
||||
ZDWM_EVENT_NONE,
|
||||
ZDWM_EVENT_KEY_PRESS,
|
||||
ZDWM_EVENT_POINTER_BUTTON_PRESS,
|
||||
ZDWM_EVENT_POINTER_BUTTON_RELEASE,
|
||||
@@ -26,12 +26,6 @@ typedef struct key_press_event_t {
|
||||
uint32_t keycode;
|
||||
} key_press_event_t;
|
||||
|
||||
typedef enum button_t {
|
||||
ZDWM_BUTTON_LEFT,
|
||||
ZDWM_BUTTON_RIGHT,
|
||||
ZDWM_BUTTON_MIDDLE,
|
||||
} button_t;
|
||||
|
||||
typedef struct pointer_button_event_t {
|
||||
modifier_mask_t modifiers;
|
||||
button_t button;
|
||||
@@ -52,12 +46,14 @@ typedef struct window_map_request_event_t {
|
||||
bool override_redirect;
|
||||
bool skip_taskbar;
|
||||
bool urgent;
|
||||
bool fixed_size;
|
||||
bool fullscreen;
|
||||
bool maximized;
|
||||
bool minimized;
|
||||
rect_t rect;
|
||||
|
||||
zdwm_size_t min_size;
|
||||
zdwm_size_t max_size;
|
||||
|
||||
window_layer_props_t props;
|
||||
window_metadata_t metadata;
|
||||
} window_map_request_event_t;
|
||||
@@ -78,11 +74,13 @@ typedef struct window_metadata_change_event_t {
|
||||
window_metadata_t metadata;
|
||||
} window_metadata_change_event_t;
|
||||
|
||||
/* clang-format off */
|
||||
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;
|
||||
/* clang-format on */
|
||||
|
||||
typedef struct window_activate_request_event_t {
|
||||
window_id_t window;
|
||||
@@ -106,11 +104,9 @@ typedef struct event_t {
|
||||
window_map_request_event_t window_map_request;
|
||||
window_remove_event_t window_remove;
|
||||
window_metadata_change_event_t window_metadata_change;
|
||||
hints_data_t hints;
|
||||
window_activate_request_event_t window_activate_request;
|
||||
window_state_request_event_t window_state_request;
|
||||
configure_data_t configure_request;
|
||||
} as;
|
||||
} event_t;
|
||||
|
||||
void event_cleanup(event_t *event);
|
||||
void event_reset(event_t *event);
|
||||
35
src/interface/tray.h
Normal file
35
src/interface/tray.h
Normal file
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "interface/types.h"
|
||||
|
||||
typedef void (*tray_icons_change_cb_t)(void *user_data);
|
||||
|
||||
typedef struct tray_api_t {
|
||||
/**
|
||||
* @brief 获取 tray 当前挂载的 bar window id
|
||||
*
|
||||
* @detail 如果返回的 window_id 是 ZDWM_WINDOW_ID_INVALID 则表示 tray
|
||||
* 功能未开启,无 tray 需要挂载
|
||||
*/
|
||||
window_id_t (*host_window)(void *handle);
|
||||
/* 获取当前 icon 数量 */
|
||||
size_t (*icon_count)(void *handle);
|
||||
/* 获取每个 icon 的边长, icon 长宽一样,都等于 bar_height */
|
||||
int32_t (*icon_size)(void *handle);
|
||||
/* 将 tray 的 icon 容器窗口起点放到 bar 中 x 这个位置 */
|
||||
void (*place)(void *handle, int32_t x);
|
||||
/* 设置 icon 数量变化回调 */
|
||||
void (*set_listener)(
|
||||
void *handle,
|
||||
tray_icons_change_cb_t cb,
|
||||
void *user_data
|
||||
);
|
||||
} tray_api_t;
|
||||
|
||||
typedef struct tray_t {
|
||||
tray_api_t api;
|
||||
void *handle;
|
||||
} tray_t;
|
||||
168
src/interface/types.h
Normal file
168
src/interface/types.h
Normal file
@@ -0,0 +1,168 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <zdwm/rules.h>
|
||||
#include <zdwm/types.h>
|
||||
|
||||
#include "base/color.h"
|
||||
|
||||
/* clang-format off */
|
||||
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 zdwm_output_id_t output_id_t;
|
||||
|
||||
typedef zdwm_rect_t rect_t;
|
||||
typedef zdwm_output_info_t output_info_t;
|
||||
|
||||
typedef zdwm_rule_match_t rule_match_t;
|
||||
typedef zdwm_rule_action_t rule_action_t;
|
||||
|
||||
typedef enum zdwm_button_t button_t;
|
||||
typedef enum zdwm_modifier_bit_t modifier_bit_t;
|
||||
typedef zdwm_modifier_mask_t modifier_mask_t;
|
||||
/* clang-format on */
|
||||
|
||||
typedef uint32_t keysym_t;
|
||||
|
||||
#define ZDWM_OUTPUT_ID_INVALID ((output_id_t)UINT32_MAX)
|
||||
|
||||
typedef struct point_t {
|
||||
int32_t x;
|
||||
int32_t y;
|
||||
} point_t;
|
||||
|
||||
typedef struct zdwm_size_t {
|
||||
int32_t width;
|
||||
int32_t height;
|
||||
} zdwm_size_t;
|
||||
|
||||
typedef struct only_window_data_t {
|
||||
window_id_t window;
|
||||
} only_window_data_t;
|
||||
|
||||
/* clang-format off */
|
||||
typedef enum configure_field_t {
|
||||
ZDWM_CONFIGURE_FIELD_X = 1u << 0,
|
||||
ZDWM_CONFIGURE_FIELD_Y = 1u << 1,
|
||||
ZDWM_CONFIGURE_FIELD_WIDTH = 1u << 2,
|
||||
ZDWM_CONFIGURE_FIELD_HEIGHT = 1u << 3,
|
||||
ZDWM_CONFIGURE_FIELD_BORDER_WIDTH = 1u << 4,
|
||||
ZDWM_CONFIGURE_FIELD_SIBLING = 1u << 5,
|
||||
ZDWM_CONFIGURE_FIELD_STACK_MODE = 1u << 6,
|
||||
} configure_field_t;
|
||||
/* clang-format on */
|
||||
|
||||
typedef struct configure_data_t {
|
||||
window_id_t window;
|
||||
uint32_t changed_fields;
|
||||
int32_t x;
|
||||
int32_t y;
|
||||
int32_t width;
|
||||
int32_t height;
|
||||
uint32_t border_width;
|
||||
window_id_t sibling;
|
||||
uint32_t stack_mode;
|
||||
} configure_data_t;
|
||||
|
||||
/* Internal semantic window types mapped from backend-specific protocols. */
|
||||
typedef enum window_type_t {
|
||||
ZDWM_WINDOW_TYPE_NORMAL = 0,
|
||||
ZDWM_WINDOW_TYPE_DESKTOP,
|
||||
ZDWM_WINDOW_TYPE_DOCK,
|
||||
ZDWM_WINDOW_TYPE_TOOLBAR,
|
||||
ZDWM_WINDOW_TYPE_DIALOG,
|
||||
ZDWM_WINDOW_TYPE_UTILITY,
|
||||
ZDWM_WINDOW_TYPE_SPLASH,
|
||||
ZDWM_WINDOW_TYPE_MENU,
|
||||
ZDWM_WINDOW_TYPE_DROPDOWN_MENU,
|
||||
ZDWM_WINDOW_TYPE_POPUP_MENU,
|
||||
ZDWM_WINDOW_TYPE_TOOLTIP,
|
||||
ZDWM_WINDOW_TYPE_COMBO,
|
||||
ZDWM_WINDOW_TYPE_DND,
|
||||
ZDWM_WINDOW_TYPE_NOTIFICATION,
|
||||
} window_type_t;
|
||||
|
||||
/* Internal semantic window states mapped from backend-specific protocols. */
|
||||
typedef enum window_state_t {
|
||||
ZDWM_WINDOW_STATE_ABOVE = 0,
|
||||
ZDWM_WINDOW_STATE_FULLSCREEN,
|
||||
ZDWM_WINDOW_STATE_MODAL,
|
||||
ZDWM_WINDOW_STATE_STICKY,
|
||||
} window_state_t;
|
||||
|
||||
typedef enum window_state_request_type_t {
|
||||
ZDWM_WINDOW_STATE_REQUEST_FULLSCREEN,
|
||||
ZDWM_WINDOW_STATE_REQUEST_MAXIMIZED,
|
||||
ZDWM_WINDOW_STATE_REQUEST_MINIMIZED,
|
||||
ZDWM_WINDOW_STATE_REQUEST_SKIP_TASKBAR,
|
||||
} window_state_request_type_t;
|
||||
|
||||
typedef enum window_state_request_action_t {
|
||||
ZDWM_WINDOW_STATE_ACTION_ADD,
|
||||
ZDWM_WINDOW_STATE_ACTION_REMOVE,
|
||||
ZDWM_WINDOW_STATE_ACTION_TOGGLE,
|
||||
} window_state_request_action_t;
|
||||
|
||||
/* 层级从低到高 */
|
||||
typedef enum window_layer_type_t {
|
||||
ZDWM_WINDOW_LAYER_DESKTOP = 0,
|
||||
ZDWM_WINDOW_LAYER_NORMAL,
|
||||
ZDWM_WINDOW_LAYER_TOP,
|
||||
ZDWM_WINDOW_LAYER_OVERLAY,
|
||||
ZDWM_WINDOW_LAYER_COUNT,
|
||||
} window_layer_type_t;
|
||||
|
||||
typedef struct window_layer_props_t {
|
||||
window_type_t *types;
|
||||
size_t type_count;
|
||||
|
||||
window_state_t *states;
|
||||
size_t state_count;
|
||||
} window_layer_props_t;
|
||||
|
||||
/* clang-format off */
|
||||
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_ROLE = 1u << 2,
|
||||
ZDWM_WINDOW_METADATA_CHANGE_CLASS = 1u << 3,
|
||||
ZDWM_WINDOW_METADATA_CHANGE_INSTANCE = 1u << 4,
|
||||
} window_metadata_change_flags_t;
|
||||
/* clang-format on */
|
||||
|
||||
typedef struct window_metadata_t {
|
||||
char *title;
|
||||
char *app_id;
|
||||
char *role;
|
||||
char *class_name;
|
||||
char *instance_name;
|
||||
} window_metadata_t;
|
||||
|
||||
typedef enum hint_field_t {
|
||||
ZDWM_HINT_FIELD_URGENT,
|
||||
ZDWM_HINT_FIELD_SIZE,
|
||||
} hint_field_t;
|
||||
|
||||
typedef struct hints_data_t {
|
||||
window_id_t window;
|
||||
uint32_t changed_fields;
|
||||
bool urgent;
|
||||
zdwm_size_t min_size;
|
||||
zdwm_size_t max_size;
|
||||
} hints_data_t;
|
||||
|
||||
typedef struct border_config_t {
|
||||
uint32_t width;
|
||||
color_t normal_color;
|
||||
color_t focused_color;
|
||||
} border_config_t;
|
||||
|
||||
static inline bool window_id_invalid(window_id_t window_id) {
|
||||
return window_id == ZDWM_WINDOW_ID_INVALID;
|
||||
}
|
||||
|
||||
static inline bool workspace_id_invalid(workspace_id_t workspace_id) {
|
||||
return workspace_id == ZDWM_WORKSPACE_ID_INVALID;
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
#include "runtime/runtime.h"
|
||||
|
||||
#include <bits/types/sigset_t.h>
|
||||
#include <dlfcn.h>
|
||||
#include <signal.h>
|
||||
#include <stddef.h>
|
||||
@@ -8,6 +7,7 @@
|
||||
#include <sys/poll.h>
|
||||
#include <sys/signalfd.h>
|
||||
#include <unistd.h>
|
||||
#include <zdwm/action.h>
|
||||
#include <zdwm/layout.h>
|
||||
|
||||
#include "bar/bar.h"
|
||||
@@ -16,21 +16,23 @@
|
||||
#include "base/log.h"
|
||||
#include "base/macros.h"
|
||||
#include "base/memory.h"
|
||||
#include "base/window_list.h"
|
||||
#include "common/event.h"
|
||||
#include "common/listeners.h"
|
||||
#include "common/window.h"
|
||||
#include "common/workspace.h"
|
||||
#include "config/runtime_config.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"
|
||||
#include "core/state.h"
|
||||
#include "core/types.h"
|
||||
#include "core/window.h"
|
||||
#include "core/wm_desc.h"
|
||||
#include "interface/backend.h"
|
||||
#include "interface/event.h"
|
||||
#include "interface/types.h"
|
||||
|
||||
typedef struct runtime_t {
|
||||
bool running;
|
||||
@@ -44,10 +46,12 @@ typedef struct runtime_t {
|
||||
layout_registry_t layouts;
|
||||
rules_t rules;
|
||||
border_config_t border;
|
||||
uint32_t fps;
|
||||
backend_t *backend;
|
||||
void *config_module_handle;
|
||||
binding_table_t *binding_table;
|
||||
listeners_t listeners;
|
||||
window_interaction_state_t interaction;
|
||||
|
||||
bar_t bar;
|
||||
window_list_t bar_windows;
|
||||
@@ -116,7 +120,10 @@ static void runtime_init_bar(runtime_t *runtime) {
|
||||
.height = bar_height,
|
||||
};
|
||||
auto color = bar->palette.bg.argb;
|
||||
auto bar_window = backend_create_bar_window(backend, bar_rect, color);
|
||||
bool enable_tray = (int32_t)i == bar->config.tray_output_index;
|
||||
auto bar_window =
|
||||
backend_create_bar_window(backend, bar_rect, color, enable_tray);
|
||||
bar->tray = bar_window.tray;
|
||||
auto bar_output = &bar->bars[i];
|
||||
bar_output->cr = bar_window.cr;
|
||||
bar_output->window_id = bar_window.window_id;
|
||||
@@ -137,6 +144,7 @@ static bool runtime_init(runtime_t *runtime, runtime_init_desc_t *desc) {
|
||||
rules_move(&desc->rules, &runtime->rules);
|
||||
runtime->signal_fd = -1;
|
||||
runtime->border = desc->border;
|
||||
runtime->fps = MAX(desc->fps, 10);
|
||||
runtime->config_module_handle = desc->config_module_handle;
|
||||
runtime->binding_table = desc->binding_table;
|
||||
runtime->listeners = desc->listeners;
|
||||
@@ -174,8 +182,10 @@ static void runtime_init_desc_cleanup(runtime_init_desc_t *desc) {
|
||||
desc->backend = nullptr;
|
||||
}
|
||||
|
||||
if (desc->layouts.slots || desc->layouts.slot_count ||
|
||||
desc->layouts.slot_capacity) {
|
||||
if (
|
||||
desc->layouts.slots || desc->layouts.slot_count ||
|
||||
desc->layouts.slot_capacity
|
||||
) {
|
||||
layout_registry_cleanup(&desc->layouts);
|
||||
}
|
||||
|
||||
@@ -197,6 +207,7 @@ static void runtime_shutdown(runtime_t *runtime) {
|
||||
binding_table_destroy(runtime->binding_table);
|
||||
runtime->binding_table = nullptr;
|
||||
listeners_cleanup(&runtime->listeners);
|
||||
p_clear(&runtime->interaction, 1);
|
||||
window_list_cleanup(&runtime->bar_windows);
|
||||
backend_destroy(runtime->backend);
|
||||
runtime->backend = nullptr;
|
||||
@@ -301,9 +312,6 @@ static const layout_result_t *runtime_layout_calc(runtime_t *runtime) {
|
||||
auto workspace = state_workspace_get(state, output->current_workspace_id);
|
||||
if (!workspace) continue;
|
||||
|
||||
auto layout_slot = layout_slot_get(&runtime->layouts, workspace->layout_id);
|
||||
if (!layout_slot || !layout_slot->fn) continue;
|
||||
|
||||
size_t window_count = 0;
|
||||
size_t window_list_capacity = 0;
|
||||
window_id_t *window_ids = nullptr;
|
||||
@@ -331,7 +339,8 @@ static const layout_result_t *runtime_layout_calc(runtime_t *runtime) {
|
||||
}
|
||||
}
|
||||
|
||||
if (window_count) {
|
||||
auto layout_func = layout_get(&runtime->layouts, workspace->layout_id);
|
||||
if (window_count && layout_func) {
|
||||
zdwm_layout_ctx_t ctx = {
|
||||
.workspace_id = workspace->id,
|
||||
.focused_window_id = workspace->focused_window_id,
|
||||
@@ -340,7 +349,7 @@ static const layout_result_t *runtime_layout_calc(runtime_t *runtime) {
|
||||
.window_ids = window_ids,
|
||||
.window_count = window_count,
|
||||
};
|
||||
layout_slot->fn(&ctx, result);
|
||||
layout_func(&ctx, result);
|
||||
}
|
||||
|
||||
if (window_ids) p_delete(&window_ids);
|
||||
@@ -402,6 +411,9 @@ static void runtime_arrange(runtime_t *runtime) {
|
||||
|
||||
static policy_context_t policy_context_init(runtime_t *runtime) {
|
||||
policy_context_t ctx = {
|
||||
.fps = runtime->fps,
|
||||
.interaction = &runtime->interaction,
|
||||
|
||||
.bind_table = runtime->binding_table,
|
||||
.state = &runtime->state,
|
||||
.rules = &runtime->rules,
|
||||
@@ -451,10 +463,36 @@ static void runtime_scan(runtime_t *runtime) {
|
||||
listeners_notify_initial_windows(&runtime->listeners, &runtime->state);
|
||||
}
|
||||
|
||||
static bool runtime_handle_bar_click(
|
||||
const policy_context_t *ctx,
|
||||
const event_t *event,
|
||||
bar_t *bar,
|
||||
command_buffer_t *command_buffer
|
||||
) {
|
||||
if (event->type != ZDWM_EVENT_POINTER_BUTTON_PRESS) return false;
|
||||
|
||||
auto data = &event->as.pointer_button_press;
|
||||
|
||||
zdwm_action_t action = {.type = ZDWM_ACTION_NONE};
|
||||
bar_click_info_t info = {
|
||||
.window = data->window,
|
||||
.x = data->local.x,
|
||||
.modifiers = data->modifiers,
|
||||
.button = data->button,
|
||||
};
|
||||
if (bar_click(bar, info, &action)) {
|
||||
policy_resolve_action(ctx, &action, command_buffer);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void runtime_handle_event(runtime_t *runtime, short int revents) {
|
||||
auto backend = runtime->backend;
|
||||
auto command_buffer = &runtime->command_buffer;
|
||||
auto plan = &runtime->plan;
|
||||
auto bar = &runtime->bar;
|
||||
auto ctx = policy_context_init(runtime);
|
||||
|
||||
if (revents & POLLHUP) {
|
||||
@@ -468,7 +506,10 @@ static void runtime_handle_event(runtime_t *runtime, short int revents) {
|
||||
command_buffer_reset(command_buffer);
|
||||
plan_reset(plan);
|
||||
|
||||
if (!runtime_handle_bar_click(&ctx, &event, bar, command_buffer)) {
|
||||
policy_route_event(&ctx, &event, command_buffer);
|
||||
}
|
||||
|
||||
policy_apply_command(&ctx, command_buffer, plan);
|
||||
if (plan->need_relayout) runtime_arrange(runtime);
|
||||
if (plan->count) backend_apply_effect(backend, plan->effects, plan->count);
|
||||
|
||||
Reference in New Issue
Block a user