# How the Actions Ring Overlay Renders at the Cursor Position Across Processes in OpenLogi

> Learn how OpenLogi renders the Actions Ring at the cursor position across processes using a dedicated overlay process and tarpc IPC for cross-process isolation and single-instance control.

- Repository: [Xuan Zhang/OpenLogi](https://github.com/AprilNEA/OpenLogi)
- Tags: internals
- Published: 2026-08-30

---

**OpenLogi renders the Actions Ring in a dedicated `openlogi-overlay` process that positions a frameless GPUI window at the cursor coordinates using `WindowBounds::Cursor`, communicating with the main agent via tarpc IPC to ensure cross-process isolation and single-instance control.**

OpenLogi uses a distributed multi-process architecture to separate input handling from user interface rendering. When a user invokes the Actions Ring, the system must calculate the exact cursor position and render an overlay without blocking the main application or agent processes. This article explains how the Actions Ring overlay renders at the cursor position across different processes, detailing the **tarpc IPC channel**, **GPUI windowing**, and role-based single-instance management implemented in the `AprilNEA/OpenLogi` repository.

## Multi-Process Architecture for Overlay Isolation

The Actions Ring operates across three distinct processes to maintain security and responsiveness:

- **`openlogi-agent`** – Runs in the background, manages HID++ device input, and triggers the overlay
- **`openlogi-overlay`** – A **pure GPUI client** that handles all visual rendering in isolation
- **`openlogi-desktop`** – The main graphical user interface

Because the overlay runs in **its own process**, it can draw a frameless, always-on-top window without interfering with the main GUI or the background agent. All state is passed through the **tarpc IPC** channel defined in [`crates/openlogi-ipc/src/ipc.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-ipc/src/ipc.rs), meaning the overlay never needs direct access to HID++ devices or the system’s input hook.

## IPC Communication and Invocation Flow

When a user triggers the Actions Ring, the agent initiates a six-step rendering pipeline:

1. **Agent Dispatch** – In [`crates/openlogi-agent/src/overlay.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-agent/src/overlay.rs), the agent calls `claim_the_role()` to ensure only one overlay instance holds the "Actions Ring" role, then sends an `ActionRingInvocation` via `OverlayCommand::Show` through the IPC channel.

2. **IPC Reception** – The `openlogi-overlay` process, via [`crates/openlogi-overlay/src/agent.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-overlay/src/agent.rs), runs `spawn_ipc()` to listen for RPCs. It receives the `ActionRingInvocation` containing the list of slots, locale, and a unique *session_id*.

3. **Window Creation** – [`crates/openlogi-overlay/src/main.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-overlay/src/main.rs) creates a GPUI `AppContext`, claims the overlay role via `claim_the_role()`, and opens a new window for every received invocation using `cx.open_window(options, …)`.

4. **Cursor Positioning** – [`crates/openlogi-overlay/src/platform.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-overlay/src/platform.rs) builds `WindowOptions` with `WindowBounds::Cursor` to center the window on the current mouse position, clamping the geometry to the active display to prevent edge cutoff.

5. **UI Construction** – [`crates/openlogi-overlay/src/ring.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-overlay/src/ring.rs) implements `RingView::new()`, which receives the invocation data and constructs the ring UI using `slot.placement()` to arrange slots radially around the cursor.

6. **Event Feedback** – User interactions (hover, activation, cancellation) are sent back to the agent via the same IPC channel using `OverlayCommand::Hover`, `OverlayCommand::Activate`, and `OverlayCommand::Cancel`, allowing the agent to execute the bound shortcuts.

## Cursor-Centered Window Positioning

The precise cursor positioning logic resides in [`crates/openlogi-overlay/src/platform.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-overlay/src/platform.rs). The `ring_window_options()` function retrieves the current mouse coordinates and configures the GPUI window to appear centered on that point:

```rust
use gpui::{WindowOptions, WindowBounds};

fn ring_window_options(cx: &AppContext) -> WindowOptions {
    let cursor = cx.mouse_position();
    WindowOptions {
        bounds: WindowBounds::Cursor(cursor),
        kind: WindowKind::Frameless,
        // … other visual options …
        ..Default::default()
    }
}

```

This approach guarantees that the **Actions Ring appears exactly where the user is looking**, while the clamping logic ensures the window remains fully visible even if the cursor is near the screen edge.

## Rendering the Ring UI with GPUI

Once the window is positioned, `RingView` in [`crates/openlogi-overlay/src/ring.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-overlay/src/ring.rs) handles the visual layout. The view reconstructs the ring statelessly from the `ActionRingInvocation` each time, calculating radial slot positions:

```rust
let (left, top) = slot.placement(WINDOW_SIZE, RADIUS, SLOT_SIZE);
div()
    .absolute()
    .left(px(left))
    .top(px(top))
    .size(px(SLOT_SIZE))
    .child(svg().path(icon_path).size(px(22.0)))

```

This **stateless rendering** ensures that each invocation starts fresh without persistent UI state between sessions, reducing memory overhead and preventing stale data issues.

## Single-Instance Control and Role Management

To prevent duplicate overlays, OpenLogi implements a role-claiming mechanism. The `claim_the_role()` function—implemented in [`crates/openlogi-overlay/src/session.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-overlay/src/session.rs)—guarantees that only one overlay process can own the "Actions Ring" role at a time. Both the agent ([`crates/openlogi-agent/src/overlay.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-agent/src/overlay.rs)) and the overlay process invoke this check before displaying the UI, ensuring system-wide single-instance semantics without requiring a global mutex.

## Summary

- **Process Isolation** – The Actions Ring runs in a dedicated `openlogi-overlay` binary separate from the agent and desktop GUI, enabling frameless, interference-free rendering.
- **IPC-Driven Rendering** – All state passes through **tarpc IPC** via `ActionRingInvocation` and `OverlayCommand` enums defined in [`crates/openlogi-ipc/src/ipc.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-ipc/src/ipc.rs).
- **Cursor-Centered Positioning** – `ring_window_options()` uses `WindowBounds::Cursor` and clamps the window to the current display edges in [`crates/openlogi-overlay/src/platform.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-overlay/src/platform.rs).
- **Stateless Architecture** – `RingView` reconstructs the UI from scratch for each invocation, ensuring no persistent state between interactions.
- **Single-Instance Guarantee** – `claim_the_role()` in the session management code prevents multiple simultaneous overlay instances.

## Frequently Asked Questions

### Why does OpenLogi use a separate process for the Actions Ring overlay?

Running the overlay in its own process (`openlogi-overlay`) isolates the GPUI rendering engine from the background agent's HID++ device handling and the main desktop GUI. This separation prevents visual glitches, allows the overlay to create frameless, always-on-top windows without affecting other application windows, and ensures that crashes in the rendering layer do not destabilize the input monitoring system.

### How does the overlay determine the exact cursor position for rendering?

The overlay queries the GPUI `AppContext` for the current mouse coordinates via `cx.mouse_position()` in [`crates/openlogi-overlay/src/platform.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-overlay/src/platform.rs). It then passes these coordinates to `WindowBounds::Cursor` when constructing the `WindowOptions`, which instructs GPUI to center the new window at that exact screen location, clamped to the active display boundaries.

### What prevents multiple Actions Ring overlays from appearing simultaneously?

Both the agent and the overlay process call `claim_the_role()` before displaying the interface. This function, implemented in [`crates/openlogi-overlay/src/session.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-overlay/src/session.rs), uses system-level role arbitration to ensure only one process can hold the "Actions Ring" role at any time. If an existing overlay is already active, subsequent attempts to claim the role fail, preventing duplicate windows.

### How do the agent and overlay communicate during an interaction?

The components communicate exclusively over a **tarpc IPC channel** defined in [`crates/openlogi-ipc/src/ipc.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-ipc/src/ipc.rs). The agent sends `ActionRingInvocation` data via `OverlayCommand::Show`, and the overlay returns user interactions (hover, activate, cancel) through the same channel. This RPC-based approach eliminates shared memory concerns and allows the processes to run with different privilege levels.