# Tolk NVDA Presence Compatibility Design > Superseded by `docs/wine-accessibility.md`, which uses the original NVDA Controller clients and > a native RPC server instead of replacement controller DLLs. ## 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. ## Recommended Approach 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