Expose visible Wine window text to the matching Go reader's independent keypad review commands, and extend standard Edit feedback to 32-bit programs such as MUSHclient without requiring 32-bit Linux GLib/Winelib libraries. Add lifetime-aware instance/window/control identities and bounded capture of standard Edit text, button/static captions, native ListBox/ListView items, and usable MSAA text. Preserve focus, caret, selection, scrolling, and the clipboard. Exclude hidden/password content before retrieval, retain both rune and UTF-16 offsets, clip visible ranges, and reject changing or oversized sources rather than returning incomplete text as a full snapshot. Export CaptureWindow on the version-1 WineReview D-Bus interface. Register with the current reader service owner and publish copied focus metadata. Authorize capture only for the current same-user owner. Use a dedicated GLib dispatcher and separate COM worker with a 500 ms deadline, one active job and one pending job, bounded payloads, and explicit stale/busy/limited/ unavailable replies. Keep menu and focus forwarding responsive when an MSAA provider stalls; retain ordinary feedback with older readers. Read the actual X11 geometry associated with Wine HWNDs using bounded XCB queries and __wine_x11_whole_window. Translate virtual-desktop coordinates and recheck physical geometry before returning captured text. Link XCB in the helper/tests and declare libxcb in the Arch package dependencies. Build edit_hook.c into the 32-bit DLL and attach both menu and input hooks to the foreground 32-bit UI thread. Release both hooks on transitions or attachment failure. Keep Scintilla-specific hooks confined to 64-bit builds. Expand Edit integration checks with read-only multiline navigation and unsuccessful deletion, plus 32-bit editor/Windows Forms lifecycle support. Add 32-bit/64-bit native capture fixtures, wire-protocol integration checks, and design/implementation documents. Document exact schema, capture limits, installation dependencies, compatibility, and live acceptance procedures. Validation during commit preparation: - git diff --check passed. - cmake --build build --parallel passed. - CTest could not start because xvfb-run is unavailable in this environment. - Further tests were skipped at the user's request; capture, protocol, and menu/editor integration tests were not run during commit preparation. Live speech and physical braille checks remain necessary, including review without moving the native caret, incoming output, plugin dialogs, and return to Linux/Fenrir. Custom painted output requires an accessible provider; RichEdit/Scintilla capture, semantic object review, Say All, typing echo, and native Wine braille routing remain unsupported by this protocol.
13 KiB
Cthulhu review keys in Wine
Status: approved, implemented, verified and installed on 2026-10-11. Independent review fixes and matching-binary desktop checks completed; MUSHclient listening and physical braille acceptance remain live checks.
Intended outcome
The user wants the existing Cthulhu review keys to work in Wine, especially
MUSHclient running in ~/.local/bard. Keep the current desktop keyboard
bindings. Review must read and navigate independently of application focus,
caret, selection, and keyboard input. The user confirmed this scope on
2026-10-11.
Support current/previous/next line, word, and character; current-word spelling and phonetics; current-character phonetics; window start/end; word above/below; and line end. Reuse current speech and braille presentation and boundary behavior. This is flat text review, not semantic parent/child object navigation, Say All, editing, mouse activation, or braille routing into Windows controls.
Cover exposed visible text and controls in 32-bit and 64-bit Wine applications. The concrete MUSHclient cases are its command Edit and read-only output notepad. Its custom game display does not become readable merely by adding this bridge; the existing output plugin remains the source of reviewable game text.
Existing behavior and cause
wine-access/main.cpp supplies normalized focus/menu/editor events to
Cthulhu's session D-Bus service. The 32-bit standard Edit feedback change is
already implemented and locally installed. Preserve that work.
cthulhu-go/internal/app/runtime_commands.go sends review commands to
review.Worker. Its CaptureReviewWindow provider currently captures only
AT-SPI objects. Wine focus events go to a separate speech presenter and never
establish a review provider or a text snapshot. A spoken caret line is not a
reviewable window.
review.FlatCursor already navigates immutable window trees and preserves a
position through stable object identities. AccessibleTree.Replace preserves
generations of live identities. Reuse these mechanisms instead of implementing
a second Wine-specific navigation algorithm or reviewing cached speech.
Architecture
Wine capture provider
Add a focused capture module beside main.cpp, rather than putting enumeration
and serialization in its event loop. The helper publishes a read-only D-Bus
capture endpoint on its existing unique session-bus connection. Multiple Wine
prefixes therefore have independent endpoints without competing for one name.
Capture uses a dedicated COM STA worker, independent of the existing native
event/menu thread. Permit one active capture and one pending capture; reject
additional requests promptly. Native accessibility objects stay on that STA
and are released there. Application queries use SendMessageTimeoutW where
available. A COM call that does not return cannot be forcibly cancelled safely;
the Go deadline discards its result, and the busy provider rejects further
capture work instead of accumulating it. Helper shutdown remains bounded by
the supervisor's existing process-stop policy.
Collect a complete snapshot of the foreground Wine window/dialog: hierarchy, stable identities, roles, captions, protected states, clipped screen bounds, visible text ranges, and the focused Edit's caret when available. Traverse MSAA objects and use existing standard-control queries when Wine exposes only a generic client. Standard Edit includes Windows Forms Edit subclasses, in both architectures. Preserve mnemonic decoding for captions and literal ampersands in values. Password text is excluded before copying or serializing it.
For Edit text, collect displayed visual lines, including soft wraps and blank
rows, with native character positions and client clipping. Do not scroll,
select text, change focus, send navigation keys, or copy to the clipboard.
The helper converts UTF-16 offsets to Unicode code-point offsets before
serialization, retaining native offsets for diagnostics. Computing absolute
offsets requires the preceding source text, so bound full Edit source reads at
one million UTF-16 units in total per window and return limited above that
bound. Only displayed ranges cross D-Bus. No approximate character geometry
is presented as native.
First implementation uses standard Edit and exposed native/MSAA captions and values. Custom painted text, RichEdit, and Scintilla text capture are excluded unless their existing accessibility data supplies usable visible text. Their caret feedback remains as it is today.
Additive protocol
Leave PresentAccessibleEvent and NVDA/Tolk contracts unchanged. Add:
RegisterWineReviewProvider(object_path: o, protocol_version: u) -> bto the Cthulhu service. Version 1 registration is associated with the actual unique D-Bus sender after the existing same-user authorization, never a caller-supplied destination name. Registration supplies no native references.PresentWineReviewFocus(instance: s, window: s, focus: s, revision: t, title: s, role: s, protected: b, bounds: (iiii)) -> bfor that registered sender. Window and focus are opaque lifetime identities. Emit on focus/owner changes and retire identities on destruction. The existing bounded foreground poll recovers missed events.CaptureWindow(expected_window: s, expected_focus: s, expected_revision: t) -> a{sv}on/org/stormux/Cthulhu1/WineReview, interfaceorg.stormux.Cthulhu1.WineReview. Return a versioned owned snapshot with its identities, revision, bounds, nodes, text ranges, and capture status.
The capture dictionary has required keys version: u, instance: s,
window: s, focus: s, revision: t, status: s, bounds: (iiii), and
nodes: aa{sv}. Status is ok, unavailable, stale, busy, or limited;
non-ok responses contain no nodes. Each node has id: s, parent: s (empty
only at root), role: s, name: s, protected: b, visible: b,
bounds: (iiii), text_known: b, caret_known: b, caret: t,
caret_utf16: t, and ranges: a(tttts(iiii)). Ranges contain code-point
start/end, native UTF-16 start/end, exact UTF-8 text, and bounds. caret
counts code points. Go validates offsets, rune counts, and the resulting
tree before use. Captions and identity fields
are individually bounded at 4096 and 256 UTF-8 bytes respectively. Limit
traversal depth to 64 and reject cycles or duplicate node identities.
Revision tracks focus/window lifetime changes, not incoming text. Within a
capture, recheck foreground/focus identity before returning and verify that
each collected control's text length and visible ranges remain consistent.
Return stale on a detected capture race, with at most one recapture under
the original deadline. Text updates do not retire live control identities.
Native handles in identities are opaque fixed-width integers. Include a helper-instance identity and a lifetime counter so reused HWNDs cannot inherit an old review position. MSAA child identities remain subordinate to their owning window lifetime; destroy/reorder events retire uncertain child identity. Do not serialize pointers, COM objects, or prefix environment data.
The capture endpoint authorizes the current Cthulhu service owner under the same-user trust model. Reject invalid versions, identities, dimensions, offsets, UTF-8, and oversized messages at both ends. Older helpers/readers retain normal speech; review reports unavailable when this optional protocol is absent.
Go ownership and integration
Place provider registry, capture client, validation, and snapshot conversion
in cohesive internal/wine files. The registry owns its session-bus connection
and tracks provider disconnection. Copied focus notices go to the event-loop
owner through a bounded mailbox; D-Bus callbacks never mutate event-loop state.
Compose a review capturer around the existing AT-SPI adapter. Wine identities
use the registered helper's unique bus name and a reserved /wine/... object
path namespace. Only this composition layer routes those identities to Wine;
they must never be sent to AT-SPI native operations. Ordinary AT-SPI capture
keeps its current path.
The event loop owns which focus source is eligible. On Xorg, corroborate Wine window bounds and metadata with the current foreground client and epoch. Support a child window inside Wine's virtual desktop using the existing foreground/title discovery. Title equality alone cannot establish ownership. When foreground ownership is unknown, reject Wine capture rather than using an earlier Linux focus. Returning to Linux restores its normal review path.
review.Worker remains the only owner of the flat cursor and capture ordering.
Before each Wine review command, obtain a fresh bounded snapshot, updating a
stable owned tree so live object generations and independent review offsets
survive text updates. Refreshing only on focus events would miss streaming
MUD output. Reuse existing capture/result queues and presentation methods;
native text is not fetched on the input or event loop.
Accept a result only if the foreground epoch, registered provider, focus, window lifetime, and source revision still match. Focus change, provider death, helper restart, window destruction, or lock activation retires Wine review ownership and queued work. Repeated focus notifications for the same live control preserve review position. Empty focused edits remain valid anchors so review can reach other visible content in the window.
Bounds, failures, and presentation
Use a 500 ms total Wine capture deadline, at most 4096 nodes, 256 visible text ranges per control, 4096 code points per visual line, 65536 captured code points per control, and 1 MiB of text per window. Enforce a 2 MiB serialized snapshot limit including metadata. Exhaustion returns a limited status; never present a partial snapshot as a complete window. The existing review command FIFO remains bounded at 16. Shutdown cancels outstanding D-Bus work and drops late results.
Unavailable capture, no visible content, and capture limits use existing complete review messages. Keep Python/current Go's silent movement boundaries, spelling and phonetic sequences, and blank/space handling. Review speech and braille go through the normal queues and settings; no direct speech calls from the helper. Debug logs include provider/source identities, epochs, native UTF-16 and converted offsets, capture timing, command, final speech, and braille text. Password contents never enter debug logs.
Verification and acceptance
Write failing behavioral checks before production changes:
- Helper fixtures, 32-bit and 64-bit: visible wrapped text and blank lines, Unicode/surrogate conversion, read-only multiline output, empty command field, captions/native control fallback, hidden/clipped content, password exclusion, unchanged application caret/selection/focus, and capture limits.
- Go protocol and review tests: real private-session D-Bus registration/capture, sender association and malformed payload rejection; all listed review units, spelling/phonetics and quiet boundaries; text updates preserving position; independent provider identities for two prefixes; focus switches, reused handles, provider death, helper restarts, stale replies, timeout/queue limits, and cancellation.
- Existing helper menu/editor/list regressions and focused Go review, remote, Wine, speech and braille tests with race checks.
- Private Xorg fixture driving physical X11 key events through the actual Go command router and both Wine architectures. Verify key press/release balance, focus switching to Linux, input responsiveness during a delayed capture, and no application caret movement caused by review keys.
Run private displays sequentially. Recorded speech/braille requests establish queue behavior, not audible speech or physical braille acceptance. Live MUSHclient acceptance must check the user's existing desktop review keys in the command field and output notepad, repeated-word spelling, reading while output arrives, plugin dialogs, and returning to Linux. Install matching tested helper and reader builds only after verification; preserve backups and game state.
Work locations and delivery
Helper changes belong in this standalone repository. Reader integration and
its tests belong in /home/deedra/git/cthulhu-go, with focused evidence in its
Wine testing note and the corresponding gap updated in MIGRATION_PLAN.md.
The reader directory and installed binaries are outside this session's writable
roots, so writes there require tool approval. Existing uncommitted helper
changes and cthulhu.log must be preserved.
After user approval of this document, write a reviewable implementation plan and obtain the required plan/execution decision. Keep source changes available for human review; do not submit them automatically.
Reference evidence
Local behavior references: Cthulhu Python flat_review_presenter.py for desktop
bindings, independent navigation, spelling, braille and silent boundaries;
Cthulhu-Go flat_review.go, flat_worker.go, flat.go, flat_layout.go,
AccessibleTree.Replace, foreground/title ownership, and remote authorization.
The Python Wine bridge provides speech events, not a reusable review provider.
Microsoft documents Edit character positions and Edit visible-line/text operations. These establish the native capture approach, not Wine or MUSHclient acceptance.