OpenLogi

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

PlatformPath
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 (currently 7). 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 unified bindings map.
  • 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:

FormMeaning
unit:6be9d300Non-zero four-byte HID++ unit id (eight lower-case hex digits)
serial:abc123Non-empty, case-folded device serial
receiver:aabbccdd:slot:1Receiver route used until the GUI can associate that slot with the device's own identity
raw:046d:c900:ff43:0202:serial:your-serialStandalone 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 = true

Capabilities 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.

KeyDefaultMeaning
launch_at_logintrueKeep the background agent available after login; platform registration details differ.
check_for_updatesfalseOpt-in. One HEAD request to the GitHub latest-release per launch; logs whether a newer version exists; never downloads on its own.
auto_install_updatesfalseOpt-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_seenfalseSet once the first-run "check for updates?" prompt has been answered, so it is never shown again.
show_in_menu_bartruemacOS menu-bar status item and Windows tray icon. Ignored on Linux.
capture_mouse_eventstrueWhether 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_targetpointerApplication used for mouse button profiles: pointer or focused. Omitted values also use pointer.
smooth_scrollfalseAdd finite smooth animation to eligible physical mouse-wheel input; native trackpad/pixel input stays untouched.
vertical_scroll_sensitivity14Traditional vertical wheel distance on a 1–100 scale; 14 is 1×. Does not scale native trackpad input.
auto_download_assetstrueFetch device renders when a device appears. false makes no asset network requests; Refresh assets in Settings still fetches on demand.
asset_sourceautomaticAsset 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_sensitivity14Thumb-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).
appearancesystemsystem, light, or dark.
ui_scalenormalsmall (90%), normal (100%), large (110%), or extra_large (125%).
device_view_modegridHome device layout: grid, list, or carousel.
app_iconopenlogimacOS 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.

KeyTypeMeaning
enabledboolfalse leaves the device completely native: no HID++ capture session, no settings re-applied on reconnect. Default true.
custom_namestringUser-assigned alias from the device card's Rename action; blank in the GUI restores the model name.
linkstableApp-managed routes, measured per-route capabilities, and optional deliberate per-route overrides.
bindingstableMaps a logical button to one action, a { short, long } pair, or a per-direction gesture table (see below).
per_app_bindingstable of tablesSparse overlays keyed by app selector. Mouse profiles use mouse_profile_target; keyboard profiles use focus. Unlisted buttons fall through to bindings.
action_ringtableActions Ring enable state, haptics, default layout, and per-app layouts.
dpi_presetsarray of intsOrdered DPI values cycled by CycleDpiPresets and indexed by SetDpiPreset.
dpiintThe committed sensor DPI. Lives in device RAM, so the agent re-applies it on reconnect.
smartshifttablemode (ratchet / free), auto_disengage, tunable_torque; re-applied on reconnect.
invert_scrollboolReverse this device's native wheel direction without touching the system trackpad direction.
scroll_resolutionstringlow or high. Persisted HID++ 0x2121 wheel resolution. Absent leaves the device's own setting alone.
thumbwheel_sensitivityintPer-device override of the app-wide value.
lightingtableStatic RGB for HID++ keyboards; see below.
lighttableStandalone light (Litra) power, brightness, temperature; see Litra lights.
camera_controlstableWebcam UVC controls, keyed by control name.
camera_profilestable of tablesUser-saved camera profiles (name → control snapshot).
camera_profilestringThe camera profile last applied from the GUI.
host_switch_targetsarray of keysDevice keys of mice that follow this keyboard's Easy-Switch channel.
fn_lockboolKeyboards only. true makes the F-row send F1–F12 without holding Fn; absent leaves the keyboard's own state alone. Re-applied on reconnect.
identitytableWritten by the app: last-known name, kind, and capabilities, so a sleeping device still renders its panels. Not meant to be hand-authored.
disabled_gesturestableWritten by the app: the direction map of a button whose gesture mode is currently off, so re-enabling restores it.

lighting

KeyDefaultMeaning
enabledtrueWhether the static color is applied.
color"ffffff"Static color as six hex digits RRGGBB (no leading #).
brightness1000–100; out-of-range values are rejected on load.

light

KeyDefaultMeaning
enabledtrueWhether the light should be on.
auto_camerafalseTurn the light on while any camera is in use, off when camera use stops (macOS).
brightness_percent1000–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 release

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. 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 = 4600

Source: CONFIGURATION.md

On this page