Files
cthulhu/docs/superpowers/specs/2026-04-10-tolk-nvda-presence-design.md
2026-04-10 14:07:25 -04:00

7.3 KiB

Tolk NVDA Presence Compatibility Design

Goal

Allow applications running under Wine or Proton to use the official upstream Tolk.dll unchanged while routing Tolk speech through the existing Linux NVDA-to-Cthulhu path.

The compatibility layer must satisfy only the checks that Tolk performs when selecting its NVDA driver. It must not require replacing Tolk.dll, patching Tolk, or using a Tolk-specific DLL override.

Confirmed Constraints

  • The shipped Tolk.dll must remain the official upstream binary.
  • The existing wine2speechd package already provides replacement nvdaControllerClient32.dll and nvdaControllerClient64.dll implementations for Linux.
  • The current blocker is Tolk detection, not the downstream speech transport.
  • Scope is limited to making Tolk believe NVDA is present; broader NVDA emulation is out of scope.

Current Tolk Behavior

From tolk/src/ScreenReaderDriverNVDA.cpp, Tolk considers NVDA active only when both of the following succeed:

  1. nvdaController_testIfRunning() == 0
  2. FindWindow(L"wxWindowClassNR", L"NVDA") returns a window handle

If either check fails, Tolk will not select the NVDA driver and speech output through Tolk will fail.

Extend the existing Wine NVDA compatibility stack with a minimal NVDA presence helper. The solution has two parts:

  1. The existing custom NVDA controller DLLs continue handling nvdaController_* API calls and forwarding them into the Linux bridge.
  2. A lightweight Windows helper process running inside Wine creates the exact window Tolk expects for NVDA detection.

This keeps the official Tolk.dll untouched and confines the compatibility contract to Tolk's actual checks.

Architecture

1. NVDA controller DLLs

The custom nvdaControllerClient32.dll and nvdaControllerClient64.dll remain the Wine-visible implementation that applications and Tolk load.

Required behavior:

  • nvdaController_speakText forwards speech to the existing Linux bridge.
  • nvdaController_brailleMessage continues current behavior.
  • nvdaController_cancelSpeech continues current behavior.
  • nvdaController_testIfRunning returns success only when the Linux bridge is reachable and the compatibility environment is operational.

nvdaController_testIfRunning must not return success solely because the DLL loaded. It is the main guard against false positive Tolk detection.

2. NVDA presence helper

A small Windows executable is added to the Wine-side compatibility package. Its only job is to create and maintain a top-level window with:

  • class name: wxWindowClassNR
  • window title: NVDA

Required behavior:

  • starts quickly and remains idle
  • single-instance per Wine prefix or session
  • exits cleanly without user interaction
  • does not present visible UI unless Wine forces a window surface
  • can be launched independently or on demand by the controller DLL

3. Startup coordination

The presence helper must be running before Tolk calls FindWindow, or Tolk detection will fail.

Acceptable coordination strategies:

  • preferred: launch the helper as part of the existing Wine accessibility startup path
  • acceptable: lazily launch the helper the first time the custom NVDA DLL is loaded, then wait briefly for the window to appear

The preferred strategy is external startup rather than in-DLL process creation because it separates concerns and avoids loader-time side effects.

Detection Contract

Tolk compatibility is considered successful only when all of the following are true:

  • official Tolk.dll loads normally
  • Tolk loads the custom NVDA controller DLL
  • nvdaController_testIfRunning() returns 0
  • FindWindow(L"wxWindowClassNR", L"NVDA") succeeds
  • Tolk_DetectScreenReader() returns NVDA
  • Tolk_Output() delivers speech through the existing Linux bridge

If the Linux bridge is unavailable, the compatibility layer must fail closed:

  • nvdaController_testIfRunning() returns failure
  • Tolk does not report NVDA as active

The dummy NVDA window alone must never make Tolk think speech is available.

Packaging

The compatibility feature belongs with the Wine NVDA compatibility stack, not in Tolk itself.

Expected package contents:

  • nvdaControllerClient32.dll
  • nvdaControllerClient64.dll
  • nvda-presence-helper.exe or similarly named helper
  • startup integration so the helper is available in Wine and Proton environments where Tolk-based games run

No Tolk.dll replacement or override is added.

Error Handling

Bridge unavailable

  • nvdaController_testIfRunning returns failure
  • speech-related entry points return the existing failure behavior
  • Tolk should not detect NVDA

Helper missing or failed to start

  • FindWindow fails
  • Tolk should not detect NVDA
  • logging should identify missing helper startup distinctly from bridge connectivity failures

Duplicate helper instances

  • duplicates must resolve harmlessly, preferably by allowing one owner and exiting the rest

Logging

Add targeted logging in the compatibility layer only. Logging should make these states distinguishable:

  • controller DLL loaded
  • bridge connectivity check succeeded or failed
  • presence helper started
  • presence window created
  • Tolk compatibility ready

No Tolk-side logging changes are needed because Tolk is not being modified.

Testing

Functional test

Create or reuse a small Wine test application that:

  1. calls Tolk_Load()
  2. calls Tolk_DetectScreenReader()
  3. calls Tolk_Output(L\"test\", false)

Expected result with bridge and helper active:

  • Tolk_DetectScreenReader() returns NVDA
  • speech reaches Cthulhu through the existing NVDA path

Negative tests

  1. Bridge down, helper up: Tolk_DetectScreenReader() must not return NVDA
  2. Bridge up, helper down: Tolk_DetectScreenReader() must not return NVDA
  3. Both down: Tolk_DetectScreenReader() must not return NVDA

Regression check

Verify that existing non-Tolk NVDA speech consumers continue using the current wine2speechd path without requiring Tolk-specific configuration.

Out of Scope

  • adding a plugin mechanism to Tolk
  • maintaining a Tolk fork
  • broader NVDA desktop emulation beyond what Tolk checks
  • compatibility with applications that perform additional NVDA-specific probing outside the current wine2speechd contract
  • anti-cheat or anti-tamper guarantees beyond avoiding Tolk replacement

Risks

Wine window behavior

The helper must create a window that FindWindow can discover reliably under Wine and Proton. If Wine normalizes or alters class registration behavior, the helper may need adjustment.

Timing

If a game calls Tolk very early, helper startup races could cause intermittent detection failure. This is why pre-starting the helper is preferred.

Split responsibility

The controller DLL and helper must agree on readiness. If they drift apart, Tolk may see the window but still fail to speak. The fail-closed testIfRunning check is the protection against this.

Recommendation

Implement the feature in the existing Wine NVDA compatibility stack, not in Tolk and not in Cthulhu core.

The smallest correct implementation is:

  1. fix or confirm nvdaController_testIfRunning() behavior in the custom DLL
  2. add a minimal Wine helper that exposes the NVDA window Tolk checks for
  3. wire startup so the helper is available before Tolk detection occurs
  4. verify with a small Tolk test program under Wine