Line data Source code
1 : /**
2 : Copyright (c) 2023 Stappler LLC <admin@stappler.dev>
3 :
4 : Permission is hereby granted, free of charge, to any person obtaining a copy
5 : of this software and associated documentation files (the "Software"), to deal
6 : in the Software without restriction, including without limitation the rights
7 : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 : copies of the Software, and to permit persons to whom the Software is
9 : furnished to do so, subject to the following conditions:
10 :
11 : The above copyright notice and this permission notice shall be included in
12 : all copies or substantial portions of the Software.
13 :
14 : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 : THE SOFTWARE.
21 : **/
22 :
23 : #ifndef XENOLITH_CORE_XLCOREINPUT_H_
24 : #define XENOLITH_CORE_XLCOREINPUT_H_
25 :
26 : #include "XLCore.h"
27 :
28 : #if WIN32
29 : #ifdef DELETE
30 : #undef DELETE
31 : #endif
32 : #endif
33 :
34 : namespace STAPPLER_VERSIONIZED stappler::xenolith::core {
35 :
36 : enum class InputFlags {
37 : None,
38 : TouchMouseInput = 1 << 0,
39 : KeyboardInput = 1 << 1,
40 : FocusInput = 1 << 2,
41 : };
42 :
43 : SP_DEFINE_ENUM_AS_MASK(InputFlags)
44 :
45 : enum class InputMouseButton : uint32_t {
46 : None,
47 : MouseLeft,
48 : MouseMiddle,
49 : MouseRight,
50 : MouseScrollUp,
51 : MouseScrollDown,
52 : MouseScrollLeft,
53 : MouseScrollRight,
54 : Mouse8,
55 : Mouse9,
56 : Mouse10,
57 : Mouse11,
58 : Mouse12,
59 : Mouse13,
60 : Mouse14,
61 : Mouse15,
62 : Max,
63 :
64 : Touch = MouseLeft
65 : };
66 :
67 : enum class InputModifier : uint32_t {
68 : None = 0,
69 : Shift = 1 << 0,
70 : CapsLock = 1 << 1,
71 : Ctrl = 1 << 2,
72 : Alt = 1 << 3,
73 : NumLock = 1 << 4,
74 : Mod3 = 1 << 5,
75 : Mod4 = 1 << 6,
76 : Mod5 = 1 << 7,
77 : Button1 = 1 << 8,
78 : Button2 = 1 << 9,
79 : Button3 = 1 << 10,
80 : Button4 = 1 << 11,
81 : Button5 = 1 << 12,
82 :
83 : // Linux-only, experimental
84 : LayoutAlternative = 1 << 13,
85 :
86 : ShiftL = 1 << 14,
87 : ShiftR = 1 << 15,
88 : CtrlL = 1 << 16,
89 : CtrlR = 1 << 17,
90 : AltL = 1 << 18,
91 : AltR = 1 << 19,
92 : Mod3L = 1 << 20,
93 : Mod3R = 1 << 21,
94 : Mod4L = 1 << 22,
95 : Mod4R = 1 << 23,
96 :
97 : ScrollLock = 1 << 24,
98 :
99 : Command = Mod3, // MacOS Command
100 : Meta = Mod3, // Android Meta
101 : Function = Mod4, // Android Function
102 : Sym = Mod5, // Android Sym
103 : Win = Mod3,
104 : WinL = Mod3L,
105 : WinR = Mod3R,
106 : Menu = Alt, // VK_MENU
107 : MenuL = AltL, // VK_LMENU
108 : MenuR = AltR, // VK_RMENU
109 :
110 : // boolean value for switch event (background/focus)
111 : ValueFalse = None,
112 : ValueTrue = uint32_t(1) << uint32_t(31),
113 : Unmanaged = ValueTrue
114 : };
115 :
116 : SP_DEFINE_ENUM_AS_MASK(InputModifier)
117 :
118 : /* Based on GLFW
119 : *
120 : * Designed to fit 128-bit bitmask for pressed key tracking
121 : * Undefined char is 0 instead of -1
122 : * Codepoints for BACKSPACE, TAB, ENTER, ESCAPE, DELETE matches ASCII positions
123 : * - Platform can send this with or without keychar
124 : * Printable keys (and keypad nums) within [32-96]
125 : * APOSTROPHE moved from 39 to 43 to fit keypad nums in single block
126 : * - expressions like KP_0 + 8 is more probable then direct casting InputKeyCode -> char
127 : * Key names based on QWERTY keyboard, but actually refers to physical key position rather then symbol
128 : * - actual physical key name defined in XKB convention
129 : * - e.g. InputKeyCode::S = AC02 key = A on QWERTY = O on Dworak
130 : * */
131 : enum class InputKeyCode : uint16_t {
132 : Unknown = 0,
133 :
134 : KP_DECIMAL = 1, // "KPDL"
135 : KP_DIVIDE = 2, // "KPDV"
136 : KP_MULTIPLY = 3, // "KPMU"
137 : KP_SUBTRACT = 4, // "KPSU"
138 : KP_ADD = 5, // "KPAD"
139 : KP_ENTER = 6, // "KPEN"
140 : KP_EQUAL = 7, // "KPEQ"
141 :
142 : BACKSPACE = 8, // "BKSP"; ASCII-compatible
143 : TAB = 9, // "TAB"; ASCII-compatible
144 : ENTER = 10, // "RTRN"; ASCII-compatible
145 :
146 : RIGHT = 11, // "RGHT"
147 : LEFT = 12, // "LEFT"
148 : DOWN = 13, // "DOWN"
149 : UP = 14, // "UP"
150 : PAGE_UP = 15, // "PGUP"
151 : PAGE_DOWN = 16, // "PGDN"
152 : HOME = 17, // "HOME"
153 : END = 18, // "END"
154 : LEFT_SHIFT = 19, // "LFSH"
155 : LEFT_CONTROL = 20, // "LCTL"
156 : LEFT_ALT = 21, // "LALT"
157 : LEFT_SUPER = 22, // "LWIN"
158 : RIGHT_SHIFT = 23, // "RTSH"
159 : RIGHT_CONTROL = 24, // "RCTL"
160 : RIGHT_ALT = 25, // "RALT", "LVL3", MDSW"
161 : RIGHT_SUPER = 26, // "RWIN"
162 :
163 : ESCAPE = 27, // "ESC"; ASCII-compatible :
164 :
165 : INSERT = 28, // "INS"
166 : CAPS_LOCK = 29, // "CAPS"
167 : SCROLL_LOCK = 30, // "SCLK"
168 : NUM_LOCK = 31, // "NMLK"
169 :
170 : SPACE = 32, // "SPCE"
171 :
172 : KP_0 = 33, // "KP0"
173 : KP_1 = 34, // "KP1"
174 : KP_2 = 35, // "KP2"
175 : KP_3 = 36, // "KP3"
176 : KP_4 = 37, // "KP4"
177 : KP_5 = 38, // "KP5"
178 : KP_6 = 39, // "KP6"
179 : KP_7 = 40, // "KP7"
180 : KP_8 = 41, // "KP8"
181 : KP_9 = 42, // "KP9"
182 :
183 : APOSTROPHE = 43, // "AC11"; '\''
184 : COMMA = 44, // "AB08"; ','
185 : MINUS = 45, // "AE11"; '-'
186 : PERIOD = 46, // "AB09"; '.'
187 : SLASH = 47, // "AB10"; '/'
188 : _0 = 48, // "AE10"
189 : _1 = 49, // "AE01"
190 : _2 = 50, // "AE02"
191 : _3 = 51, // "AE03"
192 : _4 = 52, // "AE04"
193 : _5 = 53, // "AE05"
194 : _6 = 54, // "AE06"
195 : _7 = 55, // "AE07"
196 : _8 = 56, // "AE08"
197 : _9 = 57, // "AE09"
198 : SEMICOLON = 59, // "AC10"; ';'
199 : EQUAL = 61, // "AE12"; '='
200 :
201 : WORLD_1 = 62, // "LSGT"; non-US #1 :
202 : WORLD_2 = 63, // non-US #2
203 :
204 : A = 65, // "AC01"
205 : B = 66, // "AB05"
206 : C = 67, // "AB03"
207 : D = 68, // "AC03"
208 : E = 69, // "AD03"
209 : F = 70, // "AC04"
210 : G = 71, // "AC05"
211 : H = 72, // "AC06"
212 : I = 73, // "AD08"
213 : J = 74, // "AC07"
214 : K = 75, // "AC08"
215 : L = 76, // "AC09"
216 : M = 77, // "AB07"
217 : N = 78, // "AB06"
218 : O = 79, // "AD09"
219 : P = 80, // "AD10"
220 : Q = 81, // "AD01"
221 : R = 82, // "AD04"
222 : S = 83, // "AC02"
223 : T = 84, // "AD05"
224 : U = 85, // "AD07"
225 : V = 86, // "AB04"
226 : W = 87, // "AD02"
227 : X = 88, // "AB02"
228 : Y = 89, // "AD06"
229 : Z = 90, // "AB01"
230 : LEFT_BRACKET = 91, // "AD11"; '['
231 : BACKSLASH = 92, // "BKSL"; '\\'
232 : RIGHT_BRACKET = 93, // "AD12"; ']'
233 : GRAVE_ACCENT = 96, // "TLDE"; '`'
234 :
235 : /* Function keys */
236 : F1 = 97, // "FK01"
237 : F2 = 98, // "FK02"
238 : F3 = 99, // "FK03"
239 : F4 = 100, // "FK04"
240 : F5 = 101, // "FK05"
241 : F6 = 102, // "FK06"
242 : F7 = 103, // "FK07"
243 : F8 = 104, // "FK08"
244 : F9 = 105, // "FK09"
245 : F10 = 106, // "FK10"
246 : F11 = 107, // "FK11"
247 : F12 = 108, // "FK12"
248 : F13 = 109, // "FK13"
249 : F14 = 110, // "FK14"
250 : F15 = 111, // "FK15"
251 : F16 = 112, // "FK16"
252 : F17 = 113, // "FK17"
253 : F18 = 114, // "FK18"
254 : F19 = 115, // "FK19"
255 : F20 = 116, // "FK20"
256 : F21 = 117, // "FK21"
257 : F22 = 118, // "FK22"
258 : F23 = 119, // "FK23"
259 : F24 = 120, // "FK24"
260 : F25 = 121, // "FK25"
261 :
262 : MENU = 124, // "MENU"
263 : PRINT_SCREEN = 125, // "PRSC"
264 : PAUSE = 126, // "PAUS"
265 : DELETE = 127, // "DELE"; ASCII-compatible
266 :
267 : Max
268 : };
269 :
270 : enum class InputKeyComposeState : uint16_t {
271 : Nothing = 0,
272 : Composed,
273 : Composing,
274 : Disabled, // do not use this key event for text input processing
275 : Forced, // use, when key up should be processed
276 : };
277 :
278 : enum class InputEventName : uint32_t {
279 : None,
280 : Begin,
281 : Move,
282 : End,
283 : Cancel,
284 : MouseMove,
285 : Scroll,
286 :
287 : Background,
288 : PointerEnter,
289 : FocusGain,
290 :
291 : KeyPressed,
292 : KeyRepeated,
293 : KeyReleased,
294 : KeyCanceled,
295 :
296 : Max,
297 : };
298 :
299 : struct InputEventData {
300 122 : static InputEventData BoolEvent(InputEventName event, bool value) {
301 : return InputEventData{maxOf<uint32_t>(), event, InputMouseButton::None,
302 122 : value ? InputModifier::ValueTrue : InputModifier::None};
303 : }
304 :
305 26 : static InputEventData BoolEvent(InputEventName event, bool value, const Vec2 &pt) {
306 : return InputEventData{maxOf<uint32_t>(), event, InputMouseButton::None,
307 26 : value ? InputModifier::ValueTrue : InputModifier::None, pt.x, pt.y};
308 : }
309 :
310 : static InputEventData BoolEvent(InputEventName event, bool value, InputModifier mod, const Vec2 &pt) {
311 : return InputEventData{maxOf<uint32_t>(), event, InputMouseButton::None,
312 : mod | (value ? InputModifier::ValueTrue : InputModifier::None), pt.x, pt.y};
313 : }
314 :
315 : uint32_t id = maxOf<uint32_t>();
316 : InputEventName event = InputEventName::None;
317 : InputMouseButton button = InputMouseButton::None;
318 : InputModifier modifiers = InputModifier::None;
319 : float x = 0.0f;
320 : float y = 0.0f;
321 : union {
322 : struct {
323 : float valueX = 0.0f;
324 : float valueY = 0.0f;
325 : float density = 1.0f;
326 : } point = { 0.0f, 0.0f, 1.0f };
327 : struct {
328 : InputKeyCode keycode; // layout-independent key name
329 : InputKeyComposeState compose;
330 : uint32_t keysym; // OS-dependent keysym
331 : char32_t keychar; // unicode char for key
332 : } key;
333 : };
334 :
335 : bool operator==(const uint32_t &i) const { return id == i; }
336 : bool operator!=(const uint32_t &i) const { return id != i; }
337 :
338 931 : bool getValue() const { return (modifiers & InputModifier::ValueTrue) != InputModifier::None; }
339 :
340 6790 : bool hasLocation() const {
341 6790 : switch (event) {
342 134 : case InputEventName::None:
343 : case InputEventName::Background:
344 : case InputEventName::PointerEnter:
345 : case InputEventName::FocusGain:
346 134 : return false;
347 : break;
348 : #if ANDROID
349 : case InputEventName::KeyPressed:
350 : case InputEventName::KeyReleased:
351 : case InputEventName::KeyRepeated:
352 : return false;
353 : break;
354 : #endif
355 6656 : default:
356 6656 : break;
357 : }
358 6656 : return true;
359 : }
360 :
361 6097 : bool isPointEvent() const {
362 6097 : switch (event) {
363 4376 : case InputEventName::Begin:
364 : case InputEventName::Move:
365 : case InputEventName::End:
366 : case InputEventName::Cancel:
367 : case InputEventName::MouseMove:
368 : case InputEventName::Scroll:
369 4376 : return true;
370 : break;
371 1721 : default:
372 1721 : break;
373 : }
374 1721 : return false;
375 : }
376 :
377 210 : bool isKeyEvent() const {
378 210 : switch (event) {
379 189 : case InputEventName::KeyPressed:
380 : case InputEventName::KeyRepeated:
381 : case InputEventName::KeyReleased:
382 189 : return true;
383 : break;
384 21 : default:
385 21 : break;
386 : }
387 21 : return false;
388 : }
389 :
390 : bool operator==(const InputEventData &r) const {
391 : if (id != r.id || event != r.event || button != r.button || modifiers != r.modifiers || x != r.x || y != r.y) {
392 : return false;
393 : }
394 : switch (event) {
395 : case InputEventName::Begin:
396 : case InputEventName::Move:
397 : case InputEventName::End:
398 : case InputEventName::Cancel:
399 : case InputEventName::MouseMove:
400 : case InputEventName::Scroll:
401 : // pointer event
402 : if (point.valueX != r.point.valueX || point.valueY != r.point.valueY || point.density != r.point.density) {
403 : return false;
404 : }
405 : break;
406 : case InputEventName::KeyPressed:
407 : case InputEventName::KeyRepeated:
408 : case InputEventName::KeyReleased:
409 : // key event
410 : if (key.keycode != r.key.keycode || key.compose != r.key.compose || key.keysym != r.key.keysym || key.keychar != r.key.keychar) {
411 : return false;
412 : }
413 : break;
414 : default:
415 : // bool event
416 : break;
417 : }
418 : return true;
419 : }
420 :
421 : bool operator!=(const InputEventData &r) const {
422 : return !(*this == r);
423 : }
424 :
425 : bool operator<(const InputEventData &r) const {
426 : if (id < r.id) {
427 : return true;
428 : } else if (id > r.id) {
429 : return false;
430 : }
431 :
432 : if (toInt(event) < toInt(r.event)) {
433 : return true;
434 : } else if (toInt(event) > toInt(r.event)) {
435 : return false;
436 : }
437 :
438 : if (toInt(button) < toInt(r.button)) {
439 : return true;
440 : } else if (toInt(button) > toInt(r.button)) {
441 : return false;
442 : }
443 :
444 : if (toInt(modifiers) < toInt(r.modifiers)) {
445 : return true;
446 : } else if (toInt(modifiers) > toInt(r.modifiers)) {
447 : return false;
448 : }
449 :
450 : return false;
451 : }
452 : };
453 :
454 : enum class TextInputType {
455 : Empty = 0,
456 : Date_Date = 1,
457 : Date_DateTime = 2,
458 : Date_Time = 3,
459 : Date = Date_DateTime,
460 :
461 : Number_Numbers = 4,
462 : Number_Decimial = 5,
463 : Number_Signed = 6,
464 : Number = Number_Numbers,
465 :
466 : Phone = 7,
467 :
468 : Text_Text = 8,
469 : Text_Search = 9,
470 : Text_Punctuation = 10,
471 : Text_Email = 11,
472 : Text_Url = 12,
473 : Text = Text_Text,
474 :
475 : Default = Text_Text,
476 :
477 : ClassMask = 0b00011111,
478 : PasswordBit = 0b00100000,
479 : MultiLineBit = 0b01000000,
480 : AutoCorrectionBit = 0b10000000,
481 :
482 : ReturnKeyMask = 0b00001111 << 8,
483 :
484 : ReturnKeyDefault = 1 << 8,
485 : ReturnKeyGo = 2 << 8,
486 : ReturnKeyGoogle = 3 << 8,
487 : ReturnKeyJoin = 4 << 8,
488 : ReturnKeyNext = 5 << 8,
489 : ReturnKeyRoute = 6 << 8,
490 : ReturnKeySearch = 7 << 8,
491 : ReturnKeySend = 8 << 8,
492 : ReturnKeyYahoo = 9 << 8,
493 : ReturnKeyDone = 10 << 8,
494 : ReturnKeyEmergencyCall = 11 << 8,
495 : };
496 :
497 : SP_DEFINE_ENUM_AS_MASK(TextInputType);
498 :
499 : using TextCursorPosition = ValueWrapper<uint32_t, class TextCursorPositionFlag>;
500 : using TextCursorLength = ValueWrapper<uint32_t, class TextCursorStartFlag>;
501 :
502 : struct TextCursor {
503 : static const TextCursor InvalidCursor;
504 :
505 28132 : uint32_t start;
506 27772 : uint32_t length;
507 :
508 180 : constexpr TextCursor() : start(maxOf<uint32_t>()), length(0) { }
509 0 : constexpr TextCursor(uint32_t pos) : start(pos), length(0) { }
510 132 : constexpr TextCursor(uint32_t st, uint32_t len) : start(st), length(len) { }
511 : constexpr TextCursor(TextCursorPosition pos) : start(pos.get()), length(0) { }
512 : constexpr TextCursor(TextCursorPosition pos, TextCursorLength len) : start(pos.get()), length(len.get()) { }
513 : constexpr TextCursor(TextCursorPosition first, TextCursorPosition last)
514 : : start(std::min(first.get(), last.get()))
515 : , length(((first > last)?(first - last).get():(last - first).get()) + 1) { }
516 :
517 28132 : constexpr bool operator==(const TextCursor &) const = default;
518 : };
519 :
520 : StringView getInputKeyCodeName(InputKeyCode);
521 : StringView getInputKeyCodeKeyName(InputKeyCode);
522 : StringView getInputEventName(InputEventName);
523 :
524 : StringView getInputButtonName(InputMouseButton);
525 :
526 : String getInputModifiersNames(InputModifier);
527 :
528 : }
529 :
530 : namespace std {
531 :
532 : std::ostream &operator<<(std::ostream &, STAPPLER_VERSIONIZED_NAMESPACE::xenolith::core::InputKeyCode);
533 : std::ostream &operator<<(std::ostream &, STAPPLER_VERSIONIZED_NAMESPACE::xenolith::core::InputEventName);
534 :
535 : }
536 :
537 : #endif /* XENOLITH_CORE_XLCOREINPUT_H_ */
|