How OpenLogi Handles Short and Long Press Button Events from Logitech Devices
OpenLogi handles short-press and long-press button events by mapping distinct HID++ control IDs to separate logical button identifiers, eliminating the need for software-based timing measurements.
OpenLogi is an open-source driver stack for Logitech input devices that translates raw HID++ events into high-level button events. When working with programmable buttons on Logitech presentation remotes and mice, distinguishing between a brief click and a sustained hold is essential for advanced workflows. The project encodes this distinction directly in the device firmware's control ID protocol.
How HID++ Control IDs Encode Press Types
Logitech's HID++ protocol assigns separate control IDs for short-press and long-press variants of the same physical button. In crates/openlogi-hidpp/src/feature/reprog_controls/control_ids.rs, OpenLogi defines these constants:
// Lines 12-16: Laser button control IDs
pub const LASER_BUTTON: u16 = 0xFA;
pub const LASER_BUTTON_SHORT_PRESS: u16 = 0xFB;
pub const LASER_BUTTON_LONG_PRESS: u16 = 0xFC;
The firmware itself determines whether a press qualifies as short or long. OpenLogi simply receives the appropriate control ID and routes it through the event pipeline. This design avoids timing ambiguity, reduces latency, and ensures consistent behavior across different host machines.
From Raw HID++ to MouseEvent::Button
The hook layer at crates/openlogi-hook/src/lib.rs bridges the gap between raw protocol data and application-friendly event structures. It wraps HID++ events into a MouseEvent::Button struct:
pub struct MouseEvent {
pub id: ButtonId,
pub pressed: bool,
}
The ButtonId enum derives from the control ID, so 0xFB becomes LaserButtonShortPress while 0xFC becomes LaserButtonLongPress. The pressed boolean tracks the physical state: true on button down, false on button up.
This separation means short-press and long-press events are handled as independent logical buttons throughout the system.
IPC Event Propagation
Events travel through OpenLogi's inter-process communication layer before reaching UI components. The IPC representation in crates/openlogi-ipc/src/ipc.rs simplifies the event to:
Button { button: String, pressed: bool },
The button string carries the discriminated identifier, allowing downstream consumers to match against specific press types without accessing HID++ internals.
Practical Example: Handling Both Press Types
Below is a complete pattern for reacting to laser button short-press and long-press events in an application consuming OpenLogi's IPC output:
use openlogi_ipc::ipc::Button;
// Assume `event` is received from the IPC socket
match event {
Button { button, pressed } => {
match (button.as_str(), pressed) {
("LaserButtonShortPress", true) => {
// Short press started – do the default action
println!("Laser button short‑press down");
}
("LaserButtonShortPress", false) => {
// Short press released
println!("Laser button short‑press up");
}
("LaserButtonLongPress", true) => {
// Long‑press started – maybe trigger a different command
println!("Laser button long‑press down");
}
("LaserButtonLongPress", false) => {
// Long‑press released
println!("Laser button long‑press up");
}
_ => {}
}
}
_ => {}
}
Notice that timing logic is absent from the application code—the firmware has already classified the press type.
Key Source Files
crates/openlogi-hidpp/src/feature/reprog_controls/control_ids.rs— Defines raw HID++ control IDs includingLASER_BUTTON_SHORT_PRESS(0xFB) andLASER_BUTTON_LONG_PRESS(0xFC)crates/openlogi-hook/src/lib.rs— Wraps HID++ events into the higher-levelMouseEvent::Buttonabstractioncrates/openlogi-ipc/src/ipc.rs— Serializes button events for inter-process communicationcrates/openlogi-agent-core/src/runtime/hook.rs— Core runtime that receives HID++ events and forwards them to IPC consumers
Summary
- OpenLogi receives distinct HID++ control IDs for short-press and long-press variants from the device firmware.
- The hook layer maps these control IDs to separate
ButtonIdenum values. - Events propagate with a
pressedboolean indicating physical state, not press classification. - No timing measurement occurs in software—classification happens entirely in hardware.
- Applications bind separate actions to short-press and long-press identifiers as if they were different buttons.
Frequently Asked Questions
Does OpenLogi measure time to detect long presses?
No. OpenLogi relies on the Logitech device firmware to classify press duration. The firmware reports either a short-press control ID (0xFB) or long-press control ID (0xFC), and OpenLogi passes this classification through unchanged. This approach eliminates timing inconsistencies between machines.
Can I customize the long-press duration threshold?
The threshold is determined by device firmware, not by OpenLogi. Some Logitech devices expose reprogrammable parameters through the Reprog Controls feature, but OpenLogi's current implementation treats the firmware's classification as authoritative. Check your specific hardware's HID++ feature set for available configuration options.
How do I bind different actions to short-press and long-press in my application?
Treat them as separate buttons. Match against "LaserButtonShortPress" and "LaserButtonLongPress" (or equivalent identifiers for your button) in the Button event handler. Each can trigger independent command sequences without interfering with the other.
Are all Logitech buttons capable of generating both press types?
No. Only buttons exposed through the Reprog Controls feature with distinct short-press and long-press control IDs support this functionality. The laser button on presentation remotes is a common example; standard mouse buttons typically lack long-press variants in their HID++ definitions.
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 →