docs: add Tolk NVDA presence compatibility design
This commit is contained in:
@@ -0,0 +1,195 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
## 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
|
||||||
Reference in New Issue
Block a user