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(enum KBD_SYNC_FLAGS 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 NULL;
63
64 if (!winpr_str_append("{", buffer, len, ""))
65 return NULL;
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] = { 0 };
74 (void)_snprintf(ibuffer, sizeof(ibuffer), "%s%s", prefix, SyncEventFlag2Str(flag));
75 if (!winpr_str_append(ibuffer, &buffer[1], len - 2, "|"))
76 return NULL;
77 }
78 }
79 if (!winpr_str_append("}", &buffer[1], len - 2, ""))
80 return NULL;
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 NULL;
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 = NULL;
179 rdpRdp* rdp = NULL;
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 = NULL;
209 rdpRdp* rdp = NULL;
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 = NULL;
277 rdpRdp* rdp = NULL;
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 = NULL;
388 rdpRdp* rdp = NULL;
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 = NULL;
412 BYTE eventFlags = 0;
413 rdpRdp* rdp = NULL;
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 = NULL;
442 BYTE eventFlags = 0;
443 rdpRdp* rdp = NULL;
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 = NULL;
476 rdpRdp* rdp = NULL;
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 = NULL;
514 rdpRdp* rdp = NULL;
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 = NULL;
548 rdpRdp* rdp = NULL;
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 = NULL;
615 BYTE eventFlags = 0;
616 rdpRdp* rdp = NULL;
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 = NULL;
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 = NULL;
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 return IFCALLRESULT(TRUE, input->SynchronizeEvent, input, toggleFlags);
700}
701
702static BOOL input_recv_keyboard_event(rdpInput* input, wStream* s)
703{
704 UINT16 keyboardFlags = 0;
705 UINT16 keyCode = 0;
706
707 WINPR_ASSERT(input);
708 WINPR_ASSERT(s);
709
710 if (!Stream_CheckAndLogRequiredLength(TAG, s, 6))
711 return FALSE;
712
713 Stream_Read_UINT16(s, keyboardFlags); /* keyboardFlags (2 bytes) */
714 Stream_Read_UINT16(s, keyCode); /* keyCode (2 bytes) */
715 Stream_Seek(s, 2); /* pad2Octets (2 bytes) */
716
717 if (keyboardFlags & KBD_FLAGS_RELEASE)
718 keyboardFlags &= ~KBD_FLAGS_DOWN;
719
720 if (keyCode & 0xFF00)
721 WLog_WARN(TAG,
722 "Problematic [MS-RDPBCGR] 2.2.8.1.1.3.1.1.1 Keyboard Event (TS_KEYBOARD_EVENT) "
723 "keyCode=0x%04" PRIx16
724 ", high byte values should be sent in keyboardFlags field, ignoring.",
725 keyCode);
726 return IFCALLRESULT(TRUE, input->KeyboardEvent, input, keyboardFlags, keyCode & 0xFF);
727}
728
729static BOOL input_recv_unicode_keyboard_event(rdpInput* input, wStream* s)
730{
731 UINT16 keyboardFlags = 0;
732 UINT16 unicodeCode = 0;
733
734 WINPR_ASSERT(input);
735 WINPR_ASSERT(s);
736
737 if (!Stream_CheckAndLogRequiredLength(TAG, s, 6))
738 return FALSE;
739
740 Stream_Read_UINT16(s, keyboardFlags); /* keyboardFlags (2 bytes) */
741 Stream_Read_UINT16(s, unicodeCode); /* unicodeCode (2 bytes) */
742 Stream_Seek(s, 2); /* pad2Octets (2 bytes) */
743
744 /* "fix" keyboardFlags - see comment in input_recv_keyboard_event() */
745
746 if (keyboardFlags & KBD_FLAGS_RELEASE)
747 keyboardFlags &= ~KBD_FLAGS_DOWN;
748
749 return IFCALLRESULT(TRUE, input->UnicodeKeyboardEvent, input, keyboardFlags, unicodeCode);
750}
751
752static BOOL input_recv_mouse_event(rdpInput* input, wStream* s)
753{
754 UINT16 pointerFlags = 0;
755 UINT16 xPos = 0;
756 UINT16 yPos = 0;
757
758 WINPR_ASSERT(input);
759 WINPR_ASSERT(s);
760
761 if (!Stream_CheckAndLogRequiredLength(TAG, s, 6))
762 return FALSE;
763
764 Stream_Read_UINT16(s, pointerFlags); /* pointerFlags (2 bytes) */
765 Stream_Read_UINT16(s, xPos); /* xPos (2 bytes) */
766 Stream_Read_UINT16(s, yPos); /* yPos (2 bytes) */
767 return IFCALLRESULT(TRUE, input->MouseEvent, input, pointerFlags, xPos, yPos);
768}
769
770static BOOL input_recv_relmouse_event(rdpInput* input, wStream* s)
771{
772 UINT16 pointerFlags = 0;
773 INT16 xDelta = 0;
774 INT16 yDelta = 0;
775
776 WINPR_ASSERT(input);
777 WINPR_ASSERT(s);
778
779 if (!Stream_CheckAndLogRequiredLength(TAG, s, 6))
780 return FALSE;
781
782 Stream_Read_UINT16(s, pointerFlags); /* pointerFlags (2 bytes) */
783 Stream_Read_INT16(s, xDelta); /* xPos (2 bytes) */
784 Stream_Read_INT16(s, yDelta); /* yPos (2 bytes) */
785
786 if (!freerdp_settings_get_bool(input->context->settings, FreeRDP_HasRelativeMouseEvent))
787 {
788 WLog_ERR(TAG,
789 "Received relative mouse event(flags=0x%04" PRIx16 ", xPos=%" PRId16
790 ", yPos=%" PRId16 "), but we did not announce support for that",
791 pointerFlags, xDelta, yDelta);
792 return FALSE;
793 }
794
795 return IFCALLRESULT(TRUE, input->RelMouseEvent, input, pointerFlags, xDelta, yDelta);
796}
797
798static BOOL input_recv_extended_mouse_event(rdpInput* input, wStream* s)
799{
800 UINT16 pointerFlags = 0;
801 UINT16 xPos = 0;
802 UINT16 yPos = 0;
803
804 WINPR_ASSERT(input);
805 WINPR_ASSERT(s);
806
807 if (!Stream_CheckAndLogRequiredLength(TAG, s, 6))
808 return FALSE;
809
810 Stream_Read_UINT16(s, pointerFlags); /* pointerFlags (2 bytes) */
811 Stream_Read_UINT16(s, xPos); /* xPos (2 bytes) */
812 Stream_Read_UINT16(s, yPos); /* yPos (2 bytes) */
813
814 if (!freerdp_settings_get_bool(input->context->settings, FreeRDP_HasExtendedMouseEvent))
815 {
816 WLog_ERR(TAG,
817 "Received extended mouse event(flags=0x%04" PRIx16 ", xPos=%" PRIu16
818 ", yPos=%" PRIu16 "), but we did not announce support for that",
819 pointerFlags, xPos, yPos);
820 return FALSE;
821 }
822
823 return IFCALLRESULT(TRUE, input->ExtendedMouseEvent, input, pointerFlags, xPos, yPos);
824}
825
826static BOOL input_recv_event(rdpInput* input, wStream* s)
827{
828 UINT16 messageType = 0;
829
830 WINPR_ASSERT(input);
831 WINPR_ASSERT(s);
832
833 if (!Stream_CheckAndLogRequiredLength(TAG, s, 6))
834 return FALSE;
835
836 Stream_Seek(s, 4); /* eventTime (4 bytes), ignored by the server */
837 Stream_Read_UINT16(s, messageType); /* messageType (2 bytes) */
838
839 switch (messageType)
840 {
841 case INPUT_EVENT_SYNC:
842 if (!input_recv_sync_event(input, s))
843 return FALSE;
844
845 break;
846
847 case INPUT_EVENT_SCANCODE:
848 if (!input_recv_keyboard_event(input, s))
849 return FALSE;
850
851 break;
852
853 case INPUT_EVENT_UNICODE:
854 if (!input_recv_unicode_keyboard_event(input, s))
855 return FALSE;
856
857 break;
858
859 case INPUT_EVENT_MOUSE:
860 if (!input_recv_mouse_event(input, s))
861 return FALSE;
862
863 break;
864
865 case INPUT_EVENT_MOUSEX:
866 if (!input_recv_extended_mouse_event(input, s))
867 return FALSE;
868
869 break;
870
871 case INPUT_EVENT_MOUSEREL:
872 if (!input_recv_relmouse_event(input, s))
873 return FALSE;
874
875 break;
876
877 default:
878 WLog_ERR(TAG, "Unknown messageType %" PRIu16 "", messageType);
879 /* Each input event uses 6 bytes. */
880 Stream_Seek(s, 6);
881 break;
882 }
883
884 return TRUE;
885}
886
887BOOL input_recv(rdpInput* input, wStream* s)
888{
889 UINT16 numberEvents = 0;
890
891 WINPR_ASSERT(input);
892 WINPR_ASSERT(s);
893
894 if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
895 return FALSE;
896
897 Stream_Read_UINT16(s, numberEvents); /* numberEvents (2 bytes) */
898 Stream_Seek(s, 2); /* pad2Octets (2 bytes) */
899
900 /* Each input event uses 6 exactly bytes. */
901 if (!Stream_CheckAndLogRequiredLengthOfSize(TAG, s, numberEvents, 6ull))
902 return FALSE;
903
904 for (UINT16 i = 0; i < numberEvents; i++)
905 {
906 if (!input_recv_event(input, s))
907 return FALSE;
908 }
909
910 return TRUE;
911}
912
913BOOL input_register_client_callbacks(rdpInput* input)
914{
915 rdpSettings* settings = NULL;
916
917 if (!input->context)
918 return FALSE;
919
920 settings = input->context->settings;
921
922 if (!settings)
923 return FALSE;
924
925 if (freerdp_settings_get_bool(settings, FreeRDP_FastPathInput))
926 {
927 input->SynchronizeEvent = input_send_fastpath_synchronize_event;
928 input->KeyboardEvent = input_send_fastpath_keyboard_event;
929 input->KeyboardPauseEvent = input_send_fastpath_keyboard_pause_event;
930 input->UnicodeKeyboardEvent = input_send_fastpath_unicode_keyboard_event;
931 input->MouseEvent = input_send_fastpath_mouse_event;
932 input->RelMouseEvent = input_send_fastpath_relmouse_event;
933 input->ExtendedMouseEvent = input_send_fastpath_extended_mouse_event;
934 input->FocusInEvent = input_send_fastpath_focus_in_event;
935 input->QoEEvent = input_send_fastpath_qoe_event;
936 }
937 else
938 {
939 input->SynchronizeEvent = input_send_synchronize_event;
940 input->KeyboardEvent = input_send_keyboard_event;
941 input->KeyboardPauseEvent = input_send_keyboard_pause_event;
942 input->UnicodeKeyboardEvent = input_send_unicode_keyboard_event;
943 input->MouseEvent = input_send_mouse_event;
944 input->RelMouseEvent = input_send_relmouse_event;
945 input->ExtendedMouseEvent = input_send_extended_mouse_event;
946 input->FocusInEvent = input_send_focus_in_event;
947 }
948
949 return TRUE;
950}
951
952/* Save last input timestamp and/or mouse position in prevent-session-lock mode */
953static BOOL input_update_last_event(rdpInput* input, BOOL mouse, UINT16 x, UINT16 y)
954{
955 rdp_input_internal* in = input_cast(input);
956
957 WINPR_ASSERT(input);
958 WINPR_ASSERT(input->context);
959
960 if (freerdp_settings_get_uint32(input->context->settings, FreeRDP_FakeMouseMotionInterval) > 0)
961 {
962 const time_t now = time(NULL);
963 in->lastInputTimestamp = WINPR_ASSERTING_INT_CAST(UINT64, now);
964
965 if (mouse)
966 {
967 in->lastX = x;
968 in->lastY = y;
969 }
970 }
971 return TRUE;
972}
973
974BOOL freerdp_input_send_synchronize_event(rdpInput* input, UINT32 flags)
975{
976 if (!input || !input->context)
977 return FALSE;
978
979 rdp_input_internal* in = input_cast(input);
980 const BOOL suspended =
981 freerdp_settings_get_bool(input->context->settings, FreeRDP_SuspendInput);
982 char buffer[128] = { 0 };
983 WLog_Print(in->log, WLOG_DEBUG, "Keyboard {Sync, suspend: %d} [%s]", suspended,
984 freerdp_input_keyboard_flags_string(flags, buffer, sizeof(buffer)));
985 if (suspended)
986 return TRUE;
987
988 return IFCALLRESULT(TRUE, input->SynchronizeEvent, input, flags);
989}
990
991BOOL freerdp_input_send_keyboard_event(rdpInput* input, UINT16 flags, UINT8 code)
992{
993 if (!input || !input->context)
994 return FALSE;
995
996 if (freerdp_settings_get_bool(input->context->settings, FreeRDP_SuspendInput))
997 return TRUE;
998
999 input_update_last_event(input, FALSE, 0, 0);
1000
1001 return IFCALLRESULT(TRUE, input->KeyboardEvent, input, flags, code);
1002}
1003
1004BOOL freerdp_input_send_keyboard_event_ex(rdpInput* input, BOOL down, BOOL repeat,
1005 UINT32 rdp_scancode)
1006{
1007 UINT16 flags = (RDP_SCANCODE_EXTENDED(rdp_scancode) ? KBD_FLAGS_EXTENDED : 0);
1008 if (down && repeat)
1009 flags |= KBD_FLAGS_DOWN;
1010 else if (!down)
1011 flags |= KBD_FLAGS_RELEASE;
1012
1013 return freerdp_input_send_keyboard_event(input, flags, RDP_SCANCODE_CODE(rdp_scancode));
1014}
1015
1016BOOL freerdp_input_send_unicode_keyboard_event(rdpInput* input, UINT16 flags, UINT16 code)
1017{
1018 if (!input || !input->context)
1019 return FALSE;
1020
1021 if (freerdp_settings_get_bool(input->context->settings, FreeRDP_SuspendInput))
1022 return TRUE;
1023
1024 input_update_last_event(input, FALSE, 0, 0);
1025
1026 return IFCALLRESULT(TRUE, input->UnicodeKeyboardEvent, input, flags, code);
1027}
1028
1029BOOL freerdp_input_send_mouse_event(rdpInput* input, UINT16 flags, UINT16 x, UINT16 y)
1030{
1031 if (!input || !input->context)
1032 return FALSE;
1033
1034 if (freerdp_settings_get_bool(input->context->settings, FreeRDP_SuspendInput))
1035 return TRUE;
1036
1037 input_update_last_event(
1038 input, flags & (PTR_FLAGS_MOVE | PTR_FLAGS_BUTTON1 | PTR_FLAGS_BUTTON2 | PTR_FLAGS_BUTTON3),
1039 x, y);
1040
1041 return IFCALLRESULT(TRUE, input->MouseEvent, input, flags, x, y);
1042}
1043
1044BOOL freerdp_input_send_rel_mouse_event(rdpInput* input, UINT16 flags, INT16 xDelta, INT16 yDelta)
1045{
1046 if (!input || !input->context)
1047 return FALSE;
1048
1049 if (freerdp_settings_get_bool(input->context->settings, FreeRDP_SuspendInput))
1050 return TRUE;
1051
1052 return IFCALLRESULT(TRUE, input->RelMouseEvent, input, flags, xDelta, yDelta);
1053}
1054
1055BOOL freerdp_input_send_qoe_timestamp(rdpInput* input, UINT32 timestampMS)
1056{
1057 if (!input || !input->context)
1058 return FALSE;
1059
1060 return IFCALLRESULT(TRUE, input->QoEEvent, input, timestampMS);
1061}
1062
1063BOOL freerdp_input_send_extended_mouse_event(rdpInput* input, UINT16 flags, UINT16 x, UINT16 y)
1064{
1065 if (!input || !input->context)
1066 return FALSE;
1067
1068 if (freerdp_settings_get_bool(input->context->settings, FreeRDP_SuspendInput))
1069 return TRUE;
1070
1071 input_update_last_event(input, TRUE, x, y);
1072
1073 return IFCALLRESULT(TRUE, input->ExtendedMouseEvent, input, flags, x, y);
1074}
1075
1076BOOL freerdp_input_send_focus_in_event(rdpInput* input, UINT16 toggleStates)
1077{
1078 if (!input || !input->context)
1079 return FALSE;
1080
1081 rdp_input_internal* in = input_cast(input);
1082 const BOOL suspended =
1083 freerdp_settings_get_bool(input->context->settings, FreeRDP_SuspendInput);
1084 char buffer[128] = { 0 };
1085 WLog_Print(in->log, WLOG_DEBUG, "Keyboard {FocusIn, suspend: %s} [%s]",
1086 suspended ? "true" : "false",
1087 freerdp_input_keyboard_flags_string(toggleStates, buffer, sizeof(buffer)));
1088 if (suspended)
1089 return TRUE;
1090
1091 return IFCALLRESULT(TRUE, input->FocusInEvent, input, toggleStates);
1092}
1093
1094BOOL freerdp_input_send_keyboard_pause_event(rdpInput* input)
1095{
1096 if (!input || !input->context)
1097 return FALSE;
1098
1099 rdp_input_internal* in = input_cast(input);
1100 const BOOL suspended =
1101 freerdp_settings_get_bool(input->context->settings, FreeRDP_SuspendInput);
1102 WLog_Print(in->log, WLOG_DEBUG, "Keyboard {Pause, suspend: %s}", suspended ? "true" : "false");
1103 if (suspended)
1104 return TRUE;
1105
1106 return IFCALLRESULT(TRUE, input->KeyboardPauseEvent, input);
1107}
1108
1109int input_process_events(rdpInput* input)
1110{
1111 if (!input)
1112 return FALSE;
1113
1114 return input_message_queue_process_pending_messages(input);
1115}
1116
1117static void input_free_queued_message(void* obj)
1118{
1119 wMessage* msg = (wMessage*)obj;
1120 input_message_queue_free_message(msg);
1121}
1122
1123rdpInput* input_new(rdpRdp* rdp)
1124{
1125 const wObject cb = { NULL, NULL, NULL, input_free_queued_message, NULL };
1126 rdp_input_internal* input = (rdp_input_internal*)calloc(1, sizeof(rdp_input_internal));
1127
1128 WINPR_UNUSED(rdp);
1129
1130 if (!input)
1131 return NULL;
1132
1133 input->common.context = rdp->context;
1134 input->queue = MessageQueue_New(&cb);
1135 input->log = WLog_Get(TAG);
1136
1137 if (!input->queue)
1138 {
1139 free(input);
1140 return NULL;
1141 }
1142
1143 return &input->common;
1144}
1145
1146void input_free(rdpInput* input)
1147{
1148 if (input != NULL)
1149 {
1150 rdp_input_internal* in = input_cast(input);
1151
1152 MessageQueue_Free(in->queue);
1153 free(in);
1154 }
1155}
FREERDP_API UINT32 freerdp_settings_get_uint32(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id)
Returns a UINT32 settings value.
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:57