Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
win_pointer_input.hpp
Go to the documentation of this file.
1#pragma once
2
3// Windows plug-in editor host: pointer-input and surface-lifecycle decisions.
4//
5// HWND-free logic factored out of
6// core/view/platform/win/plugin_view_host_win.cpp. The platform TU keeps the
7// Win32 calls (GetKeyState, SetCapture, SetParent, Dawn surface creation);
8// this header keeps the decisions those calls feed, so they are testable on
9// every platform. That matters here specifically: the required Pulp CI gate is
10// macOS, so a `_WIN32`-gated test of this logic would never actually run.
11//
12// Same split as pulp/view/platform/ns_role_mapping.hpp (Apple) and
13// pulp/view/platform/uia_mapping.hpp (Windows accessibility).
14//
15// This header is about INPUT. The host's GPU-surface create/destroy state
16// machine used to live here too and now has its own home in
17// win_surface_lifecycle.hpp (WAH-9) — a reader looking for surface lifetime had
18// no reason to open a header called "pointer input", and a reader changing
19// pointer dispatch had no reason to expect surface lifetime under their hands.
20
21#include <pulp/view/geometry.hpp> // Point
22#include <pulp/view/input_events.hpp> // Modifier flags
23
24#include <cstdint>
25
26namespace pulp::view::win_input {
27
32public:
34 struct Terminal {
35 uint64_t generation = 0;
37 bool cancel_gesture = false;
38 bool was_claimed = false;
39 bool owns_terminal = false;
40 };
41
42 bool begin(MouseButton button) noexcept {
43 // Terminal is still owned by the outer callback frame. It becomes
44 // reusable only after finish_terminal(), so nested button-down cannot
45 // overwrite that frame's shared drag target.
46 if (phase_ != Phase::idle) return false;
47 ++generation_;
48 button_ = button;
49 terminal_cancel_gesture_ = false;
50 terminal_was_claimed_ = false;
52 : Phase::raw;
53 return true;
54 }
55 bool active() const noexcept {
56 return phase_ != Phase::idle && phase_ != Phase::terminal;
57 }
58 bool accepts(MouseButton button) const noexcept {
59 return active() && button_ == button;
60 }
61 bool accepts(uint64_t generation, MouseButton button) const noexcept {
62 return generation_ == generation && accepts(button);
63 }
64 bool gesture_in_flight() const noexcept {
65 return phase_ == Phase::gesture_candidate ||
66 phase_ == Phase::gesture_claimed;
67 }
68 bool claimed() const noexcept { return phase_ == Phase::gesture_claimed; }
69 MouseButton button() const noexcept { return button_; }
70 Phase phase() const noexcept { return phase_; }
71 uint64_t generation() const noexcept { return generation_; }
72 void mark_claimed() noexcept {
73 if (phase_ == Phase::gesture_candidate)
75 }
76 Terminal terminalize() noexcept {
77 if (phase_ == Phase::terminal) {
78 // Cancellation dispatch is edge-triggered. A re-entrant
79 // WM_CAPTURECHANGED/CANCELMODE must not feed a second cancellation
80 // into the GestureArbiter while the first dispatch still holds its
81 // session reference.
82 return {generation_, button_, false, terminal_was_claimed_, false};
83 }
84 if (active()) {
85 terminal_cancel_gesture_ = gesture_in_flight();
86 terminal_was_claimed_ = claimed();
87 phase_ = Phase::terminal;
88 }
89 Terminal result{generation_, button_, terminal_cancel_gesture_,
90 terminal_was_claimed_, phase_ == Phase::terminal};
91 return result;
92 }
93 void finish_terminal(const Terminal& terminal) noexcept {
94 if (!terminal.owns_terminal) return;
95 if (phase_ == Phase::terminal &&
96 generation_ == terminal.generation) {
97 phase_ = Phase::idle;
98 button_ = MouseButton::none;
99 terminal_cancel_gesture_ = false;
100 terminal_was_claimed_ = false;
101 }
102 }
103
104private:
105 Phase phase_ = Phase::idle;
107 uint64_t generation_ = 0;
108 bool terminal_cancel_gesture_ = false;
109 bool terminal_was_claimed_ = false;
110};
111
112// ── Mouse-message WPARAM key-state flags ─────────────────────────────────
113// Mirrors the MK_* constants from <winuser.h>. Duplicated as plain constants
114// so this header stays parseable off Windows; static_assert'd against the
115// real MK_* values in the platform TU.
116inline constexpr uint32_t kMkLButton = 0x0001; // MK_LBUTTON
117inline constexpr uint32_t kMkRButton = 0x0002; // MK_RBUTTON
118inline constexpr uint32_t kMkShift = 0x0004; // MK_SHIFT
119inline constexpr uint32_t kMkControl = 0x0008; // MK_CONTROL
120inline constexpr uint32_t kMkMButton = 0x0010; // MK_MBUTTON
121
122// Win32 message / virtual-key values used by the pure decoders below. Kept
123// here (and static_assert'd against the SDK in the platform TU) so the Windows
124// input contract is tested on non-Windows CI too.
125inline constexpr uint32_t kWmLButtonDown = 0x0201;
126inline constexpr uint32_t kWmLButtonUp = 0x0202;
127inline constexpr uint32_t kWmRButtonDown = 0x0204;
128inline constexpr uint32_t kWmRButtonUp = 0x0205;
129inline constexpr uint32_t kWmMButtonDown = 0x0207;
130inline constexpr uint32_t kWmMButtonUp = 0x0208;
131
132inline constexpr uint32_t kVkBack = 0x08;
133inline constexpr uint32_t kVkTab = 0x09;
134inline constexpr uint32_t kVkReturn = 0x0D;
135inline constexpr uint32_t kVkEscape = 0x1B;
136inline constexpr uint32_t kVkSpace = 0x20;
137inline constexpr uint32_t kVkPrior = 0x21;
138inline constexpr uint32_t kVkNext = 0x22;
139inline constexpr uint32_t kVkEnd = 0x23;
140inline constexpr uint32_t kVkHome = 0x24;
141inline constexpr uint32_t kVkLeft = 0x25;
142inline constexpr uint32_t kVkUp = 0x26;
143inline constexpr uint32_t kVkRight = 0x27;
144inline constexpr uint32_t kVkDown = 0x28;
145inline constexpr uint32_t kVkDelete = 0x2E;
146inline constexpr uint32_t kVkF1 = 0x70;
147inline constexpr uint32_t kVkF12 = 0x7B;
148inline constexpr uint32_t kVkOem1 = 0xBA;
149inline constexpr uint32_t kVkOemComma = 0xBC;
150inline constexpr uint32_t kVkOem7 = 0xDE;
151
152// ── Coordinates ──────────────────────────────────────────────────────────
153
154// Windows packs the client-area x in the low word of LPARAM and y in the high
155// word, each a SIGNED 16-bit value. Reading them as unsigned loses the
156// negative coordinates a CAPTURED drag reports once the pointer leaves the
157// window past its left or top edge: a knob dragged up past the editor top
158// would see y jump from 0 to ~65535 and slam to the opposite end of its
159// range. LOWORD/HIWORD alone are unsigned, hence the explicit sign cast.
160constexpr int16_t lparam_x(uint32_t lparam) noexcept {
161 return static_cast<int16_t>(static_cast<uint16_t>(lparam & 0xFFFFu));
162}
163
164constexpr int16_t lparam_y(uint32_t lparam) noexcept {
165 return static_cast<int16_t>(static_cast<uint16_t>((lparam >> 16) & 0xFFFFu));
166}
167
168// Window messages report client coordinates in PHYSICAL device pixels, while
169// the view tree lives in LOGICAL units — the same physical/logical split
170// handle_wm_size() applies to WM_SIZE. A non-positive scale falls back to 1
171// rather than dividing by zero.
172//
173// This is only the window-space mapping; the host still runs the result
174// through window_to_root_point() for the design-viewport transform.
175inline Point lparam_to_logical_point(uint32_t lparam, float scale) noexcept {
176 const float s = scale > 0.0f ? scale : 1.0f;
177 return {static_cast<float>(lparam_x(lparam)) / s,
178 static_cast<float>(lparam_y(lparam)) / s};
179}
180
181// ── Modifiers ────────────────────────────────────────────────────────────
182
183// Windows packs only Shift and Control into the mouse-message WPARAM. Alt and
184// the Windows key are keyboard state, not message state, so the caller samples
185// them (GetKeyState) and passes them in — keeping this function pure.
186constexpr uint16_t mouse_modifiers(uint32_t wparam, bool alt_down,
187 bool meta_down) noexcept {
188 uint16_t mods = kModNone;
189 if (wparam & kMkShift) mods |= kModShift;
190 if (wparam & kMkControl) mods |= kModCtrl;
191 if (alt_down) mods |= kModAlt;
192 if (meta_down) mods |= kModMeta;
193 return mods;
194}
195
196// WM_MOUSEMOVE fires for plain hover too. A drag tick is only a drag while the
197// left button is still physically held: with mouse capture active we keep
198// receiving moves after the button is released outside the window, and
199// treating those as drags would let a knob follow the pointer forever.
200constexpr bool drag_continues(uint32_t wparam) noexcept {
201 return (wparam & kMkLButton) != 0;
202}
203
204constexpr uint32_t button_mask(MouseButton button) noexcept {
205 switch (button) {
206 case MouseButton::left: return kMkLButton;
207 case MouseButton::right: return kMkRButton;
208 case MouseButton::middle: return kMkMButton;
209 default: return 0;
210 }
211}
212
213constexpr bool drag_continues(uint32_t wparam, MouseButton button) noexcept {
214 const uint32_t mask = button_mask(button);
215 return mask != 0 && (wparam & mask) != 0;
216}
217
218constexpr MouseButton mouse_button_from_message(uint32_t message) noexcept {
219 switch (message) {
220 case kWmLButtonDown:
221 case kWmLButtonUp: return MouseButton::left;
222 case kWmRButtonDown:
223 case kWmRButtonUp: return MouseButton::right;
224 case kWmMButtonDown:
226 default: return MouseButton::none;
227 }
228}
229
230// GET_WHEEL_DELTA_WPARAM, expressed without windowsx.h. Vertical wheel input
231// is inverted into Pulp's positive-down convention; horizontal input keeps
232// Win32's positive-right convention.
233constexpr float wheel_steps(uint32_t wparam, bool horizontal) noexcept {
234 const auto raw = static_cast<int16_t>(static_cast<uint16_t>(wparam >> 16));
235 const float steps = static_cast<float>(raw) / 120.0f;
236 return horizontal ? steps : -steps;
237}
238
239constexpr uint16_t key_modifiers(bool shift_down, bool control_down,
240 bool alt_down, bool meta_down) noexcept {
241 uint16_t mods = kModNone;
242 if (shift_down) mods |= kModShift;
243 if (control_down) mods |= kModCtrl;
244 if (alt_down) mods |= kModAlt;
245 if (meta_down) mods |= kModMeta;
246 return mods;
247}
248
249constexpr KeyCode key_code_from_virtual_key(uint32_t vk) noexcept {
250 if (vk >= 'A' && vk <= 'Z')
251 return static_cast<KeyCode>('a' + static_cast<int>(vk - 'A'));
252 if (vk >= '0' && vk <= '9') return static_cast<KeyCode>(vk);
253 if (vk >= kVkF1 && vk <= kVkF12)
254 return static_cast<KeyCode>(static_cast<int>(KeyCode::f1) +
255 static_cast<int>(vk - kVkF1));
256 switch (vk) {
257 case kVkBack: return KeyCode::backspace;
258 case kVkTab: return KeyCode::tab;
259 case kVkReturn: return KeyCode::enter;
260 case kVkEscape: return KeyCode::escape;
261 case kVkSpace: return KeyCode::space;
262 case kVkPrior: return KeyCode::page_up;
263 case kVkNext: return KeyCode::page_down;
264 case kVkEnd: return KeyCode::end_;
265 case kVkHome: return KeyCode::home;
266 case kVkLeft: return KeyCode::left;
267 case kVkUp: return KeyCode::up;
268 case kVkRight: return KeyCode::right;
269 case kVkDown: return KeyCode::down;
270 case kVkDelete: return KeyCode::delete_;
271 case kVkOem1: return KeyCode::semicolon;
272 case kVkOemComma: return static_cast<KeyCode>(',');
273 case kVkOem7: return KeyCode::apostrophe;
274 default: return KeyCode::unknown;
275 }
276}
277
278} // namespace pulp::view::win_input
Host-neutral model of the single Win32 capture bracket owned by an editor.
Definition win_pointer_input.hpp:31
bool begin(MouseButton button) noexcept
Definition win_pointer_input.hpp:42
Terminal terminalize() noexcept
Definition win_pointer_input.hpp:76
uint64_t generation() const noexcept
Definition win_pointer_input.hpp:71
Phase
Definition win_pointer_input.hpp:33
@ gesture_claimed
Definition win_pointer_input.hpp:33
@ gesture_candidate
Definition win_pointer_input.hpp:33
@ raw
Definition win_pointer_input.hpp:33
@ idle
Definition win_pointer_input.hpp:33
@ terminal
Definition win_pointer_input.hpp:33
void mark_claimed() noexcept
Definition win_pointer_input.hpp:72
void finish_terminal(const Terminal &terminal) noexcept
Definition win_pointer_input.hpp:93
MouseButton button() const noexcept
Definition win_pointer_input.hpp:69
bool accepts(MouseButton button) const noexcept
Definition win_pointer_input.hpp:58
bool gesture_in_flight() const noexcept
Definition win_pointer_input.hpp:64
Phase phase() const noexcept
Definition win_pointer_input.hpp:70
bool active() const noexcept
Definition win_pointer_input.hpp:55
bool claimed() const noexcept
Definition win_pointer_input.hpp:68
bool accepts(uint64_t generation, MouseButton button) const noexcept
Definition win_pointer_input.hpp:61
Input event types with modifier keys and pointer IDs.
Definition win_plugin_input_router.hpp:45
constexpr uint32_t kVkHome
Definition win_pointer_input.hpp:140
constexpr uint32_t button_mask(MouseButton button) noexcept
Definition win_pointer_input.hpp:204
constexpr uint32_t kVkBack
Definition win_pointer_input.hpp:132
constexpr bool drag_continues(uint32_t wparam) noexcept
Definition win_pointer_input.hpp:200
constexpr float wheel_steps(uint32_t wparam, bool horizontal) noexcept
Definition win_pointer_input.hpp:233
constexpr uint32_t kWmMButtonUp
Definition win_pointer_input.hpp:130
constexpr uint32_t kMkMButton
Definition win_pointer_input.hpp:120
constexpr uint32_t kWmMButtonDown
Definition win_pointer_input.hpp:129
constexpr uint32_t kVkReturn
Definition win_pointer_input.hpp:134
constexpr uint32_t kVkSpace
Definition win_pointer_input.hpp:136
constexpr uint32_t kVkOem1
Definition win_pointer_input.hpp:148
constexpr uint32_t kWmLButtonDown
Definition win_pointer_input.hpp:125
constexpr uint32_t kMkControl
Definition win_pointer_input.hpp:119
constexpr uint16_t key_modifiers(bool shift_down, bool control_down, bool alt_down, bool meta_down) noexcept
Definition win_pointer_input.hpp:239
constexpr uint32_t kWmLButtonUp
Definition win_pointer_input.hpp:126
constexpr uint32_t kVkOemComma
Definition win_pointer_input.hpp:149
constexpr int16_t lparam_y(uint32_t lparam) noexcept
Definition win_pointer_input.hpp:164
Point lparam_to_logical_point(uint32_t lparam, float scale) noexcept
Definition win_pointer_input.hpp:175
constexpr KeyCode key_code_from_virtual_key(uint32_t vk) noexcept
Definition win_pointer_input.hpp:249
constexpr uint32_t kVkRight
Definition win_pointer_input.hpp:143
constexpr uint32_t kWmRButtonUp
Definition win_pointer_input.hpp:128
constexpr uint32_t kVkOem7
Definition win_pointer_input.hpp:150
constexpr uint32_t kMkShift
Definition win_pointer_input.hpp:118
constexpr uint32_t kVkDelete
Definition win_pointer_input.hpp:145
constexpr int16_t lparam_x(uint32_t lparam) noexcept
Definition win_pointer_input.hpp:160
constexpr uint32_t kVkNext
Definition win_pointer_input.hpp:138
constexpr MouseButton mouse_button_from_message(uint32_t message) noexcept
Definition win_pointer_input.hpp:218
constexpr uint32_t kVkLeft
Definition win_pointer_input.hpp:141
constexpr uint32_t kVkTab
Definition win_pointer_input.hpp:133
constexpr uint32_t kVkF1
Definition win_pointer_input.hpp:146
constexpr uint32_t kVkEnd
Definition win_pointer_input.hpp:139
constexpr uint32_t kVkPrior
Definition win_pointer_input.hpp:137
constexpr uint32_t kVkDown
Definition win_pointer_input.hpp:144
constexpr uint32_t kVkUp
Definition win_pointer_input.hpp:142
constexpr uint32_t kMkRButton
Definition win_pointer_input.hpp:117
constexpr uint32_t kVkF12
Definition win_pointer_input.hpp:147
constexpr uint32_t kMkLButton
Definition win_pointer_input.hpp:116
constexpr uint32_t kWmRButtonDown
Definition win_pointer_input.hpp:127
constexpr uint32_t kVkEscape
Definition win_pointer_input.hpp:135
constexpr uint16_t mouse_modifiers(uint32_t wparam, bool alt_down, bool meta_down) noexcept
Definition win_pointer_input.hpp:186
@ kModMeta
Windows key / Command key.
Definition input_events.hpp:20
@ kModNone
Definition input_events.hpp:16
@ kModShift
Definition input_events.hpp:17
@ kModAlt
Alt / Option.
Definition input_events.hpp:19
@ kModCtrl
Control key (Ctrl on Windows/Linux).
Definition input_events.hpp:18
MouseButton
Mouse button identifiers.
Definition input_events.hpp:67
@ none
Definition input_events.hpp:68
@ middle
Definition input_events.hpp:71
@ right
Definition input_events.hpp:70
@ left
Definition input_events.hpp:69
KeyCode
Definition input_events.hpp:35
@ delete_
Definition input_events.hpp:57
@ s
Definition input_events.hpp:41
@ home
Definition input_events.hpp:54
@ up
Definition input_events.hpp:53
@ backspace
Definition input_events.hpp:57
@ apostrophe
Definition input_events.hpp:50
@ down
Definition input_events.hpp:53
@ semicolon
Definition input_events.hpp:50
@ right
Definition input_events.hpp:53
@ left
Definition input_events.hpp:53
@ end_
Definition input_events.hpp:54
@ unknown
Definition input_events.hpp:36
@ f1
Definition input_events.hpp:61
@ page_down
Definition input_events.hpp:54
@ escape
Definition input_events.hpp:57
@ enter
Definition input_events.hpp:57
@ tab
Definition input_events.hpp:57
@ page_up
Definition input_events.hpp:54
@ space
Definition input_events.hpp:58
@ scale
Definition css_animation.hpp:126
Definition geometry.hpp:20
Definition win_pointer_input.hpp:34
MouseButton button
Definition win_pointer_input.hpp:36
bool owns_terminal
Definition win_pointer_input.hpp:39
bool was_claimed
Definition win_pointer_input.hpp:38
uint64_t generation
Definition win_pointer_input.hpp:35
bool cancel_gesture
Definition win_pointer_input.hpp:37