How OpenLogi Manages Actions Ring Overlay Geometry with IPC-Driven State
OpenLogi synchronizes Actions Ring overlay geometry by resolving display coordinates locally in the overlay process via the CursorDisplay struct while transmitting full visual state snapshots through the observe_action_ring IPC RPC, ensuring atomic updates across processes without incremental synchronization logic.
The Actions Ring in OpenLogi provides a contextual command palette that appears at the cursor location across multiple displays. Because the overlay runs as a separate lightweight process from the main GUI, the system must coordinate both spatial geometry and visual state across process boundaries. This article examines how the crates/openlogi-overlay and crates/openlogi-ipc crates collaborate to manage geometry locally while synchronizing ring content via a tarpc-based IPC contract.
Local Geometry Resolution with CursorDisplay
The overlay process cannot rely on the GUI to calculate screen coordinates because it must respond instantly to cursor movements and display configuration changes. Instead, it resolves geometry independently using the CursorDisplay struct defined in crates/openlogi-overlay/src/platform.rs (lines 89-99).
This structure encapsulates three critical values in the global coordinate space: the native display identifier, the display's top-left origin point, and its size in points. By using the same global coordinate space that the input hook's cursor_position() returns, the overlay ensures pixel-perfect alignment regardless of which screen contains the cursor.
Platform-Specific Display Detection on macOS
On macOS, the overlay invokes display_containing(x, y) to determine which display bounds contain the cursor coordinates. This function queries CoreGraphics directly, calling CGGetActiveDisplayList to enumerate active displays and CGDisplayBounds to test containment (lines 11-35 of platform.rs).
On other platforms, this function returns a no-op because GPUI already reports global origins natively. This platform abstraction allows the overlay geometry logic to remain agnostic of the underlying windowing system while maintaining precision on macOS through native framework calls.
// Geometry resolution in the overlay process
use openlogi_overlay::platform::{display_containing, CursorDisplay};
use openlogi_hook::cursor_position;
let (x, y) = cursor_position();
let display: CursorDisplay = display_containing(x, y)
.expect("cursor must be on a known display");
// Use display.origin and display.size to position the GPUI window
IPC-Driven State Synchronization
While geometry remains local to the overlay, the visual content of the Actions Ring originates from the agent process and travels across the IPC boundary. The contract lives in crates/openlogi-ipc/src/ipc.rs and centers on the observe_action_ring RPC, introduced in protocol version 21.
The RingObservation Protocol
When the GUI requests the current ring state, the RPC returns a RingObservation struct (lines 52-57) containing a generation counter and a snapshot of the ring's presentation information. This snapshot includes an ActionRingInvocation (lines 85-93) that describes the entire ring session, including the session_id, per-slot presentation data, and optional UI locale strings.
ActionRingPresentation Structure
Each slot in the ring maps to an ActionRingPresentation instance (lines 71-82) within the invocation. This struct carries the slot label, a boolean flag distinguishing literal text from localized keys, and the resolved ActionRingIcon. By packaging the complete visual specification into a single serializable structure, the IPC contract eliminates the need for the overlay to request additional metadata or resolve resources independently.
// GUI process requesting ring state from agent via IPC
use openlogi_ipc::ipc::Client;
let obs = client.observe_action_ring(current_generation).await?; // RingObservation
let invocation = obs.ring.invocation; // ActionRingInvocation
// Activate overlay for specific slot
send_overlay_command(OverlayCommand::Hover {
session_id: invocation.session_id,
slot: ActionRingSlot::Top,
});
Synchronization Flow and State Consistency
The coordination between processes follows a strict command pattern. When a user triggers Show Actions Ring, the GUI dispatches an OverlayCommand::Hover or OverlayCommand::Activate over the IPC channel to the overlay process. The overlay receives this command, extracts the session_id, and renders the ring using the most recent ActionRingInvocation obtained from its cached RingObservation.
Handling Geometry Changes
Because the overlay resolves its own geometry via display_containing, it responds immediately to display hot-plugging or cursor movement between monitors without IPC round-trips. When the cursor moves to a new display, the overlay queries the native platform APIs locally and adjusts the ring's position instantly. This design keeps the overlay lightweight and responsive, avoiding the latency that would occur if the GUI process had to mediate every display boundary transition.
Atomic State Updates
The observe_action_ring RPC transmits full snapshots rather than incremental deltas. When the overlay polls for updates or receives a push notification, it replaces its entire ActionRingInvocation reference with the new observation. This atomic update pattern ensures that the overlay never renders inconsistent intermediate states, such as displaying icons from one configuration with labels from another. The generation counter within RingObservation allows the overlay to detect and skip stale observations if messages arrive out of order.
// Overlay process combining IPC state with local geometry
use openlogi_overlay::platform::display_containing;
use openlogi_hook::cursor_position;
// Received from GUI via IPC
let session_id = command.session_id;
let invocation = latest_ring_observation.invocation;
// Local geometry resolution
let (x, y) = cursor_position();
let display = display_containing(x, y).unwrap();
// Render using both IPC-driven content and local geometry
render_ring(&display, &invocation);
Lifecycle Coordination via Session IDs
The ring's lifecycle relies on a shared session ID that both the GUI and overlay processes reference in every command. The GUI embeds this ID in OverlayCommand::Hover, OverlayCommand::Activate, and OverlayCommand::Cancel messages. When the user dismisses the ring, the GUI sends a Cancel command with the same session ID, guaranteeing that the overlay ignores delayed or out-of-order commands from previous sessions.
This session-based anchoring prevents race conditions during rapid show-hide sequences. The overlay stores the active session ID in its local state and validates incoming commands against it, ensuring that only the current ring session can modify the visual output.
Summary
- Local Geometry Resolution: The overlay calculates its own position using the
CursorDisplaystruct and platform-specific functions likedisplay_containing, avoiding IPC latency for cursor tracking. - IPC Contract: The
observe_action_ringRPC (protocol version 21) transmitsRingObservationsnapshots containing completeActionRingInvocationdata, including per-slotActionRingPresentationdetails. - Atomic Updates: Full state snapshots replace local overlay state atomically, preventing visual corruption from partial updates.
- Session Safety: Shared
session_idvalues inOverlayCommandmessages ensure that only the current ring session controls the overlay, preventing stale command execution.
Frequently Asked Questions
What is the RingObservation struct in OpenLogi?
The RingObservation struct is the primary container for Actions Ring state transmitted over IPC in crates/openlogi-ipc/src/ipc.rs (lines 52-57). It contains a generation counter for versioning and a ring snapshot that includes the ActionRingInvocation. This struct enables the overlay process to receive a complete, consistent picture of the ring's visual state without requesting individual elements separately.
How does the overlay determine which display contains the cursor?
The overlay calls display_containing(x, y) from crates/openlogi-overlay/src/platform.rs (lines 11-35). On macOS, this function queries CoreGraphics using CGGetActiveDisplayList and CGDisplayBounds to find the display whose rectangle contains the cursor coordinates. On other platforms, the function returns a no-op because GPUI provides global coordinates natively. The function returns a CursorDisplay struct containing the display's native ID, origin, and size.
Why does OpenLogi use full state snapshots instead of incremental updates?
Full state snapshots transmitted through observe_action_ring eliminate partial update states and race conditions. Because the overlay replaces its entire ActionRingInvocation reference when receiving a new RingObservation, it never renders a mix of old and new configuration data. This atomic approach simplifies the synchronization logic and ensures visual consistency, particularly when the user rapidly changes contexts or displays.
What prevents race conditions between the GUI and overlay processes?
Session IDs anchor every OverlayCommand (Hover, Activate, Cancel) to a specific ring instance. The GUI and overlay both reference the same session_id from the ActionRingInvocation (lines 85-93). When processing commands, the overlay validates that the incoming session ID matches its current session, discarding stale commands from previous ring invocations. This mechanism prevents visual glitches when users trigger the ring multiple times in quick succession.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →