# Short-Press, Long-Press, and Hold-Until-Release Actions in OpenLogi Bindings: A Complete Guide

> Learn the distinctions between short-press, long-press, and hold-until-release actions in OpenLogi bindings. This guide explains time-based event measurement for distinct button behaviors.

- Repository: [Xuan Zhang/OpenLogi](https://github.com/AprilNEA/OpenLogi)
- Tags: how-to-guide
- Published: 2026-09-03

---

**OpenLogi distinguishes between short-press (under 500ms), long-press (over 500ms), and hold-until-release actions through time-based event measurement in the agent core, allowing a single physical button to trigger three distinct behaviors.**

OpenLogi, an open-source Logi Options+ alternative for Linux and macOS, provides sophisticated button remapping through its TOML configuration system. The project implements three distinct interaction modes—short-press, long-press, and hold-until-release—that enable complex macros and multi-function bindings on Logitech peripherals. Understanding these distinctions is essential for crafting efficient input workflows in [`docs/config.example.toml`](https://github.com/AprilNEA/OpenLogi/blob/main/docs/config.example.toml) and the underlying Rust agent core.

## The Three Action Types

OpenLogi bindings support three temporal interaction modes defined in [`crates/openlogi-agent-core/src/bindings.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-agent-core/src/bindings.rs). Each mode corresponds to a specific field in the `Binding` struct: `short`, `long`, and `hold`.

### Short-Press Actions

A **short-press** triggers when a button is pressed and released within the default 500ms threshold. According to the configuration schema in [`docs/config.example.toml`](https://github.com/AprilNEA/OpenLogi/blob/main/docs/config.example.toml), this maps to quick toggle operations.

When the agent core processes a `ButtonEvent::Up` in [`crates/openlogi-agent-core/src/button.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-agent-core/src/button.rs), it calculates the elapsed duration since the matching `ButtonEvent::Down`. If the duration falls below the threshold and a `short` action is defined, the system dispatches that action exactly once upon release.

```toml

# Example: Quick desktop access

DpiToggle = { short = "ShowDesktop", long = "MissionControl" }

```

### Long-Press Actions

A **long-press** activates when a button remains depressed for **500ms or longer**. Unlike the short-press, this action fires immediately upon crossing the temporal threshold, regardless of when the user eventually releases the button.

The runtime logic in [`crates/openlogi-agent-core/src/button.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-agent-core/src/button.rs) compares the press duration against `THRESHOLD` (default 500ms). When `duration >= THRESHOLD` and a `long` binding exists, the agent emits the configured action once. This mode suits secondary functions that require intentional activation.

```toml

# Example: Mission Control requires a deliberate hold

DpiToggle = { short = "ShowDesktop", long = "MissionControl" }

```

### Hold-Until-Release Actions

**Hold-until-release** maintains an active state for the entire duration of the physical press. The action initiates immediately on `ButtonEvent::Down` and terminates precisely on `ButtonEvent::Up`, creating a momentary or modifier-style behavior.

This mode requires the `hold` field in the binding configuration. The agent core dispatches the action on button-down and automatically cancels or stops the action on button-up, making it ideal for scrolling, temporary DPI adjustments, or drag-and-drop operations.

```toml

# Example: Continuous scrolling while held

WheelUp = { hold = "ScrollUp" }

```

## Runtime Implementation

The distinction between these three modes relies on precise event timing within the agent core.

### Event Processing Logic

The [`crates/openlogi-agent-core/src/button.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-agent-core/src/button.rs) module implements the decision tree that classifies raw HID++ events into high-level actions. The system tracks press timestamps using `state.start_press(id, Instant::now())` on down events and evaluates `state.elapsed(id)` on up events.

```rust
// Simplified logic from crates/openlogi-agent-core/src/button.rs
match event {
    ButtonEvent::Down(id) => {
        state.start_press(id, Instant::now());
        if let Some(action) = binding.hold {
            send_action(action);  // hold-until-release begins immediately
        }
    }
    ButtonEvent::Up(id) => {
        let duration = state.elapsed(id);
        if duration < THRESHOLD && binding.short.is_some() {
            send_action(binding.short.unwrap());
        } else if duration >= THRESHOLD && binding.long.is_some() {
            send_action(binding.long.unwrap());
        }
        // hold-until-release actions terminate automatically here
    }
}

```

### Configuration Structure

The data structures in [`crates/openlogi-agent-core/src/bindings.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-agent-core/src/bindings.rs) define the `Binding` struct with optional fields for each action type:

- `short`: `Option<Action>` for sub-threshold releases
- `long`: `Option<Action>` for supra-threshold holds
- `hold`: `Option<Action>` for continuous activation

Each button can simultaneously define all three fields, allowing the same physical input to serve multiple contextual purposes based on user timing.

## Configuration Examples

To implement these behaviors, declare bindings in your OpenLogi configuration file using inline table syntax:

```toml

# Multi-mode button with distinct short and long behaviors

DpiToggle = { short = "ShowDesktop", long = "MissionControl" }

# Dedicated hold-based scrolling

WheelUp = { hold = "ScrollUp" }
WheelDown = { hold = "ScrollDown" }

# Combined binding using all three modes

ForwardButton = { short = "Copy", long = "Paste", hold = "MiddleClick" }

```

The default 500ms threshold separating short from long presses is compiled into the agent core, though the architecture in [`crates/openlogi-agent-core/src/button.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-agent-core/src/button.rs) supports future configurability of this value.

## Summary

- **Short-press** actions fire on button release when the press duration remains under 500ms, mapping to quick toggles defined in the `short` configuration field.
- **Long-press** actions trigger once after holding a button for 500ms or longer, enabling secondary functions through the `long` field.
- **Hold-until-release** actions activate continuously from button-down to button-up events, controlled by the `hold` field in [`crates/openlogi-agent-core/src/bindings.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-agent-core/src/bindings.rs).
- The agent core in [`crates/openlogi-agent-core/src/button.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-agent-core/src/button.rs) measures durations using `Instant` timestamps and dispatches actions based on threshold comparisons against the `THRESHOLD` constant.

## Frequently Asked Questions

### How does OpenLogi prevent short-press and long-press actions from firing simultaneously?

OpenLogi implements an exclusive dispatch model in [`crates/openlogi-agent-core/src/button.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-agent-core/src/button.rs). When processing a `ButtonEvent::Up`, the system calculates the total press duration and evaluates it against the 500ms threshold. If the duration qualifies as a long-press, only the `long` action emits; if it qualifies as a short-press, only the `short` action fires. The logic prevents both actions from triggering on a single physical press by using mutually exclusive conditional branches.

### Can I use hold-until-release alongside short-press on the same button?

Yes. The `Binding` struct supports simultaneous definition of `short`, `long`, and `hold` fields. When a button with all three fields is pressed, the `hold` action fires immediately on `ButtonEvent::Down`. If the user releases the button before 500ms elapses, the `short` action also dispatches on release. If the user holds beyond 500ms, the `long` action fires at the threshold crossing, while the `hold` action continues until physical release.

### Where is the 500ms threshold defined in the OpenLogi source code?

The default 500ms threshold is defined as a constant in [`crates/openlogi-agent-core/src/button.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-agent-core/src/button.rs), referenced as `THRESHOLD` in the duration comparison logic. This value separates short-press from long-press classifications during the `ButtonEvent::Up` handling routine. Currently, this threshold requires recompilation to modify, as it is hardcoded in the agent core rather than exposed in the TOML configuration.

### What happens if I only define a long-press without a short-press for a button?

If a binding specifies only the `long` field without a `short` field, pressing and releasing the button quickly (under 500ms) results in no action. The system evaluates the duration on release, determines it falls below the threshold, checks for a `short` action, finds `None`, and consequently dispatches nothing. To avoid dead inputs, OpenLogi recommends defining both temporal modes or using `hold` for always-active behaviors.