Coverage Report

Created: 2025-07-01 06:46

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