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 WINPR_ASSERT(input);
387 WINPR_ASSERT(input->context);
388
389 /* The FastPath Synchronization eventFlags has identical values as SlowPath,
390 * but the field is only 5 bits wide. Drop everything else, a value with
391 * undefined bits set may have been received from a peer (proxy) */
392 const UINT32 mask =
393 KBD_SYNC_SCROLL_LOCK | KBD_SYNC_NUM_LOCK | KBD_SYNC_CAPS_LOCK | KBD_SYNC_KANA_LOCK;
394 if (flags & ~mask)
395 WLog_WARN(TAG,
396 "Problematic [MS-RDPBCGR] 2.2.8.1.2.2.5 Fast-Path Synchronize Event "
397 "(TS_FP_SYNC_EVENT) flags=0x%08" PRIx32 ", ignoring undefined bits.",
398 flags);
399 rdpRdp* rdp = input->context->rdp;
400 WINPR_ASSERT(rdp);
401
402 if (!input_ensure_client_running(input))
403 return FALSE;
404
405 /* The FastPath Synchronization eventFlags has identical values as SlowPath */
406 UINT16 sec_flags = 0;
407 wStream* s =
408 fastpath_input_pdu_init(rdp->fastpath, (BYTE)flags, FASTPATH_INPUT_EVENT_SYNC, &sec_flags);
409
410 if (!s)
411 return FALSE;
412
413 return fastpath_send_input_pdu(rdp->fastpath, s, sec_flags);
414}
415
416static BOOL input_send_fastpath_keyboard_event(rdpInput* input, UINT16 flags, UINT8 code)
417{
418 UINT16 sec_flags = 0;
419 wStream* s = nullptr;
420 BYTE eventFlags = 0;
421 rdpRdp* rdp = nullptr;
422
423 WINPR_ASSERT(input);
424 WINPR_ASSERT(input->context);
425
426 rdp = input->context->rdp;
427 WINPR_ASSERT(rdp);
428
429 if (!input_ensure_client_running(input))
430 return FALSE;
431
432 eventFlags |= (flags & KBD_FLAGS_RELEASE) ? FASTPATH_INPUT_KBDFLAGS_RELEASE : 0;
433 eventFlags |= (flags & KBD_FLAGS_EXTENDED) ? FASTPATH_INPUT_KBDFLAGS_EXTENDED : 0;
434 eventFlags |= (flags & KBD_FLAGS_EXTENDED1) ? FASTPATH_INPUT_KBDFLAGS_PREFIX_E1 : 0;
435 s = fastpath_input_pdu_init(rdp->fastpath, eventFlags, FASTPATH_INPUT_EVENT_SCANCODE,
436 &sec_flags);
437
438 if (!s)
439 return FALSE;
440
441 WINPR_ASSERT(code <= UINT8_MAX);
442 Stream_Write_UINT8(s, code); /* keyCode (1 byte) */
443 return fastpath_send_input_pdu(rdp->fastpath, s, sec_flags);
444}
445
446static BOOL input_send_fastpath_unicode_keyboard_event(rdpInput* input, UINT16 flags, UINT16 code)
447{
448 UINT16 sec_flags = 0;
449 wStream* s = nullptr;
450 BYTE eventFlags = 0;
451 rdpRdp* rdp = nullptr;
452
453 WINPR_ASSERT(input);
454 WINPR_ASSERT(input->context);
455 WINPR_ASSERT(input->context->settings);
456
457 rdp = input->context->rdp;
458 WINPR_ASSERT(rdp);
459
460 if (!input_ensure_client_running(input))
461 return FALSE;
462
463 if (!freerdp_settings_get_bool(input->context->settings, FreeRDP_UnicodeInput))
464 {
465 WLog_WARN(TAG, "Unicode input not supported by server.");
466 return FALSE;
467 }
468
469 eventFlags |= (flags & KBD_FLAGS_RELEASE) ? FASTPATH_INPUT_KBDFLAGS_RELEASE : 0;
470 s = fastpath_input_pdu_init(rdp->fastpath, eventFlags, FASTPATH_INPUT_EVENT_UNICODE,
471 &sec_flags);
472
473 if (!s)
474 return FALSE;
475
476 Stream_Write_UINT16(s, code); /* unicodeCode (2 bytes) */
477 return fastpath_send_input_pdu(rdp->fastpath, s, sec_flags);
478}
479
480static BOOL input_send_fastpath_mouse_event(rdpInput* input, UINT16 flags, UINT16 x, UINT16 y)
481{
482 UINT16 sec_flags = 0;
483 wStream* s = nullptr;
484 rdpRdp* rdp = nullptr;
485
486 WINPR_ASSERT(input);
487 WINPR_ASSERT(input->context);
488 WINPR_ASSERT(input->context->settings);
489
490 rdp = input->context->rdp;
491 WINPR_ASSERT(rdp);
492
493 if (!input_ensure_client_running(input))
494 return FALSE;
495
496 if (!freerdp_settings_get_bool(input->context->settings, FreeRDP_HasHorizontalWheel))
497 {
498 if (flags & PTR_FLAGS_HWHEEL)
499 {
500 WLog_WARN(TAG,
501 "skip mouse event %" PRIu16 "x%" PRIu16 " flags=0x%04" PRIX16
502 ", no horizontal mouse wheel supported",
503 x, y, flags);
504 return TRUE;
505 }
506 }
507
508 s = fastpath_input_pdu_init(rdp->fastpath, 0, FASTPATH_INPUT_EVENT_MOUSE, &sec_flags);
509
510 if (!s)
511 return FALSE;
512
513 input_write_mouse_event(s, flags, x, y);
514 return fastpath_send_input_pdu(rdp->fastpath, s, sec_flags);
515}
516
517static BOOL input_send_fastpath_extended_mouse_event(rdpInput* input, UINT16 flags, UINT16 x,
518 UINT16 y)
519{
520 UINT16 sec_flags = 0;
521 wStream* s = nullptr;
522 rdpRdp* rdp = nullptr;
523
524 WINPR_ASSERT(input);
525 WINPR_ASSERT(input->context);
526
527 rdp = input->context->rdp;
528 WINPR_ASSERT(rdp);
529
530 if (!input_ensure_client_running(input))
531 return FALSE;
532
533 if (!freerdp_settings_get_bool(input->context->settings, FreeRDP_HasExtendedMouseEvent))
534 {
535 WLog_WARN(TAG,
536 "skip extended mouse event %" PRIu16 "x%" PRIu16 " flags=0x%04" PRIX16
537 ", no extended mouse events supported",
538 x, y, flags);
539 return TRUE;
540 }
541
542 s = fastpath_input_pdu_init(rdp->fastpath, 0, FASTPATH_INPUT_EVENT_MOUSEX, &sec_flags);
543
544 if (!s)
545 return FALSE;
546
547 input_write_extended_mouse_event(s, flags, x, y);
548 return fastpath_send_input_pdu(rdp->fastpath, s, sec_flags);
549}
550
551static BOOL input_send_fastpath_relmouse_event(rdpInput* input, UINT16 flags, INT16 xDelta,
552 INT16 yDelta)
553{
554 UINT16 sec_flags = 0;
555 wStream* s = nullptr;
556 rdpRdp* rdp = nullptr;
557
558 WINPR_ASSERT(input);
559 WINPR_ASSERT(input->context);
560 WINPR_ASSERT(input->context->settings);
561
562 rdp = input->context->rdp;
563 WINPR_ASSERT(rdp);
564
565 if (!input_ensure_client_running(input))
566 return FALSE;
567
568 if (!freerdp_settings_get_bool(input->context->settings, FreeRDP_HasRelativeMouseEvent))
569 {
570 WLog_ERR(TAG, "Sending relative fastpath mouse event, but no support for that announced");
571 return FALSE;
572 }
573
574 s = fastpath_input_pdu_init(rdp->fastpath, 0, TS_FP_RELPOINTER_EVENT, &sec_flags);
575
576 if (!s)
577 return FALSE;
578
579 Stream_Write_UINT16(s, flags); /* pointerFlags (2 bytes) */
580 Stream_Write_INT16(s, xDelta); /* xDelta (2 bytes) */
581 Stream_Write_INT16(s, yDelta); /* yDelta (2 bytes) */
582 return fastpath_send_input_pdu(rdp->fastpath, s, sec_flags);
583}
584
585static BOOL input_send_fastpath_qoe_event(rdpInput* input, UINT32 timestampMS)
586{
587 WINPR_ASSERT(input);
588 WINPR_ASSERT(input->context);
589 WINPR_ASSERT(input->context->settings);
590
591 rdpRdp* rdp = input->context->rdp;
592 WINPR_ASSERT(rdp);
593
594 if (!input_ensure_client_running(input))
595 return FALSE;
596
597 if (!freerdp_settings_get_bool(input->context->settings, FreeRDP_HasQoeEvent))
598 {
599 WLog_ERR(TAG, "Sending qoe event, but no support for that announced");
600 return FALSE;
601 }
602
603 UINT16 sec_flags = 0;
604 wStream* s = fastpath_input_pdu_init(rdp->fastpath, 0, TS_FP_QOETIMESTAMP_EVENT, &sec_flags);
605
606 if (!s)
607 return FALSE;
608
609 if (!Stream_EnsureRemainingCapacity(s, 4))
610 {
611 Stream_Free(s, TRUE);
612 return FALSE;
613 }
614
615 Stream_Write_UINT32(s, timestampMS);
616 return fastpath_send_input_pdu(rdp->fastpath, s, sec_flags);
617}
618
619static BOOL input_send_fastpath_focus_in_event(rdpInput* input, UINT16 toggleStates)
620{
621 UINT16 sec_flags = 0;
622 wStream* s = nullptr;
623 BYTE eventFlags = 0;
624 rdpRdp* rdp = nullptr;
625
626 WINPR_ASSERT(input);
627 WINPR_ASSERT(input->context);
628
629 rdp = input->context->rdp;
630 WINPR_ASSERT(rdp);
631
632 if (!input_ensure_client_running(input))
633 return FALSE;
634
635 s = fastpath_input_pdu_init_header(rdp->fastpath, &sec_flags);
636
637 if (!s)
638 return FALSE;
639
640 /* send a tab up like mstsc.exe */
641 eventFlags = FASTPATH_INPUT_KBDFLAGS_RELEASE | FASTPATH_INPUT_EVENT_SCANCODE << 5;
642 Stream_Write_UINT8(s, eventFlags); /* Key Release event (1 byte) */
643 Stream_Write_UINT8(s, 0x0f); /* keyCode (1 byte) */
644 /* send the toggle key states */
645 eventFlags = (toggleStates & 0x1F) | FASTPATH_INPUT_EVENT_SYNC << 5;
646 Stream_Write_UINT8(s, eventFlags); /* toggle state (1 byte) */
647 /* send another tab up like mstsc.exe */
648 eventFlags = FASTPATH_INPUT_KBDFLAGS_RELEASE | FASTPATH_INPUT_EVENT_SCANCODE << 5;
649 Stream_Write_UINT8(s, eventFlags); /* Key Release event (1 byte) */
650 Stream_Write_UINT8(s, 0x0f); /* keyCode (1 byte) */
651 return fastpath_send_multiple_input_pdu(rdp->fastpath, s, 3, sec_flags);
652}
653
654static BOOL input_send_fastpath_keyboard_pause_event(rdpInput* input)
655{
656 /* In ancient days, pause-down without control sent E1 1D 45 E1 9D C5,
657 * and pause-up sent nothing. However, reverse engineering mstsc shows
658 * it sending the following sequence:
659 */
660 UINT16 sec_flags = 0;
661 wStream* s = nullptr;
662 const BYTE keyDownEvent = FASTPATH_INPUT_EVENT_SCANCODE << 5;
663 const BYTE keyUpEvent = (FASTPATH_INPUT_EVENT_SCANCODE << 5) | FASTPATH_INPUT_KBDFLAGS_RELEASE;
664 rdpRdp* rdp = nullptr;
665
666 WINPR_ASSERT(input);
667 WINPR_ASSERT(input->context);
668
669 rdp = input->context->rdp;
670 WINPR_ASSERT(rdp);
671
672 if (!input_ensure_client_running(input))
673 return FALSE;
674
675 s = fastpath_input_pdu_init_header(rdp->fastpath, &sec_flags);
676
677 if (!s)
678 return FALSE;
679
680 /* Control down (0x1D) */
681 Stream_Write_UINT8(s, keyDownEvent | FASTPATH_INPUT_KBDFLAGS_PREFIX_E1);
682 Stream_Write_UINT8(s, RDP_SCANCODE_CODE(RDP_SCANCODE_LCONTROL));
683 /* Numlock down (0x45) */
684 Stream_Write_UINT8(s, keyDownEvent);
685 Stream_Write_UINT8(s, RDP_SCANCODE_CODE(RDP_SCANCODE_NUMLOCK));
686 /* Control up (0x1D) */
687 Stream_Write_UINT8(s, keyUpEvent | FASTPATH_INPUT_KBDFLAGS_PREFIX_E1);
688 Stream_Write_UINT8(s, RDP_SCANCODE_CODE(RDP_SCANCODE_LCONTROL));
689 /* Numlock down (0x45) */
690 Stream_Write_UINT8(s, keyUpEvent);
691 Stream_Write_UINT8(s, RDP_SCANCODE_CODE(RDP_SCANCODE_NUMLOCK));
692 return fastpath_send_multiple_input_pdu(rdp->fastpath, s, 4, sec_flags);
693}
694
695static BOOL input_recv_sync_event(rdpInput* input, wStream* s)
696{
697 UINT32 toggleFlags = 0;
698
699 WINPR_ASSERT(input);
700 WINPR_ASSERT(s);
701
702 if (!Stream_CheckAndLogRequiredLength(TAG, s, 6))
703 return FALSE;
704
705 Stream_Seek(s, 2); /* pad2Octets (2 bytes) */
706 Stream_Read_UINT32(s, toggleFlags); /* toggleFlags (4 bytes) */
707
708 /* only pass input if we are actually connected */
709 if (!rdp_has_reached_state(input->context->rdp, CONNECTION_STATE_ACTIVE))
710 return TRUE;
711 return IFCALLRESULT(TRUE, input->SynchronizeEvent, input, toggleFlags);
712}
713
714static BOOL input_recv_keyboard_event(rdpInput* input, wStream* s)
715{
716 UINT16 keyboardFlags = 0;
717 UINT16 keyCode = 0;
718
719 WINPR_ASSERT(input);
720 WINPR_ASSERT(s);
721
722 if (!Stream_CheckAndLogRequiredLength(TAG, s, 6))
723 return FALSE;
724
725 Stream_Read_UINT16(s, keyboardFlags); /* keyboardFlags (2 bytes) */
726 Stream_Read_UINT16(s, keyCode); /* keyCode (2 bytes) */
727 Stream_Seek(s, 2); /* pad2Octets (2 bytes) */
728
729 if (keyboardFlags & KBD_FLAGS_RELEASE)
730 keyboardFlags &= ~KBD_FLAGS_DOWN;
731
732 if (keyCode & 0xFF00)
733 WLog_WARN(TAG,
734 "Problematic [MS-RDPBCGR] 2.2.8.1.1.3.1.1.1 Keyboard Event (TS_KEYBOARD_EVENT) "
735 "keyCode=0x%04" PRIx16
736 ", high byte values should be sent in keyboardFlags field, ignoring.",
737 keyCode);
738
739 /* only pass input if we are actually connected */
740 if (!rdp_has_reached_state(input->context->rdp, CONNECTION_STATE_ACTIVE))
741 return TRUE;
742 return IFCALLRESULT(TRUE, input->KeyboardEvent, input, keyboardFlags, keyCode & 0xFF);
743}
744
745static BOOL input_recv_unicode_keyboard_event(rdpInput* input, wStream* s)
746{
747 UINT16 keyboardFlags = 0;
748 UINT16 unicodeCode = 0;
749
750 WINPR_ASSERT(input);
751 WINPR_ASSERT(s);
752
753 if (!Stream_CheckAndLogRequiredLength(TAG, s, 6))
754 return FALSE;
755
756 Stream_Read_UINT16(s, keyboardFlags); /* keyboardFlags (2 bytes) */
757 Stream_Read_UINT16(s, unicodeCode); /* unicodeCode (2 bytes) */
758 Stream_Seek(s, 2); /* pad2Octets (2 bytes) */
759
760 /* "fix" keyboardFlags - see comment in input_recv_keyboard_event() */
761
762 if (keyboardFlags & KBD_FLAGS_RELEASE)
763 keyboardFlags &= ~KBD_FLAGS_DOWN;
764
765 /* only pass input if we are actually connected */
766 if (!rdp_has_reached_state(input->context->rdp, CONNECTION_STATE_ACTIVE))
767 return TRUE;
768 return IFCALLRESULT(TRUE, input->UnicodeKeyboardEvent, input, keyboardFlags, unicodeCode);
769}
770
771static BOOL input_recv_mouse_event(rdpInput* input, wStream* s)
772{
773 UINT16 pointerFlags = 0;
774 UINT16 xPos = 0;
775 UINT16 yPos = 0;
776
777 WINPR_ASSERT(input);
778 WINPR_ASSERT(s);
779
780 if (!Stream_CheckAndLogRequiredLength(TAG, s, 6))
781 return FALSE;
782
783 Stream_Read_UINT16(s, pointerFlags); /* pointerFlags (2 bytes) */
784 Stream_Read_UINT16(s, xPos); /* xPos (2 bytes) */
785 Stream_Read_UINT16(s, yPos); /* yPos (2 bytes) */
786
787 /* only pass input if we are actually connected */
788 if (!rdp_has_reached_state(input->context->rdp, CONNECTION_STATE_ACTIVE))
789 return TRUE;
790 return IFCALLRESULT(TRUE, input->MouseEvent, input, pointerFlags, xPos, yPos);
791}
792
793static BOOL input_recv_relmouse_event(rdpInput* input, wStream* s)
794{
795 UINT16 pointerFlags = 0;
796 INT16 xDelta = 0;
797 INT16 yDelta = 0;
798
799 WINPR_ASSERT(input);
800 WINPR_ASSERT(s);
801
802 if (!Stream_CheckAndLogRequiredLength(TAG, s, 6))
803 return FALSE;
804
805 Stream_Read_UINT16(s, pointerFlags); /* pointerFlags (2 bytes) */
806 Stream_Read_INT16(s, xDelta); /* xPos (2 bytes) */
807 Stream_Read_INT16(s, yDelta); /* yPos (2 bytes) */
808
809 if (!freerdp_settings_get_bool(input->context->settings, FreeRDP_HasRelativeMouseEvent))
810 {
811 WLog_ERR(TAG,
812 "Received relative mouse event(flags=0x%04" PRIx16 ", xPos=%" PRId16
813 ", yPos=%" PRId16 "), but we did not announce support for that",
814 pointerFlags, xDelta, yDelta);
815 return FALSE;
816 }
817
818 /* only pass input if we are actually connected */
819 if (!rdp_has_reached_state(input->context->rdp, CONNECTION_STATE_ACTIVE))
820 return TRUE;
821
822 return IFCALLRESULT(TRUE, input->RelMouseEvent, input, pointerFlags, xDelta, yDelta);
823}
824
825static BOOL input_recv_extended_mouse_event(rdpInput* input, wStream* s)
826{
827 UINT16 pointerFlags = 0;
828 UINT16 xPos = 0;
829 UINT16 yPos = 0;
830
831 WINPR_ASSERT(input);
832 WINPR_ASSERT(s);
833
834 if (!Stream_CheckAndLogRequiredLength(TAG, s, 6))
835 return FALSE;
836
837 Stream_Read_UINT16(s, pointerFlags); /* pointerFlags (2 bytes) */
838 Stream_Read_UINT16(s, xPos); /* xPos (2 bytes) */
839 Stream_Read_UINT16(s, yPos); /* yPos (2 bytes) */
840
841 if (!freerdp_settings_get_bool(input->context->settings, FreeRDP_HasExtendedMouseEvent))
842 {
843 WLog_ERR(TAG,
844 "Received extended mouse event(flags=0x%04" PRIx16 ", xPos=%" PRIu16
845 ", yPos=%" PRIu16 "), but we did not announce support for that",
846 pointerFlags, xPos, yPos);
847 return FALSE;
848 }
849
850 /* only pass input if we are actually connected */
851 if (!rdp_has_reached_state(input->context->rdp, CONNECTION_STATE_ACTIVE))
852 return TRUE;
853 return IFCALLRESULT(TRUE, input->ExtendedMouseEvent, input, pointerFlags, xPos, yPos);
854}
855
856static BOOL input_recv_event(rdpInput* input, wStream* s)
857{
858 UINT16 messageType = 0;
859
860 WINPR_ASSERT(input);
861 WINPR_ASSERT(s);
862
863 if (!Stream_CheckAndLogRequiredLength(TAG, s, 6))
864 return FALSE;
865
866 Stream_Seek(s, 4); /* eventTime (4 bytes), ignored by the server */
867 Stream_Read_UINT16(s, messageType); /* messageType (2 bytes) */
868
869 switch (messageType)
870 {
871 case INPUT_EVENT_SYNC:
872 if (!input_recv_sync_event(input, s))
873 return FALSE;
874
875 break;
876
877 case INPUT_EVENT_SCANCODE:
878 if (!input_recv_keyboard_event(input, s))
879 return FALSE;
880
881 break;
882
883 case INPUT_EVENT_UNICODE:
884 if (!input_recv_unicode_keyboard_event(input, s))
885 return FALSE;
886
887 break;
888
889 case INPUT_EVENT_MOUSE:
890 if (!input_recv_mouse_event(input, s))
891 return FALSE;
892
893 break;
894
895 case INPUT_EVENT_MOUSEX:
896 if (!input_recv_extended_mouse_event(input, s))
897 return FALSE;
898
899 break;
900
901 case INPUT_EVENT_MOUSEREL:
902 if (!input_recv_relmouse_event(input, s))
903 return FALSE;
904
905 break;
906
907 default:
908 WLog_ERR(TAG, "Unknown messageType %" PRIu16 "", messageType);
909 /* Each input event uses 6 bytes. */
910 if (!Stream_SafeSeek(s, 6))
911 return FALSE;
912 break;
913 }
914
915 return TRUE;
916}
917
918BOOL input_recv(rdpInput* input, wStream* s)
919{
920 UINT16 numberEvents = 0;
921
922 WINPR_ASSERT(input);
923 WINPR_ASSERT(s);
924
925 if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
926 return FALSE;
927
928 Stream_Read_UINT16(s, numberEvents); /* numberEvents (2 bytes) */
929 Stream_Seek(s, 2); /* pad2Octets (2 bytes) */
930
931 /* Each input event uses 6 exactly bytes. */
932 if (!Stream_CheckAndLogRequiredLengthOfSize(TAG, s, numberEvents, 6ull))
933 return FALSE;
934
935 for (UINT16 i = 0; i < numberEvents; i++)
936 {
937 if (!input_recv_event(input, s))
938 return FALSE;
939 }
940
941 return TRUE;
942}
943
944BOOL input_register_client_callbacks(rdpInput* input)
945{
946 rdpSettings* settings = nullptr;
947
948 if (!input->context)
949 return FALSE;
950
951 settings = input->context->settings;
952
953 if (!settings)
954 return FALSE;
955
956 if (freerdp_settings_get_bool(settings, FreeRDP_FastPathInput))
957 {
958 input->SynchronizeEvent = input_send_fastpath_synchronize_event;
959 input->KeyboardEvent = input_send_fastpath_keyboard_event;
960 input->KeyboardPauseEvent = input_send_fastpath_keyboard_pause_event;
961 input->UnicodeKeyboardEvent = input_send_fastpath_unicode_keyboard_event;
962 input->MouseEvent = input_send_fastpath_mouse_event;
963 input->RelMouseEvent = input_send_fastpath_relmouse_event;
964 input->ExtendedMouseEvent = input_send_fastpath_extended_mouse_event;
965 input->FocusInEvent = input_send_fastpath_focus_in_event;
966 input->QoEEvent = input_send_fastpath_qoe_event;
967 }
968 else
969 {
970 input->SynchronizeEvent = input_send_synchronize_event;
971 input->KeyboardEvent = input_send_keyboard_event;
972 input->KeyboardPauseEvent = input_send_keyboard_pause_event;
973 input->UnicodeKeyboardEvent = input_send_unicode_keyboard_event;
974 input->MouseEvent = input_send_mouse_event;
975 input->RelMouseEvent = input_send_relmouse_event;
976 input->ExtendedMouseEvent = input_send_extended_mouse_event;
977 input->FocusInEvent = input_send_focus_in_event;
978 }
979
980 return TRUE;
981}
982
983/* Save last input timestamp and/or mouse position in prevent-session-lock mode */
984static BOOL input_update_last_event(rdpInput* input, BOOL mouse, UINT16 x, UINT16 y)
985{
986 rdp_input_internal* in = input_cast(input);
987
988 WINPR_ASSERT(input);
989 WINPR_ASSERT(input->context);
990
991 if (freerdp_settings_get_uint32(input->context->settings, FreeRDP_FakeMouseMotionInterval) > 0)
992 {
993 const time_t now = time(nullptr);
994 in->lastInputTimestamp = WINPR_ASSERTING_INT_CAST(UINT64, now);
995
996 if (mouse)
997 {
998 in->lastX = x;
999 in->lastY = y;
1000 }
1001 }
1002 return TRUE;
1003}
1004
1005WINPR_ATTR_NODISCARD
1006static const char* boolstr(bool value)
1007{
1008 if (value)
1009 return "true";
1010 return "false";
1011}
1012
1013WINPR_ATTR_NODISCARD
1014WINPR_ATTR_FORMAT_ARG(2, 3)
1015static BOOL input_skip(const rdpInput* input, WINPR_FORMAT_ARG const char* fmt, ...)
1016{
1017 WINPR_ASSERT(input);
1018
1019 const rdpContext* context = input->context;
1020 WINPR_ASSERT(context);
1021
1022 const rdp_input_internal* in = (const rdp_input_internal*)input;
1023 WINPR_ASSERT(in);
1024
1025 const BOOL suspended = freerdp_settings_get_bool(context->settings, FreeRDP_SuspendInput);
1026 const BOOL connected = freerdp_is_active_state(context);
1027 const DWORD loglevel = WLOG_DEBUG;
1028
1029 if (WLog_IsLevelActive(in->log, loglevel))
1030 {
1031 char buffer[128] = WINPR_C_ARRAY_INIT;
1032
1033 va_list args = WINPR_C_ARRAY_INIT;
1034 va_start(args, fmt);
1035 (void)vsnprintf(buffer, sizeof(buffer), fmt, args);
1036 va_end(args);
1037
1038 WLog_Print(in->log, loglevel, "[connected=%s, suspended=%s] %s", boolstr(connected),
1039 boolstr(suspended), buffer);
1040 }
1041
1042 return suspended || !connected;
1043}
1044
1045BOOL freerdp_input_send_synchronize_event(rdpInput* input, UINT32 flags)
1046{
1047 if (!input || !input->context)
1048 return FALSE;
1049
1050 char buffer[128] = WINPR_C_ARRAY_INIT;
1051 if (input_skip(input, "Keyboard {Sync} [%s]",
1052 freerdp_input_keyboard_flags_string(flags, buffer, sizeof(buffer))))
1053 return TRUE;
1054
1055 return IFCALLRESULT(TRUE, input->SynchronizeEvent, input, flags);
1056}
1057
1058BOOL freerdp_input_send_keyboard_event(rdpInput* input, UINT16 flags, UINT8 code)
1059{
1060 if (!input || !input->context)
1061 return FALSE;
1062
1063 char buffer[128] = WINPR_C_ARRAY_INIT;
1064 if (input_skip(input, "Keyboard {Event} [%s]",
1065 freerdp_input_keypress_flags_string(flags, buffer, sizeof(buffer))))
1066 return TRUE;
1067
1068 input_update_last_event(input, FALSE, 0, 0);
1069
1070 return IFCALLRESULT(TRUE, input->KeyboardEvent, input, flags, code);
1071}
1072
1073BOOL freerdp_input_send_keyboard_event_ex(rdpInput* input, BOOL down, BOOL repeat,
1074 UINT32 rdp_scancode)
1075{
1076 UINT16 flags = (RDP_SCANCODE_EXTENDED(rdp_scancode) ? KBD_FLAGS_EXTENDED : 0);
1077 if (down && repeat)
1078 flags |= KBD_FLAGS_DOWN;
1079 else if (!down)
1080 flags |= KBD_FLAGS_RELEASE;
1081
1082 return freerdp_input_send_keyboard_event(input, flags, RDP_SCANCODE_CODE(rdp_scancode));
1083}
1084
1085BOOL freerdp_input_send_unicode_keyboard_event(rdpInput* input, UINT16 flags, UINT16 code)
1086{
1087 if (!input || !input->context)
1088 return FALSE;
1089
1090 char buffer[128] = WINPR_C_ARRAY_INIT;
1091 if (input_skip(input, "Keyboard {Unicode} [%s]",
1092 freerdp_input_keypress_flags_string(flags, buffer, ARRAYSIZE(buffer))))
1093 return TRUE;
1094
1095 input_update_last_event(input, FALSE, 0, 0);
1096
1097 return IFCALLRESULT(TRUE, input->UnicodeKeyboardEvent, input, flags, code);
1098}
1099
1100BOOL freerdp_input_send_mouse_event(rdpInput* input, UINT16 flags, UINT16 x, UINT16 y)
1101{
1102 if (!input || !input->context)
1103 return FALSE;
1104
1105 char buffer[128] = WINPR_C_ARRAY_INIT;
1106 if (input_skip(input, "Mouse {Event} [%s]",
1107 freerdp_input_mouse_flags_string(flags, false, buffer, ARRAYSIZE(buffer))))
1108 return TRUE;
1109
1110 input_update_last_event(
1111 input, flags & (PTR_FLAGS_MOVE | PTR_FLAGS_BUTTON1 | PTR_FLAGS_BUTTON2 | PTR_FLAGS_BUTTON3),
1112 x, y);
1113
1114 return IFCALLRESULT(TRUE, input->MouseEvent, input, flags, x, y);
1115}
1116
1117BOOL freerdp_input_send_rel_mouse_event(rdpInput* input, UINT16 flags, INT16 xDelta, INT16 yDelta)
1118{
1119 if (!input || !input->context)
1120 return FALSE;
1121
1122 char buffer[128] = WINPR_C_ARRAY_INIT;
1123 if (input_skip(input, "Mouse {RelativeEvent} [%s]",
1124 freerdp_input_mouse_flags_string(flags, true, buffer, ARRAYSIZE(buffer))))
1125 return TRUE;
1126
1127 return IFCALLRESULT(TRUE, input->RelMouseEvent, input, flags, xDelta, yDelta);
1128}
1129
1130BOOL freerdp_input_send_qoe_timestamp(rdpInput* input, UINT32 timestampMS)
1131{
1132 if (!input || !input->context)
1133 return FALSE;
1134
1135 if (input_skip(input, "QOE timestamp %" PRIu32, timestampMS))
1136 return TRUE;
1137
1138 return IFCALLRESULT(TRUE, input->QoEEvent, input, timestampMS);
1139}
1140
1141BOOL freerdp_input_send_extended_mouse_event(rdpInput* input, UINT16 flags, UINT16 x, UINT16 y)
1142{
1143 if (!input || !input->context)
1144 return FALSE;
1145
1146 char buffer[128] = WINPR_C_ARRAY_INIT;
1147 if (input_skip(input, "Mouse {ExtendedEvent} %s",
1148 freerdp_input_mouse_flags_string(flags, true, buffer, ARRAYSIZE(buffer))))
1149 return TRUE;
1150
1151 input_update_last_event(input, TRUE, x, y);
1152
1153 return IFCALLRESULT(TRUE, input->ExtendedMouseEvent, input, flags, x, y);
1154}
1155
1156BOOL freerdp_input_send_focus_in_event(rdpInput* input, UINT16 toggleStates)
1157{
1158 if (!input || !input->context)
1159 return FALSE;
1160
1161 char buffer[128] = WINPR_C_ARRAY_INIT;
1162 if (input_skip(input, "Keyboard {FocusIn} [%s]",
1163 freerdp_input_keyboard_flags_string(toggleStates, buffer, sizeof(buffer))))
1164 return TRUE;
1165
1166 return IFCALLRESULT(TRUE, input->FocusInEvent, input, toggleStates);
1167}
1168
1169BOOL freerdp_input_send_keyboard_pause_event(rdpInput* input)
1170{
1171 if (!input || !input->context)
1172 return FALSE;
1173
1174 if (input_skip(input, "Keyboard {Pause}"))
1175 return TRUE;
1176
1177 return IFCALLRESULT(TRUE, input->KeyboardPauseEvent, input);
1178}
1179
1180int input_process_events(rdpInput* input)
1181{
1182 if (!input)
1183 return FALSE;
1184
1185 return input_message_queue_process_pending_messages(input);
1186}
1187
1188static void input_free_queued_message(void* obj)
1189{
1190 wMessage* msg = (wMessage*)obj;
1191 input_message_queue_free_message(msg);
1192}
1193
1194rdpInput* input_new(rdpRdp* rdp)
1195{
1196 const wObject cb = { nullptr, nullptr, nullptr, input_free_queued_message, nullptr };
1197 rdp_input_internal* input = (rdp_input_internal*)calloc(1, sizeof(rdp_input_internal));
1198
1199 WINPR_UNUSED(rdp);
1200
1201 if (!input)
1202 return nullptr;
1203
1204 input->common.context = rdp->context;
1205 input->queue = MessageQueue_New(&cb);
1206 input->log = WLog_Get(TAG);
1207
1208 if (!input->queue)
1209 {
1210 free(input);
1211 return nullptr;
1212 }
1213
1214 return &input->common;
1215}
1216
1217void input_free(rdpInput* input)
1218{
1219 if (input != nullptr)
1220 {
1221 rdp_input_internal* in = input_cast(input);
1222
1223 MessageQueue_Free(in->queue);
1224 free(in);
1225 }
1226}
1227
1228static const char* flag2str(uint32_t flag, bool extended)
1229{
1230#define EVCASE(x) \
1231 case x: \
1232 return #x
1233 if (extended)
1234 {
1235 switch (flag)
1236 {
1237 /* Extended Pointer Flags */
1238 EVCASE(PTR_XFLAGS_DOWN);
1239 EVCASE(PTR_XFLAGS_BUTTON1);
1240 EVCASE(PTR_XFLAGS_BUTTON2);
1241 default:
1242 return "PTR_XFLAGS_UNKNOWN";
1243 }
1244 }
1245
1246 switch (flag)
1247 {
1248 /* Pointer Flags */
1249 EVCASE(PTR_FLAGS_HWHEEL);
1250 EVCASE(PTR_FLAGS_WHEEL);
1251 EVCASE(PTR_FLAGS_WHEEL_NEGATIVE);
1252 EVCASE(PTR_FLAGS_MOVE);
1253 EVCASE(PTR_FLAGS_DOWN);
1254 EVCASE(PTR_FLAGS_BUTTON1);
1255 EVCASE(PTR_FLAGS_BUTTON2);
1256 EVCASE(PTR_FLAGS_BUTTON3);
1257 default:
1258 return "PTR_FLAGS_UNKNOWN";
1259 }
1260#undef EVCASE
1261}
1262
1263const char* freerdp_input_mouse_flags_string(uint32_t flags, bool extended, char* buffer,
1264 size_t len)
1265{
1266 if (len <= 2)
1267 return nullptr;
1268
1269 if (!winpr_str_append("{", buffer, len, ""))
1270 return nullptr;
1271
1272 /* Strip initial symbol so we do not get duplicate separators */
1273 for (size_t x = 0; x < 32; x++)
1274 {
1275 const uint32_t flag = 1u << x;
1276 if (flags & flag)
1277 {
1278 char ibuffer[64] = WINPR_C_ARRAY_INIT;
1279 (void)_snprintf(ibuffer, sizeof(ibuffer), "%s", flag2str(flag, extended));
1280 if (!winpr_str_append(ibuffer, &buffer[1], len - 2, "|"))
1281 return nullptr;
1282 }
1283 }
1284 if (!winpr_str_append("}", &buffer[1], len - 2, ""))
1285 return nullptr;
1286
1287 return buffer;
1288}
1289
1290static const char* kbdflag2str(uint32_t flag)
1291{
1292#define EVCASE(x) \
1293 case x: \
1294 return #x
1295
1296 switch (flag)
1297 {
1298 /* Pointer Flags */
1299 EVCASE(KBD_FLAGS_EXTENDED);
1300 EVCASE(KBD_FLAGS_EXTENDED1);
1301 EVCASE(KBD_FLAGS_DOWN);
1302 EVCASE(KBD_FLAGS_RELEASE);
1303 default:
1304 return "KBD_FLAGS_UNKNOWN";
1305 }
1306#undef EVCASE
1307}
1308
1309const char* freerdp_input_keypress_flags_string(uint32_t flags, char* buffer, size_t len)
1310{
1311 if (len <= 2)
1312 return nullptr;
1313
1314 if (!winpr_str_append("{", buffer, len, ""))
1315 return nullptr;
1316
1317 /* Strip initial symbol so we do not get duplicate separators */
1318 for (size_t x = 0; x < 32; x++)
1319 {
1320 const uint32_t flag = 1u << x;
1321 if (flags & flag)
1322 {
1323 char ibuffer[64] = WINPR_C_ARRAY_INIT;
1324 (void)_snprintf(ibuffer, sizeof(ibuffer), "%s", kbdflag2str(flag));
1325 if (!winpr_str_append(ibuffer, &buffer[1], len - 2, "|"))
1326 return nullptr;
1327 }
1328 }
1329 if (!winpr_str_append("}", &buffer[1], len - 2, ""))
1330 return nullptr;
1331
1332 return buffer;
1333}
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