FreeRDP
Loading...
Searching...
No Matches
input.c
1
20#include <time.h>
21#include <freerdp/config.h>
22
23#include <winpr/crt.h>
24#include <winpr/assert.h>
25
26#include <freerdp/input.h>
27#include <freerdp/log.h>
28
29#include "message.h"
30
31#include "input.h"
32
33#define TAG FREERDP_TAG("core")
34
35/* Input Events */
36#define INPUT_EVENT_SYNC 0x0000
37#define INPUT_EVENT_SCANCODE 0x0004
38#define INPUT_EVENT_UNICODE 0x0005
39#define INPUT_EVENT_MOUSE 0x8001
40#define INPUT_EVENT_MOUSEX 0x8002
41#define INPUT_EVENT_MOUSEREL 0x8004
42
43static const char* SyncEventFlag2Str(uint32_t flag)
44{
45 if (flag == KBD_SYNC_SCROLL_LOCK)
46 return "SYNC_SCROLL_LOCK";
47 if (flag == KBD_SYNC_NUM_LOCK)
48 return "SYNC_NUM_LOCK";
49 if (flag == KBD_SYNC_CAPS_LOCK)
50 return "SYNC_CAPS_LOCK";
51 if (flag == KBD_SYNC_KANA_LOCK)
52 return "SYNC_KANA_LOCK";
53 return "SYNC_UNKNOWN";
54}
55
56static const char* SyncEventFlags2Str(const char* prefix, uint32_t flags, char* buffer, size_t len)
57{
58 const uint32_t tflags[] = { KBD_SYNC_SCROLL_LOCK, KBD_SYNC_NUM_LOCK, KBD_SYNC_CAPS_LOCK,
59 KBD_SYNC_KANA_LOCK };
60
61 if (len <= 2)
62 return nullptr;
63
64 if (!winpr_str_append("{", buffer, len, ""))
65 return nullptr;
66
67 /* Strip initial symbol so we do not get duplicate separators */
68 for (size_t x = 0; x < ARRAYSIZE(tflags); x++)
69 {
70 const uint32_t flag = tflags[x];
71 if (flags & flag)
72 {
73 char ibuffer[64] = WINPR_C_ARRAY_INIT;
74 (void)_snprintf(ibuffer, sizeof(ibuffer), "%s%s", prefix, SyncEventFlag2Str(flag));
75 if (!winpr_str_append(ibuffer, &buffer[1], len - 2, "|"))
76 return nullptr;
77 }
78 }
79 if (!winpr_str_append("}", &buffer[1], len - 2, ""))
80 return nullptr;
81
82 return buffer;
83}
84
85const char* freerdp_input_keyboard_flags_string(uint32_t flags, char* buffer, size_t len)
86{
87 return SyncEventFlags2Str("KBD_", flags, buffer, len);
88}
89
90static void rdp_write_client_input_pdu_header(wStream* s, UINT16 number)
91{
92 WINPR_ASSERT(s);
93 WINPR_ASSERT(Stream_GetRemainingCapacity(s) >= 4);
94 Stream_Write_UINT16(s, number); /* numberEvents (2 bytes) */
95 Stream_Write_UINT16(s, 0); /* pad2Octets (2 bytes) */
96}
97
98static void rdp_write_input_event_header(wStream* s, UINT32 time, UINT16 type)
99{
100 WINPR_ASSERT(s);
101 WINPR_ASSERT(Stream_GetRemainingCapacity(s) >= 6);
102 Stream_Write_UINT32(s, time); /* eventTime (4 bytes) */
103 Stream_Write_UINT16(s, type); /* messageType (2 bytes) */
104}
105
106static wStream* rdp_client_input_pdu_init(rdpRdp* rdp, UINT16 type, UINT16* sec_flags)
107{
108 wStream* s = rdp_data_pdu_init(rdp, sec_flags);
109
110 if (!s)
111 return nullptr;
112
113 rdp_write_client_input_pdu_header(s, 1);
114 rdp_write_input_event_header(s, 0, type);
115 return s;
116}
117
118static BOOL rdp_send_client_input_pdu(rdpRdp* rdp, wStream* s, UINT16 sec_flags)
119{
120 WINPR_ASSERT(rdp);
121 WINPR_ASSERT(rdp->mcs);
122 return rdp_send_data_pdu(rdp, s, DATA_PDU_TYPE_INPUT, rdp->mcs->userId, sec_flags);
123}
124
125static void input_write_synchronize_event(wStream* s, UINT32 flags)
126{
127 WINPR_ASSERT(s);
128 WINPR_ASSERT(Stream_GetRemainingCapacity(s) >= 6);
129 Stream_Write_UINT16(s, 0); /* pad2Octets (2 bytes) */
130 Stream_Write_UINT32(s, flags); /* toggleFlags (4 bytes) */
131}
132
133static BOOL input_ensure_client_running(rdpInput* input)
134{
135 WINPR_ASSERT(input);
136 if (freerdp_shall_disconnect_context(input->context))
137 {
138 WLog_WARN(TAG, "[APPLICATION BUG] input functions called after the session terminated");
139 return FALSE;
140 }
141 return TRUE;
142}
143
144static BOOL input_send_synchronize_event(rdpInput* input, UINT32 flags)
145{
146 UINT16 sec_flags = 0;
147
148 if (!input || !input->context)
149 return FALSE;
150
151 rdpRdp* rdp = input->context->rdp;
152
153 if (!input_ensure_client_running(input))
154 return FALSE;
155
156 wStream* s = rdp_client_input_pdu_init(rdp, INPUT_EVENT_SYNC, &sec_flags);
157
158 if (!s)
159 return FALSE;
160
161 input_write_synchronize_event(s, flags);
162 return rdp_send_client_input_pdu(rdp, s, sec_flags);
163}
164
165static void input_write_keyboard_event(wStream* s, UINT16 flags, UINT16 code)
166{
167 WINPR_ASSERT(s);
168 WINPR_ASSERT(code <= UINT8_MAX);
169
170 Stream_Write_UINT16(s, flags); /* keyboardFlags (2 bytes) */
171 Stream_Write_UINT16(s, code); /* keyCode (2 bytes) */
172 Stream_Write_UINT16(s, 0); /* pad2Octets (2 bytes) */
173}
174
175static BOOL input_send_keyboard_event(rdpInput* input, UINT16 flags, UINT8 code)
176{
177 UINT16 sec_flags = 0;
178 wStream* s = nullptr;
179 rdpRdp* rdp = nullptr;
180
181 if (!input || !input->context)
182 return FALSE;
183
184 rdp = input->context->rdp;
185
186 if (!input_ensure_client_running(input))
187 return FALSE;
188
189 s = rdp_client_input_pdu_init(rdp, INPUT_EVENT_SCANCODE, &sec_flags);
190
191 if (!s)
192 return FALSE;
193
194 input_write_keyboard_event(s, flags, code);
195 return rdp_send_client_input_pdu(rdp, s, sec_flags);
196}
197
198static void input_write_unicode_keyboard_event(wStream* s, UINT16 flags, UINT16 code)
199{
200 Stream_Write_UINT16(s, flags); /* keyboardFlags (2 bytes) */
201 Stream_Write_UINT16(s, code); /* unicodeCode (2 bytes) */
202 Stream_Write_UINT16(s, 0); /* pad2Octets (2 bytes) */
203}
204
205static BOOL input_send_unicode_keyboard_event(rdpInput* input, UINT16 flags, UINT16 code)
206{
207 UINT16 sec_flags = 0;
208 wStream* s = nullptr;
209 rdpRdp* rdp = nullptr;
210
211 if (!input || !input->context)
212 return FALSE;
213
214 if (!input_ensure_client_running(input))
215 return FALSE;
216
217 if (!freerdp_settings_get_bool(input->context->settings, FreeRDP_UnicodeInput))
218 {
219 WLog_WARN(TAG, "Unicode input not supported by server.");
220 return FALSE;
221 }
222
223 rdp = input->context->rdp;
224 s = rdp_client_input_pdu_init(rdp, INPUT_EVENT_UNICODE, &sec_flags);
225
226 if (!s)
227 return FALSE;
228
229 input_write_unicode_keyboard_event(s, flags, code);
230 return rdp_send_client_input_pdu(rdp, s, sec_flags);
231}
232
233static void input_write_mouse_event(wStream* s, UINT16 flags, UINT16 x, UINT16 y)
234{
235 Stream_Write_UINT16(s, flags); /* pointerFlags (2 bytes) */
236 Stream_Write_UINT16(s, x); /* xPos (2 bytes) */
237 Stream_Write_UINT16(s, y); /* yPos (2 bytes) */
238}
239
240static BOOL input_send_mouse_event(rdpInput* input, UINT16 flags, UINT16 x, UINT16 y)
241{
242 UINT16 sec_flags = 0;
243
244 if (!input || !input->context || !input->context->settings)
245 return FALSE;
246
247 rdpRdp* rdp = input->context->rdp;
248
249 if (!input_ensure_client_running(input))
250 return FALSE;
251
252 if (!freerdp_settings_get_bool(input->context->settings, FreeRDP_HasHorizontalWheel))
253 {
254 if (flags & PTR_FLAGS_HWHEEL)
255 {
256 WLog_WARN(TAG,
257 "skip mouse event %" PRIu16 "x%" PRIu16 " flags=0x%04" PRIX16
258 ", no horizontal mouse wheel supported",
259 x, y, flags);
260 return TRUE;
261 }
262 }
263
264 wStream* s = rdp_client_input_pdu_init(rdp, INPUT_EVENT_MOUSE, &sec_flags);
265
266 if (!s)
267 return FALSE;
268
269 input_write_mouse_event(s, flags, x, y);
270 return rdp_send_client_input_pdu(rdp, s, sec_flags);
271}
272
273static BOOL input_send_relmouse_event(rdpInput* input, UINT16 flags, INT16 xDelta, INT16 yDelta)
274{
275 UINT16 sec_flags = 0;
276 wStream* s = nullptr;
277 rdpRdp* rdp = nullptr;
278
279 if (!input || !input->context || !input->context->settings)
280 return FALSE;
281
282 rdp = input->context->rdp;
283
284 if (!input_ensure_client_running(input))
285 return FALSE;
286
287 if (!freerdp_settings_get_bool(input->context->settings, FreeRDP_HasRelativeMouseEvent))
288 {
289 WLog_ERR(TAG, "Sending relative mouse event, but no support for that");
290 return FALSE;
291 }
292
293 s = rdp_client_input_pdu_init(rdp, INPUT_EVENT_MOUSEREL, &sec_flags);
294
295 if (!s)
296 return FALSE;
297
298 Stream_Write_UINT16(s, flags); /* pointerFlags (2 bytes) */
299 Stream_Write_INT16(s, xDelta); /* xDelta (2 bytes) */
300 Stream_Write_INT16(s, yDelta); /* yDelta (2 bytes) */
301
302 return rdp_send_client_input_pdu(rdp, s, sec_flags);
303}
304
305static void input_write_extended_mouse_event(wStream* s, UINT16 flags, UINT16 x, UINT16 y)
306{
307 Stream_Write_UINT16(s, flags); /* pointerFlags (2 bytes) */
308 Stream_Write_UINT16(s, x); /* xPos (2 bytes) */
309 Stream_Write_UINT16(s, y); /* yPos (2 bytes) */
310}
311
312static BOOL input_send_extended_mouse_event(rdpInput* input, UINT16 flags, UINT16 x, UINT16 y)
313{
314 UINT16 sec_flags = 0;
315
316 WINPR_ASSERT(input);
317 WINPR_ASSERT(input->context);
318 WINPR_ASSERT(input->context->settings);
319
320 rdpRdp* rdp = input->context->rdp;
321 WINPR_ASSERT(rdp);
322
323 if (!input_ensure_client_running(input))
324 return FALSE;
325
326 if (!freerdp_settings_get_bool(input->context->settings, FreeRDP_HasExtendedMouseEvent))
327 {
328 WLog_WARN(TAG,
329 "skip extended mouse event %" PRIu16 "x%" PRIu16 " flags=0x%04" PRIX16
330 ", no extended mouse events supported",
331 x, y, flags);
332 return TRUE;
333 }
334
335 wStream* s = rdp_client_input_pdu_init(rdp, INPUT_EVENT_MOUSEX, &sec_flags);
336
337 if (!s)
338 return FALSE;
339
340 input_write_extended_mouse_event(s, flags, x, y);
341 return rdp_send_client_input_pdu(rdp, s, sec_flags);
342}
343
344static BOOL input_send_focus_in_event(rdpInput* input, UINT16 toggleStates)
345{
346 /* send a tab up like mstsc.exe */
347 if (!input_send_keyboard_event(input, KBD_FLAGS_RELEASE, 0x0f))
348 return FALSE;
349
350 /* send the toggle key states */
351 if (!input_send_synchronize_event(input, (toggleStates & 0x1F)))
352 return FALSE;
353
354 /* send another tab up like mstsc.exe */
355 return input_send_keyboard_event(input, KBD_FLAGS_RELEASE, 0x0f);
356}
357
358static BOOL input_send_keyboard_pause_event(rdpInput* input)
359{
360 /* In ancient days, pause-down without control sent E1 1D 45 E1 9D C5,
361 * and pause-up sent nothing. However, reverse engineering mstsc shows
362 * it sending the following sequence:
363 */
364
365 /* Control down (0x1D) */
366 if (!input_send_keyboard_event(input, KBD_FLAGS_EXTENDED1,
367 RDP_SCANCODE_CODE(RDP_SCANCODE_LCONTROL)))
368 return FALSE;
369
370 /* Numlock down (0x45) */
371 if (!input_send_keyboard_event(input, 0, RDP_SCANCODE_CODE(RDP_SCANCODE_NUMLOCK)))
372 return FALSE;
373
374 /* Control up (0x1D) */
375 if (!input_send_keyboard_event(input, KBD_FLAGS_RELEASE | KBD_FLAGS_EXTENDED1,
376 RDP_SCANCODE_CODE(RDP_SCANCODE_LCONTROL)))
377 return FALSE;
378
379 /* Numlock up (0x45) */
380 return input_send_keyboard_event(input, KBD_FLAGS_RELEASE,
381 RDP_SCANCODE_CODE(RDP_SCANCODE_NUMLOCK));
382}
383
384static BOOL input_send_fastpath_synchronize_event(rdpInput* input, UINT32 flags)
385{
386 UINT16 sec_flags = 0;
387 wStream* s = nullptr;
388 rdpRdp* rdp = nullptr;
389
390 WINPR_ASSERT(input);
391 WINPR_ASSERT(input->context);
392
393 rdp = input->context->rdp;
394 WINPR_ASSERT(rdp);
395
396 if (!input_ensure_client_running(input))
397 return FALSE;
398
399 /* The FastPath Synchronization eventFlags has identical values as SlowPath */
400 s = fastpath_input_pdu_init(rdp->fastpath, (BYTE)flags, FASTPATH_INPUT_EVENT_SYNC, &sec_flags);
401
402 if (!s)
403 return FALSE;
404
405 return fastpath_send_input_pdu(rdp->fastpath, s, sec_flags);
406}
407
408static BOOL input_send_fastpath_keyboard_event(rdpInput* input, UINT16 flags, UINT8 code)
409{
410 UINT16 sec_flags = 0;
411 wStream* s = nullptr;
412 BYTE eventFlags = 0;
413 rdpRdp* rdp = nullptr;
414
415 WINPR_ASSERT(input);
416 WINPR_ASSERT(input->context);
417
418 rdp = input->context->rdp;
419 WINPR_ASSERT(rdp);
420
421 if (!input_ensure_client_running(input))
422 return FALSE;
423
424 eventFlags |= (flags & KBD_FLAGS_RELEASE) ? FASTPATH_INPUT_KBDFLAGS_RELEASE : 0;
425 eventFlags |= (flags & KBD_FLAGS_EXTENDED) ? FASTPATH_INPUT_KBDFLAGS_EXTENDED : 0;
426 eventFlags |= (flags & KBD_FLAGS_EXTENDED1) ? FASTPATH_INPUT_KBDFLAGS_PREFIX_E1 : 0;
427 s = fastpath_input_pdu_init(rdp->fastpath, eventFlags, FASTPATH_INPUT_EVENT_SCANCODE,
428 &sec_flags);
429
430 if (!s)
431 return FALSE;
432
433 WINPR_ASSERT(code <= UINT8_MAX);
434 Stream_Write_UINT8(s, code); /* keyCode (1 byte) */
435 return fastpath_send_input_pdu(rdp->fastpath, s, sec_flags);
436}
437
438static BOOL input_send_fastpath_unicode_keyboard_event(rdpInput* input, UINT16 flags, UINT16 code)
439{
440 UINT16 sec_flags = 0;
441 wStream* s = nullptr;
442 BYTE eventFlags = 0;
443 rdpRdp* rdp = nullptr;
444
445 WINPR_ASSERT(input);
446 WINPR_ASSERT(input->context);
447 WINPR_ASSERT(input->context->settings);
448
449 rdp = input->context->rdp;
450 WINPR_ASSERT(rdp);
451
452 if (!input_ensure_client_running(input))
453 return FALSE;
454
455 if (!freerdp_settings_get_bool(input->context->settings, FreeRDP_UnicodeInput))
456 {
457 WLog_WARN(TAG, "Unicode input not supported by server.");
458 return FALSE;
459 }
460
461 eventFlags |= (flags & KBD_FLAGS_RELEASE) ? FASTPATH_INPUT_KBDFLAGS_RELEASE : 0;
462 s = fastpath_input_pdu_init(rdp->fastpath, eventFlags, FASTPATH_INPUT_EVENT_UNICODE,
463 &sec_flags);
464
465 if (!s)
466 return FALSE;
467
468 Stream_Write_UINT16(s, code); /* unicodeCode (2 bytes) */
469 return fastpath_send_input_pdu(rdp->fastpath, s, sec_flags);
470}
471
472static BOOL input_send_fastpath_mouse_event(rdpInput* input, UINT16 flags, UINT16 x, UINT16 y)
473{
474 UINT16 sec_flags = 0;
475 wStream* s = nullptr;
476 rdpRdp* rdp = nullptr;
477
478 WINPR_ASSERT(input);
479 WINPR_ASSERT(input->context);
480 WINPR_ASSERT(input->context->settings);
481
482 rdp = input->context->rdp;
483 WINPR_ASSERT(rdp);
484
485 if (!input_ensure_client_running(input))
486 return FALSE;
487
488 if (!freerdp_settings_get_bool(input->context->settings, FreeRDP_HasHorizontalWheel))
489 {
490 if (flags & PTR_FLAGS_HWHEEL)
491 {
492 WLog_WARN(TAG,
493 "skip mouse event %" PRIu16 "x%" PRIu16 " flags=0x%04" PRIX16
494 ", no horizontal mouse wheel supported",
495 x, y, flags);
496 return TRUE;
497 }
498 }
499
500 s = fastpath_input_pdu_init(rdp->fastpath, 0, FASTPATH_INPUT_EVENT_MOUSE, &sec_flags);
501
502 if (!s)
503 return FALSE;
504
505 input_write_mouse_event(s, flags, x, y);
506 return fastpath_send_input_pdu(rdp->fastpath, s, sec_flags);
507}
508
509static BOOL input_send_fastpath_extended_mouse_event(rdpInput* input, UINT16 flags, UINT16 x,
510 UINT16 y)
511{
512 UINT16 sec_flags = 0;
513 wStream* s = nullptr;
514 rdpRdp* rdp = nullptr;
515
516 WINPR_ASSERT(input);
517 WINPR_ASSERT(input->context);
518
519 rdp = input->context->rdp;
520 WINPR_ASSERT(rdp);
521
522 if (!input_ensure_client_running(input))
523 return FALSE;
524
525 if (!freerdp_settings_get_bool(input->context->settings, FreeRDP_HasExtendedMouseEvent))
526 {
527 WLog_WARN(TAG,
528 "skip extended mouse event %" PRIu16 "x%" PRIu16 " flags=0x%04" PRIX16
529 ", no extended mouse events supported",
530 x, y, flags);
531 return TRUE;
532 }
533
534 s = fastpath_input_pdu_init(rdp->fastpath, 0, FASTPATH_INPUT_EVENT_MOUSEX, &sec_flags);
535
536 if (!s)
537 return FALSE;
538
539 input_write_extended_mouse_event(s, flags, x, y);
540 return fastpath_send_input_pdu(rdp->fastpath, s, sec_flags);
541}
542
543static BOOL input_send_fastpath_relmouse_event(rdpInput* input, UINT16 flags, INT16 xDelta,
544 INT16 yDelta)
545{
546 UINT16 sec_flags = 0;
547 wStream* s = nullptr;
548 rdpRdp* rdp = nullptr;
549
550 WINPR_ASSERT(input);
551 WINPR_ASSERT(input->context);
552 WINPR_ASSERT(input->context->settings);
553
554 rdp = input->context->rdp;
555 WINPR_ASSERT(rdp);
556
557 if (!input_ensure_client_running(input))
558 return FALSE;
559
560 if (!freerdp_settings_get_bool(input->context->settings, FreeRDP_HasRelativeMouseEvent))
561 {
562 WLog_ERR(TAG, "Sending relative fastpath mouse event, but no support for that announced");
563 return FALSE;
564 }
565
566 s = fastpath_input_pdu_init(rdp->fastpath, 0, TS_FP_RELPOINTER_EVENT, &sec_flags);
567
568 if (!s)
569 return FALSE;
570
571 Stream_Write_UINT16(s, flags); /* pointerFlags (2 bytes) */
572 Stream_Write_INT16(s, xDelta); /* xDelta (2 bytes) */
573 Stream_Write_INT16(s, yDelta); /* yDelta (2 bytes) */
574 return fastpath_send_input_pdu(rdp->fastpath, s, sec_flags);
575}
576
577static BOOL input_send_fastpath_qoe_event(rdpInput* input, UINT32 timestampMS)
578{
579 WINPR_ASSERT(input);
580 WINPR_ASSERT(input->context);
581 WINPR_ASSERT(input->context->settings);
582
583 rdpRdp* rdp = input->context->rdp;
584 WINPR_ASSERT(rdp);
585
586 if (!input_ensure_client_running(input))
587 return FALSE;
588
589 if (!freerdp_settings_get_bool(input->context->settings, FreeRDP_HasQoeEvent))
590 {
591 WLog_ERR(TAG, "Sending qoe event, but no support for that announced");
592 return FALSE;
593 }
594
595 UINT16 sec_flags = 0;
596 wStream* s = fastpath_input_pdu_init(rdp->fastpath, 0, TS_FP_QOETIMESTAMP_EVENT, &sec_flags);
597
598 if (!s)
599 return FALSE;
600
601 if (!Stream_EnsureRemainingCapacity(s, 4))
602 {
603 Stream_Free(s, TRUE);
604 return FALSE;
605 }
606
607 Stream_Write_UINT32(s, timestampMS);
608 return fastpath_send_input_pdu(rdp->fastpath, s, sec_flags);
609}
610
611static BOOL input_send_fastpath_focus_in_event(rdpInput* input, UINT16 toggleStates)
612{
613 UINT16 sec_flags = 0;
614 wStream* s = nullptr;
615 BYTE eventFlags = 0;
616 rdpRdp* rdp = nullptr;
617
618 WINPR_ASSERT(input);
619 WINPR_ASSERT(input->context);
620
621 rdp = input->context->rdp;
622 WINPR_ASSERT(rdp);
623
624 if (!input_ensure_client_running(input))
625 return FALSE;
626
627 s = fastpath_input_pdu_init_header(rdp->fastpath, &sec_flags);
628
629 if (!s)
630 return FALSE;
631
632 /* send a tab up like mstsc.exe */
633 eventFlags = FASTPATH_INPUT_KBDFLAGS_RELEASE | FASTPATH_INPUT_EVENT_SCANCODE << 5;
634 Stream_Write_UINT8(s, eventFlags); /* Key Release event (1 byte) */
635 Stream_Write_UINT8(s, 0x0f); /* keyCode (1 byte) */
636 /* send the toggle key states */
637 eventFlags = (toggleStates & 0x1F) | FASTPATH_INPUT_EVENT_SYNC << 5;
638 Stream_Write_UINT8(s, eventFlags); /* toggle state (1 byte) */
639 /* send another tab up like mstsc.exe */
640 eventFlags = FASTPATH_INPUT_KBDFLAGS_RELEASE | FASTPATH_INPUT_EVENT_SCANCODE << 5;
641 Stream_Write_UINT8(s, eventFlags); /* Key Release event (1 byte) */
642 Stream_Write_UINT8(s, 0x0f); /* keyCode (1 byte) */
643 return fastpath_send_multiple_input_pdu(rdp->fastpath, s, 3, sec_flags);
644}
645
646static BOOL input_send_fastpath_keyboard_pause_event(rdpInput* input)
647{
648 /* In ancient days, pause-down without control sent E1 1D 45 E1 9D C5,
649 * and pause-up sent nothing. However, reverse engineering mstsc shows
650 * it sending the following sequence:
651 */
652 UINT16 sec_flags = 0;
653 wStream* s = nullptr;
654 const BYTE keyDownEvent = FASTPATH_INPUT_EVENT_SCANCODE << 5;
655 const BYTE keyUpEvent = (FASTPATH_INPUT_EVENT_SCANCODE << 5) | FASTPATH_INPUT_KBDFLAGS_RELEASE;
656 rdpRdp* rdp = nullptr;
657
658 WINPR_ASSERT(input);
659 WINPR_ASSERT(input->context);
660
661 rdp = input->context->rdp;
662 WINPR_ASSERT(rdp);
663
664 if (!input_ensure_client_running(input))
665 return FALSE;
666
667 s = fastpath_input_pdu_init_header(rdp->fastpath, &sec_flags);
668
669 if (!s)
670 return FALSE;
671
672 /* Control down (0x1D) */
673 Stream_Write_UINT8(s, keyDownEvent | FASTPATH_INPUT_KBDFLAGS_PREFIX_E1);
674 Stream_Write_UINT8(s, RDP_SCANCODE_CODE(RDP_SCANCODE_LCONTROL));
675 /* Numlock down (0x45) */
676 Stream_Write_UINT8(s, keyDownEvent);
677 Stream_Write_UINT8(s, RDP_SCANCODE_CODE(RDP_SCANCODE_NUMLOCK));
678 /* Control up (0x1D) */
679 Stream_Write_UINT8(s, keyUpEvent | FASTPATH_INPUT_KBDFLAGS_PREFIX_E1);
680 Stream_Write_UINT8(s, RDP_SCANCODE_CODE(RDP_SCANCODE_LCONTROL));
681 /* Numlock down (0x45) */
682 Stream_Write_UINT8(s, keyUpEvent);
683 Stream_Write_UINT8(s, RDP_SCANCODE_CODE(RDP_SCANCODE_NUMLOCK));
684 return fastpath_send_multiple_input_pdu(rdp->fastpath, s, 4, sec_flags);
685}
686
687static BOOL input_recv_sync_event(rdpInput* input, wStream* s)
688{
689 UINT32 toggleFlags = 0;
690
691 WINPR_ASSERT(input);
692 WINPR_ASSERT(s);
693
694 if (!Stream_CheckAndLogRequiredLength(TAG, s, 6))
695 return FALSE;
696
697 Stream_Seek(s, 2); /* pad2Octets (2 bytes) */
698 Stream_Read_UINT32(s, toggleFlags); /* toggleFlags (4 bytes) */
699
700 /* only pass input if we are actually connected */
701 if (!rdp_has_reached_state(input->context->rdp, CONNECTION_STATE_ACTIVE))
702 return TRUE;
703 return IFCALLRESULT(TRUE, input->SynchronizeEvent, input, toggleFlags);
704}
705
706static BOOL input_recv_keyboard_event(rdpInput* input, wStream* s)
707{
708 UINT16 keyboardFlags = 0;
709 UINT16 keyCode = 0;
710
711 WINPR_ASSERT(input);
712 WINPR_ASSERT(s);
713
714 if (!Stream_CheckAndLogRequiredLength(TAG, s, 6))
715 return FALSE;
716
717 Stream_Read_UINT16(s, keyboardFlags); /* keyboardFlags (2 bytes) */
718 Stream_Read_UINT16(s, keyCode); /* keyCode (2 bytes) */
719 Stream_Seek(s, 2); /* pad2Octets (2 bytes) */
720
721 if (keyboardFlags & KBD_FLAGS_RELEASE)
722 keyboardFlags &= ~KBD_FLAGS_DOWN;
723
724 if (keyCode & 0xFF00)
725 WLog_WARN(TAG,
726 "Problematic [MS-RDPBCGR] 2.2.8.1.1.3.1.1.1 Keyboard Event (TS_KEYBOARD_EVENT) "
727 "keyCode=0x%04" PRIx16
728 ", high byte values should be sent in keyboardFlags field, ignoring.",
729 keyCode);
730
731 /* only pass input if we are actually connected */
732 if (!rdp_has_reached_state(input->context->rdp, CONNECTION_STATE_ACTIVE))
733 return TRUE;
734 return IFCALLRESULT(TRUE, input->KeyboardEvent, input, keyboardFlags, keyCode & 0xFF);
735}
736
737static BOOL input_recv_unicode_keyboard_event(rdpInput* input, wStream* s)
738{
739 UINT16 keyboardFlags = 0;
740 UINT16 unicodeCode = 0;
741
742 WINPR_ASSERT(input);
743 WINPR_ASSERT(s);
744
745 if (!Stream_CheckAndLogRequiredLength(TAG, s, 6))
746 return FALSE;
747
748 Stream_Read_UINT16(s, keyboardFlags); /* keyboardFlags (2 bytes) */
749 Stream_Read_UINT16(s, unicodeCode); /* unicodeCode (2 bytes) */
750 Stream_Seek(s, 2); /* pad2Octets (2 bytes) */
751
752 /* "fix" keyboardFlags - see comment in input_recv_keyboard_event() */
753
754 if (keyboardFlags & KBD_FLAGS_RELEASE)
755 keyboardFlags &= ~KBD_FLAGS_DOWN;
756
757 /* only pass input if we are actually connected */
758 if (!rdp_has_reached_state(input->context->rdp, CONNECTION_STATE_ACTIVE))
759 return TRUE;
760 return IFCALLRESULT(TRUE, input->UnicodeKeyboardEvent, input, keyboardFlags, unicodeCode);
761}
762
763static BOOL input_recv_mouse_event(rdpInput* input, wStream* s)
764{
765 UINT16 pointerFlags = 0;
766 UINT16 xPos = 0;
767 UINT16 yPos = 0;
768
769 WINPR_ASSERT(input);
770 WINPR_ASSERT(s);
771
772 if (!Stream_CheckAndLogRequiredLength(TAG, s, 6))
773 return FALSE;
774
775 Stream_Read_UINT16(s, pointerFlags); /* pointerFlags (2 bytes) */
776 Stream_Read_UINT16(s, xPos); /* xPos (2 bytes) */
777 Stream_Read_UINT16(s, yPos); /* yPos (2 bytes) */
778
779 /* only pass input if we are actually connected */
780 if (!rdp_has_reached_state(input->context->rdp, CONNECTION_STATE_ACTIVE))
781 return TRUE;
782 return IFCALLRESULT(TRUE, input->MouseEvent, input, pointerFlags, xPos, yPos);
783}
784
785static BOOL input_recv_relmouse_event(rdpInput* input, wStream* s)
786{
787 UINT16 pointerFlags = 0;
788 INT16 xDelta = 0;
789 INT16 yDelta = 0;
790
791 WINPR_ASSERT(input);
792 WINPR_ASSERT(s);
793
794 if (!Stream_CheckAndLogRequiredLength(TAG, s, 6))
795 return FALSE;
796
797 Stream_Read_UINT16(s, pointerFlags); /* pointerFlags (2 bytes) */
798 Stream_Read_INT16(s, xDelta); /* xPos (2 bytes) */
799 Stream_Read_INT16(s, yDelta); /* yPos (2 bytes) */
800
801 if (!freerdp_settings_get_bool(input->context->settings, FreeRDP_HasRelativeMouseEvent))
802 {
803 WLog_ERR(TAG,
804 "Received relative mouse event(flags=0x%04" PRIx16 ", xPos=%" PRId16
805 ", yPos=%" PRId16 "), but we did not announce support for that",
806 pointerFlags, xDelta, yDelta);
807 return FALSE;
808 }
809
810 /* only pass input if we are actually connected */
811 if (!rdp_has_reached_state(input->context->rdp, CONNECTION_STATE_ACTIVE))
812 return TRUE;
813
814 return IFCALLRESULT(TRUE, input->RelMouseEvent, input, pointerFlags, xDelta, yDelta);
815}
816
817static BOOL input_recv_extended_mouse_event(rdpInput* input, wStream* s)
818{
819 UINT16 pointerFlags = 0;
820 UINT16 xPos = 0;
821 UINT16 yPos = 0;
822
823 WINPR_ASSERT(input);
824 WINPR_ASSERT(s);
825
826 if (!Stream_CheckAndLogRequiredLength(TAG, s, 6))
827 return FALSE;
828
829 Stream_Read_UINT16(s, pointerFlags); /* pointerFlags (2 bytes) */
830 Stream_Read_UINT16(s, xPos); /* xPos (2 bytes) */
831 Stream_Read_UINT16(s, yPos); /* yPos (2 bytes) */
832
833 if (!freerdp_settings_get_bool(input->context->settings, FreeRDP_HasExtendedMouseEvent))
834 {
835 WLog_ERR(TAG,
836 "Received extended mouse event(flags=0x%04" PRIx16 ", xPos=%" PRIu16
837 ", yPos=%" PRIu16 "), but we did not announce support for that",
838 pointerFlags, xPos, yPos);
839 return FALSE;
840 }
841
842 /* only pass input if we are actually connected */
843 if (!rdp_has_reached_state(input->context->rdp, CONNECTION_STATE_ACTIVE))
844 return TRUE;
845 return IFCALLRESULT(TRUE, input->ExtendedMouseEvent, input, pointerFlags, xPos, yPos);
846}
847
848static BOOL input_recv_event(rdpInput* input, wStream* s)
849{
850 UINT16 messageType = 0;
851
852 WINPR_ASSERT(input);
853 WINPR_ASSERT(s);
854
855 if (!Stream_CheckAndLogRequiredLength(TAG, s, 6))
856 return FALSE;
857
858 Stream_Seek(s, 4); /* eventTime (4 bytes), ignored by the server */
859 Stream_Read_UINT16(s, messageType); /* messageType (2 bytes) */
860
861 switch (messageType)
862 {
863 case INPUT_EVENT_SYNC:
864 if (!input_recv_sync_event(input, s))
865 return FALSE;
866
867 break;
868
869 case INPUT_EVENT_SCANCODE:
870 if (!input_recv_keyboard_event(input, s))
871 return FALSE;
872
873 break;
874
875 case INPUT_EVENT_UNICODE:
876 if (!input_recv_unicode_keyboard_event(input, s))
877 return FALSE;
878
879 break;
880
881 case INPUT_EVENT_MOUSE:
882 if (!input_recv_mouse_event(input, s))
883 return FALSE;
884
885 break;
886
887 case INPUT_EVENT_MOUSEX:
888 if (!input_recv_extended_mouse_event(input, s))
889 return FALSE;
890
891 break;
892
893 case INPUT_EVENT_MOUSEREL:
894 if (!input_recv_relmouse_event(input, s))
895 return FALSE;
896
897 break;
898
899 default:
900 WLog_ERR(TAG, "Unknown messageType %" PRIu16 "", messageType);
901 /* Each input event uses 6 bytes. */
902 Stream_Seek(s, 6);
903 break;
904 }
905
906 return TRUE;
907}
908
909BOOL input_recv(rdpInput* input, wStream* s)
910{
911 UINT16 numberEvents = 0;
912
913 WINPR_ASSERT(input);
914 WINPR_ASSERT(s);
915
916 if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
917 return FALSE;
918
919 Stream_Read_UINT16(s, numberEvents); /* numberEvents (2 bytes) */
920 Stream_Seek(s, 2); /* pad2Octets (2 bytes) */
921
922 /* Each input event uses 6 exactly bytes. */
923 if (!Stream_CheckAndLogRequiredLengthOfSize(TAG, s, numberEvents, 6ull))
924 return FALSE;
925
926 for (UINT16 i = 0; i < numberEvents; i++)
927 {
928 if (!input_recv_event(input, s))
929 return FALSE;
930 }
931
932 return TRUE;
933}
934
935BOOL input_register_client_callbacks(rdpInput* input)
936{
937 rdpSettings* settings = nullptr;
938
939 if (!input->context)
940 return FALSE;
941
942 settings = input->context->settings;
943
944 if (!settings)
945 return FALSE;
946
947 if (freerdp_settings_get_bool(settings, FreeRDP_FastPathInput))
948 {
949 input->SynchronizeEvent = input_send_fastpath_synchronize_event;
950 input->KeyboardEvent = input_send_fastpath_keyboard_event;
951 input->KeyboardPauseEvent = input_send_fastpath_keyboard_pause_event;
952 input->UnicodeKeyboardEvent = input_send_fastpath_unicode_keyboard_event;
953 input->MouseEvent = input_send_fastpath_mouse_event;
954 input->RelMouseEvent = input_send_fastpath_relmouse_event;
955 input->ExtendedMouseEvent = input_send_fastpath_extended_mouse_event;
956 input->FocusInEvent = input_send_fastpath_focus_in_event;
957 input->QoEEvent = input_send_fastpath_qoe_event;
958 }
959 else
960 {
961 input->SynchronizeEvent = input_send_synchronize_event;
962 input->KeyboardEvent = input_send_keyboard_event;
963 input->KeyboardPauseEvent = input_send_keyboard_pause_event;
964 input->UnicodeKeyboardEvent = input_send_unicode_keyboard_event;
965 input->MouseEvent = input_send_mouse_event;
966 input->RelMouseEvent = input_send_relmouse_event;
967 input->ExtendedMouseEvent = input_send_extended_mouse_event;
968 input->FocusInEvent = input_send_focus_in_event;
969 }
970
971 return TRUE;
972}
973
974/* Save last input timestamp and/or mouse position in prevent-session-lock mode */
975static BOOL input_update_last_event(rdpInput* input, BOOL mouse, UINT16 x, UINT16 y)
976{
977 rdp_input_internal* in = input_cast(input);
978
979 WINPR_ASSERT(input);
980 WINPR_ASSERT(input->context);
981
982 if (freerdp_settings_get_uint32(input->context->settings, FreeRDP_FakeMouseMotionInterval) > 0)
983 {
984 const time_t now = time(nullptr);
985 in->lastInputTimestamp = WINPR_ASSERTING_INT_CAST(UINT64, now);
986
987 if (mouse)
988 {
989 in->lastX = x;
990 in->lastY = y;
991 }
992 }
993 return TRUE;
994}
995
996WINPR_ATTR_NODISCARD
997static const char* boolstr(bool value)
998{
999 if (value)
1000 return "true";
1001 return "false";
1002}
1003
1004WINPR_ATTR_NODISCARD
1005WINPR_ATTR_FORMAT_ARG(2, 3)
1006static BOOL input_skip(const rdpInput* input, WINPR_FORMAT_ARG const char* fmt, ...)
1007{
1008 WINPR_ASSERT(input);
1009
1010 const rdpContext* context = input->context;
1011 WINPR_ASSERT(context);
1012
1013 const rdp_input_internal* in = (const rdp_input_internal*)input;
1014 WINPR_ASSERT(in);
1015
1016 const BOOL suspended = freerdp_settings_get_bool(context->settings, FreeRDP_SuspendInput);
1017 const BOOL connected = freerdp_is_active_state(context);
1018 const DWORD loglevel = WLOG_DEBUG;
1019
1020 if (WLog_IsLevelActive(in->log, loglevel))
1021 {
1022 char buffer[128] = WINPR_C_ARRAY_INIT;
1023
1024 va_list args = WINPR_C_ARRAY_INIT;
1025 va_start(args, fmt);
1026 (void)vsnprintf(buffer, sizeof(buffer), fmt, args);
1027 va_end(args);
1028
1029 WLog_Print(in->log, loglevel, "[connected=%s, suspended=%s] %s", boolstr(connected),
1030 boolstr(suspended), buffer);
1031 }
1032
1033 return suspended || !connected;
1034}
1035
1036BOOL freerdp_input_send_synchronize_event(rdpInput* input, UINT32 flags)
1037{
1038 if (!input || !input->context)
1039 return FALSE;
1040
1041 char buffer[128] = WINPR_C_ARRAY_INIT;
1042 if (input_skip(input, "Keyboard {Sync} [%s]",
1043 freerdp_input_keyboard_flags_string(flags, buffer, sizeof(buffer))))
1044 return TRUE;
1045
1046 return IFCALLRESULT(TRUE, input->SynchronizeEvent, input, flags);
1047}
1048
1049BOOL freerdp_input_send_keyboard_event(rdpInput* input, UINT16 flags, UINT8 code)
1050{
1051 if (!input || !input->context)
1052 return FALSE;
1053
1054 char buffer[128] = WINPR_C_ARRAY_INIT;
1055 if (input_skip(input, "Keyboard {Event} [%s]",
1056 freerdp_input_keypress_flags_string(flags, buffer, sizeof(buffer))))
1057 return TRUE;
1058
1059 input_update_last_event(input, FALSE, 0, 0);
1060
1061 return IFCALLRESULT(TRUE, input->KeyboardEvent, input, flags, code);
1062}
1063
1064BOOL freerdp_input_send_keyboard_event_ex(rdpInput* input, BOOL down, BOOL repeat,
1065 UINT32 rdp_scancode)
1066{
1067 UINT16 flags = (RDP_SCANCODE_EXTENDED(rdp_scancode) ? KBD_FLAGS_EXTENDED : 0);
1068 if (down && repeat)
1069 flags |= KBD_FLAGS_DOWN;
1070 else if (!down)
1071 flags |= KBD_FLAGS_RELEASE;
1072
1073 return freerdp_input_send_keyboard_event(input, flags, RDP_SCANCODE_CODE(rdp_scancode));
1074}
1075
1076BOOL freerdp_input_send_unicode_keyboard_event(rdpInput* input, UINT16 flags, UINT16 code)
1077{
1078 if (!input || !input->context)
1079 return FALSE;
1080
1081 char buffer[128] = WINPR_C_ARRAY_INIT;
1082 if (input_skip(input, "Keyboard {Unicode} [%s]",
1083 freerdp_input_keypress_flags_string(flags, buffer, ARRAYSIZE(buffer))))
1084 return TRUE;
1085
1086 input_update_last_event(input, FALSE, 0, 0);
1087
1088 return IFCALLRESULT(TRUE, input->UnicodeKeyboardEvent, input, flags, code);
1089}
1090
1091BOOL freerdp_input_send_mouse_event(rdpInput* input, UINT16 flags, UINT16 x, UINT16 y)
1092{
1093 if (!input || !input->context)
1094 return FALSE;
1095
1096 char buffer[128] = WINPR_C_ARRAY_INIT;
1097 if (input_skip(input, "Mouse {Event} [%s]",
1098 freerdp_input_mouse_flags_string(flags, false, buffer, ARRAYSIZE(buffer))))
1099 return TRUE;
1100
1101 input_update_last_event(
1102 input, flags & (PTR_FLAGS_MOVE | PTR_FLAGS_BUTTON1 | PTR_FLAGS_BUTTON2 | PTR_FLAGS_BUTTON3),
1103 x, y);
1104
1105 return IFCALLRESULT(TRUE, input->MouseEvent, input, flags, x, y);
1106}
1107
1108BOOL freerdp_input_send_rel_mouse_event(rdpInput* input, UINT16 flags, INT16 xDelta, INT16 yDelta)
1109{
1110 if (!input || !input->context)
1111 return FALSE;
1112
1113 char buffer[128] = WINPR_C_ARRAY_INIT;
1114 if (input_skip(input, "Mouse {RelativeEvent} [%s]",
1115 freerdp_input_mouse_flags_string(flags, true, buffer, ARRAYSIZE(buffer))))
1116 return TRUE;
1117
1118 return IFCALLRESULT(TRUE, input->RelMouseEvent, input, flags, xDelta, yDelta);
1119}
1120
1121BOOL freerdp_input_send_qoe_timestamp(rdpInput* input, UINT32 timestampMS)
1122{
1123 if (!input || !input->context)
1124 return FALSE;
1125
1126 return IFCALLRESULT(TRUE, input->QoEEvent, input, timestampMS);
1127}
1128
1129BOOL freerdp_input_send_extended_mouse_event(rdpInput* input, UINT16 flags, UINT16 x, UINT16 y)
1130{
1131 if (!input || !input->context)
1132 return FALSE;
1133
1134 char buffer[128] = WINPR_C_ARRAY_INIT;
1135 if (input_skip(input, "Mouse {ExtendedEvent} %s",
1136 freerdp_input_mouse_flags_string(flags, true, buffer, ARRAYSIZE(buffer))))
1137 return TRUE;
1138
1139 input_update_last_event(input, TRUE, x, y);
1140
1141 return IFCALLRESULT(TRUE, input->ExtendedMouseEvent, input, flags, x, y);
1142}
1143
1144BOOL freerdp_input_send_focus_in_event(rdpInput* input, UINT16 toggleStates)
1145{
1146 if (!input || !input->context)
1147 return FALSE;
1148
1149 char buffer[128] = WINPR_C_ARRAY_INIT;
1150 if (input_skip(input, "Keyboard {FocusIn} [%s]",
1151 freerdp_input_keyboard_flags_string(toggleStates, buffer, sizeof(buffer))))
1152 return TRUE;
1153
1154 return IFCALLRESULT(TRUE, input->FocusInEvent, input, toggleStates);
1155}
1156
1157BOOL freerdp_input_send_keyboard_pause_event(rdpInput* input)
1158{
1159 if (!input || !input->context)
1160 return FALSE;
1161
1162 if (input_skip(input, "Keyboard {Pause}"))
1163 return TRUE;
1164
1165 return IFCALLRESULT(TRUE, input->KeyboardPauseEvent, input);
1166}
1167
1168int input_process_events(rdpInput* input)
1169{
1170 if (!input)
1171 return FALSE;
1172
1173 return input_message_queue_process_pending_messages(input);
1174}
1175
1176static void input_free_queued_message(void* obj)
1177{
1178 wMessage* msg = (wMessage*)obj;
1179 input_message_queue_free_message(msg);
1180}
1181
1182rdpInput* input_new(rdpRdp* rdp)
1183{
1184 const wObject cb = { nullptr, nullptr, nullptr, input_free_queued_message, nullptr };
1185 rdp_input_internal* input = (rdp_input_internal*)calloc(1, sizeof(rdp_input_internal));
1186
1187 WINPR_UNUSED(rdp);
1188
1189 if (!input)
1190 return nullptr;
1191
1192 input->common.context = rdp->context;
1193 input->queue = MessageQueue_New(&cb);
1194 input->log = WLog_Get(TAG);
1195
1196 if (!input->queue)
1197 {
1198 free(input);
1199 return nullptr;
1200 }
1201
1202 return &input->common;
1203}
1204
1205void input_free(rdpInput* input)
1206{
1207 if (input != nullptr)
1208 {
1209 rdp_input_internal* in = input_cast(input);
1210
1211 MessageQueue_Free(in->queue);
1212 free(in);
1213 }
1214}
1215
1216static const char* flag2str(uint32_t flag, bool extended)
1217{
1218#define EVCASE(x) \
1219 case x: \
1220 return #x
1221 if (extended)
1222 {
1223 switch (flag)
1224 {
1225 /* Extended Pointer Flags */
1226 EVCASE(PTR_XFLAGS_DOWN);
1227 EVCASE(PTR_XFLAGS_BUTTON1);
1228 EVCASE(PTR_XFLAGS_BUTTON2);
1229 default:
1230 return "PTR_XFLAGS_UNKNOWN";
1231 }
1232 }
1233
1234 switch (flag)
1235 {
1236 /* Pointer Flags */
1237 EVCASE(PTR_FLAGS_HWHEEL);
1238 EVCASE(PTR_FLAGS_WHEEL);
1239 EVCASE(PTR_FLAGS_WHEEL_NEGATIVE);
1240 EVCASE(PTR_FLAGS_MOVE);
1241 EVCASE(PTR_FLAGS_DOWN);
1242 EVCASE(PTR_FLAGS_BUTTON1);
1243 EVCASE(PTR_FLAGS_BUTTON2);
1244 EVCASE(PTR_FLAGS_BUTTON3);
1245 default:
1246 return "PTR_FLAGS_UNKNOWN";
1247 }
1248#undef EVCASE
1249}
1250
1251const char* freerdp_input_mouse_flags_string(uint32_t flags, bool extended, char* buffer,
1252 size_t len)
1253{
1254 if (len <= 2)
1255 return nullptr;
1256
1257 if (!winpr_str_append("{", buffer, len, ""))
1258 return nullptr;
1259
1260 /* Strip initial symbol so we do not get duplicate separators */
1261 for (size_t x = 0; x < 32; x++)
1262 {
1263 const uint32_t flag = 1u << x;
1264 if (flags & flag)
1265 {
1266 char ibuffer[64] = WINPR_C_ARRAY_INIT;
1267 (void)_snprintf(ibuffer, sizeof(ibuffer), "%s", flag2str(flag, extended));
1268 if (!winpr_str_append(ibuffer, &buffer[1], len - 2, "|"))
1269 return nullptr;
1270 }
1271 }
1272 if (!winpr_str_append("}", &buffer[1], len - 2, ""))
1273 return nullptr;
1274
1275 return buffer;
1276}
1277
1278static const char* kbdflag2str(uint32_t flag)
1279{
1280#define EVCASE(x) \
1281 case x: \
1282 return #x
1283
1284 switch (flag)
1285 {
1286 /* Pointer Flags */
1287 EVCASE(KBD_FLAGS_EXTENDED);
1288 EVCASE(KBD_FLAGS_EXTENDED1);
1289 EVCASE(KBD_FLAGS_DOWN);
1290 EVCASE(KBD_FLAGS_RELEASE);
1291 default:
1292 return "KBD_FLAGS_UNKNOWN";
1293 }
1294#undef EVCASE
1295}
1296
1297const char* freerdp_input_keypress_flags_string(uint32_t flags, char* buffer, size_t len)
1298{
1299 if (len <= 2)
1300 return nullptr;
1301
1302 if (!winpr_str_append("{", buffer, len, ""))
1303 return nullptr;
1304
1305 /* Strip initial symbol so we do not get duplicate separators */
1306 for (size_t x = 0; x < 32; x++)
1307 {
1308 const uint32_t flag = 1u << x;
1309 if (flags & flag)
1310 {
1311 char ibuffer[64] = WINPR_C_ARRAY_INIT;
1312 (void)_snprintf(ibuffer, sizeof(ibuffer), "%s", kbdflag2str(flag));
1313 if (!winpr_str_append(ibuffer, &buffer[1], len - 2, "|"))
1314 return nullptr;
1315 }
1316 }
1317 if (!winpr_str_append("}", &buffer[1], len - 2, ""))
1318 return nullptr;
1319
1320 return buffer;
1321}
WINPR_ATTR_NODISCARD FREERDP_API UINT32 freerdp_settings_get_uint32(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id)
Returns a UINT32 settings value.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.
This struct contains function pointer to initialize/free objects.
Definition collections.h:52