Skip to content

Latest commit

 

History

231 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Dasher for GTK

Build License: MIT

Dasher is an information-efficient text-entry interface, driven by continuous pointing gestures. It lets you write using eye gaze, a mouse, a switch, a joystick, or touch — designed for accessibility and augmentative communication (AAC).

This is the GTK frontend, built on the shared DasherCore engine.

dasher.at — downloads, user docs, and live demo Feature status — what each platform supports All repos — engine, frontends, design guide

Status

In development — early-stage GTK4 frontend aiming to replace Dasher 5. See the feature matrix for what's implemented.

Usage

Runtime controls live in the footer bar: the alphabet chooser, speed, a Learning toggle, colour and font choosers, and the Speech toggle. Keyboard mode sits on the top toolbar, and dwell-to-click is under Preferences → Input.

The live typing-rate readout (RFC 0012) lives under Preferences → Output, showing characters per second and words per minute (e.g. 4.2 cps · 50 wpm) in subtle text. It is computed engine-side over a short rolling window and refreshed about twice a second, with a Reset button that restarts the measurement. See RFC 0012.

Install

Prebuilt Linux packages are attached to each Release:

  • Flatpak — needs the GNOME 50 runtime (flatpak install flathub org.gnome.Platform//50), then flatpak install --user Dasher.flatpak and flatpak run org.alternativeinterface.dasher.
  • AppImage — chmod +x Dasher-x86_64.AppImage && ./Dasher-x86_64.AppImage (self-contained).

macOS and Windows aren't packaged yet — build from source (below). How the artifacts are produced is described under Packaging & releases.

Optional runtime features

Speech and Keyboard mode each depend on an external service:

  • Speech (spoken feedback and read-aloud) needs a working text-to-speech engine. A from-source Linux build enables the system feature, which drives a running speech-dispatcher (sudo apt install speech-dispatcher). The Flatpak and AppImage ship cloud-only, so configure a cloud engine under Preferences → Speech. With no engine available the Speech toggle stays disabled. See TTS Support for how the feature set is chosen at build time.
  • Keyboard mode (types Dasher's output into other applications) uses ydotool. Install it and run the ydotoold daemon (it needs access to /dev/uinput); on Debian/Ubuntu that is sudo apt install ydotool, on Arch sudo pacman -S ydotool plus sudo systemctl enable --now ydotool — the binary alone is not enough, Arch does not enable the service for you. Availability is checked by probing the daemon (a zero-length pointer move), and if injection fails mid-session the mode turns itself off with a message rather than dropping output silently. Clicking the Keyboard button (on the top toolbar) when ydotool isn't working opens a setup dialog with the install command for your distro and a Retry, instead of enabling the mode.

Dwell to click (hover in place to click, under Preferences → Input) needs no external dependency.

Build

Prerequisites

Platform Command
macOS brew install gtk4 gtkmm4 pkg-config cmake
Linux (Debian/Ubuntu) apt-get install build-essential libgtk-4-dev libgtkmm-4.0-dev git cmake pkg-config libspeechd-dev libclang-dev speech-dispatcher ydotool
Windows Install GTK from GVSBuild to C:\gtk, add C:\gtk\bin to PATH. Requires CMake, Git, and MSVC or Clang. Use an optimized release build for binary compatibility.

All platforms additionally require a Rust toolchain (cargo) to build the bundled rust-tts-wrapper. Install it from rustup.rs. On Linux the system TTS feature binds speech-dispatcher via bindgen, hence libspeechd-dev (headers) and libclang-dev (for bindgen) above. The speech-dispatcher and ydotool packages are runtime dependencies for the optional Speech and Keyboard-mode features (see Optional runtime features); the app still builds and runs without them (the Speech toggle greys out, and Keyboard mode offers an in-app setup helper).

Steps

git clone --recursive https://github.com/dasher-project/Dasher-GTK.git
cd Dasher-GTK
mkdir build && cd build
cmake ..
cmake --build . --config Release --parallel

The runtime files are placed in build/Dasher/, and so is the binary, except on Windows, where the default generator is Visual Studio. That one picks the configuration at build time (hence --config above) and puts the executable in a per-configuration subdirectory, so it lands in build/Dasher/Release/. The data files are not per-configuration and stay in build/Dasher/, which is why the two part company there.

Or use the launcher

run.py does the above and launches the result, on Linux, macOS and Windows alike. It is a convenience wrapper, not a required part of the build:

python run.py                 # configure, build, then launch
python run.py --build-only    # stop after the build
python run.py --tests         # build, then run the ctest suite
python run.py --clean         # start the build directory over

--build-dir (default build), --build-type (default Release) and -j/--jobs change where and how it builds; anything after a -- separator is forwarded to Dasher itself.

It checks for the system dependencies listed above and names the package to install when one is missing, initialises the submodules if you cloned without --recursive, falls back to -DTTS_WRAPPER_FEATURES=cloud when the speech-dispatcher headers are absent, and launches from the right working directory (see Running for why that matters).

If a submodule is checked out at a commit other than the one this repository records, the launcher says so and leaves it alone, since that is usually somebody testing against a different engine revision. --sync-submodules resets them to the recorded commits.

Running

Dasher must be launched from the build/Dasher/ directory so it can find its data files. Started from anywhere else it comes up looking fine, but every letter box is the same size, because the language model found no training text.

The binary is lowercase dasher on Linux and Dasher on macOS:

cd build/Dasher
./dasher      # './Dasher' on macOS

On Windows it is Dasher.exe, one directory deeper, and the working directory still has to be build/Dasher for the data files:

cd build\Dasher
.\Release\Dasher.exe

Running the Tests

Lightweight unit tests live in tests/ and build alongside the app; doctest is fetched automatically at configure time, so no extra dependency is required. After configuring and building, run the suite with ctest:

ctest --test-dir build --output-on-failure

On Windows add -C Release (or whichever configuration you built), for the same reason cmake --build needs --config: a multi-config generator has no single configuration for ctest to assume, and without it the suite reports no tests found.

CI runs these tests on every push as part of the multi-platform workflow.

TTS Support

The rust-tts-wrapper submodule provides text-to-speech support. It is included automatically when cloning with --recursive. CMake builds and links it if the submodule is present.

  • macOS: builds with avsynth,cloud features (no local speech-dispatcher needed)
  • Linux: builds with system,cloud features (uses speech-dispatcher + cloud engines); needs libspeechd-dev and libclang-dev (see Prerequisites)
  • Windows: builds with sapi,cloud features (uses the Windows SAPI engine plus cloud engines)
  • All platforms need a Rust toolchain (cargo) on PATH to compile the wrapper

Override the default feature set with -DTTS_WRAPPER_FEATURES=... at configure time — e.g. -DTTS_WRAPPER_FEATURES=cloud for a cloud-only build with no speech-dispatcher dependency (this is what the Flatpak uses).

The feature set is decided at configure time and stored in the CMake cache, so installing libspeechd-dev after a cloud-only build does not change anything on its own: that build directory stays cloud-only until you reconfigure it from scratch (run.py --clean, or delete build/).

Runtime Data Files

The CMake build copies data files into build/Dasher/Data/. The directory layout after building:

build/Dasher/
├── dasher              # executable (lowercase on Linux; `Dasher` on macOS)
├── libdasher.so        # Linux (libdasher.dylib on macOS, dasher.dll on Windows)
├── librust_tts_wrapper.so  # TTS wrapper, unless built without it
├── UIStyle.css
├── Data/
│   ├── alphabet.*.xml  # alphabet definitions
│   ├── color*.xml      # colour schemes
│   └── training*.txt   # language model training data (PPM)
├── Strings/
│   └── strings_*.json  # UI translations
└── Resources/
    └── License/

Dasher uses a PPM (Prediction by Partial Match) language model trained on text files. Each alphabet definition specifies a trainingFilename (e.g. training_english_GB.txt). Without training data, all letter boxes will be the same size and prediction will not work. Training files are copied from DasherCore/Data/training/ during the build; if letters appear uniformly sized after launch, run from build/Dasher/ so the "Data" relative path resolves, and rebuild if stale (cmake --build build).

Known Issues

  • Keyboard mode and system Speech aren't available in the Flatpak and AppImage builds. Both need host-level access the self-contained, sandboxed formats can't provide: /dev/uinput (via ydotoold) for ydotool keyboard injection, and libspeechd plus a running speech-dispatcher for system TTS. The Flatpak is cloud-only for speech; Keyboard mode expects a from-source or native install. See Optional runtime features.

Packaging & releases

Linux is distributed as Flatpak and AppImage, both built by .github/workflows/publish.yml.

Flatpak — manifest: packaging/flatpak/org.alternativeinterface.dasher.yaml (GNOME 50 runtime + the rust-stable SDK extension; builds rust-tts-wrapper with TTS_WRAPPER_FEATURES=cloud). The manifest bundles the working tree via a type: dir source, so run flatpak-builder from outside the repo to avoid copying its build directory into itself. --install-deps-from=flathub pulls the runtime, SDK and rust-stable extension at the versions the manifest declares:

flatpak remote-add --user --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
repo=$(pwd)
mkdir -p /tmp/dasher-fb && cd /tmp/dasher-fb
flatpak-builder --user --install --force-clean --install-deps-from=flathub build \
    "$repo/packaging/flatpak/org.alternativeinterface.dasher.yaml"
flatpak run org.alternativeinterface.dasher

AppImage — bash packaging/build-appimage.sh produces Dasher-x86_64.AppImage.

Cutting a release — a v* tag push makes the publish workflow build both artifacts and attach them to a new GitHub Release. Two things must be true before you tag, or CI refuses the release:

  1. The metainfo carries this release's entry — add a <release version="…"> block at the top of <releases> in packaging/org.alternativeinterface.dasher.metainfo.xml and merge it. The validate workflow fails the tag push on a mismatch, and the publish workflow's release job hard-gates on the same check — a tag without its entry will not ship packages (that gate exists because v0.2.10 shipped with the check red).
  2. The DasherCore submodule is pinned at an upstream tag, not a floating commit — tag DasherCore first and bump the pin.

The safe path is the helper, which enforces both plus the usual hygiene (clean tree, on main, tag not taken) and refuses to tag otherwise:

Scripts/tag-release.sh --push v0.2.12 # verifies, tags, pushes

Freshness is enforced by the publish workflow's tag-sanity job, which runs within seconds of the tag push and gates both artifact builds — a tag that doesn't point at origin/main's tip never starts a build. (The validate workflow repeats the check as an early warning, but the enforcement lives in publish, where failure actually blocks the release.)

If a bad tag already went out: add the missing metainfo entry, merge, then delete and re-push the tag — validate re-runs green and the publish workflow updates the existing release in place.

Privacy & analytics

Dasher-GTK includes opt-in anonymous usage analytics and crash reporting to help prioritise accessibility work. It is off by default — nothing is sent until you turn it on under Preferences → Privacy. All Dasher frontends share one self-hosted PostHog project; the complete event schema is published in analytics-events.json.

  • Collected only after opt-in: app launches, the input method / alphabet you select, which settings tab you open, and crash reports. Each event carries a random anonymous ID (resettable under Privacy) plus platform, app_variant, app_version, os_version, and $os.
  • Never collected: the text you type, clipboard contents, canvas contents, your name / email / account, training text, or game-mode targets.
  • Never located: every event, crash reports included, sets $geoip_disable. PostHog Cloud otherwise derives city, postal code and approximate coordinates from the sending IP address even with project-level IP anonymisation enabled, and this per-event flag is the only control that suppresses it — so no location is ever derived from your connection (RFC 0001: no location data).
  • Crash reports capture the exception type and the last lines of the engine log, with home-directory paths and email addresses scrubbed before anything leaves your device. An uncaught C++ exception also carries its message and the live log tail; a native signal (SIGSEGV/SIGABRT/SIGILL) is handled in an async-signal-safe path that writes a minimal record with no stack trace, and recovers the log tail from the mirrored engine.log on the next launch. A crash is written locally and only transmitted (as a PostHog $exception) on the next launch if you have opted in; otherwise it is discarded after 7 days.

Design details are in the org RFCs 0001 (analytics) and 0009 (crash reporting).

Architecture

This frontend consumes DasherCore through its C API (src/Engine/DasherBridge.cpp, backed by dasher.h). DasherBridge owns the engine handle, feeds it GTK pointer input, and receives draw commands that RenderingCanvas renders onto a GTK widget. InputManager/DwellClickHandler translate raw input, and TtsService / DirectModeService handle output and spoken feedback. The Analytics module keeps a bounded engine-log ring buffer and installs crash handlers, powering the opt-in analytics and crash reporting described under Privacy & analytics.

flowchart LR
    Input["GTK pointer / keys"] --> InputMgmt["InputManager<br/>DwellClickHandler"]
    InputMgmt --> Bridge["DasherBridge"]
    Bridge <-->|"C API (dasher.h)"| Core[("DasherCore<br/>engine")]
    Core -.->|"draw commands"| Canvas["RenderingCanvas"]
    Bridge --> Output["TtsService<br/>DirectModeService"]
Loading

See DasherCore's C API for the engine contract.

Repository layout

Path Purpose
src/Engine/ DasherBridge + CommandRenderer: C API bridge to DasherCore
src/Input/ InputManager, DwellClickHandler (pointer/switch input)
src/Output/ TtsService, DirectModeService (speech + output modes)
src/Preferences/ Settings UI (PreferencesWindow, SettingsSection)
src/UIComponents/ Reusable GTK widgets (canvas, synced controls)
src/Analytics/ Opt-in analytics + crash reporting (PostHog, RFC 0001/0009)
tests/ doctest unit tests
packaging/ Flatpak manifest + AppImage build script (Linux distribution)
DasherCore/ DasherCore submodule (do not edit here — PR upstream)
rust-tts-wrapper/ TTS wrapper submodule
Thirdparty/SDL SDL submodule (joystick input)

Contributing

See CONTRIBUTING.md for build details, code style, and DCO sign-off. For project-wide conventions (code of conduct, RFCs, security), see the org contributing guide.

Please file bug reports in the issues of this repository. To join the development group, send a pull request or reach us via Slack (OpenAAC).

License

MIT — see LICENSE.

About

GTK-based Front-end for the DasherCore. Still in very early development.

Resources

Code of conduct

Contributing

Security policy

Stars

4 stars

Watchers

3 watching

Forks

Releases

Packages

Used by

Contributors

Languages