Actions Ring Overlay Architecture in OpenLogi: How the IPC-Based UI Works

The Actions Ring overlay is a cursor-centered, eight-slot UI that runs as a separate binary (openlogi-overlay) and communicates with the OpenLogi agent via tarpc IPC, handling all GPUI rendering and input forwarding while the agent manages HID++ communication and action execution.

The Actions Ring overlay architecture enables rapid contextual action selection in the OpenLogi repository through a strict process separation model. This design delegates all visual presentation to a dedicated overlay process while keeping hardware interaction, session validation, and business logic within the main agent. Understanding this architecture reveals how modern Rust applications can achieve responsive, overlay-based UIs without blocking the main application thread.

Architecture Overview

The overlay operates as a pure IPC client that never directly accesses hardware. According to the OpenLogi source code, the agent maintains exclusive control over HID++ communication, haptic feedback, and action execution, while the overlay binary only renders the interface and forwards user inputs. This separation ensures that UI complexity never interferes with low-l latency hardware handling.

Process Separation and IPC Design

The agent (crates/openlogi-agent/src/overlay.rs) spawns the overlay via crate::agent::spawn_ipc. The overlay immediately registers itself as the sole holder of the Actions Ring role and opens an asynchronous mpsc channel structured as Ipc { invocations, commands }. This channel receives ActionRingInvocation payloads from the agent and sends OverlayCommand responses back, creating a bidirectional communication flow over tarpc.

The Eight-Slot Ring Structure

The UI presents exactly eight action slots arranged circularly around the cursor position. Each slot renders using assets from openlogi_ui::action_icons::ActionIcons defined in crates/openlogi-ui/src/action_icons.rs. The ring appears as a borderless, transparent GPUI window created with WindowKind::PopUp options, ensuring it floats above other applications without stealing focus.

Data Flow and Lifecycle

1. Agent Invocation

When the user triggers "Show Actions Ring", the agent constructs an ActionRingInvocation payload in crates/openlogi-agent/src/overlay.rs. This structure, defined in crates/openlogi-ipc/src/ipc.rs (lines 385-412), contains the eight slots to display and their associated metadata. The agent populates the slots and transmits the invocation over the IPC channel to the waiting overlay process.

2. Overlay Initialization

The overlay binary calls claim_the_role() during startup to establish a single-instance lock using a lock file. This guarantees that only one Actions Ring instance runs system-wide, preventing conflicting UI states. Once initialized, the overlay opens the mpsc channel and enters its main event loop in crates/openlogi-overlay/src/main.rs.

3. Window Placement and Rendering

Upon receiving the ActionRingInvocation, the overlay executes a precise placement sequence:

  • Locale activation: Calls openlogi_core::locale::activate to ensure proper localization
  • Cursor capture: Uses RingPlacement::capture from crates/openlogi-overlay/src/platform.rs to obtain the current cursor position
  • Edge clamping: Applies clamp_window_origin in ring.rs to adjust the window origin when near screen edges, ensuring all eight slots remain fully visible
  • Window creation: Opens the GPUI window using ring_window_options() configured with WindowKind::PopUp for a borderless, non-focusable appearance

4. User Interaction Handling

Inside the window, RingView (from crates/openlogi-overlay/src/ring.rs) instantiates interactive elements for each slot. Every slot monitors hover states and sends OverlayCommand::Hover to the agent when the user moves the cursor over it. On click, the slot dispatches OverlayCommand::Activate containing the specific slot index and session ID, then immediately calls window.remove_window() to close the UI.

5. Command Forwarding and Execution

The overlay forwards all OverlayCommand variants through the commands sender back to the agent. The agent validates the session ID against the current session state, executes the bound action through its internal handler, and manages any resulting state transitions. The wire format defined in crates/openlogi-ipc/src/ipc.rs includes version metadata to ensure compatibility between agent and overlay binaries.

6. Automatic Dismissal

A background task spawned in crates/openlogi-overlay/src/main.rs waits for DISPLAY_LIFETIME, a constant defined in openlogi_core::action_ring::DISPLAY_LIFETIME. Upon expiration, the task automatically sends OverlayCommand::Cancel to the agent and terminates the window, preventing orphaned UI elements from remaining on screen indefinitely.

7. Click-Away Detection

The ClickAwaySession struct in crates/openlogi-overlay/src/session.rs implements a global click monitor. When it detects a mouse click outside the ring's geometric bounds, it immediately dispatches a Cancel command to the agent. This ensures intuitive dismissal behavior when users click elsewhere on their desktop.

Key Implementation Details

Single-Instance Enforcement: The claim_the_role() function uses a filesystem-level lock to prevent multiple overlay instances. This mechanism is critical for maintaining consistent session state and preventing resource contention.

Edge-Aware Placement: The clamp_window_origin logic in ring.rs dynamically calculates window positioning based on screen dimensions. When the cursor is positioned near monitor edges, the algorithm adjusts the ring's origin point to ensure all eight slots remain fully visible without extending off-screen.

Session Validation: ClickAwaySession maintains a strict session ID check, verifying that only the window belonging to the current invocation can send commands. This prevents stale interactions from lingering overlay processes that may not have terminated cleanly.

IPC Protocol Versioning: The tarpc-based communication layer in crates/openlogi-ipc/src/ipc.rs defines both ActionRingInvocation and OverlayCommand with explicit version fields. This allows the agent to detect overlay version mismatches and handle backward compatibility during application updates.

Code Examples

Triggering the Actions Ring from the Agent

use openlogi_ipc::{ActionRingInvocation, ActionRingSlot};

fn show_actions_ring(runtime: &Runtime) {
    // Build the payload that the overlay will render
    let mut invocation = ActionRingInvocation::new();
    // Fill slots with actions (here we just use defaults)
    for slot in ActionRingSlot::ALL {
        invocation.slots.insert(slot, default_presentation(slot));
    }
    // Send it over the IPC channel
    runtime.ipc.send_invocation(invocation);
}

This code demonstrates constructing the invocation payload and transmitting it via the runtime's IPC channel. The ActionRingInvocation type definition resides in crates/openlogi-ipc/src/ipc.rs at lines 385-412.

Rendering Interactive Slots in RingView

div()
    .id(("ring-slot", slot.index()))
    .absolute()
    .left(px(left))
    .top(px(top))
    .size(px(SLOT_SIZE))
    .bg(if selected { color::accent_at_lightness(SELECTED_FILL_L) } else { SLOT_RESTING })
    .on_hover(cx.listener(move |this, hovered, _, cx| {
        if *hovered {
            this.hovered = Some(slot);
            let _ = this.commands.send(OverlayCommand::Hover { session_id, slot });
        } else {
            this.hovered = None;
        }
        cx.notify();
    }))
    .on_click(move |_, window, cx| {
        let _ = activate.send(OverlayCommand::Activate { session_id, slot });
        window.remove_window();
    })
    .into_any_element()

This excerpt from crates/openlogi-overlay/src/ring.rs shows how each slot handles hover states by sending OverlayCommand::Hover and triggers actions via OverlayCommand::Activate before removing the window. The visual feedback uses accent_at_lightness for selection states.

Automatic Dismissal Implementation

cx.spawn(async move |cx| {
    cx.background_executor().timer(DISPLAY_LIFETIME).await;
    let _ = timeout_commands.send(OverlayCommand::Cancel { session_id });
}).detach();

Found in crates/openlogi-overlay/src/main.rs at lines 101-113, this spawn operation creates a background task that automatically cancels the overlay after the configured display lifetime expires, ensuring the UI does not persist indefinitely.

Summary

  • The Actions Ring overlay architecture separates UI rendering from action execution through a dedicated openlogi-overlay binary that communicates via tarpc IPC with the main agent.
  • The agent in crates/openlogi-agent/src/overlay.rs controls invocation and execution, while the overlay handles GPUI rendering, cursor tracking, and input capture defined in crates/openlogi-overlay/src/ring.rs.
  • Single-instance enforcement via claim_the_role() and filesystem locks prevents conflicting UI instances.
  • Automatic dismissal uses DISPLAY_LIFETIME timeouts and ClickAwaySession monitoring to guarantee clean UI cleanup without user intervention.
  • All inter-process communication uses versioned protocols defined in crates/openlogi-ipc/src/ipc.rs to maintain compatibility across application updates.

Frequently Asked Questions

What is the Actions Ring overlay in OpenLogi?

The Actions Ring overlay is a cursor-centered, eight-slot circular interface that appears when triggered by the OpenLogi agent. It runs as a separate binary (openlogi-overlay) that renders the UI using GPUI and forwards user interactions back to the agent via IPC, keeping hardware access and action execution isolated in the main process to ensure system stability.

How does the overlay ensure only one instance runs at a time?

The overlay calls claim_the_role() during initialization in crates/openlogi-overlay/src/main.rs, which acquires a filesystem lock. This mechanism guarantees that only one overlay process can hold the Actions Ring role at any given time, preventing duplicate windows and conflicting session states that could confuse the agent's action handler.

What happens when the user clicks outside the Actions Ring?

A ClickAwaySession struct defined in crates/openlogi-overlay/src/session.rs monitors global mouse events. When it detects a click outside the ring's geometric bounds, it immediately sends an OverlayCommand::Cancel to the agent and terminates the window, ensuring the overlay dismisses intuitively when the user clicks elsewhere on screen.

How long does the Actions Ring remain visible?

The overlay automatically dismisses after the DISPLAY_LIFETIME constant defined in openlogi_core::action_ring, typically spanning several seconds. A background task spawned in main.rs handles this timeout by sending a Cancel command, while user interaction can also trigger earlier dismissal via the ClickAwaySession or explicit slot selection.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →