From c007c03e15ab42f1f9f506b038b208fb84b93860 Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Sun, 30 Aug 2026 21:28:53 +0800 Subject: [PATCH 1/2] =?UTF-8?q?feat(wayland-cursor):=20=E5=AE=A2=E6=88=B7?= =?UTF-8?q?=E7=AB=AF=E8=87=AA=E5=B7=B1=E7=9A=84=E9=BC=A0=E6=A0=87=E6=8C=87?= =?UTF-8?q?=E9=92=88,fork=20=E7=9A=84=E7=AC=AC=E5=85=AD=E4=B8=AA=E4=B9=9F?= =?UTF-8?q?=E6=98=AF=E6=9C=80=E5=90=8E=E4=B8=80=E4=B8=AA=E6=88=90=E5=91=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 上无退化。 --- mcpp.toml | 1 + pkgs/f/freedesktop.wayland-cursor.lua | 71 +++++++++++ pkgs/f/freedesktop.wayland-egl.lua | 6 +- pkgs/f/freedesktop.wayland-scanner.lua | 6 +- pkgs/f/freedesktop.wayland-server.lua | 6 +- pkgs/f/freedesktop.wayland-util.lua | 6 +- pkgs/f/freedesktop.wayland.lua | 6 +- tests/examples/wayland-cursor/mcpp.toml | 19 +++ .../wayland-cursor/tests/wayland-cursor.cpp | 119 ++++++++++++++++++ 9 files changed, 225 insertions(+), 15 deletions(-) create mode 100644 pkgs/f/freedesktop.wayland-cursor.lua create mode 100644 tests/examples/wayland-cursor/mcpp.toml create mode 100644 tests/examples/wayland-cursor/tests/wayland-cursor.cpp diff --git a/mcpp.toml b/mcpp.toml index 5a01282..1c85667 100644 --- a/mcpp.toml +++ b/mcpp.toml @@ -87,6 +87,7 @@ members = [ "tests/examples/expat", "tests/examples/wayland", "tests/examples/wayland-egl", + "tests/examples/wayland-cursor", "tests/examples/egl", "tests/examples/gl", "tests/examples/libevdev", diff --git a/pkgs/f/freedesktop.wayland-cursor.lua b/pkgs/f/freedesktop.wayland-cursor.lua new file mode 100644 index 0000000..b5ac2e4 --- /dev/null +++ b/pkgs/f/freedesktop.wayland-cursor.lua @@ -0,0 +1,71 @@ +-- freedesktop.wayland-cursor — the pointer image a Wayland client sets on +-- itself. +-- +-- Wayland has NO SERVER-SIDE CURSOR. A client that wants a pointer loads a +-- cursor theme, turns the image into a `wl_buffer`, and attaches it to a +-- surface it hands to `wl_pointer.set_cursor`. This library is the first two +-- steps — without it every application parses the XCursor file format itself, +-- which is what `upstream/cursor/xcursor.c` is. +-- +-- NOT `compat.xcursor`, which is X11's `libXcursor.so.1` and talks to an X +-- server. This one has no X dependency and produces a `wl_buffer` via `wl_shm`. +-- +-- ───────────────────────────────────────────────────────────────────────── +-- THE COMPILED-IN THEME PATH IS EMPTY, AND THAT IS A DECISION +-- +-- `xcursor.c:493` bakes in +-- +-- "~/.icons:/usr/share/icons:/usr/share/pixmaps:~/.cursors:" +-- "/usr/share/cursors/xorg-x11:" ICONDIR +-- +-- — a list of HOST paths. After relocation those name the host machine's +-- themes, which is the same silent host edge that gave Vulkan an llvmpipe +-- device instead of the GPU. +-- +-- `xcursor_library_path()` reads `getenv("XCURSOR_PATH")` first and returns it +-- verbatim when set (`xcursor.c:515`), so the compiled-in list is a FALLBACK +-- and the environment is the real interface — the same shape as +-- `LIBINPUT_QUIRKS_DIR` and `XKB_CONFIG_ROOT`, and the same answer: compile it +-- empty, let the ecosystem declare `XCURSOR_PATH`. +-- +-- What that costs, named rather than discovered: a client with no +-- `XCURSOR_PATH` gets a theme with no cursors from `wl_cursor_theme_load`. +-- Visible, rather than a pointer that works on the developer's machine and +-- nowhere else. Nothing in this index or in xim ships cursor themes yet; when +-- something does, it declares the variable, exactly as `xim:xkeyboard-config` +-- declares `XKB_CONFIG_ROOT`. +-- +-- ───────────────────────────────────────────────────────────────────────── +-- SIXTH AND LAST MEMBER OF THE SAME TARBALL +-- +-- `freedesktop.wayland`, `-server`, `-util`, `-scanner`, `-egl` and this one +-- are six index entries backed by ONE archive, each naming a different +-- workspace member. With this one the fork builds every library upstream +-- ships; there is no seventh. +-- +-- Adding it changed the archive, so all six carry the new sha256 in the same +-- commit — and the tag was left alone with a new release ASSET published +-- instead, for the reason recorded when `-egl` was added. +package = { + spec = "1", + namespace = "freedesktop", + name = "wayland-cursor", + description = "libwayland-cursor 1.26.0 — load an XCursor theme into a wl_buffer, for clients that set their own pointer", + licenses = {"MIT"}, + repo = "https://github.com/mcpplibs/wayland", + type = "package", + + xpm = { + linux = { + ["1.26.0"] = { + url = { + GLOBAL = "https://github.com/mcpplibs/wayland/releases/download/v1.26.0/wayland-1.26.0-mcpp3.tar.gz", + CN = "https://gitcode.com/mcpp-res/wayland/releases/download/1.26.0/wayland-1.26.0-mcpp3.tar.gz", + }, + sha256 = "b95537b21b0df2119a84ec8b3b833a564259e52fbbe2b119c5f9dd7cbaad55a0", + }, + }, + }, + + mcpp = "*/mcpp/cursor/mcpp.toml", +} diff --git a/pkgs/f/freedesktop.wayland-egl.lua b/pkgs/f/freedesktop.wayland-egl.lua index 6d1830c..372343d 100644 --- a/pkgs/f/freedesktop.wayland-egl.lua +++ b/pkgs/f/freedesktop.wayland-egl.lua @@ -39,10 +39,10 @@ package = { linux = { ["1.26.0"] = { url = { - GLOBAL = "https://github.com/mcpplibs/wayland/releases/download/v1.26.0/wayland-1.26.0-mcpp2.tar.gz", - CN = "https://gitcode.com/mcpp-res/wayland/releases/download/1.26.0/wayland-1.26.0-mcpp2.tar.gz", + GLOBAL = "https://github.com/mcpplibs/wayland/releases/download/v1.26.0/wayland-1.26.0-mcpp3.tar.gz", + CN = "https://gitcode.com/mcpp-res/wayland/releases/download/1.26.0/wayland-1.26.0-mcpp3.tar.gz", }, - sha256 = "bcf388cc1dd6617fdce5cb595defbe2aa1fae8db292ca5d7fd84afda2811be32", + sha256 = "b95537b21b0df2119a84ec8b3b833a564259e52fbbe2b119c5f9dd7cbaad55a0", }, }, }, diff --git a/pkgs/f/freedesktop.wayland-scanner.lua b/pkgs/f/freedesktop.wayland-scanner.lua index 058d72f..214f2e9 100644 --- a/pkgs/f/freedesktop.wayland-scanner.lua +++ b/pkgs/f/freedesktop.wayland-scanner.lua @@ -33,10 +33,10 @@ package = { linux = { ["1.26.0"] = { url = { - GLOBAL = "https://github.com/mcpplibs/wayland/releases/download/v1.26.0/wayland-1.26.0-mcpp2.tar.gz", - CN = "https://gitcode.com/mcpp-res/wayland/releases/download/1.26.0/wayland-1.26.0-mcpp2.tar.gz", + GLOBAL = "https://github.com/mcpplibs/wayland/releases/download/v1.26.0/wayland-1.26.0-mcpp3.tar.gz", + CN = "https://gitcode.com/mcpp-res/wayland/releases/download/1.26.0/wayland-1.26.0-mcpp3.tar.gz", }, - sha256 = "bcf388cc1dd6617fdce5cb595defbe2aa1fae8db292ca5d7fd84afda2811be32", + sha256 = "b95537b21b0df2119a84ec8b3b833a564259e52fbbe2b119c5f9dd7cbaad55a0", }, }, }, diff --git a/pkgs/f/freedesktop.wayland-server.lua b/pkgs/f/freedesktop.wayland-server.lua index 9fcda61..a7a9aca 100644 --- a/pkgs/f/freedesktop.wayland-server.lua +++ b/pkgs/f/freedesktop.wayland-server.lua @@ -33,10 +33,10 @@ package = { linux = { ["1.26.0"] = { url = { - GLOBAL = "https://github.com/mcpplibs/wayland/releases/download/v1.26.0/wayland-1.26.0-mcpp2.tar.gz", - CN = "https://gitcode.com/mcpp-res/wayland/releases/download/1.26.0/wayland-1.26.0-mcpp2.tar.gz", + GLOBAL = "https://github.com/mcpplibs/wayland/releases/download/v1.26.0/wayland-1.26.0-mcpp3.tar.gz", + CN = "https://gitcode.com/mcpp-res/wayland/releases/download/1.26.0/wayland-1.26.0-mcpp3.tar.gz", }, - sha256 = "bcf388cc1dd6617fdce5cb595defbe2aa1fae8db292ca5d7fd84afda2811be32", + sha256 = "b95537b21b0df2119a84ec8b3b833a564259e52fbbe2b119c5f9dd7cbaad55a0", }, }, }, diff --git a/pkgs/f/freedesktop.wayland-util.lua b/pkgs/f/freedesktop.wayland-util.lua index 67ee923..94b94cf 100644 --- a/pkgs/f/freedesktop.wayland-util.lua +++ b/pkgs/f/freedesktop.wayland-util.lua @@ -33,10 +33,10 @@ package = { linux = { ["1.26.0"] = { url = { - GLOBAL = "https://github.com/mcpplibs/wayland/releases/download/v1.26.0/wayland-1.26.0-mcpp2.tar.gz", - CN = "https://gitcode.com/mcpp-res/wayland/releases/download/1.26.0/wayland-1.26.0-mcpp2.tar.gz", + GLOBAL = "https://github.com/mcpplibs/wayland/releases/download/v1.26.0/wayland-1.26.0-mcpp3.tar.gz", + CN = "https://gitcode.com/mcpp-res/wayland/releases/download/1.26.0/wayland-1.26.0-mcpp3.tar.gz", }, - sha256 = "bcf388cc1dd6617fdce5cb595defbe2aa1fae8db292ca5d7fd84afda2811be32", + sha256 = "b95537b21b0df2119a84ec8b3b833a564259e52fbbe2b119c5f9dd7cbaad55a0", }, }, }, diff --git a/pkgs/f/freedesktop.wayland.lua b/pkgs/f/freedesktop.wayland.lua index b783a49..bb8a7ba 100644 --- a/pkgs/f/freedesktop.wayland.lua +++ b/pkgs/f/freedesktop.wayland.lua @@ -33,10 +33,10 @@ package = { linux = { ["1.26.0"] = { url = { - GLOBAL = "https://github.com/mcpplibs/wayland/releases/download/v1.26.0/wayland-1.26.0-mcpp2.tar.gz", - CN = "https://gitcode.com/mcpp-res/wayland/releases/download/1.26.0/wayland-1.26.0-mcpp2.tar.gz", + GLOBAL = "https://github.com/mcpplibs/wayland/releases/download/v1.26.0/wayland-1.26.0-mcpp3.tar.gz", + CN = "https://gitcode.com/mcpp-res/wayland/releases/download/1.26.0/wayland-1.26.0-mcpp3.tar.gz", }, - sha256 = "bcf388cc1dd6617fdce5cb595defbe2aa1fae8db292ca5d7fd84afda2811be32", + sha256 = "b95537b21b0df2119a84ec8b3b833a564259e52fbbe2b119c5f9dd7cbaad55a0", }, }, }, diff --git a/tests/examples/wayland-cursor/mcpp.toml b/tests/examples/wayland-cursor/mcpp.toml new file mode 100644 index 0000000..3fab395 --- /dev/null +++ b/tests/examples/wayland-cursor/mcpp.toml @@ -0,0 +1,19 @@ +# wayland-cursor test member — the client-side pointer. +# +# Own `[indices]` for the reason the sibling wayland members document: the +# workspace root maps `compat` to this checkout and lookup is BY NAMESPACE. +[indices] +freedesktop = { path = "../../.." } + +# ONE package named, and libwayland-client arrives with it — `wayland-cursor` +# declares it as a PATH dependency (they are workspace members of one fork), and +# naming it here as a version dependency too is rejected outright. Same shape as +# tests/examples/wayland-egl; the note there explains why the diagnostic is +# right. +[package] +name = "wayland-cursor-tests" +version = "0.1.0" +standard = "c++23" + +[target.'cfg(linux)'.dependencies.freedesktop] +wayland-cursor = "1.26.0" diff --git a/tests/examples/wayland-cursor/tests/wayland-cursor.cpp b/tests/examples/wayland-cursor/tests/wayland-cursor.cpp new file mode 100644 index 0000000..d3b21c7 --- /dev/null +++ b/tests/examples/wayland-cursor/tests/wayland-cursor.cpp @@ -0,0 +1,119 @@ +// freedesktop.wayland-cursor — the client-side pointer. +// +// WHAT CANNOT BE ASSERTED HERE, AND WHY THE FIRST VERSION OF THIS FILE CRASHED +// +// `wl_cursor_theme_load(name, size, shm)` is not callable without a compositor, +// and not merely inadvisable: line 410 of upstream's wayland-cursor.c is +// +// theme->pool = shm_pool_create(shm, size * size * 4); +// +// which reaches `wl_shm_create_pool(shm, ...)` and dereferences the proxy with +// no null check. The first version of this test passed `nullptr` on the theory +// that only the theme PARSER would run — it segfaulted (exit 139). A `wl_shm` +// comes from a compositor's registry, so the buffer half of this library +// belongs in a program that has one, exactly as opening /dev/input/event* +// belongs outside tests/examples/libinput. +// +// WHAT IS ASSERTED IS STILL THE PART THAT CARRIES THE PACKAGING RISK: that the +// hand-written module wrapper exports every name, that the two PUBLIC structs +// are usable as values rather than opaque handles, and that libwayland-client +// is really linked in. The compiled-in cursor search path being empty — the +// other packaging decision — is checked in the fork's CI by grepping the +// binary, which is the only place it is visible. + +#ifdef __linux__ + +#include +#include + +#include +#include +#include + +import freedesktop.wayland.cursor; + +namespace { + +int failures = 0; + +void check(bool ok, const char *what) +{ + std::printf("%-58s %s\n", what, ok ? "ok" : "FAILED"); + if (!ok) { + ++failures; + } +} + +} // namespace + +int main() +{ + // ── 1. The module carries the API ──────────────────────────────────── + // Taking the address of each: a name that vanished from the hand-written + // module wrapper is a compile error here. That is the guarantee + // mcpp/tools/genmod.py gives the client and server wrappers, which this + // one is too small to justify. + check(&wl_cursor_theme_load != nullptr, "module exports wl_cursor_theme_load"); + check(&wl_cursor_theme_destroy != nullptr, "module exports wl_cursor_theme_destroy"); + check(&wl_cursor_theme_get_cursor != nullptr, "module exports wl_cursor_theme_get_cursor"); + check(&wl_cursor_image_get_buffer != nullptr, "module exports wl_cursor_image_get_buffer"); + check(&wl_cursor_frame != nullptr, "module exports wl_cursor_frame"); + check(&wl_cursor_frame_and_duration != nullptr, "module exports wl_cursor_frame_and_duration"); + + // ── 2. The public structs are VALUES, not opaque handles ───────────── + // `wl_cursor` and `wl_cursor_image` have their fields in the public header + // and a client reads them to place the hotspot every time the pointer + // enters a surface. Exporting them as opaque would compile here and fail + // at the caller's first `image->hotspot_x`, which is why the fields are + // touched rather than just the types named. + wl_cursor_image img{}; + img.width = 24; img.height = 24; + img.hotspot_x = 4; img.hotspot_y = 4; img.delay = 50; + check(img.width == 24 && img.hotspot_x == 4 && img.delay == 50, + "wl_cursor_image fields are reachable through the module"); + + wl_cursor cur{}; + wl_cursor_image *one = &img; + cur.image_count = 1; + cur.images = &one; + cur.name = const_cast("left_ptr"); + check(cur.image_count == 1 && cur.images[0]->hotspot_y == 4, + "wl_cursor fields are reachable, including the images array"); + + // ── 3. wl_cursor_frame works on a theme-less cursor ────────────────── + // The animation helper is pure arithmetic over the images array — no shm, + // no compositor — so it IS callable here, and it is the one piece of real + // behaviour this test can exercise. A single-image cursor is not animated, + // so every timestamp must select frame 0. + check(wl_cursor_frame(&cur, 0) == 0, "wl_cursor_frame(t=0) selects the only frame"); + check(wl_cursor_frame(&cur, 100000) == 0, "…and so does a far-future timestamp"); + + uint32_t duration = 12345; // wayland-cursor.h includes , not + const int f = wl_cursor_frame_and_duration(&cur, 0, &duration); + std::printf(" frame_and_duration -> frame %d, duration %u\n", f, duration); + check(f == 0, "wl_cursor_frame_and_duration agrees on the frame"); + + // ── 4. It links the client for real ────────────────────────────────── + // Unlike libwayland-egl, this library CALLS into libwayland-client — that + // is exactly why §3's theme loader needs a live wl_shm. The two are in one + // program by necessity rather than by convention. + check(&wl_shm_create_pool != nullptr, + "libwayland-client is linked in (wl_shm_create_pool)"); + + // ── 5. Report the environment the ecosystem is expected to fill ─────── + // Not asserted: nothing in the index or in xim ships cursor themes yet, so + // an unset XCURSOR_PATH is the current correct state rather than a defect. + // When a theme package exists this becomes an assertion, exactly as the + // RMLVO check in tests/examples/libxkbcommon did once xkeyboard-config + // landed. + const char *xp = std::getenv("XCURSOR_PATH"); + std::printf(" XCURSOR_PATH = %s\n", + xp ? xp : "(unset — no cursor-theme package exists yet)"); + + std::printf("\n%d check(s) failed\n", failures); + return failures == 0 ? 0 : 1; +} + +#else +int main() { return 0; } +#endif From 4483fc65612ad43b2083e699edb76af2c39887b4 Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Sun, 30 Aug 2026 21:34:27 +0800 Subject: [PATCH 2/2] =?UTF-8?q?fix(test):=20=E7=94=A8=20wl=5Fdisplay=5Fcon?= =?UTF-8?q?nect=20=E8=80=8C=E9=9D=9E=20static=20inline=20=E7=9A=84?= =?UTF-8?q?=E5=8D=8F=E8=AE=AE=E5=8C=85=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- ...26-08-30-graphics-stack-coverage-design.md | 92 ++++++++++++++++++- .../wayland-cursor/tests/wayland-cursor.cpp | 23 ++++- 2 files changed, 112 insertions(+), 3 deletions(-) diff --git a/.agents/docs/2026-08-30-graphics-stack-coverage-design.md b/.agents/docs/2026-08-30-graphics-stack-coverage-design.md index 970fabb..464f889 100644 --- a/.agents/docs/2026-08-30-graphics-stack-coverage-design.md +++ b/.agents/docs/2026-08-30-graphics-stack-coverage-design.md @@ -1,6 +1,6 @@ # mcpp 图形栈:从「能跑通」到「能开发」的覆盖面设计 -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 客户端侧)** +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 仍缺的部分)** ## 0. 这份文档解决什么 @@ -1074,3 +1074,93 @@ main 是坏的,而重切方案无论顺序怎么排都有。 前两个是普通工作量。**wlroots 是需要决策的**:它是几乎每个现代合成器的基座 (sway / hyprland / river / wayfire),没有它,「用 mcpp 写合成器」的意思是从 DRM/GBM/EGL 直接起手写一万行。 + +### 12.5 `libwayland-cursor` —— 第六个也是最后一个成员(#306) + +Wayland **没有服务端光标**:客户端要指针,就得自己加载主题、把图像变成 +`wl_buffer`、附到交给 `wl_pointer.set_cursor` 的 surface 上。没有这个库,每个应用 +都得自己解析 XCursor 文件格式。 + +⚠ 不是 `compat.xcursor` —— 那是 X11 的 `libXcursor.so.1`,要和 X 服务器说话。 + +**至此 fork 建齐了上游发布的每一个库,没有第七个。** 与 `-egl` 的实质差别:cursor +**真的链** libwayland-client(`wl_shm_create_pool` 等),egl 只要头。两者 manifest +长得一样而实质不同,所以 CI 分别断言。 + +**第七个发现变量:`XCURSOR_PATH`。** `xcursor.c:493` 写死 +`"~/.icons:/usr/share/icons:/usr/share/pixmaps:…"`,而 `xcursor.c:515` 先读 +`getenv("XCURSOR_PATH")` 且设置了就原样返回 —— 和 `LIBINPUT_QUIRKS_DIR` / +`XKB_CONFIG_ROOT` 完全对称,所以同样编译期置空。fork CI 里 **grep 二进制**确认, +因为值错了的失败方式是**在恰好有 `/usr/share/icons` 的机器上静默正常工作**。 + +--- + +## 13. 仍缺的部分(收尾备注) + +排在这里的都**核实过**,不是猜测。 + +### 13.1 wlroots 只卡在两个包 —— 从它自己的 meson 逐条读出 + +| wlroots 0.18.2 需要 | 状态 | +|---|---| +| wayland-server / client / protocols / scanner / **egl** | ✅ 六个成员齐了 | +| libdrm · pixman · xkbcommon · libinput · libudev · libseat · egl · glesv2 · gbm | ✅ | +| vulkan(可选渲染器)· lcms2(可选色彩管理)· libliftoff(可选) | ✅ / 可选 | +| **hwdata**(构建期,读 `pnp.ids` 生成 `pnpids.c`) | ❌ | +| **libdisplay-info** | ❌ | +| cairo | **不需要** —— 只在 `examples/`,`required: false` | + +⚠ 本文档早前把 cairo 列为 wlroots 的缺口,**是错的**;而把 `libdisplay-info` 标为 +「小」也没说清 —— 它体量确实小,但**它是整个 wlroots 的闸门**。 + +`libdisplay-info` 该走 **fork + 预生成**(和 libevdev / libxkbcommon 同形):它要跑 +`tool/gen-search-table.py` 生成 2568 行的 `pnp-id-table.c`,放不进 +`generated_files` 字面量。而且 **`pnp.ids` 必须取上游 hwdata 的发布物,不能用宿主 +那份** —— 上游 meson 找不到 hwdata 时会回落到 `/usr/share/hwdata/pnp.ids`,正是要 +消的宿主边;这也是 libevdev 那次「宿主内核头给出不同的表」的同一教训。 + +### 13.2 桌面侧仍缺的 + +| 缺口 | 后果 | 规模 | +|---|---|---| +| `fontconfig` | **按名字找字体**。`freetype` ✓ `harfbuzz` ✓ 只给字形,不给发现 | 中 | +| `pango` | 段落级排版:换行、双向文字、CJK、组合字符 | 中(拖 glib) | +| `cairo` | 2D 矢量绘制,或全用 GLES 自己画 | 中 | +| `dbus` | 通知、portal、会话、媒体键 | 中 | +| `PipeWire` | 音频 + 屏幕共享 | 大 | +| `libjpeg-turbo` | png/webp 有了,jpeg 没有 | 小 | +| XWayland | 跑 X 应用(本轮明确不考虑) | 大 | + +**数据包(xim 侧,和 `xkeyboard-config` 同形)**:光标主题(填 `XCURSOR_PATH`)、 +图标主题、字体。三者都是「机制已就位、缺提供方」。 + +### 13.3 发现变量总表(收尾状态) + +| 子系统 | 变量 | 提供方 | +|--------|------|--------| +| DRI 驱动 | `LIBGL_DRIVERS_PATH` | `xim:mesa` ✅ | +| EGL vendor | `__EGL_VENDOR_LIBRARY_DIRS` | `xim:mesa` + host-link 哨兵 ✅ | +| Vulkan ICD | `XDG_DATA_DIRS` | 同上 ✅ | +| GBM 后端 | `GBM_BACKENDS_PATH` | `xim:mesa` ✅ | +| 键盘布局 | `XKB_CONFIG_ROOT` | `xim:xkeyboard-config` ✅ | +| 输入 quirks | `LIBINPUT_QUIRKS_DIR` | `xim:libinput-quirks` ✅ | +| **光标主题** | **`XCURSOR_PATH`** | **— 机制已就位,缺提供方** | +| USB 名字库 | `USB_IDS_PATH` | — 经核实**不该补**(§10.9.4) | + +七格里六格有提供方,第七格(光标主题)是纯数据包,补法与 xkeyboard-config 完全相同。 + +### 13.4 本轮被自己的测试抓住的三处 + +都记在这里,因为它们是同一类:**我从假设写断言,而不是从源码**。 + +1. **`get_attached_size` 返回创建尺寸** → 实际 `0 x 0`。`attached_*` 是 EGL 实现 + 附加缓冲时才写的字段(§12.3)。 +2. **null `wl_shm` 只会跑到主题解析器** → 实际 `wayland-cursor.c:410` 无保护解引用, + 段错误 exit 139。`wl_cursor_theme_load` 真的需要活的 `wl_shm`。 +3. **`&wl_shm_create_pool` 能证明链了 client** → 它是 wayland-scanner 的 + `static inline` 协议包装,取地址会**强制本 TU 实例化**,把 + `wl_proxy_marshal_flags` 等私有符号拖进来。GNU ld 经传递 DT_NEEDED 解析, + **lld 不会,而 lld 是对的**。改用 `wl_display_connect`(真外部符号)。 + +第 3 条只有 llvm 那条腿抓得到 —— 它没有 sysroot 且用 lld,而这正是 +`validate.yml` 注释里说的「gcc 腿结构上看不见这一类 bug」。 diff --git a/tests/examples/wayland-cursor/tests/wayland-cursor.cpp b/tests/examples/wayland-cursor/tests/wayland-cursor.cpp index d3b21c7..207a02b 100644 --- a/tests/examples/wayland-cursor/tests/wayland-cursor.cpp +++ b/tests/examples/wayland-cursor/tests/wayland-cursor.cpp @@ -97,8 +97,27 @@ int main() // Unlike libwayland-egl, this library CALLS into libwayland-client — that // is exactly why §3's theme loader needs a live wl_shm. The two are in one // program by necessity rather than by convention. - check(&wl_shm_create_pool != nullptr, - "libwayland-client is linked in (wl_shm_create_pool)"); + // + // `wl_display_connect` AND NOT `wl_shm_create_pool`, which is what this + // check said first and what the llvm leg rejected: + // + // ld.lld: error: undefined symbol: wl_proxy_get_version + // >>> wayland-cursor.o:(wl_shm_create_pool(wl_shm*, int, int)) + // + // `wl_shm_create_pool` is one of wayland-scanner's `static inline` + // protocol wrappers (mcpp/generated/wayland-client-protocol.h:2202), so + // taking its address FORCES this translation unit to emit a copy — and + // that copy calls `wl_proxy_marshal_flags` and friends, which live in + // libwayland-client. GNU ld resolves those through the transitive + // DT_NEEDED of libwayland-cursor; lld deliberately does not, and lld is + // right: a program that names a symbol should link the library that + // defines it. + // + // `wl_display_connect` is an ordinary exported function, so referencing it + // proves the same thing without emitting anything. Same form the sibling + // wayland-egl test uses, which is why that one was green on both legs. + check(&wl_display_connect != nullptr, + "libwayland-client is linked in (wl_display_connect)"); // ── 5. Report the environment the ecosystem is expected to fill ─────── // Not asserted: nothing in the index or in xim ships cursor themes yet, so