Skip to content

Latest commit

 

History

History
164 lines (136 loc) · 8.81 KB

File metadata and controls

164 lines (136 loc) · 8.81 KB

Configuration

OpenLogi stores settings as plain TOML. The GUI and agent read the same file:

  • macOS and Linux: $XDG_CONFIG_HOME/openlogi/config.toml (normally ~/.config/openlogi/config.toml)
  • Windows: %USERPROFILE%\.config\openlogi\config.toml

The complete, tested example is config.example.toml. Copy only the sections you need and replace its example physical device keys with keys already written by OpenLogi for your devices.

Editing and recovery

The GUI writes atomically and keeps config.toml.backup.1 through config.toml.backup.5. Existing comments and formatting are retained when the GUI updates known fields.

When config.toml is a symbolic link, saves update its target and preserve the link. A missing target and its parent directories are created. Backups stay beside the configured config.toml, while a persistent .lock file stays beside the target so different links to the same file share the writer lock.

The schema is strict: misspelled, obsolete, and out-of-range fields stop the config from loading instead of silently selecting a default or disappearing on the next save. The GUI then opens in read-only mode and shows the exact TOML error. Fix the file and relaunch OpenLogi.

If the file changes in an editor while the GUI is open, the next GUI save is refused rather than overwriting the external edit. Relaunch to load that revision. Opening the GUI also tells the resident agent to reload the current file, so hand edits and runtime behavior converge immediately.

Schema versions newer than the running build are rejected before their fields are parsed. v1 binding maps and the v2–v3 gesture-owner layout migrate on load. The v3 physical-device-key transition cannot safely assign older model-scoped device settings when two identical devices exist, so v2 model-key entries must be copied manually to the generated physical keys. Pre-v7 thumb-wheel scroll pairs on the old defaults are migrated in both device and per-application profiles so they keep their native direction.

Shape

schema_version is required and currently 7. selected_device is an optional physical device key.

[app_settings] contains application-wide preferences:

  • startup, update, menu-bar / tray, input-capture, and asset-download toggles
  • asset_source: automatic, openlogi, cloudflare, or fastly
  • language, appearance, device_view_mode (grid, list, or carousel), optional theme names, and optional UI radius
  • smooth_scroll toggles finite animation for traditional mouse-wheel input. With it off, a diverted horizontal thumb wheel still scrolls as a phased gesture (ended after 120 ms idle) so apps that reveal content on horizontal swipes, like Messages, react; vertical wheel output stays a plain wheel
  • vertical_scroll_sensitivity, from 1 through 100 (14 is 1×); continuous trackpad input remains native
  • thumbwheel_sensitivity, from 1 through 100 (14 is 1×)
  • mouse_profile_target: pointer (default, including existing configs that omit this preference) or focused. Mouse button profiles follow the window under the pointer; the desktop uses the global bindings. Keyboard profiles continue to follow the focused application. Pointer targeting is supported on macOS, Windows, and X11; unsupported sessions such as Wayland use the focused application, and so does a pointer over a surface OpenLogi cannot identify, such as a system overlay, or a lookup that fails. An unidentified surface uses the focused application's profile rather than the desktop's; with no focused application there is no such profile and the global bindings apply, as in focused mode. OpenLogi never activates a background window to send a shortcut: over an identified window or the desktop, mouse bindings that produce keystrokes or run workflows are skipped unless the hovered window is focused. Global actions such as desktop switching can run without changing application focus.

[devices."<physical-key>"] contains per-device state. Receiver keys look like receiver:<receiver-id>:slot:<number>; direct, raw-HID, and camera devices use other generated keys. Do not substitute a model id such as 2b042.

A camera without a USB serial has no unique port-stable identity. Its custom_name key therefore follows the OS capture id so two same-model cameras remain distinguishable; moving it to another USB port may require naming it again.

Common device fields are:

  • custom_name, enabled, dpi, dpi_presets, thumb-wheel sensitivity, scroll inversion, and scroll resolution
  • bindings: a button maps to one action, an independent short/long action pair, or a gesture-direction map. Thumbwheel is the thumb wheel's capacitive tap — it has no GUI control and stays inert unless bound here, because the wheel reports taps from incidental thumb contact as well as from deliberate ones. Keyboard keys are named by their HID++ 0x1b04 control: a catalog name such as KeyScreenCapture, KeyCalculator or KeyLock, or control:0x<cid> for any control openlogi diag controls reports as divertable. A bound key is diverted to OpenLogi while the agent runs; an unbound key keeps its firmware function, so binding None is the same as removing the entry. Naming one control under both spellings in the same table is an error, not a merge
  • per_app_bindings: sparse action overlays keyed by macOS bundle id (or the exact executable path for applications without a bundle id), Linux application id, exact lower-cased Windows executable path, or exe:<filename>.exe. macOS paths retain their original spelling and case. The Buttons panel edits these under its Profile selector. After bringing an application to the foreground, choose it from Recent applications to use the identifier the agent observed. Identifiers differ between platforms, so a profile authored under one namespace will not match under another. An overlay holds one action per button; gesture-direction maps live in bindings
  • action_ring: default and complete per-application eight-slot layouts; action_ring.per_app takes the same selectors as per_app_bindings, including the Windows exe:<filename> fallback
  • lighting, smartshift, standalone light, and camera controls / profiles
  • host_switch_targets and fn_lock for compatible keyboards
  • identity and disabled_gestures, which are application-managed metadata

[keyboard.bindings] contains global key triggers such as f1 or shift+command+f5. Supported trigger modifiers are shift, control, option, and command; aliases such as ctrl, alt, and cmd are accepted.

Actions

Action names are the serialized Rust variant names, including Copy, BrowserBack, PlayPause, CycleDpiPresets, and ShowActionsRing. Payload actions use a one-key inline table:

Back = { CustomShortcut = "Cmd+Shift+P" }
Forward = { HoldShortcut = "Ctrl+Space" }
MiddleClick = { OpenApplication = { path = "~/Downloads", display_name = "Downloads" } }
DpiToggle = { short = "ShowDesktop", long = "MissionControl" }

Chord modifiers are Cmd, Ctrl, Alt, Shift, and Super. Cmd is the cross-platform primary modifier: Command on macOS, Control on Linux and Windows, so Cmd+C copies everywhere. Super (aliases Win, Meta) always presses the platform's logo key — Command on macOS, the Windows key, KEY_LEFTMETA on Linux — so Win+L locks Windows and Super+End reaches GNOME and KDE shortcuts bound to Super.

CustomShortcut emits an immediate key-down/key-up pair. HoldShortcut keeps the chord down until the originating physical button is released, and also releases it if capture is interrupted, the binding becomes invalid, or the agent shuts down. Use it for push-to-talk and other hold-to-activate controls.

A { short = ..., long = ... } binding waits for the button's outcome instead of firing on press. Releasing before 500 ms fires short; keeping the button down for 500 ms fires long exactly once, and the later release does not also fire short. If capture is interrupted, the binding changes, or the agent shuts down before either outcome, neither action fires. A source that can only report an instantaneous button pulse falls back to short. long may itself be a HoldShortcut, in which case its chord stays down from the 500 ms threshold until the physical release.

Long-press pairs currently apply to global device bindings and are authored in TOML. The GUI presents their short action; changing that button in the GUI replaces the whole pair with the selected single action. per_app_bindings and keyboard.bindings remain single-action maps.

An Actions Ring entry wraps the action and may add an icon or literal label:

Top = { action = { CustomShortcut = "Cmd+Shift+P" }, icon = "Keyboard", label = "Command Palette" }

ShowActionsRing is rejected inside a ring slot to prevent recursive rings.