1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
//! Rebindable mouse/keyboard button identifiers.
use std::fmt;
use serde::{Deserialize, Serialize};
/// One of the user-rebindable hotspots on a Logi mouse. The order matches the
/// physical layout from front to side; [`ButtonId::ALL`] is consumed by the
/// default-binding generator and the popover trigger list.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
pub enum ButtonId {
/// The primary button. Rebindable in the config schema, but the OS hook
/// never suppresses it — see [`ButtonId::is_os_hook_button`].
LeftClick,
/// The secondary button. Like [`ButtonId::LeftClick`], it always passes
/// through the OS hook.
RightClick,
/// The wheel click — one of the three buttons the OS hook remaps.
MiddleClick,
/// The thumb-side "back" button (mouse button 4), remapped by the OS hook.
Back,
/// The thumb-side "forward" button (mouse button 5), remapped by the OS hook.
Forward,
/// The "ModeShift" button under the wheel — typically used for SmartShift /
/// DPI cycle. Named `DpiToggle` for historical reasons.
DpiToggle,
/// The horizontal thumb wheel's click. Kept in [`ButtonId::ALL`] so its
/// default still seeds and dispatches when the wheel is diverted, even
/// though the mouse model surfaces one paired rotation control instead of
/// the click (see `mouse_model::geometry`).
Thumbwheel,
/// Rotating the thumb wheel "up" (positive rotation). Bound, by default, to
/// continuous horizontal scroll; see the agent-core `watchers`-side dispatch.
ThumbwheelScrollUp,
/// Rotating the thumb wheel "down" (negative rotation).
ThumbwheelScrollDown,
/// The HID++ gesture button on MX-line devices. The press itself
/// fires the bound action; swipe directions are P1.5 territory.
GestureButton,
/// Keyboard F-row "Search" control (`0x1b04` CID `0x00d4`,
/// `MultiPlatform_Search`) — F4 on the Signature series.
KeySearch,
/// Keyboard "Dictation" control (CID `0x0103`) — F5 on the Signature series.
KeyDictation,
/// Keyboard "Emoji" control (CID `0x0108`) — F6 on the Signature series.
KeyEmoji,
/// Keyboard "Screen Capture" control (CID `0x010a`) — F7 on the Signature
/// series.
KeyScreenCapture,
/// Keyboard "Mute Microphone" control (CID `0x011c`) — F8 on the Signature
/// series.
KeyMicMute,
/// Keyboard "Play/Pause" control (CID `0x00e5`) — F9 on the Signature series.
KeyPlayPause,
/// Keyboard "Mute" control (CID `0x00e7`) — F10 on the Signature series.
KeyMute,
/// Keyboard "Volume Down" control (CID `0x00e8`) — F11 on the Signature
/// series.
KeyVolumeDown,
/// Keyboard "Volume Up" control (CID `0x00e9`) — F12 on the Signature
/// series.
KeyVolumeUp,
}
impl ButtonId {
/// Every rebindable button in declaration (physical front-to-side) order —
/// the iteration source for default-binding seeding and the popover
/// trigger list.
pub const ALL: [ButtonId; 10] = [
ButtonId::LeftClick,
ButtonId::RightClick,
ButtonId::MiddleClick,
ButtonId::Back,
ButtonId::Forward,
ButtonId::DpiToggle,
ButtonId::Thumbwheel,
ButtonId::ThumbwheelScrollUp,
ButtonId::ThumbwheelScrollDown,
ButtonId::GestureButton,
];
/// The divertable keyboard F-row controls, in F-row order. Kept out of
/// [`ButtonId::ALL`]: that array seeds mouse defaults and the mouse
/// popover trigger list, while keyboard keys stay native unless the user
/// binds them (an unbound key is never diverted).
pub const KEYBOARD_KEYS: [ButtonId; 9] = [
ButtonId::KeySearch,
ButtonId::KeyDictation,
ButtonId::KeyEmoji,
ButtonId::KeyScreenCapture,
ButtonId::KeyMicMute,
ButtonId::KeyPlayPause,
ButtonId::KeyMute,
ButtonId::KeyVolumeDown,
ButtonId::KeyVolumeUp,
];
/// Whether this button is one the OS hook (macOS `CGEventTap` / Linux evdev)
/// remaps: Middle, Back, or Forward. The primary L/R clicks always pass
/// through (suppressing them would brick the mouse), and the DPI / thumb /
/// dedicated gesture controls aren't visible to the OS hook at all (they're
/// captured over HID++). These are exactly the buttons that can become an
/// OS-hook gesture button, so the hook's remap gate and the gesture-owner
/// projection share this one definition.
#[must_use]
pub fn is_os_hook_button(self) -> bool {
matches!(
self,
ButtonId::MiddleClick | ButtonId::Back | ButtonId::Forward
)
}
/// Human-readable label for popovers and tooltips.
#[must_use]
pub fn label(self) -> &'static str {
match self {
ButtonId::LeftClick => "Left Click",
ButtonId::RightClick => "Right Click",
ButtonId::MiddleClick => "Middle Click",
ButtonId::Back => "Back",
ButtonId::Forward => "Forward",
ButtonId::DpiToggle => "DPI Toggle",
ButtonId::Thumbwheel => "Thumb Wheel",
ButtonId::ThumbwheelScrollUp => "Thumb Wheel Up",
ButtonId::ThumbwheelScrollDown => "Thumb Wheel Down",
ButtonId::GestureButton => "Gesture Button",
ButtonId::KeySearch => "Search Key",
ButtonId::KeyDictation => "Dictation Key",
ButtonId::KeyEmoji => "Emoji Key",
ButtonId::KeyScreenCapture => "Screen Capture Key",
ButtonId::KeyMicMute => "Mic Mute Key",
ButtonId::KeyPlayPause => "Play/Pause Key",
ButtonId::KeyMute => "Mute Key",
ButtonId::KeyVolumeDown => "Volume Down Key",
ButtonId::KeyVolumeUp => "Volume Up Key",
}
}
}
impl fmt::Display for ButtonId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.label())
}
}