Skip to content

Commit 1b55da3

Browse files
authored
feat(wayland-cursor): 客户端自己的鼠标指针 —— fork 的第六个也是最后一个成员 (#306)
* feat(wayland-cursor): 客户端自己的鼠标指针,fork 的第六个也是最后一个成员 Wayland 没有服务端光标:客户端要指针,就得自己加载主题、把图像变成 wl_buffer、 附到交给 wl_pointer.set_cursor 的 surface 上。没有这个库,每个应用都得自己解析 XCursor 文件格式。 不要和 compat.xcursor 搞混:那是 X11 的 libXcursor.so.1,要和 X 服务器说话。 至此 fork 建齐了上游发布的每一个库,没有第七个。 ## 又一处编译期宿主路径,同样置空 xcursor.c:493 写死 "~/.icons:/usr/share/icons:/usr/share/pixmaps:..." —— 重定位之后指的是宿主的主题。而 xcursor_library_path() 先读 getenv("XCURSOR_PATH") 且设置了就原样返回(xcursor.c:515),所以这是兜底不是 唯一入口,和 LIBINPUT_QUIRKS_DIR / XKB_CONFIG_ROOT 完全对称。 fork CI 里 grep 二进制确认路径真没被编进去——这条只能这么查,因为值错了的失败 方式是在恰好有 /usr/share/icons 的机器上静默正常工作。 ## 一份 tarball 六个条目,tag 仍未动 发 wayland-1.26.0-mcpp3.tar.gz 作为 release 资产,六个描述符同一个提交里换指向。 旧资产原样保留,没有任何窗口里 main 是坏的。两镜像逐字节一致: GLOBAL/CN b95537b21b0df211... 1032352 bytes ## 测试成员:第一版段错误了 我以为传 null wl_shm 只会跑到主题解析器。实际 wayland-cursor.c:410 的 shm_pool_create(shm, ...) 直达 wl_shm_create_pool 并无保护解引用,exit 139。 wl_shm 只能来自合成器的 registry,所以这个库的 buffer 那半属于有合成器的程序, 正如打开 /dev/input/event* 不属于 tests/examples/libinput。 改成断言仍然承载打包风险的部分:模块导出全部六个名字、两个**公共结构**能当值用 (不是不透明句柄——导出成不透明会在这里编过、在调用者第一个 image->hotspot_x 上 炸)、以及真的链了 client。外加 wl_cursor_frame 系列——纯粹是 images 数组上的 算术,无需 shm,是本测试唯一能跑的真实行为。 13 项断言全过;wayland(2 passed)与 wayland-egl(1 passed)在新 tarball 上无退化。 * fix(test): 用 wl_display_connect 而非 static inline 的协议包装 llvm 那条腿抓到的,gcc 腿结构上看不见: ld.lld: error: undefined symbol: wl_proxy_get_version >>> wayland-cursor.o:(wl_shm_create_pool(wl_shm*, int, int)) wl_shm_create_pool 是 wayland-scanner 的 static inline 协议包装,取它的地址 **强制本 TU 实例化一份**,而那份会调 wl_proxy_marshal_flags 等 libwayland-client 私有符号。GNU ld 经 libwayland-cursor 的传递 DT_NEEDED 解析得到,lld 故意不—— 而 lld 是对的:命名了某个符号的程序应当链接定义它的库。 wl_display_connect 是普通导出函数,引用它证明同一件事而不实例化任何东西。 和 wayland-egl 测试同一形状,那个因此两条腿都绿。 同时把收尾备注写进文档: §12.5 libwayland-cursor 与第七个发现变量 XCURSOR_PATH §13.1 wlroots 只卡 hwdata + libdisplay-info——从它自己的 meson 逐条读出。 并纠正本文档早前两处:cairo **不是** wlroots 的缺口(只在 examples/, required: false);libdisplay-info 体量虽小但是整个 wlroots 的闸门 §13.2 桌面侧仍缺的七项,含规模 §13.3 发现变量总表:七格六有,第七格(光标主题)机制已就位缺提供方 §13.4 本轮被自己的测试抓住的三处——都是从假设写断言而不是从源码 版本 v1.1 -> v1.2
1 parent 698af52 commit 1b55da3

10 files changed

Lines changed: 335 additions & 16 deletions

.agents/docs/2026-08-30-graphics-stack-coverage-design.md

Lines changed: 91 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# mcpp 图形栈:从「能跑通」到「能开发」的覆盖面设计
22

3-
Date: 2026-08-30 · 前置:[`2026-08-30-gbm-cross-repo-closed-loop-plan.md`](2026-08-30-gbm-cross-repo-closed-loop-plan.md) §19/§20 · **状态:已实现并闭环验证(v1.1,见 §11 交付总账 / §12 客户端侧)**
3+
Date: 2026-08-30 · 前置:[`2026-08-30-gbm-cross-repo-closed-loop-plan.md`](2026-08-30-gbm-cross-repo-closed-loop-plan.md) §19/§20 · **状态:已实现并闭环验证(v1.2,见 §11 交付总账 / §12 客户端侧 / §13 仍缺的部分)**
44

55
## 0. 这份文档解决什么
66

@@ -1074,3 +1074,93 @@ main 是坏的,而重切方案无论顺序怎么排都有。
10741074
前两个是普通工作量。**wlroots 是需要决策的**:它是几乎每个现代合成器的基座
10751075
(sway / hyprland / river / wayfire),没有它,「用 mcpp 写合成器」的意思是从
10761076
DRM/GBM/EGL 直接起手写一万行。
1077+
1078+
### 12.5 `libwayland-cursor` —— 第六个也是最后一个成员(#306)
1079+
1080+
Wayland **没有服务端光标**:客户端要指针,就得自己加载主题、把图像变成
1081+
`wl_buffer`、附到交给 `wl_pointer.set_cursor` 的 surface 上。没有这个库,每个应用
1082+
都得自己解析 XCursor 文件格式。
1083+
1084+
⚠ 不是 `compat.xcursor` —— 那是 X11 的 `libXcursor.so.1`,要和 X 服务器说话。
1085+
1086+
**至此 fork 建齐了上游发布的每一个库,没有第七个。**`-egl` 的实质差别:cursor
1087+
**真的链** libwayland-client(`wl_shm_create_pool` 等),egl 只要头。两者 manifest
1088+
长得一样而实质不同,所以 CI 分别断言。
1089+
1090+
**第七个发现变量:`XCURSOR_PATH`** `xcursor.c:493` 写死
1091+
`"~/.icons:/usr/share/icons:/usr/share/pixmaps:…"`,而 `xcursor.c:515` 先读
1092+
`getenv("XCURSOR_PATH")` 且设置了就原样返回 —— 和 `LIBINPUT_QUIRKS_DIR` /
1093+
`XKB_CONFIG_ROOT` 完全对称,所以同样编译期置空。fork CI 里 **grep 二进制**确认,
1094+
因为值错了的失败方式是**在恰好有 `/usr/share/icons` 的机器上静默正常工作**
1095+
1096+
---
1097+
1098+
## 13. 仍缺的部分(收尾备注)
1099+
1100+
排在这里的都**核实过**,不是猜测。
1101+
1102+
### 13.1 wlroots 只卡在两个包 —— 从它自己的 meson 逐条读出
1103+
1104+
| wlroots 0.18.2 需要 | 状态 |
1105+
|---|---|
1106+
| wayland-server / client / protocols / scanner / **egl** | ✅ 六个成员齐了 |
1107+
| libdrm · pixman · xkbcommon · libinput · libudev · libseat · egl · glesv2 · gbm ||
1108+
| vulkan(可选渲染器)· lcms2(可选色彩管理)· libliftoff(可选) | ✅ / 可选 |
1109+
| **hwdata**(构建期,读 `pnp.ids` 生成 `pnpids.c`) ||
1110+
| **libdisplay-info** ||
1111+
| cairo | **不需要** —— 只在 `examples/`,`required: false` |
1112+
1113+
⚠ 本文档早前把 cairo 列为 wlroots 的缺口,**是错的**;而把 `libdisplay-info` 标为
1114+
「小」也没说清 —— 它体量确实小,但**它是整个 wlroots 的闸门**
1115+
1116+
`libdisplay-info` 该走 **fork + 预生成**(和 libevdev / libxkbcommon 同形):它要跑
1117+
`tool/gen-search-table.py` 生成 2568 行的 `pnp-id-table.c`,放不进
1118+
`generated_files` 字面量。而且 **`pnp.ids` 必须取上游 hwdata 的发布物,不能用宿主
1119+
那份** —— 上游 meson 找不到 hwdata 时会回落到 `/usr/share/hwdata/pnp.ids`,正是要
1120+
消的宿主边;这也是 libevdev 那次「宿主内核头给出不同的表」的同一教训。
1121+
1122+
### 13.2 桌面侧仍缺的
1123+
1124+
| 缺口 | 后果 | 规模 |
1125+
|---|---|---|
1126+
| `fontconfig` | **按名字找字体**`freetype``harfbuzz` ✓ 只给字形,不给发现 ||
1127+
| `pango` | 段落级排版:换行、双向文字、CJK、组合字符 | 中(拖 glib) |
1128+
| `cairo` | 2D 矢量绘制,或全用 GLES 自己画 ||
1129+
| `dbus` | 通知、portal、会话、媒体键 ||
1130+
| `PipeWire` | 音频 + 屏幕共享 ||
1131+
| `libjpeg-turbo` | png/webp 有了,jpeg 没有 ||
1132+
| XWayland | 跑 X 应用(本轮明确不考虑) ||
1133+
1134+
**数据包(xim 侧,和 `xkeyboard-config` 同形)**:光标主题(填 `XCURSOR_PATH`)、
1135+
图标主题、字体。三者都是「机制已就位、缺提供方」。
1136+
1137+
### 13.3 发现变量总表(收尾状态)
1138+
1139+
| 子系统 | 变量 | 提供方 |
1140+
|--------|------|--------|
1141+
| DRI 驱动 | `LIBGL_DRIVERS_PATH` | `xim:mesa`|
1142+
| EGL vendor | `__EGL_VENDOR_LIBRARY_DIRS` | `xim:mesa` + host-link 哨兵 ✅ |
1143+
| Vulkan ICD | `XDG_DATA_DIRS` | 同上 ✅ |
1144+
| GBM 后端 | `GBM_BACKENDS_PATH` | `xim:mesa`|
1145+
| 键盘布局 | `XKB_CONFIG_ROOT` | `xim:xkeyboard-config`|
1146+
| 输入 quirks | `LIBINPUT_QUIRKS_DIR` | `xim:libinput-quirks`|
1147+
| **光标主题** | **`XCURSOR_PATH`** | **— 机制已就位,缺提供方** |
1148+
| USB 名字库 | `USB_IDS_PATH` | — 经核实**不该补**(§10.9.4) |
1149+
1150+
七格里六格有提供方,第七格(光标主题)是纯数据包,补法与 xkeyboard-config 完全相同。
1151+
1152+
### 13.4 本轮被自己的测试抓住的三处
1153+
1154+
都记在这里,因为它们是同一类:**我从假设写断言,而不是从源码**
1155+
1156+
1. **`get_attached_size` 返回创建尺寸** → 实际 `0 x 0``attached_*` 是 EGL 实现
1157+
附加缓冲时才写的字段(§12.3)。
1158+
2. **null `wl_shm` 只会跑到主题解析器** → 实际 `wayland-cursor.c:410` 无保护解引用,
1159+
段错误 exit 139。`wl_cursor_theme_load` 真的需要活的 `wl_shm`
1160+
3. **`&wl_shm_create_pool` 能证明链了 client** → 它是 wayland-scanner 的
1161+
`static inline` 协议包装,取地址会**强制本 TU 实例化**,把
1162+
`wl_proxy_marshal_flags` 等私有符号拖进来。GNU ld 经传递 DT_NEEDED 解析,
1163+
**lld 不会,而 lld 是对的**。改用 `wl_display_connect`(真外部符号)。
1164+
1165+
第 3 条只有 llvm 那条腿抓得到 —— 它没有 sysroot 且用 lld,而这正是
1166+
`validate.yml` 注释里说的「gcc 腿结构上看不见这一类 bug」。

mcpp.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ members = [
8787
"tests/examples/expat",
8888
"tests/examples/wayland",
8989
"tests/examples/wayland-egl",
90+
"tests/examples/wayland-cursor",
9091
"tests/examples/egl",
9192
"tests/examples/gl",
9293
"tests/examples/libevdev",
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
-- freedesktop.wayland-cursor — the pointer image a Wayland client sets on
2+
-- itself.
3+
--
4+
-- Wayland has NO SERVER-SIDE CURSOR. A client that wants a pointer loads a
5+
-- cursor theme, turns the image into a `wl_buffer`, and attaches it to a
6+
-- surface it hands to `wl_pointer.set_cursor`. This library is the first two
7+
-- steps — without it every application parses the XCursor file format itself,
8+
-- which is what `upstream/cursor/xcursor.c` is.
9+
--
10+
-- NOT `compat.xcursor`, which is X11's `libXcursor.so.1` and talks to an X
11+
-- server. This one has no X dependency and produces a `wl_buffer` via `wl_shm`.
12+
--
13+
-- ─────────────────────────────────────────────────────────────────────────
14+
-- THE COMPILED-IN THEME PATH IS EMPTY, AND THAT IS A DECISION
15+
--
16+
-- `xcursor.c:493` bakes in
17+
--
18+
-- "~/.icons:/usr/share/icons:/usr/share/pixmaps:~/.cursors:"
19+
-- "/usr/share/cursors/xorg-x11:" ICONDIR
20+
--
21+
-- — a list of HOST paths. After relocation those name the host machine's
22+
-- themes, which is the same silent host edge that gave Vulkan an llvmpipe
23+
-- device instead of the GPU.
24+
--
25+
-- `xcursor_library_path()` reads `getenv("XCURSOR_PATH")` first and returns it
26+
-- verbatim when set (`xcursor.c:515`), so the compiled-in list is a FALLBACK
27+
-- and the environment is the real interface — the same shape as
28+
-- `LIBINPUT_QUIRKS_DIR` and `XKB_CONFIG_ROOT`, and the same answer: compile it
29+
-- empty, let the ecosystem declare `XCURSOR_PATH`.
30+
--
31+
-- What that costs, named rather than discovered: a client with no
32+
-- `XCURSOR_PATH` gets a theme with no cursors from `wl_cursor_theme_load`.
33+
-- Visible, rather than a pointer that works on the developer's machine and
34+
-- nowhere else. Nothing in this index or in xim ships cursor themes yet; when
35+
-- something does, it declares the variable, exactly as `xim:xkeyboard-config`
36+
-- declares `XKB_CONFIG_ROOT`.
37+
--
38+
-- ─────────────────────────────────────────────────────────────────────────
39+
-- SIXTH AND LAST MEMBER OF THE SAME TARBALL
40+
--
41+
-- `freedesktop.wayland`, `-server`, `-util`, `-scanner`, `-egl` and this one
42+
-- are six index entries backed by ONE archive, each naming a different
43+
-- workspace member. With this one the fork builds every library upstream
44+
-- ships; there is no seventh.
45+
--
46+
-- Adding it changed the archive, so all six carry the new sha256 in the same
47+
-- commit — and the tag was left alone with a new release ASSET published
48+
-- instead, for the reason recorded when `-egl` was added.
49+
package = {
50+
spec = "1",
51+
namespace = "freedesktop",
52+
name = "wayland-cursor",
53+
description = "libwayland-cursor 1.26.0 — load an XCursor theme into a wl_buffer, for clients that set their own pointer",
54+
licenses = {"MIT"},
55+
repo = "https://github.com/mcpplibs/wayland",
56+
type = "package",
57+
58+
xpm = {
59+
linux = {
60+
["1.26.0"] = {
61+
url = {
62+
GLOBAL = "https://github.com/mcpplibs/wayland/releases/download/v1.26.0/wayland-1.26.0-mcpp3.tar.gz",
63+
CN = "https://gitcode.com/mcpp-res/wayland/releases/download/1.26.0/wayland-1.26.0-mcpp3.tar.gz",
64+
},
65+
sha256 = "b95537b21b0df2119a84ec8b3b833a564259e52fbbe2b119c5f9dd7cbaad55a0",
66+
},
67+
},
68+
},
69+
70+
mcpp = "*/mcpp/cursor/mcpp.toml",
71+
}

pkgs/f/freedesktop.wayland-egl.lua

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,10 @@ package = {
3939
linux = {
4040
["1.26.0"] = {
4141
url = {
42-
GLOBAL = "https://github.com/mcpplibs/wayland/releases/download/v1.26.0/wayland-1.26.0-mcpp2.tar.gz",
43-
CN = "https://gitcode.com/mcpp-res/wayland/releases/download/1.26.0/wayland-1.26.0-mcpp2.tar.gz",
42+
GLOBAL = "https://github.com/mcpplibs/wayland/releases/download/v1.26.0/wayland-1.26.0-mcpp3.tar.gz",
43+
CN = "https://gitcode.com/mcpp-res/wayland/releases/download/1.26.0/wayland-1.26.0-mcpp3.tar.gz",
4444
},
45-
sha256 = "bcf388cc1dd6617fdce5cb595defbe2aa1fae8db292ca5d7fd84afda2811be32",
45+
sha256 = "b95537b21b0df2119a84ec8b3b833a564259e52fbbe2b119c5f9dd7cbaad55a0",
4646
},
4747
},
4848
},

pkgs/f/freedesktop.wayland-scanner.lua

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,10 @@ package = {
3333
linux = {
3434
["1.26.0"] = {
3535
url = {
36-
GLOBAL = "https://github.com/mcpplibs/wayland/releases/download/v1.26.0/wayland-1.26.0-mcpp2.tar.gz",
37-
CN = "https://gitcode.com/mcpp-res/wayland/releases/download/1.26.0/wayland-1.26.0-mcpp2.tar.gz",
36+
GLOBAL = "https://github.com/mcpplibs/wayland/releases/download/v1.26.0/wayland-1.26.0-mcpp3.tar.gz",
37+
CN = "https://gitcode.com/mcpp-res/wayland/releases/download/1.26.0/wayland-1.26.0-mcpp3.tar.gz",
3838
},
39-
sha256 = "bcf388cc1dd6617fdce5cb595defbe2aa1fae8db292ca5d7fd84afda2811be32",
39+
sha256 = "b95537b21b0df2119a84ec8b3b833a564259e52fbbe2b119c5f9dd7cbaad55a0",
4040
},
4141
},
4242
},

pkgs/f/freedesktop.wayland-server.lua

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,10 @@ package = {
3333
linux = {
3434
["1.26.0"] = {
3535
url = {
36-
GLOBAL = "https://github.com/mcpplibs/wayland/releases/download/v1.26.0/wayland-1.26.0-mcpp2.tar.gz",
37-
CN = "https://gitcode.com/mcpp-res/wayland/releases/download/1.26.0/wayland-1.26.0-mcpp2.tar.gz",
36+
GLOBAL = "https://github.com/mcpplibs/wayland/releases/download/v1.26.0/wayland-1.26.0-mcpp3.tar.gz",
37+
CN = "https://gitcode.com/mcpp-res/wayland/releases/download/1.26.0/wayland-1.26.0-mcpp3.tar.gz",
3838
},
39-
sha256 = "bcf388cc1dd6617fdce5cb595defbe2aa1fae8db292ca5d7fd84afda2811be32",
39+
sha256 = "b95537b21b0df2119a84ec8b3b833a564259e52fbbe2b119c5f9dd7cbaad55a0",
4040
},
4141
},
4242
},

pkgs/f/freedesktop.wayland-util.lua

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,10 @@ package = {
3333
linux = {
3434
["1.26.0"] = {
3535
url = {
36-
GLOBAL = "https://github.com/mcpplibs/wayland/releases/download/v1.26.0/wayland-1.26.0-mcpp2.tar.gz",
37-
CN = "https://gitcode.com/mcpp-res/wayland/releases/download/1.26.0/wayland-1.26.0-mcpp2.tar.gz",
36+
GLOBAL = "https://github.com/mcpplibs/wayland/releases/download/v1.26.0/wayland-1.26.0-mcpp3.tar.gz",
37+
CN = "https://gitcode.com/mcpp-res/wayland/releases/download/1.26.0/wayland-1.26.0-mcpp3.tar.gz",
3838
},
39-
sha256 = "bcf388cc1dd6617fdce5cb595defbe2aa1fae8db292ca5d7fd84afda2811be32",
39+
sha256 = "b95537b21b0df2119a84ec8b3b833a564259e52fbbe2b119c5f9dd7cbaad55a0",
4040
},
4141
},
4242
},

pkgs/f/freedesktop.wayland.lua

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,10 @@ package = {
3333
linux = {
3434
["1.26.0"] = {
3535
url = {
36-
GLOBAL = "https://github.com/mcpplibs/wayland/releases/download/v1.26.0/wayland-1.26.0-mcpp2.tar.gz",
37-
CN = "https://gitcode.com/mcpp-res/wayland/releases/download/1.26.0/wayland-1.26.0-mcpp2.tar.gz",
36+
GLOBAL = "https://github.com/mcpplibs/wayland/releases/download/v1.26.0/wayland-1.26.0-mcpp3.tar.gz",
37+
CN = "https://gitcode.com/mcpp-res/wayland/releases/download/1.26.0/wayland-1.26.0-mcpp3.tar.gz",
3838
},
39-
sha256 = "bcf388cc1dd6617fdce5cb595defbe2aa1fae8db292ca5d7fd84afda2811be32",
39+
sha256 = "b95537b21b0df2119a84ec8b3b833a564259e52fbbe2b119c5f9dd7cbaad55a0",
4040
},
4141
},
4242
},
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# wayland-cursor test member — the client-side pointer.
2+
#
3+
# Own `[indices]` for the reason the sibling wayland members document: the
4+
# workspace root maps `compat` to this checkout and lookup is BY NAMESPACE.
5+
[indices]
6+
freedesktop = { path = "../../.." }
7+
8+
# ONE package named, and libwayland-client arrives with it — `wayland-cursor`
9+
# declares it as a PATH dependency (they are workspace members of one fork), and
10+
# naming it here as a version dependency too is rejected outright. Same shape as
11+
# tests/examples/wayland-egl; the note there explains why the diagnostic is
12+
# right.
13+
[package]
14+
name = "wayland-cursor-tests"
15+
version = "0.1.0"
16+
standard = "c++23"
17+
18+
[target.'cfg(linux)'.dependencies.freedesktop]
19+
wayland-cursor = "1.26.0"

0 commit comments

Comments
 (0)