Skip to content

feat(xkeyboard-config): 补上键盘布局数据集 —— 让 libxkbcommon 不再落到宿主 - #732

Merged
Sunrisepeak merged 2 commits into
mainfrom
feat/xkeyboard-config
Aug 30, 2026
Merged

feat(xkeyboard-config): 补上键盘布局数据集 —— 让 libxkbcommon 不再落到宿主#732
Sunrisepeak merged 2 commits into
mainfrom
feat/xkeyboard-config

Conversation

@Sunrisepeak

Copy link
Copy Markdown
Member

libxkbcommon 只会编译键映射,它自己一个布局都不带。xkb_keymap_new_from_names(rules, model, layout, …) —— 合成器手里真正有的那个 RMLVO 元组 —— 是去磁盘上读答案的。

xim:libxkbcommon 的 payload 是 bin include lib share/{bash-completion,man},没有 xkb 数据。所以 subos 里问 "us" 布局,答案来自宿主的 /usr/share/X11/xkb —— 和当初 Vulkan 静默落到 llvmpipe 同一类静默宿主边。宿主上没有 X11 数据的机器则直接失败。

三件事

1. DISCOVERY 加 XKB_CONFIG_ROOT 行。 机制早就在,只是这个子系统一直没有自己那一行 —— 和 GBM_BACKENDS_PATH 当初缺失的原因一样。

2. 这一行是 op = "set",是表里第一个非列表项。

上面四个是冒号分隔的搜索路径,prepend 在那里既正确又无损。XKB_CONFIG_ROOT标量:libxkbcommon 读一次当一个目录用,prepend 上第二个提供方就变成 dirA:dirB —— 一个不存在的路径,而失败信息只会说「keymap 编译失败」。

今天只有一个提供方,所以 prepend 行为相同 —— 并且会在出现第二个提供方那天出错。set 才是这个变量的语义。为此给 DISCOVERY 行加了可选的 op,默认仍是 prepend

3. declare_xkb,和 declare_dri / declare_gbm 同形。

声明变量和放置内容是两件事。 第一版只声明不放置,结果是:

XKB_CONFIG_ROOT=[.../share/X11/xkb]
ls: cannot access '.../share/X11/xkb': No such file or directory

变量在 shell 里读得好好的,指向不存在的目录 —— 而 libxkbcommon 对此的报告,和布局真的坏了是同一句话。

为什么这个包在 xim 而不在 mcpp-index

这栈的其它每一块 —— libxkbcommon、libinput、libevdev、wayland —— 都是 mcpp-index 描述符,因为 mcpp-index 打包代码

这个是纯数据,而 mcpp-index 没有让包发布一个目录的机制(对全部 13 个用 [runtime] 的包核对过)。xim 有,而且就是 mesa 的 DRI 模块和 glvnd vendor JSON 已经在用的那个。所以这不是绕路,是两个索引各做各建模的事。

数据集是预生成的:上游 meson 唯一的实活是跑规则编译器(rules/evdev 由 ~40 个片段拼出来)。消费者要的是结果不是配方,而为一个不含代码的包把 meson + python 拉进依赖闭包不划算。所以一次生成、与上游产物核对、作为数据归档发布 —— 322 条,505143 字节,CN 镜像逐字节一致(用 sha256 比,不是可达性)。

关于 subos.env 那条警告

xlings 提醒「声明的变量可能把我们 payload 里的代码载入我们不拥有的进程」,并要求在 recipe 里写明理由。理由是这一个不可能:路径尽头是文本文件(rules / symbols / keycodes / types / compat),由 libxkbcommon 自己的解析器读。没有 dlopen,坏树最多让 keymap 编不出来。RPATH 在这里不是替代方案 —— 没有任何东西被链接,这个变量是上游自己的接口(getenv("XKB_CONFIG_ROOT") 优先于编译期默认值)。已写进 recipe。

验证

subos eco-2026-8-30-1,装完:

XKB_CONFIG_ROOT=[.../subos/eco-2026-8-30-1/share/X11/xkb]
compat geometry keycodes rules symbols types
rules/evdev: 512 行

再用 mcpp 侧的 freedesktop.libxkbcommon 消费它 —— 跨索引闭环:

XKB_CONFIG_ROOT = .../subos/eco-2026-8-30-1/share/X11/xkb
xkb_keymap_new_from_names(evdev/pc105/us) against that root ok
   real layout: keycode 24 -> "q"
…and the real us layout maps keycode 24 to "q"           ok
0 check(s) failed

消费侧的那个断言随 mcpplibs/mcpp-index#298 一起进。

libxkbcommon 只会**编译**键映射,它自己一个布局都不带。
xkb_keymap_new_from_names(rules, model, layout, ...) —— 合成器手里真正
有的那个 RMLVO 元组 —— 是去磁盘上读答案的。

而 xim:libxkbcommon 的 payload 是 bin include lib share/{bash-completion,man},
没有 xkb 数据。所以 subos 里问 "us" 布局,答案来自宿主的
/usr/share/X11/xkb —— 和当初 Vulkan 静默落到 llvmpipe 同一类静默宿主边。
宿主上没有 X11 数据的机器则直接失败。

## 三件事

1. DISCOVERY 加 XKB_CONFIG_ROOT 行。机制早就在,只是这个子系统一直没有自己
   那一行——和 GBM_BACKENDS_PATH 当初缺失的原因一样。

2. 这一行是 op = "set",是表里第一个非列表项。上面四个是冒号分隔的搜索路径,
   prepend 在那里既正确又无损;XKB_CONFIG_ROOT 是**标量**,libxkbcommon 读一
   次当一个目录用,prepend 上第二个提供方就变成 dirA:dirB —— 一个不存在的路
   径,失败信息只会说"keymap 编译失败"。现在只有一个提供方,所以 prepend 今
   天行为相同,而在出现第二个提供方那天出错。set 才是这个变量的语义。

3. declare_xkb,和 declare_dri / declare_gbm 同形。**声明变量和放置内容是两
   件事**:第一版只声明不放置,结果是

       XKB_CONFIG_ROOT=[.../share/X11/xkb]
       ls: cannot access '.../share/X11/xkb': No such file or directory

   变量在 shell 里读得好好的,指向不存在的目录,而 libxkbcommon 对此的报告
   和布局真坏了是同一句话。

## 为什么这个包在 xim 而不在 mcpp-index

这栈的其它每一块——libxkbcommon、libinput、libevdev、wayland——都是 mcpp-index
描述符,因为 mcpp-index 打包**代码**。这个是纯数据,而 mcpp-index 没有让包发布
一个**目录**的机制(对全部 13 个用 [runtime] 的包核对过)。xim 有,而且就是
mesa 的 DRI 模块和 glvnd vendor JSON 已经在用的那个。所以这不是绕路,是两个索
引各做各建模的事。

数据集是预生成的:上游 meson 唯一的实活是跑规则编译器(rules/evdev 由 ~40 个
片段拼出来)。消费者要的是结果不是配方,而为一个不含代码的包把 meson + python
拉进依赖闭包是不划算的。所以一次生成、与上游产物核对、作为数据归档发布。
322 条,505143 字节,CN 镜像逐字节一致(用 sha256 比,不是可达性)。

## 验证

subos eco-2026-8-30-1,装完:

    XKB_CONFIG_ROOT=[.../subos/eco-2026-8-30-1/share/X11/xkb]
    compat geometry keycodes rules symbols types
    rules/evdev: 512 行

再用 mcpp 侧的 freedesktop.libxkbcommon 消费它(跨索引闭环):

    XKB_CONFIG_ROOT = .../subos/eco-2026-8-30-1/share/X11/xkb
    xkb_keymap_new_from_names(evdev/pc105/us) against that root ok
       real layout: keycode 24 -> "q"
    …and the real us layout maps keycode 24 to "q"           ok
    0 check(s) failed
@Sunrisepeak
Sunrisepeak merged commit 9564a10 into main Aug 30, 2026
15 checks passed
@Sunrisepeak
Sunrisepeak deleted the feat/xkeyboard-config branch August 30, 2026 10:45
Sunrisepeak added a commit that referenced this pull request Aug 30, 2026
#732 往 DISCOVERY 表加了 XKB_CONFIG_ROOT。mesa 调 declare_subos_env(tag) 时
不传 only,而不传 only 的含义是「声明**每一行**」——于是 mesa 开始替键盘布局
声明路径,而它的 payload 里是 share/{drirc.d,glvnd,vulkan},根本没有 share/X11。

实测(eco-2026-8-30-2,只装 mesa):

    [warn] xim:mesa@25.0.7.2 declares XKB_CONFIG_ROOT = ${subosdir}/share/X11/xkb

今天这个值碰巧无害——xkeyboard-config 声明同一条相对路径、而且是它真正把树放进
去的。但在**没装** xkeyboard-config 的 subos 上,mesa 会把 XKB_CONFIG_ROOT 指向
一个不存在的目录,正是 #732 里 declare_xkb 那段注释描述的失败形态。

修法不是给 mesa 打补丁,是把规则写进类型:

  graphics.RENDER_PATHS  —— 四条渲染路径,mesa 传它
  declare_subos_env 的注释点明:省略 only 是一个会在调用方背后增长的声明,
  任何包都不该这么写。

三个调用点现在都传显式集合(RENDER_PATHS / EGL_VENDOR_AND_ICD / XKB_ONLY)。

规则本身早就在,只是 declare_subos_env 之前没法表达:declare_dri /
declare_gbm / declare_egl_vendor 都用 os.isdir 检查、缺了就拒绝声明。
declare_subos_env 没有 payload 可查,所以集合就是提供方说「我填哪些」的方式。

验证(全新 subos eco-2026-8-30-3,只装 mesa):

    XKB_CONFIG_ROOT    = <未声明>
    GBM_BACKENDS_PATH  = .../usr/lib/gbm
    LIBGL_DRIVERS_PATH = .../usr/lib/dri
    XDG_DATA_DIRS 含 subos: yes

Co-authored-by: sunrisepeak <x.d2learn.org@gmail.com>
Sunrisepeak added a commit that referenced this pull request Aug 30, 2026
* fix(graphics): consumer_envs 跳过标量行——#732/#734 的第二处回归

自我 review 时发现的,和 #733 同一类但在**另一个发射器**上。

DISCOVERY 表有两个发射器:
  S3  declare_subos_env  → subos shell,能指定 op
  S2  consumer_envs      → 消费者 shim,**不能**

xvm.add{ envs = ... } 收的是纯 { NAME = "value" } 映射,没有任何"设定而非合并"
的表达方式——每一项都按 PATH 式前插合并处理。对四条搜索路径这正是想要的;对
XKB_CONFIG_ROOT 和 LIBINPUT_QUIRKS_DIR 是破坏性的:它们指**一个**目录,所以一个
已经 export 了 XKB_CONFIG_ROOT=/usr/share/X11/xkb 的用户会拿到

    <subos>/share/X11/xkb:/usr/share/X11/xkb

而 libxkbcommon 会在一个根本不是路径的路径里找不到 rules。

唯一的消费者是 pkgs/g/godot.lua:440。#732 之前它的 shim 不带这两个变量,之后带
了——所以这是我引进的,而且当时没验。

修法:让 op 这个字段承担唯一含义「这是标量」,两个发射器各按自己的机制正确处理
——S3 用 set,S2 弃权。

代价写在注释里:从普通登录 shell 经 shim 启动的消费者拿不到键盘/quirks 数据集,
也就是这两行存在之前的状态,所以不构成退步;在 subos use 里 S3 照常设对。真正
关闭它需要 xvm.add 支持 per-variable op,在那之前,静默破坏一个标量是两种失败里
更坏的那个。

验证(桩加载 graphics.lua 直接调 consumer_envs):

    GBM_BACKENDS_PATH            ${XLINGS_DYNAMIC_SUBOS_DIR}/usr/lib/gbm
    LIBGL_DRIVERS_PATH           ${XLINGS_DYNAMIC_SUBOS_DIR}/usr/lib/dri
    XDG_DATA_DIRS                ${XLINGS_DYNAMIC_SUBOS_DIR}/share
    __EGL_VENDOR_LIBRARY_DIRS    ${XLINGS_DYNAMIC_SUBOS_DIR}/share/glvnd/egl_vendor.d

    XKB_CONFIG_ROOT      出现在 consumer_envs? false
    LIBINPUT_QUIRKS_DIR  出现在 consumer_envs? false

* ci: 把 libs/** 纳入触发路径——它是盲区里最不该有的那一个

#735 只改 libs/graphics.lua,推上去之后:

    no checks reported on the 'fix/consumer-envs-skips-scalars' branch

一个 CI 都没跑。ci-test.yml 与 ci-xpkg-test.yml 的 paths 过滤器里有
pkgs/** 和 tests/**,没有 libs/**。

而 libs/graphics.lua 被 mesa、nvidia-gl-host-link、xkeyboard-config、
libinput-quirks 四个 recipe import,它们的 install/config 钩子大部分实体就在
这个文件里。所以「只改 libs/」是本索引里**波及面最大**的一类改动,却恰好是
唯一完全不跑 CI 的一类。

这也解释了本轮两次回归为什么都只能靠自我 review 发现(#733 mesa 声明了不该声明
的路径、#735 consumer_envs 把标量拼成冒号路径)——它们都改到了 libs/,CI 从来
没有机会看见。

---------

Co-authored-by: sunrisepeak <x.d2learn.org@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants