Files
cthulhu/README-REMOTE-CONTROLLER.md
2026-04-26 13:18:23 -04:00

13 KiB

Cthulhu Remote Controller (D-Bus Interface)

This documentation covers the Cthulhu fork maintained by Storm Dragon. Cthulhu is forked from Orca; former Orca maintainers and contributors are part of the project's upstream history, but they are not current maintainers of this fork.

⚠️⚠️ WORK IN PROGRESS: This D-Bus interface is brand new and not yet feature complete. Low-risk feature additions will continue to be made. The API may be modified beyond bug fixes in future versions based on feedback from consumers of this support. Such changes will be documented here.

💡 Desktop-Agnostic Design: Cthulhu's D-Bus Remote Controller is built on standard D-Bus session bus infrastructure and works across all desktop environments (GNOME, KDE Plasma, XFCE, i3, Sway, etc.). The D-Bus service uses no desktop-specific dependencies and follows universal D-Bus conventions, making it suitable for integration with any application or automation tool on any Linux desktop environment or window manager.

[TOC]

Overview

Cthulhu exposes a D-Bus service at:

  • Service Name: org.stormux.Cthulhu1.Service
  • Main Object Path: /org/stormux/Cthulhu1/Service
  • Module Object Paths: /org/stormux/Cthulhu1/Service/ModuleName (e.g., /org/stormux/Cthulhu1/Service/SpeechManager)
  • Module Interfaces: org.stormux.Cthulhu1.ModuleName

See REMOTE-CONTROLLER-COMMANDS.md for a complete list of available commands.

Dependencies

The D-Bus interface requires:

Alternative Tools for D-Bus Interaction

While this documentation primarily uses gdbus for examples, you can use any D-Bus tool or library:

Using busctl (systemd D-Bus tool)

busctl --user call org.stormux.Cthulhu1.Service /org/stormux/Cthulhu1/Service \
    org.stormux.Cthulhu1.Service GetVersion

Using Python with dasbus

from dasbus.connection import SessionMessageBus
bus = SessionMessageBus()
proxy = bus.get_proxy("org.stormux.Cthulhu1.Service", "/org/stormux/Cthulhu1/Service")
version = proxy.GetVersion()

Using qdbus (Qt D-Bus tool - available on KDE)

qdbus org.stormux.Cthulhu1.Service /org/stormux/Cthulhu1/Service \
    org.stormux.Cthulhu1.Service.GetVersion

Service-Level Commands

Commands available directly on the main service (/org/stormux/Cthulhu1/Service):

Get Cthulhu's Version

gdbus call --session --dest org.stormux.Cthulhu1.Service \
    --object-path /org/stormux/Cthulhu1/Service \
    --method org.stormux.Cthulhu1.Service.GetVersion

Returns: String containing the version (and revision if available)

Present a Custom Message in Speech and/or Braille

gdbus call --session --dest org.stormux.Cthulhu1.Service \
    --object-path /org/stormux/Cthulhu1/Service \
    --method org.stormux.Cthulhu1.Service.PresentMessage "Your message here"

Parameters:

  • message (string): The message to present to the user

Returns: Boolean indicating success

Show Cthulhu's Preferences GUI

gdbus call --session --dest org.stormux.Cthulhu1.Service \
    --object-path /org/stormux/Cthulhu1/Service \
    --method org.stormux.Cthulhu1.Service.ShowPreferences

Returns: Boolean indicating success

Quit Cthulhu

gdbus call --session --dest org.stormux.Cthulhu1.Service \
    --object-path /org/stormux/Cthulhu1/Service \
    --method org.stormux.Cthulhu1.Service.Quit

Returns: Boolean indicating if the quit request was accepted

Discovering Modules and Their Capabilities

Use the standard DBus introspection interface to discover registered modules:

gdbus introspect --session --dest org.stormux.Cthulhu1.Service \
    --object-path /org/stormux/Cthulhu1/Service --recurse

The child <node> entries beneath /org/stormux/Cthulhu1/Service are the registered modules. To inspect the methods and properties for one module:

gdbus introspect --session --dest org.stormux.Cthulhu1.Service \
    --object-path /org/stormux/Cthulhu1/Service/SpeechManager

Interacting with Modules

Each registered module exposes its own native DBus interface. Based on the underlying Cthulhu code, these are categorized as Commands and Properties:

  • Commands: Actions that perform a task. These typically correspond to Cthulhu commands bound to a keystroke (e.g., IncreaseRate).
  • Properties: Runtime values, often settings (e.g., Rate). Setting a property does not cause it to become permanently saved.

You can discover and execute these for each module.

Plugin Modules

Plugins that expose D-Bus decorators are automatically registered as modules using the naming convention Plugin_<ModuleName> (e.g., Plugin_GameMode, Plugin_WindowTitleReader). Use standard DBus introspection to discover available plugin modules at runtime.

Plugin_WindowTitleReader

Controls for the Window Title Reader plugin:

  • Method: SetEnabled (enabled: bool, notify_user: bool)
  • Property: Enabled

Example:

gdbus call --session --dest org.stormux.Cthulhu1.Service \
    --object-path /org/stormux/Cthulhu1/Service/Plugin_WindowTitleReader \
    --method org.stormux.Cthulhu1.Plugin_WindowTitleReader.SetEnabled true false

# Check current state
gdbus call --session --dest org.stormux.Cthulhu1.Service \
    --object-path /org/stormux/Cthulhu1/Service/Plugin_WindowTitleReader \
    --method org.freedesktop.DBus.Properties.Get \
    org.stormux.Cthulhu1.Plugin_WindowTitleReader Enabled

Busctl example:

busctl --user call org.stormux.Cthulhu1.Service \
    /org/stormux/Cthulhu1/Service/Plugin_WindowTitleReader \
    org.stormux.Cthulhu1.Plugin_WindowTitleReader SetEnabled bb true false

# Check current state
busctl --user call org.stormux.Cthulhu1.Service \
    /org/stormux/Cthulhu1/Service/Plugin_WindowTitleReader \
    org.freedesktop.DBus.Properties Get ss \
    org.stormux.Cthulhu1.Plugin_WindowTitleReader Enabled

PluginSystemManager Module

The PluginSystemManager module provides session-only plugin control:

  • ListPlugins
  • ListActivePlugins
  • IsPluginActive
  • SetPluginActive
  • RescanPlugins

These calls do not persist changes to user preferences.

Executing Module Operations

Get a Property

gdbus call --session --dest org.stormux.Cthulhu1.Service \
    --object-path /org/stormux/Cthulhu1/Service/ModuleName \
    --method org.freedesktop.DBus.Properties.Get \
    org.stormux.Cthulhu1.ModuleName PropertyName
Example: Get the current speech rate
gdbus call --session --dest org.stormux.Cthulhu1.Service \
    --object-path /org/stormux/Cthulhu1/Service/SpeechManager \
    --method org.freedesktop.DBus.Properties.Get \
    org.stormux.Cthulhu1.SpeechManager Rate

Set a Property

gdbus call --session --dest org.stormux.Cthulhu1.Service \
    --object-path /org/stormux/Cthulhu1/Service/ModuleName \
    --method org.freedesktop.DBus.Properties.Set \
    org.stormux.Cthulhu1.ModuleName PropertyName '<value>'
Example: Set the current speech rate
gdbus call --session --dest org.stormux.Cthulhu1.Service \
    --object-path /org/stormux/Cthulhu1/Service/SpeechManager \
    --method org.freedesktop.DBus.Properties.Set \
    org.stormux.Cthulhu1.SpeechManager Rate '<90>'

Execute a Module Command

# With user notification
gdbus call --session --dest org.stormux.Cthulhu1.Service \
    --object-path /org/stormux/Cthulhu1/Service/ModuleName \
    --method org.stormux.Cthulhu1.ModuleName.CommandName true

# Without user notification (silent)
gdbus call --session --dest org.stormux.Cthulhu1.Service \
    --object-path /org/stormux/Cthulhu1/Service/ModuleName \
    --method org.stormux.Cthulhu1.ModuleName.CommandName false
  • notify_user (boolean): Whether to notify the user of the action (see section below)

Execute a Parameterized Command

gdbus call --session --dest org.stormux.Cthulhu1.Service \
    --object-path /org/stormux/Cthulhu1/Service/ModuleName \
    --method org.stormux.Cthulhu1.ModuleName.CommandName \
    "value1" "value2" false

Parameters:

  • notify_user (boolean): Whether to notify the user of the action
Example: Get voices for a specific language
gdbus call --session --dest org.stormux.Cthulhu1.Service \
    --object-path /org/stormux/Cthulhu1/Service/SpeechManager \
    --method org.stormux.Cthulhu1.SpeechManager.GetVoicesForLanguage "en-us" "" false

This will return a list of available voices for US English.

User Notification Applicability

Setting notify_user=true is not a guarantee that feedback will be presented.

Some commands inherently don't make sense to announce. For example:

# This command should simply stop speech, not announce that it is stopping speech.
gdbus call --session --dest org.stormux.Cthulhu1.Service \
    --object-path /org/stormux/Cthulhu1/Service/SpeechManager \
    --method org.stormux.Cthulhu1.SpeechManager.InterruptSpeech true

In those cases Cthulhu will ignore the value of notify_user.

Setting notify_user=false is not a guarantee that Cthulhu will remain silent, though for the most part Cthulhu will try to respect this value. The exceptions are:

  1. If executing the command has resulted in UI being shown, such as a dialog or menu, the newly-shown UI will be presented in speech and/or braille based on the user's settings. Failure to announce that the user has been removed from one window and placed in another could be extremely confusing.
  2. If the sole purpose of the command is to announce something without making any other changes, e.g. PresentTime, executing it with notify_user=false makes no sense. Adding checks and early returns to handle this possibility does not seem worth doing. If you don't want Cthulhu to present the time, don't ask Cthulhu to present the time. 😃

Navigator Module "Enabled" State Applicability

In the Remote Controller, Navigator commands are expected to work even when not "enabled."

Some of Cthulhu's Navigator modules, namely Table Navigator, Caret Navigator, and Structural Navigator, have an "enabled" state. The reason for this is very much tied to the keyboard-centric nature of Cthulhu's commands. For instance, if Cthulhu always grabbed "H" (for heading navigation) and the arrow keys (for caret navigation), normal interaction with applications would be completely broken. For this reason, Navigator modules whose commands will prevent normal, native interaction with applications are typically not enabled by default and can be easily disabled.

In contrast, performing Navigator commands via D-Bus does not prevent native interaction with applications. For instance, one could use the Remote Controller to move to the next heading without causing H to stop functioning in editable fields. For this reason, and to avoid a performance hit, the decision was made to not check if (keyboard-centric) navigation commands were enabled. As a result, it should be possible to use Remote Controller navigation even in "focus mode" or other cases where Cthulhu is not controlling the caret. This is by design.

Given the keyboard-centric nature of Cthulhu's commands, there may be instances in which one uses the Remote Controller for navigation and Cthulhu fails to correctly update its location in response. If Cthulhu correctly updates its location when the same navigation command is executed via keyboard, please report the Remote Controller failure as a new bug in Cthulhu's issue tracker.

The "Stickiness" (or Lack Thereof) of On-The-Fly Settings Changes

Cthulhu has a number of keyboard commands to temporarily change settings such as speech rate, pitch, volume; capitalization style; punctuation level; etc., etc. The question is: how long should on-the-fly modifications to settings persist?

Early on in Cthulhu's development, the conclusion was that on-the-fly settings changes should be seen as quite temporary, presumed to be used to address a specific one-time need. For instance, if reading some difficult-to-understand text, one might want to reduce the speed just for that text. If one were doing a final proofread of some content, one might want to briefly set the punctuation level to all. If one needs slow speed and/or verbose punctuation all the time, those should be set in Cthulhu's Preferences dialogs -- either globally or on a per-app basis. Cthulhu also has a profile feature through which the user can save settings and quickly load/unload them by switching profiles*.

Whether or not that historical decision was the right decision goes beyond the scope of the Remote Controller. The primary purpose of the Remote Controller is to provide D-Bus access to commands and runtime settings as if they were performed by the user via keyboard command. Thus if a setting changed via Remote Controller persists (or fails to persist) in the same way as when changed via keyboard command, it is not a Remote Controller bug. (It may be a general Cthulhu bug or feature request, and you are encouraged to file it as such.) On the other hand, if the behavior of the Remote Controller differs from that of the corresponding or related keyboard command, please report that Remote Controller failure as a new bug in Cthulhu's issue tracker.

* Note: Remote Controller support for profile management is still pending.