[BRLTTY] Enhancing MacOS support for Brltty

Yannick PLASSIARD plassiardyannick at gmail.com
Sun May 17 11:31:37 UTC 2026


Hello,

Submitting a stack of four PRs that together make brltty practical to run on macOS — particularly with HID-class braille displays while VoiceOver is also active — and incidentally tighten a few cross-platform paths along the way.
Current state on macOS (before this work)

Plugging in a HID-class braille display (Baum VarioUltra, Brailliant, etc.) makes IOHIDFamily claim it exclusively at the kernel level. brltty's existing USB acquisition path hits kIOReturnExclusiveAccess and gives up — the display is unreachable unless VoiceOver is fully quit and the device is replugged in a specific order.
The IOKit USB layer's synchronous WritePipe on interrupt OUT pipes blocks indefinitely when the device NAKs (which HID-class displays do while they're streaming IN reports). Result: occasional 10–60 s freezes during fast typing.
No screen driver knows how to read macOS application content. Even if the device worked, brltty would just see an empty screen.
Input-side latency profile: ~200 ms keystroke → braille refresh. Most of that is brltty-internal (USB poll cadence, alarm-suspend re-queueing, update-schedule debounce).
The stack

Four PRs, each self-contained and reviewable in isolation, intended to be merged in order:
https://github.com/brltty/brltty/pull/529 — Cross-platform responsiveness tuning (5 commits, no Darwin code)
https://github.com/brltty/brltty/pull/530 — Darwin: USB transport for HID-class displays
https://github.com/brltty/brltty/pull/531 — Darwin: add the MacOSAccessibility screen driver (mo)
https://github.com/brltty/brltty/pull/532 — Darwin: register FileViewer / Tmux / Screen / TerminalEmulator drivers
Each PR's description has the per-commit rationale; below is the shape of the analysis behind them.
Cross-platform tuning (PR #529)

While instrumenting the input path on macOS, five issues turned out to be platform-agnostic and worth fixing independently:
BRAILLE_DRIVER_INPUT_POLL_INTERVAL interacts badly with the per-host-controller bIntervalcalculation in usbAwaitInput: on USB 2.0 high-speed devices with bInterval=10 the per-iteration sleep ends up at 64 ms. Capped at the existing USB_INPUT_AWAIT_RETRY_INTERVAL_MINIMUM.
setCommandAlarm refused to schedule while commandQueueSuspendCount > 0, which adds the input-handler's full duration to command-dispatch latency. The suspend region is inside the alarm loop, not synchronous user code, and env->handlingCommandalready guards re-entry — the gate is safe to drop.
UPDATE_SCHEDULE_DELAY was 15 ms (sized for slow USB writes on older hardware). On modern USB it shows up directly in keystroke → refresh latency. Lowered to 5 ms.
Default HID timeouts in gioInitializeDescriptorwere left at 0. Aligned with the USB / Bluetooth defaults (1000 ms).
Baum's HID probe path was treating user input that happens to arrive during probing as unexpected packets and aborting. Made it skip routing / display / entry / joystick reports cleanly.
Darwin USB transport (PR #530)

Two coordinated changes in Programs/usb_darwin.c:
Device capture: USBDeviceReEnumerate(kUSBReEnumerateCaptureDeviceMask) detaches the device from IOHIDFamily so brltty can claim the USB interface. Released on shutdown.
Async I/O: a pthread-managed CFRunLoop thread owns the IOKit async event source; completedRequests is mutex-protected; IN endpoints wake brltty's main loop through a self-pipe + asyncMonitorFileInput; OUT writes go through WritePipeAsync + pthread_cond_timedwait + AbortPipe / ClearPipeStallBothEnds on timeout. The synchronous WritePipe-blocks-forever pathology is gone.
Effect: input latency drops to ~50–80 ms; no more multi-second hangs.
MacOSAccessibility screen driver (PR #531)

New Drivers/Screen/MacOSAccessibility driver code mo:
Reads frontmost app content via AX (visible character range + cursor via parameterized attributes). AXObserver runs on a dedicated CFRunLoop thread; main loop wakes via self-pipe.
Posts CGEvents for key injection. Layout-aware keycode lookup via UCKeyTranslate so Cmd+A on AZERTY actually triggers Select All (rather than the keycode-0 → kVK_ANSI_A coincidence that masks the bug on QWERTY).
SWITCHVT_PREV / NEXT / N mapped to Ctrl+Shift+Tab / Ctrl+Tab / Cmd+N, with post-switch focus repair on the new tab's text view.
Additional drivers (PR #532)

brltty-pty: skip the isatty checks when invoked with --driver-directives so the em screen driver can spawn it as a daemon child.
Build fv / tx / sc / em on darwin too. fv is needed for the built-in help and learn-mode; it links against -lncurses on macOS (no separate libtinfo).
What I'd appreciate

Architectural sanity check on PR #530's CFRunLoop-thread approach — there might be an idiom in brltty I missed.
Confirmation on the chosen default modifier + character behaviour (explicit press/release of kVK_Command etc.) in PR #531 — was considered for upstream but I haven't seen it elsewhere in the codebase.
Whether the existing brltty-pty TTY check (PR #532) was intentional for some scenario I'm overlooking.
Each PR is currently in draft so it doesn't trigger CI prematurely; I'll flip them ready as soon as discussion starts. Happy to split, squash, or rebase as the review prefers.
Thanks, 
— 
Yannick
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://brltty.app/pipermail/brltty/attachments/20260517/79ebf14b/attachment.htm>


More information about the BRLTTY mailing list