Skip to content

macOS build is structurally inaccessible to blind users (VoiceOver) — toolkit-level problem, not metadata #315

Description

@w9fyi

Summary

I'm a blind ham (AI5OS, VoiceOver user on macOS Apple Silicon) and I want to use D-Rats with my Kenwood TH-D75. After auditing the codebase I have to report that the macOS build of D-Rats is structurally inaccessible to blind users, and the cause is not something that can be fixed by adding accessibility metadata. I want to file this honestly so it's on the public record, and to share what I'm doing about it.

What I observe on macOS

Why this is a structural problem, not a metadata problem

I want to be careful here, because this is the part that most often gets misunderstood. The issue is not that D-Rats is missing setAccessibleName calls (although it is — grep for a11y and atk- in ui/mainwindow.glade returns 0 results). The issue is the toolkit itself.

GTK 3 on macOS draws its own widgets via Quartz; it does not create real NSView / NSButton / NSTextField / etc. AppKit, and therefore NSAccessibility (the macOS accessibility API that VoiceOver reads), sees a single opaque NSView for the entire D-Rats window. ATK (GTK's own accessibility layer) bridges to AT-SPI on Linux and to IAccessible2 on Windows, but the bridge from ATK to NSAccessibility was never finished and is not part of upstream GTK. There have been proof-of-concept efforts over the years (most visible in gtk-mac-integration, which handles menu bar integration but not general AX), but no production GTK→NSAccessibility bridge exists today on either GTK 3 or GTK 4.

What this means concretely: even if every widget in ui/mainwindow.glade had perfect ATK accessibility names and roles, VoiceOver on macOS would still see one giant unlabeled "Window" element with nothing inside it. There is no metadata fix that resolves this. The only ways forward are:

  1. Replace the toolkit on the macOS build — port the UI layer to a Mac-friendly Python toolkit (Toga / BeeWare → real AppKit → real NSAccessibility, or PySide6 / Qt6 → which does have a working QAccessible→NSAccessibility bridge). The protocol/session/transport layers would not change. This is a significant amount of work and would arguably benefit Linux and Windows users too if it were done as a clean abstraction.
  2. Build a separate native macOS client that speaks the D-Rats protocol — i.e., a Swift/SwiftUI app that reimplements DDT2 framing, yEncode, CRC, the session state machines, and the serial/Bluetooth transport in Swift, with first-class VoiceOver support from day one.
  3. Headless D-Rats core + web UI — restructure the Python app so the core can run without GTK, then build a Flask/FastAPI web UI on top. This makes the full feature set accessible from any browser, which is the most accessible UI platform on the planet. The downside is decoupling mainapp.py and mainwindow.py is a substantial refactor, and "always-on host" becomes a deployment requirement.

What I'm doing

I'm starting on option 2 — a clean native macOS Swift client called MacRats, GPL-3 licensed (matching D-Rats upstream so the lineage is honored). It will reimplement the DDT2 wire format, yEncode, CRC, zlib framing, and the chat + file transfer session state machines in Swift, with a SwiftUI UI built accessibility-first against the macOS NSAccessibility API. v1 will target chat, station list, file transfer, and config — the bare minimum to actually make a D-STAR data QSO from a Mac. My test radio will be a Kenwood TH-D75 over USB serial.

I want to be very clear: MacRats is not a fork of D-Rats and is not trying to replace D-Rats on Linux or Windows. D-Rats is the right tool on those platforms and I have no interest in fragmenting the project. MacRats exists because GTK on macOS is a structural dead end for screen reader users, and the only honest path forward is a separate native client. If the upstream maintainers ever want to take a different path (a Toga port, a Qt6 port, a headless+web split), that would be wonderful and I would gladly contribute and stop maintaining MacRats.

What I'd ask of the maintainers

I am not asking for someone to drop everything and rewrite the toolkit. I am asking for three smaller things, in order of how much they would help:

  1. Acknowledge in the README and the install docs that the current macOS build is not accessible to blind users with VoiceOver, and link to whatever alternative exists. Once MacRats has a v1 release I will offer to add the link via PR. Until then, a one-line note pointing to this issue would be enough. Blind hams searching for "D-Rats macOS" deserve to find an honest answer rather than to spend hours fighting Python venvs and GTK installs only to discover the result is unusable to them.
  2. Treat D-STAR protocol documentation as a first-class artifact. The DDT2 frame format, the session state machines, yEncode usage, CRC algorithm, and the wire-level description of chat / file / form sessions currently live only in the Python source. If you ever produced a short docs/PROTOCOL.md describing the on-air bytes (even just by copying what's already in d_rats/ddt2.py, d_rats/transport.py, d_rats/sessions/*.py into prose), it would dramatically help anyone building an interoperable client — including MacRats.
  3. If you're ever interested in a longer conversation about a Toga or PySide6 port of the UI layer to make the macOS build first-class, I'd be happy to help scope it. I genuinely think a clean d_rats/ui/ abstraction with multiple backends would be valuable to the project's long-term health on all three platforms, not just for accessibility.

I want to thank you both — Maurizio (IZ2LXI) and John (WB8TYW) — for keeping D-Rats alive and for the Python 3 conversion. D-Rats fills a genuinely important niche for emergency communications and rural ham operation, and the work of maintaining it is real and unglamorous. Nothing in this issue is meant as criticism of that work. The toolkit limitation predates either of you and isn't anyone's fault.

Environment

  • Platform: macOS 14/15 (Apple Silicon)
  • Screen reader: VoiceOver
  • Radio I'd like to use D-Rats with: Kenwood TH-D75 (built-in TNC, USB-C serial, D-STAR DV slow-data)

References

73,
AI5OS

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions