Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 23 additions & 9 deletions crates/openlogi-agent/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,13 +125,20 @@ async fn run(config: Config) {
// LaunchAgent, before `config` moves into the orchestrator.
launch_agent::reconcile(config.app_settings.launch_at_login);

// Read the hook kill-switch before `config` moves into the orchestrator.
// Startup-only on purpose (like `show_in_menu_bar`): flipping it requires
// an agent restart, which the config docs state.
let capture_mouse_events = config.app_settings.capture_mouse_events;

// The agent owns the CGEventTap, so it must be the binary the user authorizes
// for Accessibility. Fire the prompt at startup when we're not yet trusted so
// openlogi-agent appears (named correctly) in System Settings even on a
// launchd start with no GUI. macOS only shows the dialog when we're not
// already in the list, so this doesn't nag on every login. The GUI's grant
// button drives the same prompt over IPC (`request_accessibility_prompt`).
if !Hook::has_accessibility() {
// With the hook disabled the agent needs no Accessibility at all, so the
// opt-out also silences the permission prompt.
if capture_mouse_events && !Hook::has_accessibility() {
Hook::prompt_accessibility();
}

Expand Down Expand Up @@ -220,14 +227,21 @@ async fn run(config: Config) {
hook_installed.store(false, Ordering::Relaxed);
}
if granted && hook.is_none() {
info!("accessibility granted — installing OS mouse hook");
hook = hook_runtime::start(
shared.hook_maps.clone(),
shared.dpi_cycle.clone(),
shared.capture_channel.clone(),
Arc::clone(&event_monitor),
);
hook_installed.store(hook.is_some(), Ordering::Relaxed);
if capture_mouse_events {
info!("accessibility granted — installing OS mouse hook");
hook = hook_runtime::start(
shared.hook_maps.clone(),
shared.dpi_cycle.clone(),
shared.capture_channel.clone(),
Arc::clone(&event_monitor),
);
hook_installed.store(hook.is_some(), Ordering::Relaxed);
} else {
info!(
"OS mouse hook disabled by app_settings.capture_mouse_events — \
button remapping is off"
);
}
}
}
else => break,
Expand Down
22 changes: 20 additions & 2 deletions crates/openlogi-core/src/config/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,21 @@ pub struct AppSettings {
/// while a window is open). Ignored on Linux.
#[serde(default = "default_true")]
pub show_in_menu_bar: bool,
/// Whether the agent installs the OS-level mouse hook (CGEventTap /
/// exclusive `evdev` grab / `WH_MOUSE_LL`) that intercepts mouse events
/// for button remapping. `true` (default) keeps remapping active;
/// `false` is an escape hatch that leaves every input device untouched
/// (on Linux: no exclusive grabs at all; on macOS the agent also skips
/// the startup Accessibility prompt). HID++-side features — DPI,
/// SmartShift, the gesture button, the thumb wheel — are unaffected.
/// Takes effect on agent restart.
#[serde(default = "default_true")]
pub capture_mouse_events: bool,
/// Whether the GUI automatically downloads device images from
/// `assets.openlogi.org` when a device appears. `true` (default) keeps
/// the current behavior; `false` makes no asset network requests at all
/// (the app falls back to bundled art and the synthetic silhouette). A
/// manual "Refresh assets" in Settings still fetches on demand regardless.
/// Whether the GUI automatically downloads device images from the selected
/// source when a device appears. `true` (default) keeps the current behavior;
/// `false` makes no asset network requests at all (the app falls back to
Expand Down Expand Up @@ -160,6 +175,7 @@ impl Default for AppSettings {
auto_install_updates: false,
update_prompt_seen: false,
show_in_menu_bar: true,
capture_mouse_events: true,
auto_download_assets: true,
asset_source: AssetSourcePreference::Automatic,
language: None,
Expand All @@ -172,8 +188,10 @@ impl Default for AppSettings {
}
}

/// serde default for [`AppSettings::show_in_menu_bar`]: `true`, so the menu-bar
/// icon is on out of the box and configs predating the field keep that behavior.
/// serde default for the on-by-default [`AppSettings`] toggles
/// ([`AppSettings::show_in_menu_bar`], [`AppSettings::capture_mouse_events`],
/// [`AppSettings::auto_download_assets`]), so configs predating a field keep the
/// out-of-the-box behavior.
fn default_true() -> bool {
true
}
Expand Down
Loading