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. The Buttons workspace also has a Profile selector for editing per-app overlays.
GUI saves are atomic and preserve existing comments and formatting. If an editor changes the file after OpenLogi loaded it, the next GUI save is refused instead of overwriting that edit. Relaunch OpenLogi to load the external revision; opening the GUI also tells the resident agent to reload it.
File location
| Platform | Path |
|---|---|
| macOS / Linux | $XDG_CONFIG_HOME/openlogi/config.toml (default ~/.config/openlogi/config.toml) |
| Windows | %USERPROFILE%\.config\openlogi\config.toml |
The schema is strict. Unknown, obsolete, malformed, and out-of-range values stop
the file from loading instead of silently resetting it. The GUI opens a
read-only error screen with the TOML error and buttons to open the config folder
or relaunch after you fix it. Before the first save in each process, OpenLogi
copies the previous file to config.toml.backup.1 and rotates through
config.toml.backup.5; a migrated file also gets a versioned copy such as
config.toml.v4.bak. Files are written atomically and, on Unix, with 0600
permissions.
Top-level layout
schema_version = 7 # required; zero or a newer version is refused
selected_device = "unit:6be9d300" # physical key of the selected device
[app_settings] # app-wide preferences (omitted entirely when default)
# …
[devices."unit:6be9d300"] # one block per physical device
# …
[keyboard.bindings] # OS-level function-key remapper
# …schema_version— the layout version (currently7). Supported older files migrate on load; a newer version is refused before its fields are parsed. v5 made a device's own identity transport-independent, v6 added short/long button pairs, and v7 normalized thumb-wheel directions. Earlier migrations still cover the v4 gesture-owner removal, v3 physical-device keys, and v2's unifiedbindingsmap.selected_device— remembers which device was selected; 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
Device blocks are keyed by physical identity, not model, so two identical mice do not share settings. Since schema v5, a known HID++ identity is independent of how the device is connected:
| Form | Meaning |
|---|---|
unit:6be9d300 | Non-zero four-byte HID++ unit id (eight lower-case hex digits) |
serial:abc123 | Non-empty, case-folded device serial |
receiver:aabbccdd:slot:1 | Receiver route used until the GUI can associate that slot with the device's own identity |
raw:046d:c900:ff43:0202:serial:your-serial | Standalone raw-HID identity such as a Litra light; stable:<id> is also valid when its driver supplies one |
Once the GUI observes unit:6be9d300 over both a Bolt receiver and a cable, it
folds both routes into that one device entry. The routes remain as link keys:
[devices."unit:6be9d300".links."receiver:aabbccdd:slot:1"]
[devices."unit:6be9d300".links."direct:046d:c08d".capabilities]
buttons = true
pointer = trueCapabilities and deliberate setting differences may therefore be stored per
link while ordinary settings follow the physical device across transports.
Do not compose keys by hand: configure or rename the device once in the app and
copy the generated key from config.toml. A direct HID++ device with neither a
serial nor a non-zero unit id cannot get a stable persisted identity. A camera
without a USB serial instead uses its OS capture id, so moving ports may require
renaming it again.
[app_settings]
mouse_profile_target selects the application for mouse button profiles. The default, pointer, follows the window under the pointer on macOS, Windows, and X11; unsupported sessions such as Wayland use the focused application. Set focused to follow keyboard focus on every platform. Keyboard profiles and Actions Ring layouts always follow the focused application.
When pointer targeting is active, hovering over the desktop background uses global mouse bindings. OpenLogi does not activate a background window to deliver shortcuts: mouse bindings that send keystrokes or run workflows are skipped unless the target window is focused. See Per-app profiles for examples.
| Key | Default | Meaning |
|---|---|---|
launch_at_login | true | Keep the background agent available after login; platform registration details differ. |
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. |
mouse_profile_target | pointer | Application used for mouse button profiles: pointer or focused. Omitted values also use pointer. |
smooth_scroll | false | Add finite smooth animation to eligible physical mouse-wheel input; native trackpad/pixel input stays untouched. |
vertical_scroll_sensitivity | 14 | Traditional vertical wheel distance on a 1–100 scale; 14 is 1×. Does not scale native trackpad input. |
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) | Bundled UI locale such as en, de, pt-BR, or 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. |
ui_scale | normal | small (90%), normal (100%), large (110%), or extra_large (125%). |
device_view_mode | grid | Home device layout: grid, list, or carousel. |
app_icon | openlogi | macOS app icon: openlogi or prism; inert on Linux and Windows. |
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. |
custom_name | string | User-assigned alias from the device card's Rename action; blank in the GUI restores the model name. |
links | table | App-managed routes, measured per-route capabilities, and optional deliberate per-route overrides. |
bindings | table | Maps a logical button to one action, a { short, long } pair, or a per-direction gesture table (see below). |
per_app_bindings | table of tables | Sparse overlays keyed by app selector. Mouse profiles use mouse_profile_target; keyboard profiles use focus. Unlisted buttons fall 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; out-of-range values are rejected 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 capacitive tap),
ThumbwheelScrollUp, ThumbwheelScrollDown, GestureButton, HapticPanel
(the MX Master 4 Haptic Sense Panel), WheelTiltLeft, and WheelTiltRight.
Thumbwheel is actually the wheel's noisy capacitive tap rather than a
mechanical click; it is inert by default and has no GUI control.
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,HoldShortcut,TypeText,RunAppleScript,RunShellCommand,Workflow
ShowActionsRing is available in the action picker. Parameterized actions are
written as a single-key table:
HapticPanel = "ShowActionsRing" # plain action
DpiToggle = { SetDpiPreset = 2 } # preset index
Back = { CustomShortcut = "Cmd+Shift+P" } # key chord
Forward = { OpenApplication = { path = "~/Downloads", display_name = "Downloads" } }
MiddleClick = { HoldShortcut = "Ctrl+Space" } # held until physical releaseOpenApplication 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.
GUI support for payload actions differs by editor; the mouse inspector's action
picker exposes the plain catalog, while these table forms remain available for
hand-authored bindings. HoldShortcut has additional caveats below.
Short/long presses and held shortcuts
A device-global button may use a lowercase short / long pair:
DpiToggle = { short = "ShowDesktop", long = "MissionControl" }
Back = { short = "MouseBack", long = { HoldShortcut = "Ctrl+Space" } }Releasing before 500 ms fires short. Reaching 500 ms fires long once;
the later release does not also fire short. If capture is interrupted, the
binding changes, or the agent shuts down before either result, neither action
fires. A source that reports only an instantaneous pulse falls back to short.
CustomShortcut presses and releases its chord immediately. HoldShortcut
keeps the chord down until the originating physical button is released, and
also releases it on capture cancellation, binding invalidation, or shutdown.
It is useful for push-to-talk; as a long action, its hold starts at the 500 ms
threshold and ends at physical release.
The GUI currently shows a pair's short action; changing it in the picker
replaces the whole pair with one action. Author pairs and HoldShortcut in TOML.
per_app_bindings and keyboard.bindings remain single-action maps.
Gesture bindings
Any supported 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."unit:6be9d300".bindings.GestureButton]
Up = "MissionControl"
Down = "ShowDesktop"
Left = "PrevTab"
Right = "NextTab"
Click = "AppExpose"The GUI offers new gestures for Back, Forward, DPI/ModeShift, the dedicated Gesture Button, and the MX Master 4 Haptic Sense Panel. Back/Forward use the OS hook. The three dedicated HID++ controls use raw-XY diversion; DPI/ModeShift is offered only when the device has measured raw-XY support. A middle-click gesture map written by v0.8.0 is preserved and remains editable until disabled, but the GUI cannot enable it again. Gesture direction maps are device-global; a per-app single-action override temporarily replaces the whole gesture button.
[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 = 7
selected_device = "unit:6be9d300"
[app_settings]
launch_at_login = true
language = "zh-CN"
thumbwheel_sensitivity = 14
smooth_scroll = false
vertical_scroll_sensitivity = 14
appearance = "system"
ui_scale = "normal"
device_view_mode = "grid"
# One mouse, regardless of whether it is on its receiver or cable.
[devices."unit:6be9d300"]
custom_name = "Office mouse"
dpi_presets = [800, 1600, 3200]
dpi = 1600
invert_scroll = true
scroll_resolution = "high"
[devices."unit:6be9d300".bindings]
Back = "MouseBack"
Forward = { short = "MouseForward", long = "MissionControl" }
MiddleClick = { HoldShortcut = "Ctrl+Space" }
HapticPanel = "ShowActionsRing"
ThumbwheelScrollUp = "HorizontalScrollLeft"
ThumbwheelScrollDown = "HorizontalScrollRight"
# The gesture button binds per direction; Click is the plain press.
[devices."unit:6be9d300".bindings.GestureButton]
Left = "PrevTab"
Right = "NextTab"
Click = "PlayPause"
# Undo requires VS Code to be the mouse target and focused.
[devices."unit:6be9d300".per_app_bindings."com.microsoft.VSCode"]
Back = "Undo"
[devices."unit:6be9d300".smartshift]
mode = "ratchet"
auto_disengage = 16
tunable_torque = 0
[devices."unit:6be9d300".action_ring]
enabled = true
haptics = true
[devices."unit:6be9d300".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" }
# App-managed routes for the same physical mouse.
[devices."unit:6be9d300".links."receiver:aabbccdd:slot:1"]
[devices."unit:6be9d300".links."direct:046d:c08d"]
# A Signature-series keyboard: F-row keys diverted over HID++, Fn-lock off.
[devices."receiver:aabbccdd:slot:2"]
fn_lock = false
host_switch_targets = ["unit:6be9d300"]
[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