Skip to content

feat: opt-in D-Bus activation for compositor global shortcuts - #568

Open
webenefits wants to merge 5 commits into
VocaHQ:mainfrom
webenefits:feature/plasma-shortcut-activation
Open

feat: opt-in D-Bus activation for compositor global shortcuts#568
webenefits wants to merge 5 commits into
VocaHQ:mainfrom
webenefits:feature/plasma-shortcut-activation

Conversation

@webenefits

Copy link
Copy Markdown
Contributor

What

Adds an opt-in way to start/stop dictation via an external D-Bus trigger, so a
compositor global shortcut (e.g. KDE Plasma bound to vocalinux --toggle) can
drive activation instead of the built-in evdev/pynput hotkey listener.

Why

On Wayland the internal listener reads /dev/input (needs the input group and
is effectively a system-wide key reader). Delegating activation to the compositor
removes that: no /dev/input access and no input group needed just to start
dictation. Text injection (IBus) is unchanged and independent of activation.

How

  • New session-bus service com.vocalinux.Vocalinux (Gio/GDBus) with
    Toggle/Start/Stop; handlers marshal onto the GTK main thread.
  • CLI vocalinux --toggle/--start/--stop forwards the command to a running
    instance and exits before the single-instance lock, so it never starts a
    second instance.
  • Opt-in shortcuts.disable_internal_hotkey skips the internal listener.
  • Settings → Shortcuts toggle for it (live-applied; greys out the built-in
    shortcut controls when enabled). Shown on all desktops — the feature is
    compositor-independent.
  • docs/USER_GUIDE.md section and unit tests (CLI dispatch, config gate, D-Bus
    handlers, settings wiring).

Default behaviour is unchanged — double-tap Ctrl still works out of the box.

Testing

  • pytest tests/test_dbus_activation.py tests/test_settings_shortcuts.py
  • Verified on KDE Plasma Wayland: with the option on, no /dev/input file
    descriptors are held by the process; activation works via a bound
    vocalinux --toggle shortcut.

@github-actions github-actions Bot added documentation Improvements or additions to documentation app Core Python application (src, packaging) tests Test suite changes labels Jul 22, 2026
@webenefits webenefits changed the title Feature: opt-in D-Bus activation for compositor global shortcuts feat: opt-in D-Bus activation for compositor global shortcuts Jul 22, 2026
@codecov

codecov Bot commented Jul 22, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 86.25%. Comparing base (cc69c7e) to head (8925781).

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #568      +/-   ##
==========================================
+ Coverage   86.04%   86.25%   +0.21%     
==========================================
  Files          35       36       +1     
  Lines        6470     6578     +108     
  Branches     1052     1064      +12     
==========================================
+ Hits         5567     5674     +107     
- Misses        682      683       +1     
  Partials      221      221              
Files with missing lines Coverage Δ
src/vocalinux/dbus_service.py 100.00% <100.00%> (ø)
src/vocalinux/main.py 93.57% <100.00%> (+0.49%) ⬆️
src/vocalinux/ui/config_manager.py 98.70% <ø> (ø)
src/vocalinux/ui/tray_indicator.py 83.72% <100.00%> (+0.27%) ⬆️
🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@jatinkrmalik jatinkrmalik left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@webenefits Can you please rebase this branch to latest main? There are a bunch of merge conflicts that you need to resolve.

@jatinkrmalik jatinkrmalik added the stale The pull request is stale and/or has merge conflicts. label Jul 28, 2026
Alexander added 5 commits July 29, 2026 08:04
Add an opt-in external trigger so a compositor global shortcut (e.g. KDE
Plasma bound to `vocalinux --toggle`) can start/stop dictation instead of the
built-in evdev/pynput hotkey listener reading /dev/input.

- New session-bus service com.vocalinux.Vocalinux (Gio/GDBus) with
  Toggle/Start/Stop; handlers marshal onto the GTK main thread and call the
  existing TrayIndicator methods.
- CLI vocalinux --toggle/--start/--stop forwards the command to a running
  instance and exits (before the single-instance lock), without starting a
  second instance.
- Opt-in shortcuts.disable_internal_hotkey skips the internal evdev/pynput
  listener, so no /dev/input access is required. Default behaviour unchanged.
- USER_GUIDE section and unit tests (CLI dispatch, config gate, D-Bus
  handlers). Text injection (IBus) is untouched.
Add an "External Activation (Desktop Shortcut)" switch in Settings -> Shortcuts
that toggles shortcuts.disable_internal_hotkey without editing config.json.

- Live-apply: toggling starts/stops the internal listener immediately via a
  new callback (TrayIndicator._setup_keyboard_shortcuts), no restart needed.
- When enabled, the mode/shortcut/custom-shortcut rows are greyed out and a
  hint points to 'vocalinux --toggle'.
- Shown on all session-bus desktops (not limited to Plasma/Wayland) — the
  feature is compositor-independent.
- Initial state loaded from config on open (no auto-apply during init).
- Tests: source inspection in test_settings_shortcuts.py (repo pattern, since
  the dialog subclasses the mocked Gtk.Dialog).
send_command only caught GLib.Error. With no session bus available (e.g.
DBUS_SESSION_BUS_ADDRESS unset in an SSH/systemd context) or a different
exception type from the bindings, `vocalinux --toggle` raised an unhandled
traceback instead of exiting with code 1.

- Add a generic fallback except clause -> deterministic non-zero exit.
- Test for the fallback path (GLib.Error patched to a real class in the
  harness).
The D-Bus activation tests imported vocalinux modules (and TrayIndicator) at
module level. Because the file is collected early, that froze
tray_indicator.GLib against a stale gi.repository mock; other test files swap
sys.modules["gi.repository"] at runtime, so later GLib-identity assertions
(e.g. GLib.SOURCE_REMOVE in test_suspend_handler) and single-instance tests
failed depending on collection order.

Make all vocalinux imports in test_dbus_activation.py lazy, mock
single_instance via sys.modules instead of patching acquire_lock, and move the
_setup_keyboard_shortcuts gate tests to test_tray_indicator_ext.py, where
tray_indicator is imported late (after the gi.repository swaps).
Raise patch coverage on the external-activation feature:
- dbus_service.py to 100%: bus-acquired/name-acquired/name-lost callbacks,
  _register / _on_bus_acquired / _invoke / shutdown error branches, and the
  GLib.Error path in send_command.
- tray_indicator external Start/Stop handlers: state-guarded start/stop so
  repeated --start/--stop calls are idempotent.

The TrayIndicator tests live in test_tray_indicator_ext.py to keep the real
module imported late (avoiding the gi.repository identity issues fixed in the
previous commit).
@webenefits
webenefits force-pushed the feature/plasma-shortcut-activation branch from b36b909 to 8925781 Compare July 29, 2026 08:21
@webenefits

Copy link
Copy Markdown
Contributor Author

@jatinkrmalik done! Rebased onto the latest main and resolved all conflicts. The branch is now linear on top of current upstream main (through cc69c7e), with no merge commits.

The conflicts were all complementary: the external D-Bus activation feature and the newer auto_pause_monitor / model_keepalive additions touch adjacent lines in main.py, tray_indicator.py and settings_dialog.py. Both sides are kept, nothing dropped. Full test suite passes locally (Python 3.9 / 3.11 / 3.13); flake8 / black / isort clean.

Ready for another look, could you re-review when you get a chance? Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

app Core Python application (src, packaging) documentation Improvements or additions to documentation stale The pull request is stale and/or has merge conflicts. tests Test suite changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants