feat(linux): launch_at_login + input device access permission check#172
feat(linux): launch_at_login + input device access permission check#172cserby wants to merge 10 commits into
Conversation
Greptile SummaryAdds Linux support for two previously macOS-only features:
Confidence Score: 5/5Safe to merge; all changes are well-structured Linux additions with no regressions to existing macOS/Windows paths. The Linux systemd and permission-probe implementations are logically correct and covered by 11 new unit tests. The two observations (incomplete backslash escaping in escape_systemd_exec and per-render filesystem work in probe_logitech_hidraw) are quality improvements rather than current defects — backslash in an executable path is essentially impossible in practice, and the performance concern only manifests while the Settings window is open. crates/openlogi-agent/src/launch_agent.rs — the escape_systemd_exec helper Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A([reconcile enabled]) --> B{unit_path OK?}
B -- Err --> Z([return Err])
B -- Ok --> C{current_exe OK?}
C -- Err --> Z
C -- Ok --> D[render desired unit content\nor None if !enabled]
D --> E[read current unit file\nok = Some, missing = None]
E --> F{desired vs current}
F -- Same content --> G([debug: already current])
F -- Want to write --> H[create_dir_all parent]
H --> I[fs::write unit file]
I --> J[systemctl --user daemon-reload\nbest-effort]
J --> K[systemctl --user enable\nbest-effort]
K --> G
F -- Want to remove --> L[systemctl --user disable\nbest-effort]
L --> M[fs::remove_file]
M -- Err --> Z
M -- Ok --> N[systemctl --user daemon-reload\nbest-effort]
N --> G
F -- Both absent --> O([debug: already absent])
Reviews (10): Last reviewed commit: "fix(gui): gate platform-specific imports..." | Re-trigger Greptile |
- Remove misplaced sysfs comment above the open() call in probe_logitech_hidraw (the sysfs check is in is_logitech_hidraw) - Remove dead #[cfg(not(target_os = "macos"))] suppressor inside the already-macOS-gated permission_field function - Split Denied/Unknown description text: Unknown means uinput is accessible but no Logitech device is connected, so point the user at the device rather than the udev install guide Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- escape_systemd_exec: double $ → $$ to prevent systemd variable substitution in ExecStart paths containing a literal dollar sign - paths: add pub xdg_config_home() that returns the raw XDG config base without the openlogi sub-directory; refactor config_dir() to call it - unit_path: use xdg_config_home() directly instead of relying on the fragile .parent() traversal from config_dir() Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove misplaced sysfs comment above the open() call in probe_logitech_hidraw (the sysfs check is in is_logitech_hidraw) - Remove dead #[cfg(not(target_os = "macos"))] suppressor inside the already-macOS-gated permission_field function - Split Denied/Unknown description text: Unknown means uinput is accessible but no Logitech device is connected, so point the user at the device rather than the udev install guide Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- escape_systemd_exec: double $ → $$ to prevent systemd variable substitution in ExecStart paths containing a literal dollar sign - paths: add pub xdg_config_home() that returns the raw XDG config base without the openlogi sub-directory; refactor config_dir() to call it - unit_path: use xdg_config_home() directly instead of relying on the fragile .parent() traversal from config_dir() Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
c9bc3df to
811a693
Compare
- Remove misplaced sysfs comment above the open() call in probe_logitech_hidraw (the sysfs check is in is_logitech_hidraw) - Remove dead #[cfg(not(target_os = "macos"))] suppressor inside the already-macOS-gated permission_field function - Split Denied/Unknown description text: Unknown means uinput is accessible but no Logitech device is connected, so point the user at the device rather than the udev install guide Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- escape_systemd_exec: double $ → $$ to prevent systemd variable substitution in ExecStart paths containing a literal dollar sign - paths: add pub xdg_config_home() that returns the raw XDG config base without the openlogi sub-directory; refactor config_dir() to call it - unit_path: use xdg_config_home() directly instead of relying on the fragile .parent() traversal from config_dir() Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Reconcile the agent's autostart state on Linux by writing/removing a systemd user unit at $XDG_CONFIG_HOME/systemd/user/openlogi-agent.service. Mirrors the macOS LaunchAgent semantics exactly: - Restart=on-failure (crash respawns; clean exit(0) stays stopped) - WantedBy=graphical-session.target (takes effect at next login) - ExecStart escaped for systemd (% doubled, spaces quoted) - Idempotent write/remove — only touches disk when content changes - systemctl --user daemon-reload + enable/disable best-effort Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add a Linux-specific permission probe and settings page row: - probe_uinput(): checks write access to /dev/uinput - probe_logitech_hidraw(): iterates /dev/hidraw*, confirms Logitech vendor (HID_ID sysfs field parsed numerically — 0000046D matches 046d) - classify(uinput_ok, hidraw_ok): pure function → Granted/Denied/Unknown - Settings → Permissions shows one "Input device access" row on Linux with description only when access is not yet granted (no noise when everything works) - macOS permission rows and helpers gated #[cfg(target_os = "macos")] Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Accessibility footer in the main window hidden on Linux - "Open" button in permission rows gated to macOS only - Launch-at-login description no longer says "log in to macOS" Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove misplaced sysfs comment above the open() call in probe_logitech_hidraw (the sysfs check is in is_logitech_hidraw) - Remove dead #[cfg(not(target_os = "macos"))] suppressor inside the already-macOS-gated permission_field function - Split Denied/Unknown description text: Unknown means uinput is accessible but no Logitech device is connected, so point the user at the device rather than the udev install guide Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- escape_systemd_exec: double $ → $$ to prevent systemd variable substitution in ExecStart paths containing a literal dollar sign - paths: add pub xdg_config_home() that returns the raw XDG config base without the openlogi sub-directory; refactor config_dir() to call it - unit_path: use xdg_config_home() directly instead of relying on the fragile .parent() traversal from config_dir() Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Added missing translations for the Linux permission row label introduced in this PR. Follows the same pattern as 'Input Monitoring'. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… scope - Add 'Unifying receiver' and 'Input device access' keys to en.yml (all locale files must match en.yml for the i18n test) - Add 'Ricevitore Unifying' to it.yml for key parity - Move InteractiveElement import outside #[cfg(target_os = "macos")] so on_action() calls for CloseWindow/Minimize/Zoom work on Linux - Fix rustfmt line-wrap in launch_agent::unit_path Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add StatefulInteractiveElement as _ to unconditional gpui import so on_click() resolves on macOS (fixes E0599) - Gate status_badge with any(macos, linux) since both platforms call it (fixes Windows dead_code warning) - Remove unreachable input_device_access stub for non-mac/non-linux targets; nothing calls it there (fixes Windows dead_code warning) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Upstream added Option<bool> accessibility_granted with a 3-state match. Merge that with our Linux cfg structure: - Take upstream's Some(true)/Some(false)/None match for accessibility - Restore #[cfg(target_os = "macos")] on permission_field, permission_item, Permission import, and StatefulInteractiveElement import — all are macOS-only; gating them unconditionally caused dead_code / unused-import errors on Linux Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
bee3c3f to
aa95e08
Compare
On Windows, PermissionStatus, classify, rgb, and the permissions module are all dead — they only exist for macOS/Linux permission dialogs. Gate each with the appropriate cfg so Windows clippy -D warnings passes. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Summary
launch_at_loginon Linux — writes/removes a systemd user unit at$XDG_CONFIG_HOME/systemd/user/openlogi-agent.service. Mirrors the macOS LaunchAgent behaviour exactly:Restart=on-failure(crash respawns; cleanexit(0)stays stopped),WantedBy=graphical-session.target(takes effect at next login). Unit content is compared before writing (idempotent).systemctl --user enable/disableis best-effort — failures are logged, not propagated.Settings → Permissionsnow shows a Linux-specific row ("Input device access") backed by probes of/dev/uinput(write) and/dev/hidraw*(read/write, Logitech vendor verified numerically from sysfsHID_ID). ReturnsGranted/Denied/Unknown(unknown = uinput accessible but no Logitech device connected yet). Description is shown only when access is not yet granted.Test plan
cargo test -p openlogi-agent -p openlogi-guipasses on Linux (7 new unit tests inlaunch_agent.rs, 4 inpermissions.rs)cargo clippy --workspace --all-targets -- -D warningscleancargo fmt --checkclean~/.config/systemd/user/openlogi-agent.serviceappears;systemctl --user is-enabled openlogi-agentreportsenabled; toggle off → file removed,disabled🤖 Generated with Claude Code