docs: Wayland
One popup per session, so the frontend is picked at startup: a compositor with zwp_input_method_v2 gets that frontend and neither XIM nor the X11 popup is started. The answer for a user is one sentence -- on Wayland set none of the module variables -- and the two exceptions the plan asked to check turned out this way: GTK 4 binds text-input-v3 by itself, with GTK_IM_MODULE unset (4.22 under sway 1.12, typed and composed), and Chromium is beyond help either way, since it asks for text-input-v1 and wlroots implements only v3. The plan is deleted, as it said to be once the README described what landed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
47
README.md
47
README.md
@@ -1,8 +1,10 @@
|
|||||||
# strans
|
# strans
|
||||||
|
|
||||||
strans is a small, single-user input method for Korean, Japanese, English,
|
strans is a small, single-user input method for Korean, Japanese, English,
|
||||||
emoji, and symbols. It provides one engine for IBus (which GTK 4 and
|
emoji, and symbols. It provides one engine for Wayland compositors that
|
||||||
Qt use), XIM, and GTK 3. Vietnamese Telex is also available as a
|
speak `zwp_input_method_v2` — the wlroots and smithay families, so sway,
|
||||||
|
river, labwc, Wayfire, dwl, niri and COSMIC — and for IBus (which GTK 4
|
||||||
|
and Qt use), XIM, and GTK 3. Vietnamese Telex is also available as a
|
||||||
compatibility mode.
|
compatibility mode.
|
||||||
|
|
||||||
## Input modes
|
## Input modes
|
||||||
@@ -72,6 +74,7 @@ clicking elsewhere commits what is pending.
|
|||||||
|
|
||||||
| Frontend | Preedit | Candidates |
|
| Frontend | Preedit | Candidates |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
|
| Wayland input method | inline in the client | strans popup |
|
||||||
| GTK 3 and IBus | inline in the client | strans popup |
|
| GTK 3 and IBus | inline in the client | strans popup |
|
||||||
| XIM PreeditCallbacks | inline through XIM callbacks | strans popup |
|
| XIM PreeditCallbacks | inline through XIM callbacks | strans popup |
|
||||||
| XIM PreeditPosition | strans popup | strans popup |
|
| XIM PreeditPosition | strans popup | strans popup |
|
||||||
@@ -90,11 +93,13 @@ style, and above the line when there is no room below; otherwise the popup
|
|||||||
sits under the focus window, or under the client window when no focus
|
sits under the focus window, or under the client window when no focus
|
||||||
window is set.
|
window is set.
|
||||||
|
|
||||||
The popup is an X11 window sized by `GDK_SCALE` on HiDPI displays, XIM is
|
On a compositor with `zwp_input_method_v2` the popup is a Wayland surface
|
||||||
X11-only, and GTK popup placement converts the GTK caret to X11 root
|
that the compositor itself puts at the text cursor, flipping it above the
|
||||||
coordinates. There is no native Wayland popup surface or Wayland caret
|
line when there is no room below; a client that reports no cursor location
|
||||||
positioning. GTK and IBus clients can still use inline preedit where their
|
gets it under its window instead. Everywhere else the popup is an X11 window,
|
||||||
display environment supports it.
|
placed from the XIM spot or from the GTK caret converted to root
|
||||||
|
coordinates, and XIM is X11-only. Either way `GDK_SCALE` sizes it for
|
||||||
|
HiDPI displays.
|
||||||
|
|
||||||
## Build and test
|
## Build and test
|
||||||
|
|
||||||
@@ -109,7 +114,7 @@ make docker-build
|
|||||||
Build output:
|
Build output:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
strans daemon with IBus and XIM frontends
|
strans daemon with Wayland, IBus, and XIM frontends
|
||||||
gtk/im-strans.so GTK 3 frontend module
|
gtk/im-strans.so GTK 3 frontend module
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -133,6 +138,8 @@ development files for:
|
|||||||
- Plan 9 port (`9c`, `9l`, `libthread`, and `libbio`);
|
- Plan 9 port (`9c`, `9l`, `libthread`, and `libbio`);
|
||||||
- D-Bus development files;
|
- D-Bus development files;
|
||||||
- XCB, XCB RandR, XCB XKB, xcb-util, xcb-imdkit, xkbcommon, and xkbcommon-x11;
|
- XCB, XCB RandR, XCB XKB, xcb-util, xcb-imdkit, xkbcommon, and xkbcommon-x11;
|
||||||
|
- the Wayland client library and `wayland-scanner`, which writes the
|
||||||
|
input-method and virtual-keyboard bindings from the XML in `proto/`;
|
||||||
- Pango, PangoCairo, Cairo, and Fontconfig;
|
- Pango, PangoCairo, Cairo, and Fontconfig;
|
||||||
- GTK 3, plus libibus and Xlib for the live clients;
|
- GTK 3, plus libibus and Xlib for the live clients;
|
||||||
- Xvfb for the X11 live tests;
|
- Xvfb for the X11 live tests;
|
||||||
@@ -158,8 +165,9 @@ build-tree daemon in the background, and returns to the shell. It requires
|
|||||||
`pkill` from procps. Startup and frontend failures remain visible on the
|
`pkill` from procps. Startup and frontend failures remain visible on the
|
||||||
caller's standard error. A service supervisor should instead run
|
caller's standard error. A service supervisor should instead run
|
||||||
`./strans map` directly in the foreground, with the session's
|
`./strans map` directly in the foreground, with the session's
|
||||||
`XDG_RUNTIME_DIR` and `DISPLAY`: the GTK module finds the daemon at
|
`XDG_RUNTIME_DIR` and its `WAYLAND_DISPLAY` or `DISPLAY`: the GTK module
|
||||||
`$XDG_RUNTIME_DIR/strans.sock` (else `/tmp/strans.UID`), and IBus clients
|
finds the daemon at `$XDG_RUNTIME_DIR/strans.sock` (else
|
||||||
|
`/tmp/strans.UID`), and IBus clients
|
||||||
through the address file libibus expects under `~/.config/ibus/bus/`,
|
through the address file libibus expects under `~/.config/ibus/bus/`,
|
||||||
which strans writes as `ibus-daemon` would, named after `WAYLAND_DISPLAY`
|
which strans writes as `ibus-daemon` would, named after `WAYLAND_DISPLAY`
|
||||||
when there is one and `DISPLAY` otherwise.
|
when there is one and `DISPLAY` otherwise.
|
||||||
@@ -169,6 +177,20 @@ opens `hira.map`, `kata.map`, `telex.map`, `kanji.dict`, `emoji.dict`, and
|
|||||||
`hanja.dict` from `DIR` at runtime; `run.sh` passes the tracked `map/`
|
`hanja.dict` from `DIR` at runtime; `run.sh` passes the tracked `map/`
|
||||||
directory. Only the GTK subdirectory has install and uninstall targets.
|
directory. Only the GTK subdirectory has install and uninstall targets.
|
||||||
|
|
||||||
|
strans shows one popup per session, so it picks its frontend at startup: a
|
||||||
|
session whose compositor offers `zwp_input_method_v2` gets that frontend,
|
||||||
|
and neither XIM nor the X11 popup is started; every other session gets XIM
|
||||||
|
as before. The IBus endpoint and the GTK 3 module are served in both.
|
||||||
|
|
||||||
|
On Wayland, then, set none of the variables below. GTK, Qt and Firefox
|
||||||
|
speak text-input-v3 themselves and need nothing — GTK 4 binds it without
|
||||||
|
`GTK_IM_MODULE`, checked with 4.22 under sway 1.12 — and `GTK_IM_MODULE`
|
||||||
|
is what pushes them off the path that works. A client that reaches strans
|
||||||
|
over IBus or the GTK 3 module while the Wayland frontend runs still
|
||||||
|
composes inline, but shows no candidate list, because the popup belongs to
|
||||||
|
that frontend. Chromium is helped by neither: it still asks for
|
||||||
|
`text-input-v1`, which wlroots does not implement at all.
|
||||||
|
|
||||||
Configure clients as needed:
|
Configure clients as needed:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
@@ -201,8 +223,9 @@ doas make -C gtk uninstall
|
|||||||
strans provides its own IBus endpoint; `ibus-daemon` and fcitx are not
|
strans provides its own IBus endpoint; `ibus-daemon` and fcitx are not
|
||||||
required. IBus clients that process keys synchronously — GTK 4, or GTK 3
|
required. IBus clients that process keys synchronously — GTK 4, or GTK 3
|
||||||
with `IBUS_ENABLE_SYNC_MODE=1` — get their commits and preedits through
|
with `IBUS_ENABLE_SYNC_MODE=1` — get their commits and preedits through
|
||||||
`PostProcessKeyEvent`, in order with the key. Without `DISPLAY`, the daemon and IBus frontend still work, but XIM
|
`PostProcessKeyEvent`, in order with the key. With neither
|
||||||
is not started and the popup is disabled.
|
`WAYLAND_DISPLAY` nor `DISPLAY`, the daemon, its IBus endpoint and its
|
||||||
|
socket still work, but no frontend draws a popup.
|
||||||
|
|
||||||
## Dictionary data
|
## Dictionary data
|
||||||
|
|
||||||
|
|||||||
301
WAYLAND.md
301
WAYLAND.md
@@ -1,301 +0,0 @@
|
|||||||
# Wayland input method: design and plan
|
|
||||||
|
|
||||||
Plan for work not yet written. Delete this file once the work has landed and
|
|
||||||
the README describes what is there.
|
|
||||||
|
|
||||||
## 1. What is wrong today
|
|
||||||
|
|
||||||
The popup is X11 only. `win.c` is the sole consumer of `drawc`, it connects
|
|
||||||
with `xcb_connect`, and when that fails it prints `popup disabled` and
|
|
||||||
returns (`win.c:206`). Under a Wayland session with no X server there are no
|
|
||||||
candidates at all, which makes Kanji, Hanja and Emoji unusable. Preedit still
|
|
||||||
works for clients that reach us over IBus or the GTK module.
|
|
||||||
|
|
||||||
A second, smaller fault: libibus sends `SetCursorLocationRelative` instead of
|
|
||||||
`SetCursorLocation` when the client runs on Wayland (ibus
|
|
||||||
`client/gtk3/ibusimcontext.c:1767`). `ictab` (`ibus.c:904`) does not list it,
|
|
||||||
so libdbus answers `UnknownMethod` and the caret never arrives.
|
|
||||||
|
|
||||||
## 2. What to implement, and what not
|
|
||||||
|
|
||||||
Implement `zwp_input_method_v2` with `zwp_virtual_keyboard_v1`. One protocol
|
|
||||||
covers every compositor we can serve:
|
|
||||||
|
|
||||||
| Compositor | Protocol |
|
|
||||||
| --- | --- |
|
|
||||||
| wlroots: sway, river, labwc, Wayfire, dwl | `zwp_input_method_v2` |
|
|
||||||
| smithay: niri, COSMIC | `zwp_input_method_v2` |
|
|
||||||
| KWin | `zwp_input_method_v1` only |
|
|
||||||
| GNOME | none |
|
|
||||||
|
|
||||||
Not `zwp_input_method_v1`: it buys KWin alone, and KWin starts the input
|
|
||||||
method itself from its own configuration.
|
|
||||||
|
|
||||||
Not GNOME: it has no input-method protocol, and our IBus endpoint writes the
|
|
||||||
address file that gnome-shell's own ibus-daemon owns. strans cannot work
|
|
||||||
there whatever we write.
|
|
||||||
|
|
||||||
`experimental/xx-input-method` in wayland-protocols is where this is heading:
|
|
||||||
a popup positioner and a per-key consume/passthrough filter that matches our
|
|
||||||
engine exactly. No compositor implements it (checked: wlroots, smithay, KWin
|
|
||||||
trees have no trace of it). Revisit once v2 works and we have something to
|
|
||||||
say about it.
|
|
||||||
|
|
||||||
## 3. Design
|
|
||||||
|
|
||||||
### 3.1 One new file
|
|
||||||
|
|
||||||
`wl.c`, about 600 lines, holding the frontend and its popup. Two protocol XML
|
|
||||||
files are vendored under `proto/` and turned into `imv2.[ch]` and `vkv1.[ch]`
|
|
||||||
by wayland-scanner at build time; only the XML is tracked.
|
|
||||||
|
|
||||||
The popup lives in `wl.c` rather than beside `win.c` because
|
|
||||||
`get_input_popup_surface` is a request on the input-method object: the popup
|
|
||||||
surface must come from the same connection. Splitting it would mean exporting
|
|
||||||
the connection, which is a worse trade than breaking the symmetry.
|
|
||||||
|
|
||||||
### 3.2 No drawing process
|
|
||||||
|
|
||||||
`imhandlekey` produces the picture before it answers:
|
|
||||||
|
|
||||||
```c
|
|
||||||
redraw(); /* strans.c:999, channbsend(drawc, &dc) */
|
|
||||||
...
|
|
||||||
chansend(kr->reply, &res); /* strans.c:1006 */
|
|
||||||
```
|
|
||||||
|
|
||||||
So by the time a frontend has its `Keyres`, the picture it caused is already
|
|
||||||
in `drawc`, and `redraw` drains its own stale sends before pushing a new one
|
|
||||||
(`strans.c:257`), so `drawc` holds at most one, and that one is the engine's
|
|
||||||
current state. `wl.c` therefore draws inline:
|
|
||||||
|
|
||||||
```c
|
|
||||||
sendrequest(Keypress, ks, mod, &res);
|
|
||||||
...
|
|
||||||
if(channbrecv(drawc, &dc) > 0)
|
|
||||||
popup(&dc);
|
|
||||||
```
|
|
||||||
|
|
||||||
after `Keypress`, always take and draw; after the `Keycap` owner poll, draw
|
|
||||||
only when `res.eaten` says the engine is still ours, else hide.
|
|
||||||
|
|
||||||
The consequence is the point: **one process touches libwayland**. It reads
|
|
||||||
events, dispatches them, draws, and handles `wl_buffer.release`, all in
|
|
||||||
order. No `wl_display_prepare_read` dance, no mutex, no pipe from a channel
|
|
||||||
into a poll loop. fcitx5 spends `waylandeventreader.cpp`, 144 lines of thread
|
|
||||||
plus mutex plus condition variable, on exactly this problem, because its
|
|
||||||
reader and its dispatcher are different threads. We do not have that problem
|
|
||||||
and must not invent it.
|
|
||||||
|
|
||||||
`main.c` gains one `proccreate` and `fn.h` one line. `drawthread` is not
|
|
||||||
started under Wayland, so `drawc` keeps its single consumer.
|
|
||||||
|
|
||||||
### 3.3 One popup per session
|
|
||||||
|
|
||||||
```c
|
|
||||||
/* main.c */
|
|
||||||
if(getenv("WAYLAND_DISPLAY") != nil)
|
|
||||||
proccreate(wlthread, nil, 32768);
|
|
||||||
else{
|
|
||||||
proccreate(drawthread, nil, 16384);
|
|
||||||
if(getenv("DISPLAY") != nil)
|
|
||||||
proccreate(ximthread, nil, 32768);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
`srvthread` and `ibusthread` start as they do now, in both cases.
|
|
||||||
|
|
||||||
What this costs: XIM is not served under Wayland, and clients that reach us
|
|
||||||
over IBus or the GTK module while on Wayland get inline preedit with no
|
|
||||||
candidate list. Both are documented, and the answer for a user is the same
|
|
||||||
one sentence: **on Wayland, set none of the X11 variables.** GTK, Qt and
|
|
||||||
Firefox speak text-input-v3 themselves; `GTK_IM_MODULE` is what pushes them
|
|
||||||
off the path that works. Two exceptions to check while writing commit 5:
|
|
||||||
GTK 4 has been reported in 2026 not to bind `zwp_text_input_manager_v3`
|
|
||||||
unless `GTK_IM_MODULE=wayland` is set, and Chromium still defaults to
|
|
||||||
text-input-v1.
|
|
||||||
|
|
||||||
### 3.4 Keys
|
|
||||||
|
|
||||||
* `activate`, `deactivate` and `content_type` are pending state applied at
|
|
||||||
`done`; count `done` events, that count is the serial `commit` takes.
|
|
||||||
* Grab the keyboard on every activate, releasing the previous grab first. Two
|
|
||||||
activates can arrive without a deactivate between them when focus moves
|
|
||||||
between clients, and the second `grab_keyboard` then fails and leaves a
|
|
||||||
dead object.
|
|
||||||
* `keymap` gives an fd: compile it with `xkb_keymap_new_from_string` and hand
|
|
||||||
the same fd to `zwp_virtual_keyboard_v1.keymap`, so a key we forward means
|
|
||||||
what it meant.
|
|
||||||
* `key` gives an evdev code: `xkb_state_key_get_one_sym(state, code+8)`, then
|
|
||||||
`composekey` (shared with the XIM and IBus frontends, no new table), then
|
|
||||||
`ipckeysym` and the modifier mask to the engine.
|
|
||||||
* A key the engine did not eat goes back through
|
|
||||||
`zwp_virtual_keyboard_v1.key`. Record forwarded codes in a `u32int
|
|
||||||
sent[8]` bitmap: eat the release of a press we ate, and release whatever is
|
|
||||||
still down when the input method deactivates, or the client is left holding
|
|
||||||
a key.
|
|
||||||
* The virtual keyboard must be created on the same `wl_display` as the input
|
|
||||||
method. Nothing in the protocol says so; sway skips the grab for a virtual
|
|
||||||
keyboard whose client owns the grab (`sway/input/keyboard.c:419-431`, with
|
|
||||||
a TODO pointing at wlroots#2322), and on another connection our forwarded
|
|
||||||
keys would come straight back to us. fcitx5 relies on the same contract.
|
|
||||||
* Order within a key: `commit_string`, `set_preedit_string`, `commit(serial)`,
|
|
||||||
then `vk.key`, then one flush. Requests on one connection are processed in
|
|
||||||
order, which is what keeps a commit ahead of the key that caused it.
|
|
||||||
|
|
||||||
We do not commit ordinary keys as text. fcitx5 does by default
|
|
||||||
(`waylandimserverbase.cpp:35-48`, unmodified printable keys become
|
|
||||||
`commit_string`), to dodge exactly the ordering question above. Our `eaten =
|
|
||||||
0` means "this key is not mine", and a key event is the honest answer to
|
|
||||||
that; committing text would lie to terminals and to anything with a
|
|
||||||
keybinding. If ordering does break on some compositor, that workaround is
|
|
||||||
known and can be added then.
|
|
||||||
|
|
||||||
### 3.5 Popup
|
|
||||||
|
|
||||||
* One `wl_surface` and one `zwp_input_popup_surface_v2`, created once. The
|
|
||||||
compositor makes them visible on activate and invisible on deactivate, and
|
|
||||||
places them at the text cursor: `popuparea` and `popupposition` are not
|
|
||||||
used here and stay X11-only.
|
|
||||||
* Two shm buffers, used in turn, each busy from `attach` until
|
|
||||||
`wl_buffer.release`. `win.c`'s single grow-only image would be redrawn
|
|
||||||
while the compositor was still reading it.
|
|
||||||
* `WL_SHM_FORMAT_XRGB8888` is `CAIRO_FORMAT_RGB24` on a little-endian host,
|
|
||||||
so `popupdraw` writes the buffer directly, as it does for X11. `win.c`
|
|
||||||
already checks the same assumption for the X server (`validformat`).
|
|
||||||
* Hide by attaching a nil buffer and committing. If a compositor keeps a
|
|
||||||
stale popup, destroy the surface instead, which is what fcitx5 does
|
|
||||||
(`waylandinputwindow.cpp:158-168`).
|
|
||||||
* `popuplayout` wants an area: take it from the `wl_output` the surface
|
|
||||||
entered, else the first output.
|
|
||||||
* Scale stays `popupscale` from `GDK_SCALE`, as on X11.
|
|
||||||
`wl_surface.preferred_buffer_scale` is the better answer, later.
|
|
||||||
|
|
||||||
### 3.6 What does not change
|
|
||||||
|
|
||||||
| | |
|
|
||||||
| --- | --- |
|
|
||||||
| `strans.c`, `Keyreq`, `Keyres`, `Drawcmd`, `ipc.*` | nothing |
|
|
||||||
| `popup_layout.c`, `font.c` | nothing |
|
|
||||||
| `ibus.c`, `srv.c`, `xim.c`, `gtk/` | nothing, beyond the one-line IBus fix |
|
|
||||||
| `dat.h` | `Ibuspurposepassword`, `Ibuspurposepin` move out of `ibus.c` as `Purposepassword`, `Purposepin`; both frontends read the same two numbers |
|
|
||||||
| `main.c` | frontend choice |
|
|
||||||
| `fn.h` | `void wlthread(void*);` |
|
|
||||||
|
|
||||||
The engine needing no change is the argument that this is the right shape.
|
|
||||||
`Keyres.eaten` is already consume-or-pass, `Keyres.commit` is already
|
|
||||||
`commit_string`, `Keyres.preedit` is already `set_preedit_string`, and
|
|
||||||
`clientpre` is simply always true here, which leaves the popup showing
|
|
||||||
candidates and the mode mark and nothing else.
|
|
||||||
|
|
||||||
## 4. Rejected
|
|
||||||
|
|
||||||
**A popup per display, routed by context.** This is what fcitx5 does:
|
|
||||||
`uis_["x11:..."]` and `uis_["wayland:..."]` side by side
|
|
||||||
(`classicui.cpp:80-121`), chosen by the focus group's display name. It would
|
|
||||||
keep XIM alive under XWayland. It costs a channel in `Keyreq`, a `lastdraw`
|
|
||||||
per sink, blanking the loser on every owner change, and `font.c`'s single
|
|
||||||
Pango layout drawn from two processes. It also makes the frontends know about
|
|
||||||
each other: fcitx5 has to drop its X keyboard grab whenever a Wayland client
|
|
||||||
takes focus (`xcb/xcbconnection.cpp:443-460`). fcitx5 needs this because it
|
|
||||||
is a multi-seat, multi-display, multi-user daemon. strans is one user's. If
|
|
||||||
XIM under XWayland is ever wanted, the routing key to use is the one fcitx5
|
|
||||||
uses, and it is about ten lines.
|
|
||||||
|
|
||||||
**A second process for drawing, or a second connection.** See 3.2. The popup
|
|
||||||
surface cannot come from a second connection, and a second process sharing
|
|
||||||
the first buys only the locking rules we currently do not need.
|
|
||||||
|
|
||||||
**A GNOME fallback to the X11 popup.** Machinery for a case that cannot work
|
|
||||||
anyway.
|
|
||||||
|
|
||||||
**Hand-marshalling with `wl_proxy_marshal_flags`** instead of
|
|
||||||
wayland-scanner. Less code, unreadable. The generated files are 1448 lines
|
|
||||||
and none of them are ours to read.
|
|
||||||
|
|
||||||
**Choosing candidates with the mouse.** fcitx5's popup takes clicks, hover,
|
|
||||||
wheel and touch. Ours eats clicks and does nothing, on X11 today; keep it
|
|
||||||
that way.
|
|
||||||
|
|
||||||
## 5. Commit plan
|
|
||||||
|
|
||||||
1. `ibus: SetCursorLocationRelative is a caret too` — one entry in `ictab`.
|
|
||||||
Unrelated to the rest, and wrong today.
|
|
||||||
2. `build: the input-method and virtual-keyboard protocols` — two XML files,
|
|
||||||
the scanner rules, the LICENSES note. No behaviour.
|
|
||||||
3. `wl: the input-method frontend` — activate, deactivate, content type,
|
|
||||||
done and its serial; grab per activate; keys through xkb and `composekey`;
|
|
||||||
commit and preedit; unhandled keys through the virtual keyboard, released
|
|
||||||
on deactivate. Started from `main.c` under `WAYLAND_DISPLAY`. No popup
|
|
||||||
yet, so the X11 popup still owns `drawc` and nothing collides.
|
|
||||||
4. `wl: the candidate popup` — surface, two shm buffers, drawn from `drawc`
|
|
||||||
where the reply arrives. In the same commit, `main.c` stops starting
|
|
||||||
`drawthread` and `ximthread` under Wayland, because that is where the two
|
|
||||||
popups would collide.
|
|
||||||
5. `docs: Wayland` — set nothing; no XIM; no candidates for IBus and GTK
|
|
||||||
module clients.
|
|
||||||
|
|
||||||
Later, separately: key repeat, `preferred_buffer_scale`, reconversion through
|
|
||||||
`surrounding_text`, and a headless live test.
|
|
||||||
|
|
||||||
## 6. Verification
|
|
||||||
|
|
||||||
The toolchain question is already answered. In the container, wayland-scanner
|
|
||||||
output, libwayland-client, xkbcommon and Plan 9 port headers compile together
|
|
||||||
under `9c -Wall -Wextra` with no warnings, and `9l` links them with
|
|
||||||
`-lthread -lbio`:
|
|
||||||
|
|
||||||
```
|
|
||||||
imv2.c 133 imv2.h 955 vkv1.c 78 vkv1.h 282 (generated, not tracked)
|
|
||||||
9c -I/src $(pkg-config --cflags wayland-client xkbcommon) -Wall -Wextra -c wl.c
|
|
||||||
9l -o strans ... -lthread -lbio $(pkg-config --libs wayland-client xkbcommon)
|
|
||||||
```
|
|
||||||
|
|
||||||
`Dockerfile` needs `wayland` added, and `sway` (extra, 1.12) when the live
|
|
||||||
test arrives.
|
|
||||||
|
|
||||||
Unit tests cover only what is pure: the xkb modifier mask and the forwarded
|
|
||||||
key bitmap, in the shape `xim_adapter_test` already uses, compiling `wl.c`
|
|
||||||
into the test and building a keymap from a string, no server. Everything else
|
|
||||||
is checked by running it under sway.
|
|
||||||
|
|
||||||
A headless live test — a compositor, a text-input-v3 client, and a second
|
|
||||||
virtual keyboard to type with — is the size of `ibus_live_test.c`. Decide
|
|
||||||
after commits 3 and 4 work by hand; do not build it first.
|
|
||||||
|
|
||||||
## 7. Known limits
|
|
||||||
|
|
||||||
* A key we eat does not repeat. `zwp_input_method_v2`'s grab has no
|
|
||||||
compositor-side repeat unless the compositor sends `wl_keyboard`'s
|
|
||||||
`repeated` key state through it, which is opportunistic
|
|
||||||
(fcitx5 `waylandimserverv2.cpp:494-503`). Doing it ourselves is a timer
|
|
||||||
over `repeat_info`, twenty lines, later.
|
|
||||||
* Compositor keybindings run before the input method (sway
|
|
||||||
`keyboard.c:563`), so a chord like `Ctrl+N` can be taken from us.
|
|
||||||
* Text pending when the client loses focus may be lost: `deactivate` is the
|
|
||||||
first we hear of it and a `commit_string` afterwards can be dropped.
|
|
||||||
* The protocol gives one input context per seat, so we cannot tell which
|
|
||||||
application we are typing into. We keep no per-application state, so this
|
|
||||||
costs us nothing; fcitx5 tracks the focused window over a separate protocol
|
|
||||||
to work around it.
|
|
||||||
|
|
||||||
## 8. References
|
|
||||||
|
|
||||||
Checked at the versions current on 2026-08-17.
|
|
||||||
|
|
||||||
* wayland-protocols 1.49: `unstable/text-input/text-input-unstable-v3.xml`,
|
|
||||||
`experimental/xx-input-method/xx-input-method-v2.xml`,
|
|
||||||
`experimental/xx-keyboard-filter/xx-keyboard-filter-v1.xml`.
|
|
||||||
* wlroots: `protocol/input-method-unstable-v2.xml`,
|
|
||||||
`protocol/virtual-keyboard-unstable-v1.xml`, the two files to vendor.
|
|
||||||
* sway `sway/input/keyboard.c:419-431`, the virtual keyboard client-identity
|
|
||||||
rule; `:563`, bindings before the grab.
|
|
||||||
* fcitx5 `a31cff7`: `frontend/waylandim/waylandimserverv2.cpp:170` serial,
|
|
||||||
`:186-193` releasing forwarded keys, `:213-218` regrabbing,
|
|
||||||
`:494-503` repeat; `waylandimserverbase.cpp:35-48` commit-as-text;
|
|
||||||
`ui/classic/classicui.cpp:80-121` a UI per display;
|
|
||||||
`ui/classic/waylandshmwindow.cpp:69-113` two buffers;
|
|
||||||
`ui/classic/waylandinputwindow.cpp:158-168` hiding;
|
|
||||||
`modules/wayland/waylandeventreader.cpp` the reader thread;
|
|
||||||
`modules/xcb/xcbconnection.cpp:443-460` the two frontends interfering.
|
|
||||||
* ibus `client/gtk3/ibusimcontext.c:1767`, the relative caret on Wayland.
|
|
||||||
Reference in New Issue
Block a user