FreeRDP
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Modules Pages
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 void rdp_write_client_input_pdu_header(wStream* s, UINT16 number)
44{
45 WINPR_ASSERT(s);
46 WINPR_ASSERT(Stream_GetRemainingCapacity(s) >= 4);
47 Stream_Write_UINT16(s, number); /* numberEvents (2 bytes) */
48 Stream_Write_UINT16(s, 0); /* pad2Octets (2 bytes) */
49}
50
51static void rdp_write_input_event_header(wStream* s, UINT32 time, UINT16 type)
52{
53 WINPR_ASSERT(s);
54 WINPR_ASSERT(Stream_GetRemainingCapacity(s) >= 6);
55 Stream_Write_UINT32(s, time); /* eventTime (4 bytes) */
56 Stream_Write_UINT16(s, type); /* messageType (2 bytes) */
57}
58
59static wStream* rdp_client_input_pdu_init(rdpRdp* rdp, UINT16 type, UINT16* sec_flags)
60{
61 wStream* s = rdp_data_pdu_init(rdp, sec_flags);
62
63 if (!s)
64 return NULL;
65
66 rdp_write_client_input_pdu_header(s, 1);
67 rdp_write_input_event_header(s, 0, type);
68 return s;
69}
70
71static BOOL rdp_send_client_input_pdu(rdpRdp* rdp, wStream* s, UINT16 sec_flags)
72{
73 WINPR_ASSERT(rdp);
74 WINPR_ASSERT(rdp->mcs);
75 return rdp_send_data_pdu(rdp, s, DATA_PDU_TYPE_INPUT, rdp->mcs->userId, sec_flags);
76}
77
78static void input_write_synchronize_event(wStream* s, UINT32 flags)
79{
80 WINPR_ASSERT(s);
81 WINPR_ASSERT(Stream_GetRemainingCapacity(s) >= 6);
82 Stream_Write_UINT16(s, 0); /* pad2Octets (2 bytes) */
83 Stream_Write_UINT32(s, flags); /* toggleFlags (4 bytes) */
84}
85
86static BOOL input_ensure_client_running(rdpInput* input)
87{
88 WINPR_ASSERT(input);
89 if (freerdp_shall_disconnect_context(input->context))
90 {
91 WLog_WARN(TAG, "[APPLICATION BUG] input functions called after the session terminated");
92 return FALSE;
93 }
94 return TRUE;
95}
96
97static BOOL input_send_synchronize_event(rdpInput* input, UINT32 flags)
98{
99 UINT16 sec_flags = 0;
100
101 if (!input || !input->context)
102 return FALSE;
103
104 rdpRdp* rdp = input->context->rdp;
105
106 if (!input_ensure_client_running(input))
107 return FALSE;
108
109 wStream* s = rdp_client_input_pdu_init(rdp, INPUT_EVENT_SYNC, &sec_flags);
110
111 if (!s)
112 return FALSE;
113
114 input_write_synchronize_event(s, flags);
115 return rdp_send_client_input_pdu(rdp, s, sec_flags);
116}
117
118static void input_write_keyboard_event(wStream* s, UINT16 flags, UINT16 code)
119{
120 WINPR_ASSERT(s);
121 WINPR_ASSERT(code <= UINT8_MAX);
122
123 Stream_Write_UINT16(s, flags); /* keyboardFlags (2 bytes) */
124 Stream_Write_UINT16(s, code); /* keyCode (2 bytes) */
125 Stream_Write_UINT16(s, 0); /* pad2Octets (2 bytes) */
126}
127
128static BOOL input_send_keyboard_event(rdpInput* input, UINT16 flags, UINT8 code)
129{
130 UINT16 sec_flags = 0;
131 wStream* s = NULL;
132 rdpRdp* rdp = NULL;
133
134 if (!input || !input->context)
135 return FALSE;
136
137 rdp = input->context->rdp;
138
139 if (!input_ensure_client_running(input))
140 return FALSE;
141
142 s = rdp_client_input_pdu_init(rdp, INPUT_EVENT_SCANCODE, &sec_flags);
143
144 if (!s)
145 return FALSE;
146
147 input_write_keyboard_event(s, flags, code);
148 return rdp_send_client_input_pdu(rdp, s, sec_flags);
149}
150
151static void input_write_unicode_keyboard_event(wStream* s, UINT16 flags, UINT16 code)
152{
153 Stream_Write_UINT16(s, flags); /* keyboardFlags (2 bytes) */
154 Stream_Write_UINT16(s, code); /* unicodeCode (2 bytes) */
155 Stream_Write_UINT16(s, 0); /* pad2Octets (2 bytes) */
156}
157
158static BOOL input_send_unicode_keyboard_event(rdpInput* input, UINT16 flags, UINT16 code)
159{
160 UINT16 sec_flags = 0;
161 wStream* s = NULL;
162 rdpRdp* rdp = NULL;
163
164 if (!input || !input->context)
165 return FALSE;
166
167 if (!input_ensure_client_running(input))
168 return FALSE;
169
170 if (!freerdp_settings_get_bool(input->context->settings, FreeRDP_UnicodeInput))
171 {
172 WLog_WARN(TAG, "Unicode input not supported by server.");
173 return FALSE;
174 }
175
176 rdp = input->context->rdp;
177 s = rdp_client_input_pdu_init(rdp, INPUT_EVENT_UNICODE, &sec_flags);
178
179 if (!s)
180 return FALSE;
181
182 input_write_unicode_keyboard_event(s, flags, code);
183 return rdp_send_client_input_pdu(rdp, s, sec_flags);
184}
185
186static void input_write_mouse_event(wStream* s, UINT16 flags, UINT16 x, UINT16 y)
187{
188 Stream_Write_UINT16(s, flags); /* pointerFlags (2 bytes) */
189 Stream_Write_UINT16(s, x); /* xPos (2 bytes) */
190 Stream_Write_UINT16(s, y); /* yPos (2 bytes) */
191}
192
193static BOOL input_send_mouse_event(rdpInput* input, UINT16 flags, UINT16 x, UINT16 y)
194{
195 UINT16 sec_flags = 0;
196
197 if (!input || !input->context || !input->context->settings)
198 return FALSE;
199
200 rdpRdp* rdp = input->context->rdp;
201
202 if (!input_ensure_client_running(input))
203 return FALSE;
204
205 if (!freerdp_settings_get_bool(input->context->settings, FreeRDP_HasHorizontalWheel))
206 {
207 if (flags & PTR_FLAGS_HWHEEL)
208 {
209 WLog_WARN(TAG,
210 "skip mouse event %" PRIu16 "x%" PRIu16 " flags=0x%04" PRIX16
211 ", no horizontal mouse wheel supported",
212 x, y, flags);
213 return TRUE;
214 }
215 }
216
217 wStream* s = rdp_client_input_pdu_init(rdp, INPUT_EVENT_MOUSE, &sec_flags);
218
219 if (!s)
220 return FALSE;
221
222 input_write_mouse_event(s, flags, x, y);
223 return rdp_send_client_input_pdu(rdp, s, sec_flags);
224}
225
226static BOOL input_send_relmouse_event(rdpInput* input, UINT16 flags, INT16 xDelta, INT16 yDelta)
227{
228 UINT16 sec_flags = 0;
229 wStream* s = NULL;
230 rdpRdp* rdp = NULL;
231
232 if (!input || !input->context || !input->context->settings)
233 return FALSE;
234
235 rdp = input->context->rdp;
236
237 if (!input_ensure_client_running(input))
238 return FALSE;
239
240 if (!freerdp_settings_get_bool(input->context->settings, FreeRDP_HasRelativeMouseEvent))
241 {
242 WLog_ERR(TAG, "Sending relative mouse event, but no support for that");
243 return FALSE;
244 }
245
246 s = rdp_client_input_pdu_init(rdp, INPUT_EVENT_MOUSEREL, &sec_flags);
247
248 if (!s)
249 return FALSE;
250
251 Stream_Write_UINT16(s, flags); /* pointerFlags (2 bytes) */
252 Stream_Write_INT16(s, xDelta); /* xDelta (2 bytes) */
253 Stream_Write_INT16(s, yDelta); /* yDelta (2 bytes) */
254
255 return rdp_send_client_input_pdu(rdp, s, sec_flags);
256}
257
258static void input_write_extended_mouse_event(wStream* s, UINT16 flags, UINT16 x, UINT16 y)
259{
260 Stream_Write_UINT16(s, flags); /* pointerFlags (2 bytes) */
261 Stream_Write_UINT16(s, x); /* xPos (2 bytes) */
262 Stream_Write_UINT16(s, y); /* yPos (2 bytes) */
263}
264
265static BOOL input_send_extended_mouse_event(rdpInput* input, UINT16 flags, UINT16 x, UINT16 y)
266{
267 UINT16 sec_flags = 0;
268
269 WINPR_ASSERT(input);
270 WINPR_ASSERT(input->context);
271 WINPR_ASSERT(input->context->settings);
272
273 rdpRdp* rdp = input->context->rdp;
274 WINPR_ASSERT(rdp);
275
276 if (!input_ensure_client_running(input))
277 return FALSE;
278
279 if (!freerdp_settings_get_bool(input->context->settings, FreeRDP_HasExtendedMouseEvent))
280 {
281 WLog_WARN(TAG,
282 "skip extended mouse event %" PRIu16 "x%" PRIu16 " flags=0x%04" PRIX16
283 ", no extended mouse events supported",
284 x, y, flags);
285 return TRUE;
286 }
287
288 wStream* s = rdp_client_input_pdu_init(rdp, INPUT_EVENT_MOUSEX, &sec_flags);
289
290 if (!s)
291 return FALSE;
292
293 input_write_extended_mouse_event(s, flags, x, y);
294 return rdp_send_client_input_pdu(rdp, s, sec_flags);
295}
296
297static BOOL input_send_focus_in_event(rdpInput* input, UINT16 toggleStates)
298{
299 /* send a tab up like mstsc.exe */
300 if (!input_send_keyboard_event(input, KBD_FLAGS_RELEASE, 0x0f))
301 return FALSE;
302
303 /* send the toggle key states */
304 if (!input_send_synchronize_event(input, (toggleStates & 0x1F)))
305 return FALSE;
306
307 /* send another tab up like mstsc.exe */
308 return input_send_keyboard_event(input, KBD_FLAGS_RELEASE, 0x0f);
309}
310
311static BOOL input_send_keyboard_pause_event(rdpInput* input)
312{
313 /* In ancient days, pause-down without control sent E1 1D 45 E1 9D C5,
314 * and pause-up sent nothing. However, reverse engineering mstsc shows
315 * it sending the following sequence:
316 */
317
318 /* Control down (0x1D) */
319 if (!input_send_keyboard_event(input, KBD_FLAGS_EXTENDED1,
320 RDP_SCANCODE_CODE(RDP_SCANCODE_LCONTROL)))
321 return FALSE;
322
323 /* Numlock down (0x45) */
324 if (!input_send_keyboard_event(input, 0, RDP_SCANCODE_CODE(RDP_SCANCODE_NUMLOCK)))
325 return FALSE;
326
327 /* Control up (0x1D) */
328 if (!input_send_keyboard_event(input, KBD_FLAGS_RELEASE | KBD_FLAGS_EXTENDED1,
329 RDP_SCANCODE_CODE(RDP_SCANCODE_LCONTROL)))
330 return FALSE;
331
332 /* Numlock up (0x45) */
333 return input_send_keyboard_event(input, KBD_FLAGS_RELEASE,
334 RDP_SCANCODE_CODE(RDP_SCANCODE_NUMLOCK));
335}
336
337static BOOL input_send_fastpath_synchronize_event(rdpInput* input, UINT32 flags)
338{
339 UINT16 sec_flags = 0;
340 wStream* s = NULL;
341 rdpRdp* rdp = NULL;
342
343 WINPR_ASSERT(input);
344 WINPR_ASSERT(input->context);
345
346 rdp = input->context->rdp;
347 WINPR_ASSERT(rdp);
348
349 if (!input_ensure_client_running(input))
350 return FALSE;
351
352 /* The FastPath Synchronization eventFlags has identical values as SlowPath */
353 s = fastpath_input_pdu_init(rdp->fastpath, (BYTE)flags, FASTPATH_INPUT_EVENT_SYNC, &sec_flags);
354
355 if (!s)
356 return FALSE;
357
358 return fastpath_send_input_pdu(rdp->fastpath, s, sec_flags);
359}
360
361static BOOL input_send_fastpath_keyboard_event(rdpInput* input, UINT16 flags, UINT8 code)
362{
363 UINT16 sec_flags = 0;
364 wStream* s = NULL;
365 BYTE eventFlags = 0;
366 rdpRdp* rdp = NULL;
367
368 WINPR_ASSERT(input);
369 WINPR_ASSERT(input->context);
370
371 rdp = input->context->rdp;
372 WINPR_ASSERT(rdp);
373
374 if (!input_ensure_client_running(input))
375 return FALSE;
376
377 eventFlags |= (flags & KBD_FLAGS_RELEASE) ? FASTPATH_INPUT_KBDFLAGS_RELEASE : 0;
378 eventFlags |= (flags & KBD_FLAGS_EXTENDED) ? FASTPATH_INPUT_KBDFLAGS_EXTENDED : 0;
379 eventFlags |= (flags & KBD_FLAGS_EXTENDED1) ? FASTPATH_INPUT_KBDFLAGS_PREFIX_E1 : 0;
380 s = fastpath_input_pdu_init(rdp->fastpath, eventFlags, FASTPATH_INPUT_EVENT_SCANCODE,
381 &sec_flags);
382
383 if (!s)
384 return FALSE;
385
386 WINPR_ASSERT(code <= UINT8_MAX);
387 Stream_Write_UINT8(s, code); /* keyCode (1 byte) */
388 return fastpath_send_input_pdu(rdp->fastpath, s, sec_flags);
389}
390
391static BOOL input_send_fastpath_unicode_keyboard_event(rdpInput* input, UINT16 flags, UINT16 code)
392{
393 UINT16 sec_flags = 0;
394 wStream* s = NULL;
395 BYTE eventFlags = 0;
396 rdpRdp* rdp = NULL;
397
398 WINPR_ASSERT(input);
399 WINPR_ASSERT(input->context);
400 WINPR_ASSERT(input->context->settings);
401
402 rdp = input->context->rdp;
403 WINPR_ASSERT(rdp);
404
405 if (!input_ensure_client_running(input))
406 return FALSE;
407
408 if (!freerdp_settings_get_bool(input->context->settings, FreeRDP_UnicodeInput))
409 {
410 WLog_WARN(TAG, "Unicode input not supported by server.");
411 return FALSE;
412 }
413
414 eventFlags |= (flags & KBD_FLAGS_RELEASE) ? FASTPATH_INPUT_KBDFLAGS_RELEASE : 0;
415 s = fastpath_input_pdu_init(rdp->fastpath, eventFlags, FASTPATH_INPUT_EVENT_UNICODE,
416 &sec_flags);
417
418 if (!s)
419 return FALSE;
420
421 Stream_Write_UINT16(s, code); /* unicodeCode (2 bytes) */
422 return fastpath_send_input_pdu(rdp->fastpath, s, sec_flags);
423}
424
425static BOOL input_send_fastpath_mouse_event(rdpInput* input, UINT16 flags, UINT16 x, UINT16 y)
426{
427 UINT16 sec_flags = 0;
428 wStream* s = NULL;
429 rdpRdp* rdp = NULL;
430
431 WINPR_ASSERT(input);
432 WINPR_ASSERT(input->context);
433 WINPR_ASSERT(input->context->settings);
434
435 rdp = input->context->rdp;
436 WINPR_ASSERT(rdp);
437
438 if (!input_ensure_client_running(input))
439 return FALSE;
440
441 if (!freerdp_settings_get_bool(input->context->settings, FreeRDP_HasHorizontalWheel))
442 {
443 if (flags & PTR_FLAGS_HWHEEL)
444 {
445 WLog_WARN(TAG,
446 "skip mouse event %" PRIu16 "x%" PRIu16 " flags=0x%04" PRIX16
447 ", no horizontal mouse wheel supported",
448 x, y, flags);
449 return TRUE;
450 }
451 }
452
453 s = fastpath_input_pdu_init(rdp->fastpath, 0, FASTPATH_INPUT_EVENT_MOUSE, &sec_flags);
454
455 if (!s)
456 return FALSE;
457
458 input_write_mouse_event(s, flags, x, y);
459 return fastpath_send_input_pdu(rdp->fastpath, s, sec_flags);
460}
461
462static BOOL input_send_fastpath_extended_mouse_event(rdpInput* input, UINT16 flags, UINT16 x,
463 UINT16 y)
464{
465 UINT16 sec_flags = 0;
466 wStream* s = NULL;
467 rdpRdp* rdp = NULL;
468
469 WINPR_ASSERT(input);
470 WINPR_ASSERT(input->context);
471
472 rdp = input->context->rdp;
473 WINPR_ASSERT(rdp);
474
475 if (!input_ensure_client_running(input))
476 return FALSE;
477
478 if (!freerdp_settings_get_bool(input->context->settings, FreeRDP_HasExtendedMouseEvent))
479 {
480 WLog_WARN(TAG,
481 "skip extended mouse event %" PRIu16 "x%" PRIu16 " flags=0x%04" PRIX16
482 ", no extended mouse events supported",
483 x, y, flags);
484 return TRUE;
485 }
486
487 s = fastpath_input_pdu_init(rdp->fastpath, 0, FASTPATH_INPUT_EVENT_MOUSEX, &sec_flags);
488
489 if (!s)
490 return FALSE;
491
492 input_write_extended_mouse_event(s, flags, x, y);
493 return fastpath_send_input_pdu(rdp->fastpath, s, sec_flags);
494}
495
496static BOOL input_send_fastpath_relmouse_event(rdpInput* input, UINT16 flags, INT16 xDelta,
497 INT16 yDelta)
498{
499 UINT16 sec_flags = 0;
500 wStream* s = NULL;
501 rdpRdp* rdp = NULL;
502
503 WINPR_ASSERT(input);
504 WINPR_ASSERT(input->context);
505 WINPR_ASSERT(input->context->settings);
506
507 rdp = input->context->rdp;
508 WINPR_ASSERT(rdp);
509
510 if (!input_ensure_client_running(input))
511 return FALSE;
512
513 if (!freerdp_settings_get_bool(input->context->settings, FreeRDP_HasRelativeMouseEvent))
514 {
515 WLog_ERR(TAG, "Sending relative fastpath mouse event, but no support for that announced");
516 return FALSE;
517 }
518
519 s = fastpath_input_pdu_init(rdp->fastpath, 0, TS_FP_RELPOINTER_EVENT, &sec_flags);
520
521 if (!s)
522 return FALSE;
523
524 Stream_Write_UINT16(s, flags); /* pointerFlags (2 bytes) */
525 Stream_Write_INT16(s, xDelta); /* xDelta (2 bytes) */
526 Stream_Write_INT16(s, yDelta); /* yDelta (2 bytes) */
527 return fastpath_send_input_pdu(rdp->fastpath, s, sec_flags);
528}
529
530static BOOL input_send_fastpath_qoe_event(rdpInput* input, UINT32 timestampMS)
531{
532 WINPR_ASSERT(input);
533 WINPR_ASSERT(input->context);
534 WINPR_ASSERT(input->context->settings);
535
536 rdpRdp* rdp = input->context->rdp;
537 WINPR_ASSERT(rdp);
538
539 if (!input_ensure_client_running(input))
540 return FALSE;
541
542 if (!freerdp_settings_get_bool(input->context->settings, FreeRDP_HasQoeEvent))
543 {
544 WLog_ERR(TAG, "Sending qoe event, but no support for that announced");
545 return FALSE;
546 }
547
548 UINT16 sec_flags = 0;
549 wStream* s = fastpath_input_pdu_init(rdp->fastpath, 0, TS_FP_QOETIMESTAMP_EVENT, &sec_flags);
550
551 if (!s)
552 return FALSE;
553
554 if (!Stream_EnsureRemainingCapacity(s, 4))
555 {
556 Stream_Free(s, TRUE);
557 return FALSE;
558 }
559
560 Stream_Write_UINT32(s, timestampMS);
561 return fastpath_send_input_pdu(rdp->fastpath, s, sec_flags);
562}
563
564static BOOL input_send_fastpath_focus_in_event(rdpInput* input, UINT16 toggleStates)
565{
566 UINT16 sec_flags = 0;
567 wStream* s = NULL;
568 BYTE eventFlags = 0;
569 rdpRdp* rdp = NULL;
570
571 WINPR_ASSERT(input);
572 WINPR_ASSERT(input->context);
573
574 rdp = input->context->rdp;
575 WINPR_ASSERT(rdp);
576
577 if (!input_ensure_client_running(input))
578 return FALSE;
579
580 s = fastpath_input_pdu_init_header(rdp->fastpath, &sec_flags);
581
582 if (!s)
583 return FALSE;
584
585 /* send a tab up like mstsc.exe */
586 eventFlags = FASTPATH_INPUT_KBDFLAGS_RELEASE | FASTPATH_INPUT_EVENT_SCANCODE << 5;
587 Stream_Write_UINT8(s, eventFlags); /* Key Release event (1 byte) */
588 Stream_Write_UINT8(s, 0x0f); /* keyCode (1 byte) */
589 /* send the toggle key states */
590 eventFlags = (toggleStates & 0x1F) | FASTPATH_INPUT_EVENT_SYNC << 5;
591 Stream_Write_UINT8(s, eventFlags); /* toggle state (1 byte) */
592 /* send another tab up like mstsc.exe */
593 eventFlags = FASTPATH_INPUT_KBDFLAGS_RELEASE | FASTPATH_INPUT_EVENT_SCANCODE << 5;
594 Stream_Write_UINT8(s, eventFlags); /* Key Release event (1 byte) */
595 Stream_Write_UINT8(s, 0x0f); /* keyCode (1 byte) */
596 return fastpath_send_multiple_input_pdu(rdp->fastpath, s, 3, sec_flags);
597}
598
599static BOOL input_send_fastpath_keyboard_pause_event(rdpInput* input)
600{
601 /* In ancient days, pause-down without control sent E1 1D 45 E1 9D C5,
602 * and pause-up sent nothing. However, reverse engineering mstsc shows
603 * it sending the following sequence:
604 */
605 UINT16 sec_flags = 0;
606 wStream* s = NULL;
607 const BYTE keyDownEvent = FASTPATH_INPUT_EVENT_SCANCODE << 5;
608 const BYTE keyUpEvent = (FASTPATH_INPUT_EVENT_SCANCODE << 5) | FASTPATH_INPUT_KBDFLAGS_RELEASE;
609 rdpRdp* rdp = NULL;
610
611 WINPR_ASSERT(input);
612 WINPR_ASSERT(input->context);
613
614 rdp = input->context->rdp;
615 WINPR_ASSERT(rdp);
616
617 if (!input_ensure_client_running(input))
618 return FALSE;
619
620 s = fastpath_input_pdu_init_header(rdp->fastpath, &sec_flags);
621
622 if (!s)
623 return FALSE;
624
625 /* Control down (0x1D) */
626 Stream_Write_UINT8(s, keyDownEvent | FASTPATH_INPUT_KBDFLAGS_PREFIX_E1);
627 Stream_Write_UINT8(s, RDP_SCANCODE_CODE(RDP_SCANCODE_LCONTROL));
628 /* Numlock down (0x45) */
629 Stream_Write_UINT8(s, keyDownEvent);
630 Stream_Write_UINT8(s, RDP_SCANCODE_CODE(RDP_SCANCODE_NUMLOCK));
631 /* Control up (0x1D) */
632 Stream_Write_UINT8(s, keyUpEvent | FASTPATH_INPUT_KBDFLAGS_PREFIX_E1);
633 Stream_Write_UINT8(s, RDP_SCANCODE_CODE(RDP_SCANCODE_LCONTROL));
634 /* Numlock down (0x45) */
635 Stream_Write_UINT8(s, keyUpEvent);
636 Stream_Write_UINT8(s, RDP_SCANCODE_CODE(RDP_SCANCODE_NUMLOCK));
637 return fastpath_send_multiple_input_pdu(rdp->fastpath, s, 4, sec_flags);
638}
639
640static BOOL input_recv_sync_event(rdpInput* input, wStream* s)
641{
642 UINT32 toggleFlags = 0;
643
644 WINPR_ASSERT(input);
645 WINPR_ASSERT(s);
646
647 if (!Stream_CheckAndLogRequiredLength(TAG, s, 6))
648 return FALSE;
649
650 Stream_Seek(s, 2); /* pad2Octets (2 bytes) */
651 Stream_Read_UINT32(s, toggleFlags); /* toggleFlags (4 bytes) */
652 return IFCALLRESULT(TRUE, input->SynchronizeEvent, input, toggleFlags);
653}
654
655static BOOL input_recv_keyboard_event(rdpInput* input, wStream* s)
656{
657 UINT16 keyboardFlags = 0;
658 UINT16 keyCode = 0;
659
660 WINPR_ASSERT(input);
661 WINPR_ASSERT(s);
662
663 if (!Stream_CheckAndLogRequiredLength(TAG, s, 6))
664 return FALSE;
665
666 Stream_Read_UINT16(s, keyboardFlags); /* keyboardFlags (2 bytes) */
667 Stream_Read_UINT16(s, keyCode); /* keyCode (2 bytes) */
668 Stream_Seek(s, 2); /* pad2Octets (2 bytes) */
669
670 if (keyboardFlags & KBD_FLAGS_RELEASE)
671 keyboardFlags &= ~KBD_FLAGS_DOWN;
672
673 if (keyCode & 0xFF00)
674 WLog_WARN(TAG,
675 "Problematic [MS-RDPBCGR] 2.2.8.1.1.3.1.1.1 Keyboard Event (TS_KEYBOARD_EVENT) "
676 "keyCode=0x%04" PRIx16
677 ", high byte values should be sent in keyboardFlags field, ignoring.",
678 keyCode);
679 return IFCALLRESULT(TRUE, input->KeyboardEvent, input, keyboardFlags, keyCode & 0xFF);
680}
681
682static BOOL input_recv_unicode_keyboard_event(rdpInput* input, wStream* s)
683{
684 UINT16 keyboardFlags = 0;
685 UINT16 unicodeCode = 0;
686
687 WINPR_ASSERT(input);
688 WINPR_ASSERT(s);
689
690 if (!Stream_CheckAndLogRequiredLength(TAG, s, 6))
691 return FALSE;
692
693 Stream_Read_UINT16(s, keyboardFlags); /* keyboardFlags (2 bytes) */
694 Stream_Read_UINT16(s, unicodeCode); /* unicodeCode (2 bytes) */
695 Stream_Seek(s, 2); /* pad2Octets (2 bytes) */
696
697 /* "fix" keyboardFlags - see comment in input_recv_keyboard_event() */
698
699 if (keyboardFlags & KBD_FLAGS_RELEASE)
700 keyboardFlags &= ~KBD_FLAGS_DOWN;
701
702 return IFCALLRESULT(TRUE, input->UnicodeKeyboardEvent, input, keyboardFlags, unicodeCode);
703}
704
705static BOOL input_recv_mouse_event(rdpInput* input, wStream* s)
706{
707 UINT16 pointerFlags = 0;
708 UINT16 xPos = 0;
709 UINT16 yPos = 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, pointerFlags); /* pointerFlags (2 bytes) */
718 Stream_Read_UINT16(s, xPos); /* xPos (2 bytes) */
719 Stream_Read_UINT16(s, yPos); /* yPos (2 bytes) */
720 return IFCALLRESULT(TRUE, input->MouseEvent, input, pointerFlags, xPos, yPos);
721}
722
723static BOOL input_recv_relmouse_event(rdpInput* input, wStream* s)
724{
725 UINT16 pointerFlags = 0;
726 INT16 xDelta = 0;
727 INT16 yDelta = 0;
728
729 WINPR_ASSERT(input);
730 WINPR_ASSERT(s);
731
732 if (!Stream_CheckAndLogRequiredLength(TAG, s, 6))
733 return FALSE;
734
735 Stream_Read_UINT16(s, pointerFlags); /* pointerFlags (2 bytes) */
736 Stream_Read_INT16(s, xDelta); /* xPos (2 bytes) */
737 Stream_Read_INT16(s, yDelta); /* yPos (2 bytes) */
738
739 if (!freerdp_settings_get_bool(input->context->settings, FreeRDP_HasRelativeMouseEvent))
740 {
741 WLog_ERR(TAG,
742 "Received relative mouse event(flags=0x%04" PRIx16 ", xPos=%" PRId16
743 ", yPos=%" PRId16 "), but we did not announce support for that",
744 pointerFlags, xDelta, yDelta);
745 return FALSE;
746 }
747
748 return IFCALLRESULT(TRUE, input->RelMouseEvent, input, pointerFlags, xDelta, yDelta);
749}
750
751static BOOL input_recv_extended_mouse_event(rdpInput* input, wStream* s)
752{
753 UINT16 pointerFlags = 0;
754 UINT16 xPos = 0;
755 UINT16 yPos = 0;
756
757 WINPR_ASSERT(input);
758 WINPR_ASSERT(s);
759
760 if (!Stream_CheckAndLogRequiredLength(TAG, s, 6))
761 return FALSE;
762
763 Stream_Read_UINT16(s, pointerFlags); /* pointerFlags (2 bytes) */
764 Stream_Read_UINT16(s, xPos); /* xPos (2 bytes) */
765 Stream_Read_UINT16(s, yPos); /* yPos (2 bytes) */
766
767 if (!freerdp_settings_get_bool(input->context->settings, FreeRDP_HasExtendedMouseEvent))
768 {
769 WLog_ERR(TAG,
770 "Received extended mouse event(flags=0x%04" PRIx16 ", xPos=%" PRIu16
771 ", yPos=%" PRIu16 "), but we did not announce support for that",
772 pointerFlags, xPos, yPos);
773 return FALSE;
774 }
775
776 return IFCALLRESULT(TRUE, input->ExtendedMouseEvent, input, pointerFlags, xPos, yPos);
777}
778
779static BOOL input_recv_event(rdpInput* input, wStream* s)
780{
781 UINT16 messageType = 0;
782
783 WINPR_ASSERT(input);
784 WINPR_ASSERT(s);
785
786 if (!Stream_CheckAndLogRequiredLength(TAG, s, 6))
787 return FALSE;
788
789 Stream_Seek(s, 4); /* eventTime (4 bytes), ignored by the server */
790 Stream_Read_UINT16(s, messageType); /* messageType (2 bytes) */
791
792 switch (messageType)
793 {
794 case INPUT_EVENT_SYNC:
795 if (!input_recv_sync_event(input, s))
796 return FALSE;
797
798 break;
799
800 case INPUT_EVENT_SCANCODE:
801 if (!input_recv_keyboard_event(input, s))
802 return FALSE;
803
804 break;
805
806 case INPUT_EVENT_UNICODE:
807 if (!input_recv_unicode_keyboard_event(input, s))
808 return FALSE;
809
810 break;
811
812 case INPUT_EVENT_MOUSE:
813 if (!input_recv_mouse_event(input, s))
814 return FALSE;
815
816 break;
817
818 case INPUT_EVENT_MOUSEX:
819 if (!input_recv_extended_mouse_event(input, s))
820 return FALSE;
821
822 break;
823
824 case INPUT_EVENT_MOUSEREL:
825 if (!input_recv_relmouse_event(input, s))
826 return FALSE;
827
828 break;
829
830 default:
831 WLog_ERR(TAG, "Unknown messageType %" PRIu16 "", messageType);
832 /* Each input event uses 6 bytes. */
833 Stream_Seek(s, 6);
834 break;
835 }
836
837 return TRUE;
838}
839
840BOOL input_recv(rdpInput* input, wStream* s)
841{
842 UINT16 numberEvents = 0;
843
844 WINPR_ASSERT(input);
845 WINPR_ASSERT(s);
846
847 if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
848 return FALSE;
849
850 Stream_Read_UINT16(s, numberEvents); /* numberEvents (2 bytes) */
851 Stream_Seek(s, 2); /* pad2Octets (2 bytes) */
852
853 /* Each input event uses 6 exactly bytes. */
854 if (!Stream_CheckAndLogRequiredLengthOfSize(TAG, s, numberEvents, 6ull))
855 return FALSE;
856
857 for (UINT16 i = 0; i < numberEvents; i++)
858 {
859 if (!input_recv_event(input, s))
860 return FALSE;
861 }
862
863 return TRUE;
864}
865
866BOOL input_register_client_callbacks(rdpInput* input)
867{
868 rdpSettings* settings = NULL;
869
870 if (!input->context)
871 return FALSE;
872
873 settings = input->context->settings;
874
875 if (!settings)
876 return FALSE;
877
878 if (freerdp_settings_get_bool(settings, FreeRDP_FastPathInput))
879 {
880 input->SynchronizeEvent = input_send_fastpath_synchronize_event;
881 input->KeyboardEvent = input_send_fastpath_keyboard_event;
882 input->KeyboardPauseEvent = input_send_fastpath_keyboard_pause_event;
883 input->UnicodeKeyboardEvent = input_send_fastpath_unicode_keyboard_event;
884 input->MouseEvent = input_send_fastpath_mouse_event;
885 input->RelMouseEvent = input_send_fastpath_relmouse_event;
886 input->ExtendedMouseEvent = input_send_fastpath_extended_mouse_event;
887 input->FocusInEvent = input_send_fastpath_focus_in_event;
888 input->QoEEvent = input_send_fastpath_qoe_event;
889 }
890 else
891 {
892 input->SynchronizeEvent = input_send_synchronize_event;
893 input->KeyboardEvent = input_send_keyboard_event;
894 input->KeyboardPauseEvent = input_send_keyboard_pause_event;
895 input->UnicodeKeyboardEvent = input_send_unicode_keyboard_event;
896 input->MouseEvent = input_send_mouse_event;
897 input->RelMouseEvent = input_send_relmouse_event;
898 input->ExtendedMouseEvent = input_send_extended_mouse_event;
899 input->FocusInEvent = input_send_focus_in_event;
900 }
901
902 return TRUE;
903}
904
905/* Save last input timestamp and/or mouse position in prevent-session-lock mode */
906static BOOL input_update_last_event(rdpInput* input, BOOL mouse, UINT16 x, UINT16 y)
907{
908 rdp_input_internal* in = input_cast(input);
909
910 WINPR_ASSERT(input);
911 WINPR_ASSERT(input->context);
912
913 if (freerdp_settings_get_uint32(input->context->settings, FreeRDP_FakeMouseMotionInterval) > 0)
914 {
915 const time_t now = time(NULL);
916 in->lastInputTimestamp = WINPR_ASSERTING_INT_CAST(UINT64, now);
917
918 if (mouse)
919 {
920 in->lastX = x;
921 in->lastY = y;
922 }
923 }
924 return TRUE;
925}
926
927BOOL freerdp_input_send_synchronize_event(rdpInput* input, UINT32 flags)
928{
929 if (!input || !input->context)
930 return FALSE;
931
932 if (freerdp_settings_get_bool(input->context->settings, FreeRDP_SuspendInput))
933 return TRUE;
934
935 return IFCALLRESULT(TRUE, input->SynchronizeEvent, input, flags);
936}
937
938BOOL freerdp_input_send_keyboard_event(rdpInput* input, UINT16 flags, UINT8 code)
939{
940 if (!input || !input->context)
941 return FALSE;
942
943 if (freerdp_settings_get_bool(input->context->settings, FreeRDP_SuspendInput))
944 return TRUE;
945
946 input_update_last_event(input, FALSE, 0, 0);
947
948 return IFCALLRESULT(TRUE, input->KeyboardEvent, input, flags, code);
949}
950
951BOOL freerdp_input_send_keyboard_event_ex(rdpInput* input, BOOL down, BOOL repeat,
952 UINT32 rdp_scancode)
953{
954 UINT16 flags = (RDP_SCANCODE_EXTENDED(rdp_scancode) ? KBD_FLAGS_EXTENDED : 0);
955 if (down && repeat)
956 flags |= KBD_FLAGS_DOWN;
957 else if (!down)
958 flags |= KBD_FLAGS_RELEASE;
959
960 return freerdp_input_send_keyboard_event(input, flags, RDP_SCANCODE_CODE(rdp_scancode));
961}
962
963BOOL freerdp_input_send_unicode_keyboard_event(rdpInput* input, UINT16 flags, UINT16 code)
964{
965 if (!input || !input->context)
966 return FALSE;
967
968 if (freerdp_settings_get_bool(input->context->settings, FreeRDP_SuspendInput))
969 return TRUE;
970
971 input_update_last_event(input, FALSE, 0, 0);
972
973 return IFCALLRESULT(TRUE, input->UnicodeKeyboardEvent, input, flags, code);
974}
975
976BOOL freerdp_input_send_mouse_event(rdpInput* input, UINT16 flags, UINT16 x, UINT16 y)
977{
978 if (!input || !input->context)
979 return FALSE;
980
981 if (freerdp_settings_get_bool(input->context->settings, FreeRDP_SuspendInput))
982 return TRUE;
983
984 input_update_last_event(
985 input, flags & (PTR_FLAGS_MOVE | PTR_FLAGS_BUTTON1 | PTR_FLAGS_BUTTON2 | PTR_FLAGS_BUTTON3),
986 x, y);
987
988 return IFCALLRESULT(TRUE, input->MouseEvent, input, flags, x, y);
989}
990
991BOOL freerdp_input_send_rel_mouse_event(rdpInput* input, UINT16 flags, INT16 xDelta, INT16 yDelta)
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 return IFCALLRESULT(TRUE, input->RelMouseEvent, input, flags, xDelta, yDelta);
1000}
1001
1002BOOL freerdp_input_send_qoe_timestamp(rdpInput* input, UINT32 timestampMS)
1003{
1004 if (!input || !input->context)
1005 return FALSE;
1006
1007 return IFCALLRESULT(TRUE, input->QoEEvent, input, timestampMS);
1008}
1009
1010BOOL freerdp_input_send_extended_mouse_event(rdpInput* input, UINT16 flags, UINT16 x, UINT16 y)
1011{
1012 if (!input || !input->context)
1013 return FALSE;
1014
1015 if (freerdp_settings_get_bool(input->context->settings, FreeRDP_SuspendInput))
1016 return TRUE;
1017
1018 input_update_last_event(input, TRUE, x, y);
1019
1020 return IFCALLRESULT(TRUE, input->ExtendedMouseEvent, input, flags, x, y);
1021}
1022
1023BOOL freerdp_input_send_focus_in_event(rdpInput* input, UINT16 toggleStates)
1024{
1025 if (!input || !input->context)
1026 return FALSE;
1027
1028 if (freerdp_settings_get_bool(input->context->settings, FreeRDP_SuspendInput))
1029 return TRUE;
1030
1031 return IFCALLRESULT(TRUE, input->FocusInEvent, input, toggleStates);
1032}
1033
1034BOOL freerdp_input_send_keyboard_pause_event(rdpInput* input)
1035{
1036 if (!input || !input->context)
1037 return FALSE;
1038
1039 if (freerdp_settings_get_bool(input->context->settings, FreeRDP_SuspendInput))
1040 return TRUE;
1041
1042 return IFCALLRESULT(TRUE, input->KeyboardPauseEvent, input);
1043}
1044
1045int input_process_events(rdpInput* input)
1046{
1047 if (!input)
1048 return FALSE;
1049
1050 return input_message_queue_process_pending_messages(input);
1051}
1052
1053static void input_free_queued_message(void* obj)
1054{
1055 wMessage* msg = (wMessage*)obj;
1056 input_message_queue_free_message(msg);
1057}
1058
1059rdpInput* input_new(rdpRdp* rdp)
1060{
1061 const wObject cb = { NULL, NULL, NULL, input_free_queued_message, NULL };
1062 rdp_input_internal* input = (rdp_input_internal*)calloc(1, sizeof(rdp_input_internal));
1063
1064 WINPR_UNUSED(rdp);
1065
1066 if (!input)
1067 return NULL;
1068
1069 input->common.context = rdp->context;
1070 input->queue = MessageQueue_New(&cb);
1071
1072 if (!input->queue)
1073 {
1074 free(input);
1075 return NULL;
1076 }
1077
1078 return &input->common;
1079}
1080
1081void input_free(rdpInput* input)
1082{
1083 if (input != NULL)
1084 {
1085 rdp_input_internal* in = input_cast(input);
1086
1087 MessageQueue_Free(in->queue);
1088 free(in);
1089 }
1090}
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