Configuration
Reference for the TOML config file: location, schema, all keys, actions, and gesture bindings.
OpenLogi stores everything in a single TOML file. The GUI writes it for you: the main window edits button bindings, the Actions Ring, DPI presets, SmartShift, scrolling, lighting, and camera controls, and the Settings window (⌘,) covers the app-wide preferences, but the file is plain text and safe to hand-edit. Per-app binding overlays have no dedicated editor yet, so those are authored here directly.
The agent reads the config on startup and rewrites it atomically on every
change. Hand-edit it while OpenLogi is quit, or the running agent will
overwrite your edits the next time it saves. Before its first save in each
process, OpenLogi copies the previous file to config.toml.backup.1 and
rotates up to config.toml.backup.5.
File location
| Platform | Path |
|---|---|
| macOS / Linux | $XDG_CONFIG_HOME/openlogi/config.toml (default ~/.config/openlogi/config.toml) |
| Windows | %USERPROFILE%\.config\openlogi\config.toml |
The file is written atomically (temp file + rename) and, on Unix, with 0600
permissions.
Top-level layout
schema_version = 4 # required; a newer version is refused
selected_device = "receiver:aabbccdd:slot:1" # physical key of the device shown in the carousel
[app_settings] # app-wide preferences (omitted entirely when default)
# …
[devices."receiver:aabbccdd:slot:1"] # one block per physical device
# …
[keyboard.bindings] # OS-level function-key remapper
# …schema_version— the layout version (currently4). Older files are migrated on load; a file declaring a newer version is refused rather than silently misread. v4 dropped the one-gesture-button-per-device owner lock (gesture_owner), v3 moved the device map from model keys to physical-device keys, and v2 mergedbutton_bindings/gesture_bindingsinto onebindingsmap.selected_device— remembers which device the carousel was on; omitted when unset.[app_settings]— see below; the whole block is omitted while every field is at its default.[devices.<key>]— per-device settings, keyed by physical device identity (see below).[keyboard]— device-independent function-key remapping, driven by the OS hook rather than HID++.
Device keys
Since schema v3 a device block is keyed by the physical device, not by model, so two identical mice never share one entry:
| Form | Used for |
|---|---|
receiver:<receiver-uid>:slot:<n> | Paired to a Bolt / Unifying / Lightspeed receiver |
direct:<vid>:<pid>:serial:<serial> (or :unit:<hex>) | Bluetooth-direct or USB-wired HID++ device |
raw:<vid>:<pid>:<usage-page>:<usage-id>:serial:<serial> | Raw-HID device such as a Litra light |
Keys are lower-case hex. The GUI writes the right key for you; the reliable way
to get one is to configure the device once in the app, then read the key back out
of config.toml. A device that reports no serial and no unit id has no stable
identity, so OpenLogi does not persist settings for it.
[app_settings]
| Key | Default | Meaning |
|---|---|---|
launch_at_login | false | Start the agent on login. A LaunchAgent plist on macOS, a systemd user unit on Linux. |
check_for_updates | false | Opt-in. One HEAD request to the GitHub latest-release per launch; logs whether a newer version exists; never downloads on its own. |
auto_install_updates | false | Opt-in, and only acts when check_for_updates is on: downloads and stages a newer version in the background, applied on the next restart. |
update_prompt_seen | false | Set once the first-run "check for updates?" prompt has been answered, so it is never shown again. |
show_in_menu_bar | true | macOS menu-bar status item and Windows tray icon. Ignored on Linux. |
capture_mouse_events | true | Whether the agent installs the OS mouse hook at all. false stops button remapping and grabs no input device; DPI, SmartShift, and the other HID++ features keep working. Takes effect on agent restart. |
auto_download_assets | true | Fetch device renders when a device appears. false makes no asset network requests; Refresh assets in Settings still fetches on demand. |
asset_source | automatic | Asset mirror: automatic (race every built-in mirror), openlogi, cloudflare, or fastly. |
language | (follow system) | UI locale, one of the 20 bundled locales (en, de, pt-BR, zh-CN, …). Unset follows the system locale. |
thumbwheel_sensitivity | 14 | Thumb-wheel responsiveness on a 1–100 scale; the default is 1× native scroll (the wheel is only diverted from native scrolling once this leaves the default). |
appearance | system | system, light, or dark. |
theme_light | (brand theme) | Theme name used in light mode, e.g. "OpenLogi Light". |
theme_dark | (brand theme) | Theme name used in dark mode. |
ui_radius | (theme default) | Corner-radius override in pixels; the Appearance page offers 0 / 6 / 12. |
Per-device blocks
Each [devices.<key>] block holds the settings for one physical device.
| Key | Type | Meaning |
|---|---|---|
enabled | bool | false leaves the device completely native: no HID++ capture session, no settings re-applied on reconnect. Default true. |
bindings | table | Maps a logical button to a binding: a single action, or a per-direction gesture table (see Buttons and Gesture bindings). |
per_app_bindings | table of tables | Overlays keyed by app id. While that app is frontmost its entries win; everything else falls through to bindings. |
action_ring | table | Actions Ring enable state, haptics, default layout, and per-app layouts. |
dpi_presets | array of ints | Ordered DPI values cycled by CycleDpiPresets and indexed by SetDpiPreset. |
dpi | int | The committed sensor DPI. Lives in device RAM, so the agent re-applies it on reconnect. |
smartshift | table | mode (ratchet / free), auto_disengage, tunable_torque; re-applied on reconnect. |
invert_scroll | bool | Reverse this device's native wheel direction without touching the system trackpad direction. |
scroll_resolution | string | low or high. Persisted HID++ 0x2121 wheel resolution. Absent leaves the device's own setting alone. |
thumbwheel_sensitivity | int | Per-device override of the app-wide value. |
lighting | table | Static RGB for HID++ keyboards; see below. |
light | table | Standalone light (Litra) power, brightness, temperature; see Litra lights. |
camera_controls | table | Webcam UVC controls, keyed by control name. |
camera_profiles | table of tables | User-saved camera profiles (name → control snapshot). |
camera_profile | string | The camera profile last applied from the GUI. |
host_switch_targets | array of keys | Device keys of mice that follow this keyboard's Easy-Switch channel. |
fn_lock | bool | Keyboards only. true makes the F-row send F1–F12 without holding Fn; absent leaves the keyboard's own state alone. Re-applied on reconnect. |
identity | table | Written by the app: last-known name, kind, and capabilities, so a sleeping device still renders its panels. Not meant to be hand-authored. |
disabled_gestures | table | Written by the app: the direction map of a button whose gesture mode is currently off, so re-enabling restores it. |
lighting
| Key | Default | Meaning |
|---|---|---|
enabled | true | Whether the static color is applied. |
color | "ffffff" | Static color as six hex digits RRGGBB (no leading #). |
brightness | 100 | 0–100; clamped on load. |
light
| Key | Default | Meaning |
|---|---|---|
enabled | true | Whether the light should be on. |
auto_camera | false | Turn the light on while any camera is in use, off when camera use stops (macOS). |
brightness_percent | 100 | 0–100, mapped to the device's native range (a Litra's 20–250 lumens, for example). |
temperature_kelvin | (unset) | Colour temperature, when the device supports it (Litra: 2700–6500 K in 100 K steps). |
Buttons
bindings and per_app_bindings are keyed by a logical button.
Mouse controls: LeftClick, RightClick, MiddleClick, Back, Forward,
DpiToggle (the mode-shift button under the wheel), Thumbwheel (its click),
ThumbwheelScrollUp, ThumbwheelScrollDown, GestureButton, HapticPanel
(the MX Master 4 Haptic Sense Panel).
Keyboard F-row controls, diverted over HID++ only when you bind them:
KeySearch, KeyDictation, KeyEmoji, KeyScreenCapture, KeyMicMute,
KeyPlayPause, KeyMute, KeyVolumeDown, KeyVolumeUp. See
Keyboards.
Actions
Binding values are action names, written verbatim:
- Suppress —
None(capture the input but do nothing) - Mouse —
LeftClick,RightClick,MiddleClick,MouseBack,MouseForward(the real extra-button events most apps treat as native back/forward) - Editing —
Copy,Paste,Cut,Undo,Redo,SelectAll,Find,Save - Browser & tabs —
BrowserBack,BrowserForward,NewTab,CloseTab,ReopenTab,NextTab,PrevTab,ReloadPage - Window & desktop (macOS) —
MissionControl,AppExpose,PreviousDesktop,NextDesktop,ShowDesktop,LaunchpadShow - System —
LockScreen,Screenshot,CaptureRegion,Sleep,ShowActionsRing,OpenApplication - Media —
PlayPause,NextTrack,PrevTrack,VolumeUp,VolumeDown,MuteVolume - DPI & wheel —
CycleDpiPresets,SetDpiPreset,ToggleSmartShift - Scroll —
ScrollUp,ScrollDown,HorizontalScrollLeft,HorizontalScrollRight - Power user —
CustomShortcut,TypeText,RunAppleScript,RunShellCommand,Workflow
The picker lists 44 plain actions. ShowActionsRing is written by hand (it isn't
in the picker), and the parameterized actions are written as a single-key table:
MiddleClick = "MissionControl" # plain action
DpiToggle = { SetDpiPreset = 2 } # preset index
Back = { CustomShortcut = "Cmd+Shift+P" } # key chord
Forward = { OpenApplication = { path = "~/Downloads", display_name = "Downloads" } }OpenApplication takes an application, folder, filesystem path, or URL; a
leading ~ is expanded when the action runs. CustomShortcut stores a
platform-neutral chord such as Cmd+Shift+P, Ctrl+Alt+Left, or F5. The GUI
can author all of these: custom shortcuts and Open application from the
action picker, and TypeText / RunAppleScript / RunShellCommand / Workflow
under its Power User submenu.
Gesture bindings
Any capable button can be in gesture mode: its bindings entry becomes a
sub-table keyed by Up, Down, Left, Right, and Click (the plain press,
no swipe) instead of holding a single action. Since schema v4 that is a
per-button fact; several buttons can be in gesture mode at once, and the old
device-wide gesture_owner key is gone (a v3 file's owner is migrated to the
equivalent binding shapes on load).
[devices."receiver:aabbccdd:slot:1".bindings.GestureButton]
Up = "MissionControl"
Down = "ShowDesktop"
Left = "PrevTab"
Right = "NextTab"
Click = "AppExpose"The dedicated Gesture Button and the MX Master 4 Haptic Sense Panel are captured over HID++ raw-XY; middle / back / forward gestures ride the OS hook.
[keyboard]
A device-independent remapper for function keys on any keyboard, driven by
the OS hook. Keys are triggers of the form [modifier+]…key, with modifiers
shift, control (ctrl), option (alt), command (cmd), and keys esc
and f1–f19:
[keyboard.bindings]
f1 = "MissionControl"
"shift+f2" = "ShowDesktop"
"cmd+f5" = { CustomShortcut = "Cmd+Shift+P" }This is separate from a Logitech keyboard's HID++ F-row bindings under
[devices.<key>.bindings]; see Keyboards for when to
use which.
Example
schema_version = 4
selected_device = "receiver:aabbccdd:slot:1"
[app_settings]
launch_at_login = true
language = "zh-CN"
thumbwheel_sensitivity = 14
appearance = "system"
# MX Master 4 in slot 1 of a Bolt receiver.
[devices."receiver:aabbccdd:slot:1"]
dpi_presets = [800, 1600, 3200]
dpi = 1600
invert_scroll = true
scroll_resolution = "high"
[devices."receiver:aabbccdd:slot:1".bindings]
Back = "BrowserBack"
Forward = "BrowserForward"
MiddleClick = "MissionControl"
HapticPanel = "ShowActionsRing"
# The gesture button binds per direction; Click is the plain press.
[devices."receiver:aabbccdd:slot:1".bindings.GestureButton]
Left = "PrevTab"
Right = "NextTab"
Click = "PlayPause"
# Back becomes Undo only while VS Code is frontmost.
[devices."receiver:aabbccdd:slot:1".per_app_bindings."com.microsoft.VSCode"]
Back = "Undo"
[devices."receiver:aabbccdd:slot:1".smartshift]
mode = "ratchet"
auto_disengage = 16
tunable_torque = 0
[devices."receiver:aabbccdd:slot:1".action_ring]
enabled = true
haptics = true
[devices."receiver:aabbccdd:slot:1".action_ring.default.slots]
Top = { action = "Cut" }
TopRight = { action = "Copy" }
Right = { action = "Paste", label = "Paste It" }
BottomRight = { action = "BrowserForward" }
Bottom = { action = "PlayPause" }
BottomLeft = { action = "BrowserBack" }
Left = { action = "Undo" }
TopLeft = { action = "Redo" }
# A Signature-series keyboard: F-row keys diverted over HID++, Fn-lock off.
[devices."receiver:aabbccdd:slot:2"]
fn_lock = false
host_switch_targets = ["receiver:aabbccdd:slot:1"]
[devices."receiver:aabbccdd:slot:2".bindings]
KeySearch = "MissionControl"
KeyScreenCapture = "CaptureRegion"
[devices."receiver:aabbccdd:slot:2".lighting]
enabled = true
color = "ff0000"
brightness = 80
# A Litra Glow, keyed by its raw-HID identity.
[devices."raw:046d:c900:ff43:0202:serial:YOUR-SERIAL".light]
enabled = true
auto_camera = true
brightness_percent = 65
temperature_kelvin = 4600Source: CONFIGURATION.md