# How the Omarchy Idle, Lock, and Screensaver Service Works

> Discover how the Omarchy idle service monitors user input and triggers screensaver or session locks. Learn about its configurable timeouts and IPC for status bar integration.

- Repository: [Omacom/omarchy](https://github.com/omacom/omarchy)
- Tags: internals
- Published: 2026-09-10

---

**The Omarchy idle service monitors user input through Quickshell’s IdleMonitor and automatically triggers screensaver windows and session locks after configurable timeouts, while exposing runtime state via shell IPC for integration with status bars and scripts.**

Omarchy implements its idle, lock, and screensaver functionality as a first-party service plugin (`omarchy.idle`) within its Quickshell-based desktop environment. The service coordinates with the lock plugin to secure the session after periods of inactivity, reading timeout values from user configuration and providing command-line controls to override behavior on demand.

## Core Architecture and Configuration

The idle service is modular, separating concerns between monitoring, configuration parsing, and window management across distinct source files.

### Service.qml and IdleModel.js

The primary implementation resides in `shell/plugins/services/idle/Service.qml`, which instantiates an **IdleMonitor** and manages the idle cycle timers. Helper utilities for parsing timeout strings and tracking screensaver windows live in [`shell/plugins/services/idle/IdleModel.js`](https://github.com/omacom/omarchy/blob/main/shell/plugins/services/idle/IdleModel.js).

The service reads idle thresholds from `~/.config/omarchy/idle.json` or the `idle` block within the global Omarchy configuration. Two critical values control behavior:

- **`screensaver`** – Seconds of inactivity before the screensaver window appears.
- **`lock`** – Seconds of inactivity before the system locks (only triggers if the screensaver is already active).

```json
{
  "idle": {
    "screensaver": 120,
    "lock": 300
  }
}

```

The `IdleModel.secondsFromConfig()` function converts these configuration values into seconds for internal timer use.

## The Idle Detection Cycle

When the `IdleMonitor` detects no input events for the configured duration, it emits the `isIdle` signal. The service checks the `idleEnabled` property (which is `false` when **stay-awake** mode is active) before invoking `startIdleCycle()`.

### Screensaver Activation

Upon entering the idle cycle, the service starts a screensaver timer. If it expires, the service calls `idle.screensaverWindowsAfter()`, which creates a new window via `shellApi.createWindow()` and appends it to the `screensaverWindows` list. The `screensaverWindowCount` property increments, allowing UI components like the status bar to reflect active screensaver states.

### Automatic Locking

A separate lock timer runs concurrently. When this timer fires—**only if the screensaver is already showing**—the service invokes `root.lockSystem("lock-timeout")`. This delegates to `shell/plugins/lock/Service.qml`, which blanks the screen and prompts for authentication via its `idleBlankTimer`.

### Wake-Up and Reset

Any user activity (mouse or keyboard) resets the `IdleMonitor`, which triggers `cancelIdleCycle()`. This function destroys all screensaver windows by clearing `root.screensaverWindows = {}`, cancels both timers, and resets flags like `screensaverStartedThisCycle`.

## State Management and IPC Interface

The service exposes its runtime state through the shell IPC interface, accessible via the `shell_ipc` command:

```bash
shell_ipc idle status | jq .

```

This returns a JSON object containing `idleEnabled`, `stayAwake`, `screensaverStartedThisCycle`, and `screensaverWindowCount`. The bar indicator at `shell/plugins/bar/indicators/StayAwake.qml` consumes these properties to display whether idle detection is currently active.

## CLI Control and User Interaction

Users can override idle behavior without modifying configuration files using the `omarchy-toggle-idle` utility.

Prevent the screensaver and lock from triggering:

```bash
omarchy-toggle-idle stay-awake

```

Re-enable normal idle handling:

```bash
omarchy-toggle-idle allow-idle

```

These commands toggle the `stayAwake` flag, which sets `idleEnabled` to `false` within `Service.qml`, effectively pausing the idle monitor until explicitly re-enabled.

## Summary

- The idle service is implemented in `shell/plugins/services/idle/Service.qml` with helpers in [`IdleModel.js`](https://github.com/omacom/omarchy/blob/main/IdleModel.js).
- Configuration resides in `~/.config/omarchy/idle.json`, defining `screensaver` and `lock` timeouts.
- The **IdleMonitor** triggers `startIdleCycle()`, which schedules screensaver windows first, then locks the session via `shell/plugins/lock/Service.qml`.
- The lock only activates if the screensaver is already visible and the lock timeout expires.
- Use `omarchy-toggle-idle` to temporarily disable idle detection, and query state with `shell_ipc idle status`.

## Frequently Asked Questions

### How do I check if the idle service is currently enabled?

Run `shell_ipc idle status` from a terminal. The JSON output includes the `idleEnabled` boolean (which is `false` when stay-awake mode is active) and the current window count, allowing you to verify whether the service is monitoring inactivity or suspended.

### Why does the lock only trigger after the screensaver appears?

According to the implementation in `Service.qml`, the lock timer only fires if `screensaverStartedThisCycle` is true. This ensures the screensaver acts as a visual warning before the session locks, preventing immediate lockouts during brief periods of inactivity.

### Can I create custom screensaver windows?

Yes. Create a QML window that registers itself via `idle.screensaverWindowsAfter()`. The idle service automatically manages the lifecycle of these windows, destroying them when `cancelIdleCycle()` runs due to user activity or manual wake commands.

### Where does the lock logic actually execute?

While `Service.qml` in the idle plugin initiates the lock via `lockSystem("lock-timeout")`, the actual screen blanking and authentication prompt are handled by `shell/plugins/lock/Service.qml`, which receives the call through the shell’s plugin communication layer.