FreeRDP
Loading...
Searching...
No Matches
xf_event.c
1
21#include <freerdp/config.h>
22
23#include <X11/Xlib.h>
24#include <X11/Xutil.h>
25
26#include <string.h>
27#include <math.h>
28
29#include <winpr/assert.h>
30#include <winpr/path.h>
31
32#include <freerdp/log.h>
33#include <freerdp/locale/keyboard.h>
34
35#include "xf_rail.h"
36#include "xf_window.h"
37#include "xf_cliprdr.h"
38#include "xf_disp.h"
39#include "xf_input.h"
40#include "xf_gfx.h"
41#include "xf_graphics.h"
42#include "xf_utils.h"
43
44#include "xf_debug.h"
45#include "xf_event.h"
46
47#define CLAMP_COORDINATES(x, y) \
48 do \
49 { \
50 if ((x) < 0) \
51 (x) = 0; \
52 if ((y) < 0) \
53 (y) = 0; \
54 } while (0)
55
56static const DWORD mouseLogLevel = WLOG_TRACE;
57
58const char* x11_event_string(int event)
59{
60 switch (event)
61 {
62 case KeyPress:
63 return "KeyPress";
64
65 case KeyRelease:
66 return "KeyRelease";
67
68 case ButtonPress:
69 return "ButtonPress";
70
71 case ButtonRelease:
72 return "ButtonRelease";
73
74 case MotionNotify:
75 return "MotionNotify";
76
77 case EnterNotify:
78 return "EnterNotify";
79
80 case LeaveNotify:
81 return "LeaveNotify";
82
83 case FocusIn:
84 return "FocusIn";
85
86 case FocusOut:
87 return "FocusOut";
88
89 case KeymapNotify:
90 return "KeymapNotify";
91
92 case Expose:
93 return "Expose";
94
95 case GraphicsExpose:
96 return "GraphicsExpose";
97
98 case NoExpose:
99 return "NoExpose";
100
101 case VisibilityNotify:
102 return "VisibilityNotify";
103
104 case CreateNotify:
105 return "CreateNotify";
106
107 case DestroyNotify:
108 return "DestroyNotify";
109
110 case UnmapNotify:
111 return "UnmapNotify";
112
113 case MapNotify:
114 return "MapNotify";
115
116 case MapRequest:
117 return "MapRequest";
118
119 case ReparentNotify:
120 return "ReparentNotify";
121
122 case ConfigureNotify:
123 return "ConfigureNotify";
124
125 case ConfigureRequest:
126 return "ConfigureRequest";
127
128 case GravityNotify:
129 return "GravityNotify";
130
131 case ResizeRequest:
132 return "ResizeRequest";
133
134 case CirculateNotify:
135 return "CirculateNotify";
136
137 case CirculateRequest:
138 return "CirculateRequest";
139
140 case PropertyNotify:
141 return "PropertyNotify";
142
143 case SelectionClear:
144 return "SelectionClear";
145
146 case SelectionRequest:
147 return "SelectionRequest";
148
149 case SelectionNotify:
150 return "SelectionNotify";
151
152 case ColormapNotify:
153 return "ColormapNotify";
154
155 case ClientMessage:
156 return "ClientMessage";
157
158 case MappingNotify:
159 return "MappingNotify";
160
161 case GenericEvent:
162 return "GenericEvent";
163
164 default:
165 return "UNKNOWN";
166 }
167}
168
169static BOOL xf_action_script_append(xfContext* xfc, const char* buffer, size_t size,
170 WINPR_ATTR_UNUSED void* user, const char* what, const char* arg)
171{
172 WINPR_ASSERT(xfc);
173 WINPR_UNUSED(what);
174 WINPR_UNUSED(arg);
175
176 if (buffer || (size == 0))
177 return TRUE;
178
179 if (!ArrayList_Append(xfc->xevents, buffer))
180 {
181 ArrayList_Clear(xfc->xevents);
182 return FALSE;
183 }
184 return TRUE;
185}
186
187BOOL xf_event_action_script_init(xfContext* xfc)
188{
189 WINPR_ASSERT(xfc);
190
191 xf_event_action_script_free(xfc);
192
193 char* val = getConfigOption(TRUE, "isActionScriptAllowed");
194
195 /* We default to enabled if there is no global config file. */
196 xfc->isActionScriptAllowed = !val || (_stricmp(val, "true") == 0);
197 free(val);
198
199 if (!xfc->isActionScriptAllowed)
200 return TRUE;
201
202 xfc->xevents = ArrayList_New(TRUE);
203
204 if (!xfc->xevents)
205 return FALSE;
206
207 wObject* obj = ArrayList_Object(xfc->xevents);
208 WINPR_ASSERT(obj);
209 obj->fnObjectNew = winpr_ObjectStringClone;
210 obj->fnObjectFree = winpr_ObjectStringFree;
211
212 return run_action_script(xfc, "xevent", nullptr, xf_action_script_append, nullptr);
213}
214
215void xf_event_action_script_free(xfContext* xfc)
216{
217 if (xfc->xevents)
218 {
219 ArrayList_Free(xfc->xevents);
220 xfc->xevents = nullptr;
221 }
222}
223
224static BOOL action_script_run(xfContext* xfc, const char* buffer, size_t size, void* user,
225 const char* what, const char* arg)
226{
227 WINPR_UNUSED(xfc);
228 if (!xfc->isActionScriptAllowed)
229 return TRUE;
230
231 WINPR_UNUSED(what);
232 WINPR_UNUSED(arg);
233 WINPR_ASSERT(user);
234 int* pstatus = user;
235
236 if (size == 0)
237 {
238 WLog_Print(xfc->log, WLOG_WARN, "ActionScript xevent: script did not return data");
239 return FALSE;
240 }
241
242 if (winpr_PathFileExists(buffer))
243 {
244 char* cmd = nullptr;
245 size_t cmdlen = 0;
246 winpr_asprintf(&cmd, &cmdlen, "%s %s %s", buffer, what, arg);
247 if (!cmd)
248 return FALSE;
249
250 FILE* fp = popen(cmd, "w");
251 free(cmd);
252 if (!fp)
253 {
254 WLog_Print(xfc->log, WLOG_ERROR, "Failed to execute '%s'", buffer);
255 return FALSE;
256 }
257
258 *pstatus = pclose(fp);
259 if (*pstatus < 0)
260 {
261 WLog_Print(xfc->log, WLOG_ERROR, "Command '%s' returned %d", buffer, *pstatus);
262 return FALSE;
263 }
264 }
265 else
266 {
267 WLog_Print(xfc->log, WLOG_WARN, "ActionScript xevent: No such file '%s'", buffer);
268 return FALSE;
269 }
270
271 return TRUE;
272}
273
274static BOOL xf_event_execute_action_script(xfContext* xfc, const XEvent* event)
275{
276 size_t count = 0;
277 char* name = nullptr;
278 BOOL match = FALSE;
279 const char* xeventName = nullptr;
280
281 if (!xfc->actionScriptExists || !xfc->xevents || !xfc->window)
282 return FALSE;
283
284 if (event->type > LASTEvent)
285 return FALSE;
286
287 xeventName = x11_event_string(event->type);
288 count = ArrayList_Count(xfc->xevents);
289
290 for (size_t index = 0; index < count; index++)
291 {
292 name = (char*)ArrayList_GetItem(xfc->xevents, index);
293
294 if (_stricmp(name, xeventName) == 0)
295 {
296 match = TRUE;
297 break;
298 }
299 }
300
301 if (!match)
302 return FALSE;
303
304 char command[2048] = WINPR_C_ARRAY_INIT;
305 char arg[2048] = WINPR_C_ARRAY_INIT;
306 (void)_snprintf(command, sizeof(command), "xevent %s", xeventName);
307 (void)_snprintf(arg, sizeof(arg), "%lu", (unsigned long)xfc->window->handle);
308 return run_action_script(xfc, command, arg, action_script_run, nullptr);
309}
310
311void xf_adjust_coordinates_to_screen(xfContext* xfc, UINT32* x, UINT32* y)
312{
313 if (!xfc || !xfc->common.context.settings || !y || !x)
314 return;
315
316 rdpSettings* settings = xfc->common.context.settings;
317 INT64 tx = *x;
318 INT64 ty = *y;
319 if (!xfc->remote_app)
320 {
321#ifdef WITH_XRENDER
322
323 if (xf_picture_transform_required(xfc))
324 {
325 const double dw = freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth);
326 const double dh = freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight);
327 double xScalingFactor = xfc->scaledWidth / dw;
328 double yScalingFactor = xfc->scaledHeight / dh;
329 tx = (INT64)lround((1.0 * (*x) + xfc->offset_x) * xScalingFactor);
330 ty = (INT64)lround((1.0 * (*y) + xfc->offset_y) * yScalingFactor);
331 }
332
333#endif
334 }
335
336 CLAMP_COORDINATES(tx, ty);
337 *x = (UINT32)tx;
338 *y = (UINT32)ty;
339}
340
341void xf_event_adjust_coordinates(xfContext* xfc, int* x, int* y)
342{
343 if (!xfc || !xfc->common.context.settings || !y || !x)
344 return;
345
346 if (!xfc->remote_app)
347 {
348#ifdef WITH_XRENDER
349 rdpSettings* settings = xfc->common.context.settings;
350 if (xf_picture_transform_required(xfc))
351 {
352 double xScalingFactor = freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth) /
353 (double)xfc->scaledWidth;
354 double yScalingFactor = freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight) /
355 (double)xfc->scaledHeight;
356 *x = (int)((*x - xfc->offset_x) * xScalingFactor);
357 *y = (int)((*y - xfc->offset_y) * yScalingFactor);
358 }
359
360#endif
361 }
362
363 CLAMP_COORDINATES(*x, *y);
364}
365
366static BOOL xf_event_Expose(xfContext* xfc, const XExposeEvent* event, BOOL app)
367{
368 WINPR_ASSERT(xfc);
369 WINPR_ASSERT(event);
370
371 rdpSettings* settings = xfc->common.context.settings;
372 WINPR_ASSERT(settings);
373
374 if (!app && (freerdp_settings_get_bool(settings, FreeRDP_SmartSizing) ||
375 freerdp_settings_get_bool(settings, FreeRDP_MultiTouchGestures)))
376 {
377 xfc->exposedArea.x = 0;
378 xfc->exposedArea.y = 0;
379 xfc->exposedArea.w = WINPR_ASSERTING_INT_CAST(
380 int, freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth));
381 xfc->exposedArea.h = WINPR_ASSERTING_INT_CAST(
382 int, freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight));
383 }
384 else
385 {
386 xfc->exposedArea.x = event->x;
387 xfc->exposedArea.y = event->y;
388 xfc->exposedArea.w = event->width;
389 xfc->exposedArea.h = event->height;
390 }
391
392 xfc->exposedWindow = event->window;
393 xfc->exposeRequested = true;
394
395 return TRUE;
396}
397
398static BOOL xf_event_VisibilityNotify(xfContext* xfc, const XVisibilityEvent* event, BOOL app)
399{
400 WINPR_UNUSED(app);
401 xfc->unobscured = event->state == VisibilityUnobscured;
402 return TRUE;
403}
404
405BOOL xf_generic_MotionNotify_(xfContext* xfc, int x, int y, Window window, BOOL app,
406 const char* file, const char* fkt, size_t line)
407{
408 Window childWindow = None;
409
410 if (WLog_IsLevelActive(xfc->log, mouseLogLevel))
411 WLog_PrintTextMessage(xfc->log, mouseLogLevel, line, file, fkt,
412 "%s: x=%d, y=%d, window=0x%08lx, app=%d", __func__, x, y, window,
413 app);
414
415 WINPR_ASSERT(xfc);
416 WINPR_ASSERT(xfc->common.context.settings);
417
418 if (app)
419 {
420 /* make sure window exists */
421 xfAppWindow* appWindow = xf_AppWindowFromX11Window(xfc, window);
422 xf_rail_return_window(appWindow, FALSE);
423 if (!appWindow)
424 return TRUE;
425
426 /* Translate to desktop coordinates */
427 XTranslateCoordinates(xfc->display, window, RootWindowOfScreen(xfc->screen), x, y, &x, &y,
428 &childWindow);
429 }
430
431 xf_event_adjust_coordinates(xfc, &x, &y);
432 freerdp_client_send_button_event(&xfc->common, FALSE, PTR_FLAGS_MOVE, x, y);
433
434 if (xfc->fullscreen && !app)
435 {
436 if (xfc->window)
437 XSetInputFocus(xfc->display, xfc->window->handle, RevertToPointerRoot, CurrentTime);
438 }
439
440 return TRUE;
441}
442
443BOOL xf_generic_RawMotionNotify_(xfContext* xfc, int x, int y, WINPR_ATTR_UNUSED Window window,
444 BOOL app, const char* file, const char* fkt, size_t line)
445{
446 WINPR_ASSERT(xfc);
447
448 if (WLog_IsLevelActive(xfc->log, mouseLogLevel))
449 WLog_PrintTextMessage(xfc->log, mouseLogLevel, line, file, fkt,
450 "%s: x=%d, y=%d, window=0x%08lx, app=%d", __func__, x, y, window,
451 app);
452
453 if (app)
454 {
455 WLog_Print(xfc->log, WLOG_ERROR,
456 "Relative mouse input is not supported with remoate app mode!");
457 return FALSE;
458 }
459
460 return freerdp_client_send_button_event(&xfc->common, TRUE, PTR_FLAGS_MOVE, x, y);
461}
462
463static BOOL xf_event_MotionNotify(xfContext* xfc, const XMotionEvent* event, BOOL app)
464{
465 WINPR_ASSERT(xfc);
466
467 if (xfc->window)
468 xf_floatbar_set_root_y(xfc->window->floatbar, event->y);
469
470 if (xfc->xi_event || xfc->xi_rawevent || (xfc->common.mouse_grabbed && xf_use_rel_mouse(xfc)))
471 return TRUE;
472
473 return xf_generic_MotionNotify(xfc, event->x, event->y, event->window, app);
474}
475
476BOOL xf_generic_ButtonEvent_(xfContext* xfc, int x, int y, int button, Window window, BOOL app,
477 BOOL down, const char* file, const char* fkt, size_t line)
478{
479 UINT16 flags = 0;
480 Window childWindow = None;
481
482 if (WLog_IsLevelActive(xfc->log, mouseLogLevel))
483 WLog_PrintTextMessage(xfc->log, mouseLogLevel, line, file, fkt,
484 "%s: x=%d, y=%d, button=%d, window=0x%08lx, app=%d, down=%d",
485 __func__, x, y, button, window, app, down);
486
487 WINPR_ASSERT(xfc);
488 if (button < 0)
489 return FALSE;
490
491 for (size_t i = 0; i < ARRAYSIZE(xfc->button_map); i++)
492 {
493 const button_map* cur = &xfc->button_map[i];
494
495 if (cur->button == (UINT32)button)
496 {
497 flags = cur->flags;
498 break;
499 }
500 }
501
502 if (flags != 0)
503 {
504 if (flags & (PTR_FLAGS_WHEEL | PTR_FLAGS_HWHEEL))
505 {
506 if (down)
507 freerdp_client_send_wheel_event(&xfc->common, flags);
508 }
509 else
510 {
511 BOOL extended = FALSE;
512
513 if (flags & (PTR_XFLAGS_BUTTON1 | PTR_XFLAGS_BUTTON2))
514 {
515 extended = TRUE;
516
517 if (down)
518 flags |= PTR_XFLAGS_DOWN;
519 }
520 else if (flags & (PTR_FLAGS_BUTTON1 | PTR_FLAGS_BUTTON2 | PTR_FLAGS_BUTTON3))
521 {
522 if (down)
523 flags |= PTR_FLAGS_DOWN;
524 }
525
526 if (app)
527 {
528 /* make sure window exists */
529 xfAppWindow* appWindow = xf_AppWindowFromX11Window(xfc, window);
530 xf_rail_return_window(appWindow, FALSE);
531 if (!appWindow)
532 return TRUE;
533
534 /* Translate to desktop coordinates */
535 XTranslateCoordinates(xfc->display, window, RootWindowOfScreen(xfc->screen), x, y,
536 &x, &y, &childWindow);
537 }
538
539 xf_event_adjust_coordinates(xfc, &x, &y);
540
541 if (extended)
542 freerdp_client_send_extended_button_event(&xfc->common, FALSE, flags, x, y);
543 else
544 freerdp_client_send_button_event(&xfc->common, FALSE, flags, x, y);
545 }
546 }
547
548 return TRUE;
549}
550
551static BOOL xf_grab_mouse(xfContext* xfc)
552{
553 WINPR_ASSERT(xfc);
554
555 if (!xfc->window)
556 return FALSE;
557
558 if (freerdp_settings_get_bool(xfc->common.context.settings, FreeRDP_GrabMouse))
559 {
560 XGrabPointer(xfc->display, xfc->window->handle, False,
561 ButtonPressMask | ButtonReleaseMask | PointerMotionMask | FocusChangeMask |
562 EnterWindowMask | LeaveWindowMask,
563 GrabModeAsync, GrabModeAsync, xfc->window->handle, None, CurrentTime);
564 xfc->common.mouse_grabbed = TRUE;
565 }
566 return TRUE;
567}
568
569static BOOL xf_grab_kbd(xfContext* xfc)
570{
571 WINPR_ASSERT(xfc);
572
573 if (!xfc->window)
574 return FALSE;
575
576 XGrabKeyboard(xfc->display, xfc->window->handle, TRUE, GrabModeAsync, GrabModeAsync,
577 CurrentTime);
578 return TRUE;
579}
580
581static BOOL xf_event_ButtonPress(xfContext* xfc, const XButtonEvent* event, BOOL app)
582{
583 xf_grab_mouse(xfc);
584
585 if (xfc->xi_event || xfc->xi_rawevent || (xfc->common.mouse_grabbed && xf_use_rel_mouse(xfc)))
586 return TRUE;
587 if (!app && xfc_is_floatbar_window(xfc, event->window))
588 return TRUE;
589 return xf_generic_ButtonEvent(xfc, event->x, event->y,
590 WINPR_ASSERTING_INT_CAST(int, event->button), event->window, app,
591 TRUE);
592}
593
594static BOOL xf_event_ButtonRelease(xfContext* xfc, const XButtonEvent* event, BOOL app)
595{
596 xf_grab_mouse(xfc);
597
598 if (xfc->xi_event || xfc->xi_rawevent || (xfc->common.mouse_grabbed && xf_use_rel_mouse(xfc)))
599 return TRUE;
600 return xf_generic_ButtonEvent(xfc, event->x, event->y,
601 WINPR_ASSERTING_INT_CAST(int, event->button), event->window, app,
602 FALSE);
603}
604
605static BOOL xf_event_KeyPress(xfContext* xfc, const XKeyEvent* event, BOOL app)
606{
607 KeySym keysym = 0;
608 char str[256] = WINPR_C_ARRAY_INIT;
609 union
610 {
611 const XKeyEvent* cev;
612 XKeyEvent* ev;
613 } cnv;
614 cnv.cev = event;
615 WINPR_UNUSED(app);
616 XLookupString(cnv.ev, str, sizeof(str), &keysym, nullptr);
617 xf_keyboard_key_press(xfc, event, keysym);
618 return TRUE;
619}
620
621static BOOL xf_event_KeyRelease(xfContext* xfc, const XKeyEvent* event, BOOL app)
622{
623 KeySym keysym = 0;
624 char str[256] = WINPR_C_ARRAY_INIT;
625 union
626 {
627 const XKeyEvent* cev;
628 XKeyEvent* ev;
629 } cnv;
630 cnv.cev = event;
631
632 WINPR_UNUSED(app);
633 XLookupString(cnv.ev, str, sizeof(str), &keysym, nullptr);
634 xf_keyboard_key_release(xfc, event, keysym);
635 return TRUE;
636}
637
638/* Release a key, but ignore the event in case of autorepeat.
639 */
640static BOOL xf_event_KeyReleaseOrIgnore(xfContext* xfc, const XKeyEvent* event, BOOL app)
641{
642 WINPR_ASSERT(xfc);
643 WINPR_ASSERT(event);
644
645 if ((event->type == KeyRelease) && XEventsQueued(xfc->display, QueuedAfterReading))
646 {
647 XEvent nev = WINPR_C_ARRAY_INIT;
648 XPeekEvent(xfc->display, &nev);
649
650 if ((nev.type == KeyPress) && (nev.xkey.time == event->time) &&
651 (nev.xkey.keycode == event->keycode))
652 {
653 /* Key wasn’t actually released */
654 return TRUE;
655 }
656 }
657
658 return xf_event_KeyRelease(xfc, event, app);
659}
660
661static BOOL xf_event_FocusIn(xfContext* xfc, const XFocusInEvent* event, BOOL app)
662{
663 if (event->mode == NotifyGrab)
664 return TRUE;
665
666 xfc->focused = TRUE;
667
668 if (xfc->mouse_active && !app)
669 {
670 xf_grab_mouse(xfc);
671 if (!xf_grab_kbd(xfc))
672 return FALSE;
673 }
674
675 /* Release all keys, should already be done at FocusOut but might be missed
676 * if the WM decided to use an alternate event order */
677 if (!app)
678 xf_keyboard_release_all_keypress(xfc);
679 else
680 {
681 if (!xf_rail_send_activate(xfc, event->window, TRUE))
682 return FALSE;
683 }
684
685 xf_pointer_update_scale(xfc);
686
687 if (app)
688 {
689 xfAppWindow* appWindow = xf_AppWindowFromX11Window(xfc, event->window);
690
691 /* Update the server with any window changes that occurred while the window was not focused.
692 */
693 if (appWindow)
694 xf_rail_adjust_position(xfc, appWindow);
695 xf_rail_return_window(appWindow, FALSE);
696 }
697
698 xf_keyboard_focus_in(xfc);
699 return TRUE;
700}
701
702static BOOL xf_event_FocusOut(xfContext* xfc, const XFocusOutEvent* event, BOOL app)
703{
704 if (event->mode == NotifyUngrab)
705 return TRUE;
706
707 xfc->focused = FALSE;
708
709 if (event->mode == NotifyWhileGrabbed)
710 XUngrabKeyboard(xfc->display, CurrentTime);
711
712 xf_keyboard_release_all_keypress(xfc);
713 if (app)
714 return xf_rail_send_activate(xfc, event->window, FALSE);
715
716 return TRUE;
717}
718
719static BOOL xf_event_MappingNotify(xfContext* xfc, const XMappingEvent* event, BOOL app)
720{
721 WINPR_UNUSED(app);
722
723 switch (event->request)
724 {
725 case MappingModifier:
726 return xf_keyboard_update_modifier_map(xfc);
727 case MappingKeyboard:
728 WLog_Print(xfc->log, WLOG_TRACE, "[%d] MappingKeyboard", event->request);
729 return xf_keyboard_init(xfc);
730 case MappingPointer:
731 WLog_Print(xfc->log, WLOG_TRACE, "[%d] MappingPointer", event->request);
732 xf_button_map_init(xfc);
733 return TRUE;
734 default:
735 WLog_Print(xfc->log, WLOG_WARN,
736 "[%d] Unsupported MappingNotify::request, must be one "
737 "of[MappingModifier(%d), MappingKeyboard(%d), MappingPointer(%d)]",
738 event->request, MappingModifier, MappingKeyboard, MappingPointer);
739 return FALSE;
740 }
741}
742
743static BOOL xf_event_ClientMessage(xfContext* xfc, const XClientMessageEvent* event, BOOL app)
744{
745 if ((event->message_type == xfc->WM_PROTOCOLS) &&
746 ((Atom)event->data.l[0] == xfc->WM_DELETE_WINDOW))
747 {
748 if (app)
749 {
750 BOOL rc = TRUE;
751 xfAppWindow* appWindow = xf_AppWindowFromX11Window(xfc, event->window);
752
753 if (appWindow)
754 rc = xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_CLOSE);
755 xf_rail_return_window(appWindow, FALSE);
756 return rc;
757 }
758 else
759 {
760 WLog_Print(xfc->log, WLOG_TRACE, "Main window closed");
761 return FALSE;
762 }
763 }
764
765 return TRUE;
766}
767
768static BOOL xf_event_EnterNotify(xfContext* xfc, const XEnterWindowEvent* event, BOOL app)
769{
770 if (!app)
771 {
772 if (!xfc->window)
773 return FALSE;
774
775 xfc->mouse_active = TRUE;
776
777 if (xfc->fullscreen)
778 XSetInputFocus(xfc->display, xfc->window->handle, RevertToPointerRoot, CurrentTime);
779
780 if (xfc->focused)
781 xf_grab_kbd(xfc);
782 }
783 else
784 {
785 xfAppWindow* appWindow = xf_AppWindowFromX11Window(xfc, event->window);
786
787 /* keep track of which window has focus so that we can apply pointer updates */
788 xfc->appWindow = appWindow;
789 xf_rail_return_window(appWindow, FALSE);
790 }
791
792 return TRUE;
793}
794
795static BOOL xf_event_LeaveNotify(xfContext* xfc, const XLeaveWindowEvent* event, BOOL app)
796{
797 if (event->mode == NotifyGrab || event->mode == NotifyUngrab)
798 return TRUE;
799 if (!app)
800 {
801 xfc->mouse_active = FALSE;
802 XUngrabKeyboard(xfc->display, CurrentTime);
803 }
804 else
805 {
806 xfAppWindow* appWindow = xf_AppWindowFromX11Window(xfc, event->window);
807
808 /* keep track of which window has focus so that we can apply pointer updates */
809 if (xfc->appWindow == appWindow)
810 xfc->appWindow = nullptr;
811 xf_rail_return_window(appWindow, FALSE);
812 }
813 return TRUE;
814}
815
816static BOOL xf_event_ConfigureNotify(xfContext* xfc, const XConfigureEvent* event, BOOL app)
817{
818 Window childWindow = None;
819 xfAppWindow* appWindow = nullptr;
820
821 WINPR_ASSERT(xfc);
822 WINPR_ASSERT(event);
823
824 const rdpSettings* settings = xfc->common.context.settings;
825 WINPR_ASSERT(settings);
826
827 WLog_Print(xfc->log, WLOG_DEBUG, "x=%" PRId32 ", y=%" PRId32 ", w=%" PRId32 ", h=%" PRId32,
828 event->x, event->y, event->width, event->height);
829
830 if (!app)
831 {
832 if (!xfc->window)
833 return FALSE;
834
835 if (xfc->window->left != event->x)
836 xfc->window->left = event->x;
837
838 if (xfc->window->top != event->y)
839 xfc->window->top = event->y;
840
841 if (xfc->window->width != event->width || xfc->window->height != event->height)
842 {
843 xfc->window->width = event->width;
844 xfc->window->height = event->height;
845#ifdef WITH_XRENDER
846 xfc->offset_x = 0;
847 xfc->offset_y = 0;
848
849 if (freerdp_settings_get_bool(settings, FreeRDP_SmartSizing) ||
850 freerdp_settings_get_bool(settings, FreeRDP_MultiTouchGestures))
851 {
852 xfc->scaledWidth = xfc->window->width;
853 xfc->scaledHeight = xfc->window->height;
854 xf_draw_screen(
855 xfc, 0, 0,
856 WINPR_ASSERTING_INT_CAST(
857 int32_t, freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth)),
858 WINPR_ASSERTING_INT_CAST(
859 int32_t, freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight)));
860 }
861 else
862 {
863 xfc->scaledWidth = WINPR_ASSERTING_INT_CAST(
864 int, freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth));
865 xfc->scaledHeight = WINPR_ASSERTING_INT_CAST(
866 int, freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight));
867 }
868
869#endif
870 }
871
872 if (freerdp_settings_get_bool(settings, FreeRDP_DynamicResolutionUpdate))
873 {
874 const int alignedWidth = (xfc->window->width / 2) * 2;
875 const int alignedHeight = (xfc->window->height / 2) * 2;
876 /* ask the server to resize using the display channel */
877 xf_disp_handle_configureNotify(xfc, alignedWidth, alignedHeight);
878 }
879 }
880 else
881 {
882 appWindow = xf_AppWindowFromX11Window(xfc, event->window);
883
884 if (appWindow)
885 {
886 /*
887 * ConfigureNotify coordinates are expressed relative to the window parent.
888 * Translate these to root window coordinates.
889 */
890 XTranslateCoordinates(xfc->display, appWindow->handle, RootWindowOfScreen(xfc->screen),
891 0, 0, &appWindow->x, &appWindow->y, &childWindow);
892 appWindow->width = event->width;
893 appWindow->height = event->height;
894
895 xf_AppWindowResize(xfc, appWindow);
896
897 /*
898 * Additional checks for not in a local move and not ignoring configure to send
899 * position update to server, also should the window not be focused then do not
900 * send to server yet (i.e. resizing using window decoration).
901 * The server will be updated when the window gets refocused.
902 */
903 if (appWindow->decorations)
904 {
905 /* moving resizing using window decoration */
906 xf_rail_adjust_position(xfc, appWindow);
907 }
908 else
909 {
910 if ((!event->send_event || appWindow->local_move.state == LMS_NOT_ACTIVE) &&
911 !appWindow->rail_ignore_configure && xfc->focused)
912 xf_rail_adjust_position(xfc, appWindow);
913 }
914 }
915 xf_rail_return_window(appWindow, FALSE);
916 }
917 return xf_pointer_update_scale(xfc);
918}
919
920static BOOL xf_event_MapNotify(xfContext* xfc, const XMapEvent* event, BOOL app)
921{
922 WINPR_ASSERT(xfc);
923 if (!app)
924 {
925 if (!gdi_send_suppress_output(xfc->common.context.gdi, FALSE))
926 return FALSE;
927 }
928 else
929 {
930 xfAppWindow* appWindow = xf_AppWindowFromX11Window(xfc, event->window);
931
932 if (appWindow)
933 {
934 /* local restore event */
935 /* This is now handled as part of the PropertyNotify
936 * Doing this here would inhibit the ability to restore a maximized window
937 * that is minimized back to the maximized state
938 */
939 // xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_RESTORE);
940 appWindow->is_mapped = TRUE;
941 }
942 xf_rail_return_window(appWindow, FALSE);
943 }
944
945 return TRUE;
946}
947
948static BOOL xf_event_UnmapNotify(xfContext* xfc, const XUnmapEvent* event, BOOL app)
949{
950 WINPR_ASSERT(xfc);
951 WINPR_ASSERT(event);
952
953 if (!app)
954 xf_keyboard_release_all_keypress(xfc);
955
956 if (!app)
957 return gdi_send_suppress_output(xfc->common.context.gdi, TRUE);
958
959 {
960 xfAppWindow* appWindow = xf_AppWindowFromX11Window(xfc, event->window);
961
962 if (appWindow)
963 appWindow->is_mapped = FALSE;
964 xf_rail_return_window(appWindow, FALSE);
965 }
966
967 return TRUE;
968}
969
970static BOOL xf_event_PropertyNotify(xfContext* xfc, const XPropertyEvent* event, BOOL app)
971{
972 BOOL rc = TRUE;
973 WINPR_ASSERT(xfc);
974 WINPR_ASSERT(event);
975
976 /*
977 * This section handles sending the appropriate commands to the rail server
978 * when the window has been minimized, maximized, restored locally
979 * ie. not using the buttons on the rail window itself
980 */
981 if (((event->atom == xfc->NET_WM_STATE) && (event->state != PropertyDelete)) ||
982 ((event->atom == xfc->WM_STATE) && (event->state != PropertyDelete)))
983 {
984 BOOL status = FALSE;
985 BOOL minimized = FALSE;
986 BOOL minimizedChanged = FALSE;
987 unsigned long nitems = 0;
988 unsigned long bytes = 0;
989 unsigned char* prop = nullptr;
990 xfAppWindow* appWindow = nullptr;
991
992 if (app)
993 {
994 appWindow = xf_AppWindowFromX11Window(xfc, event->window);
995
996 if (!appWindow)
997 goto fail;
998 }
999
1000 if (event->atom == xfc->NET_WM_STATE)
1001 {
1002 status = xf_GetWindowProperty(xfc, event->window, xfc->NET_WM_STATE, 12, &nitems,
1003 &bytes, &prop);
1004
1005 if (status)
1006 {
1007 if (appWindow)
1008 {
1009 appWindow->maxVert = FALSE;
1010 appWindow->maxHorz = FALSE;
1011 }
1012 for (unsigned long i = 0; i < nitems; i++)
1013 {
1014 if ((Atom)((UINT16**)prop)[i] ==
1015 Logging_XInternAtom(xfc->log, xfc->display, "_NET_WM_STATE_MAXIMIZED_VERT",
1016 False))
1017 {
1018 if (appWindow)
1019 appWindow->maxVert = TRUE;
1020 }
1021
1022 if ((Atom)((UINT16**)prop)[i] ==
1023 Logging_XInternAtom(xfc->log, xfc->display, "_NET_WM_STATE_MAXIMIZED_HORZ",
1024 False))
1025 {
1026 if (appWindow)
1027 appWindow->maxHorz = TRUE;
1028 }
1029 }
1030
1031 XFree(prop);
1032 }
1033 }
1034
1035 if (event->atom == xfc->WM_STATE)
1036 {
1037 status =
1038 xf_GetWindowProperty(xfc, event->window, xfc->WM_STATE, 1, &nitems, &bytes, &prop);
1039
1040 if (status)
1041 {
1042 /* If the window is in the iconic state */
1043 if (((UINT32)*prop == 3) && !IsGnome())
1044 {
1045 minimized = TRUE;
1046 if (appWindow)
1047 appWindow->minimized = TRUE;
1048 }
1049 else
1050 {
1051 minimized = FALSE;
1052 if (appWindow)
1053 appWindow->minimized = FALSE;
1054 }
1055
1056 minimizedChanged = TRUE;
1057 XFree(prop);
1058 }
1059 }
1060
1061 if (app)
1062 {
1063 WINPR_ASSERT(appWindow);
1064 if (appWindow->maxVert && appWindow->maxHorz && !appWindow->minimized)
1065 {
1066 if (appWindow->rail_state != WINDOW_SHOW_MAXIMIZED)
1067 {
1068 appWindow->rail_state = WINDOW_SHOW_MAXIMIZED;
1069 rc = xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_MAXIMIZE);
1070 }
1071 }
1072 else if (appWindow->minimized)
1073 {
1074 if (appWindow->rail_state != WINDOW_SHOW_MINIMIZED)
1075 {
1076 appWindow->rail_state = WINDOW_SHOW_MINIMIZED;
1077 rc = xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_MINIMIZE);
1078 }
1079 }
1080 else
1081 {
1082 if (appWindow->rail_state != WINDOW_SHOW && appWindow->rail_state != WINDOW_HIDE)
1083 {
1084 appWindow->rail_state = WINDOW_SHOW;
1085 rc = xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_RESTORE);
1086 }
1087 }
1088 }
1089 else if (minimizedChanged)
1090 rc = gdi_send_suppress_output(xfc->common.context.gdi, minimized);
1091
1092 fail:
1093 xf_rail_return_window(appWindow, FALSE);
1094 }
1095
1096 return rc;
1097}
1098
1099static BOOL xf_event_suppress_events(xfContext* xfc, xfAppWindow* appWindow, const XEvent* event)
1100{
1101 if (!xfc->remote_app)
1102 return FALSE;
1103
1104 switch (appWindow->local_move.state)
1105 {
1106 case LMS_NOT_ACTIVE:
1107
1108 /* No local move in progress, nothing to do */
1109
1110 /* Prevent Configure from happening during indeterminant state of Horz or Vert Max only
1111 */
1112 if ((event->type == ConfigureNotify) && appWindow->rail_ignore_configure)
1113 {
1114 appWindow->rail_ignore_configure = FALSE;
1115 return TRUE;
1116 }
1117
1118 break;
1119
1120 case LMS_STARTING:
1121
1122 /* Local move initiated by RDP server, but we have not yet seen any updates from the X
1123 * server */
1124 switch (event->type)
1125 {
1126 case ConfigureNotify:
1127 /* Starting to see move events from the X server. Local move is now in progress.
1128 */
1129 appWindow->local_move.state = LMS_ACTIVE;
1130 /* Allow these events to be processed during move to keep our state up to date.
1131 */
1132 break;
1133
1134 case ButtonPress:
1135 case ButtonRelease:
1136 case KeyPress:
1137 case KeyRelease:
1138 case UnmapNotify:
1139 /*
1140 * A button release event means the X window server did not grab the
1141 * mouse before the user released it. In this case we must cancel the
1142 * local move. The event will be processed below as normal, below.
1143 */
1144 break;
1145
1146 case VisibilityNotify:
1147 case PropertyNotify:
1148 case Expose:
1149 /* Allow these events to pass */
1150 break;
1151
1152 default:
1153 /* Eat any other events */
1154 return TRUE;
1155 }
1156
1157 break;
1158
1159 case LMS_ACTIVE:
1160
1161 /* Local move is in progress */
1162 switch (event->type)
1163 {
1164 case ConfigureNotify:
1165 case VisibilityNotify:
1166 case PropertyNotify:
1167 case Expose:
1168 case GravityNotify:
1169 /* Keep us up to date on position */
1170 break;
1171
1172 default:
1173 /* Any other event terminates move */
1174 xf_rail_end_local_move(xfc, appWindow);
1175 break;
1176 }
1177
1178 break;
1179
1180 case LMS_TERMINATING:
1181 /* Already sent RDP end move to server. Allow events to pass. */
1182 break;
1183 default:
1184 break;
1185 }
1186
1187 return FALSE;
1188}
1189
1190BOOL xf_event_process(freerdp* instance, const XEvent* event)
1191{
1192 BOOL status = TRUE;
1193
1194 WINPR_ASSERT(instance);
1195 WINPR_ASSERT(event);
1196
1197 xfContext* xfc = (xfContext*)instance->context;
1198 WINPR_ASSERT(xfc);
1199
1200 rdpSettings* settings = xfc->common.context.settings;
1201 WINPR_ASSERT(settings);
1202
1203 if (xfc->remote_app)
1204 {
1205 xfAppWindow* appWindow = xf_AppWindowFromX11Window(xfc, event->xany.window);
1206
1207 if (appWindow)
1208 {
1209 /* Update "current" window for cursor change orders */
1210 xfc->appWindow = appWindow;
1211
1212 const BOOL rc = xf_event_suppress_events(xfc, appWindow, event);
1213 xf_rail_return_window(appWindow, FALSE);
1214 if (rc)
1215 return TRUE;
1216 }
1217 }
1218
1219 if (xfc->window)
1220 {
1221 xfFloatbar* floatbar = xfc->window->floatbar;
1222 if (xf_floatbar_check_event(floatbar, event))
1223 {
1224 xf_floatbar_event_process(floatbar, event);
1225 return TRUE;
1226 }
1227
1228 if (xf_floatbar_is_locked(floatbar))
1229 {
1230 /* Filter input events, floatbar is locked do not forward anything to the session */
1231 switch (event->type)
1232 {
1233 case MotionNotify:
1234 case ButtonPress:
1235 case ButtonRelease:
1236 case KeyPress:
1237 case KeyRelease:
1238 case FocusIn:
1239 case FocusOut:
1240 case EnterNotify:
1241 case LeaveNotify:
1242 return TRUE;
1243 default:
1244 break;
1245 }
1246 }
1247 }
1248
1249 xf_event_execute_action_script(xfc, event);
1250
1251 if (event->type != MotionNotify)
1252 {
1253 WLog_Print(xfc->log, WLOG_TRACE, "%s Event(%d): wnd=0x%08lX", x11_event_string(event->type),
1254 event->type, (unsigned long)event->xany.window);
1255 }
1256
1257 switch (event->type)
1258 {
1259 case Expose:
1260 status = xf_event_Expose(xfc, &event->xexpose, xfc->remote_app);
1261 break;
1262
1263 case VisibilityNotify:
1264 status = xf_event_VisibilityNotify(xfc, &event->xvisibility, xfc->remote_app);
1265 break;
1266
1267 case MotionNotify:
1268 status = xf_event_MotionNotify(xfc, &event->xmotion, xfc->remote_app);
1269 break;
1270
1271 case ButtonPress:
1272 status = xf_event_ButtonPress(xfc, &event->xbutton, xfc->remote_app);
1273 break;
1274
1275 case ButtonRelease:
1276 status = xf_event_ButtonRelease(xfc, &event->xbutton, xfc->remote_app);
1277 break;
1278
1279 case KeyPress:
1280 status = xf_event_KeyPress(xfc, &event->xkey, xfc->remote_app);
1281 break;
1282
1283 case KeyRelease:
1284 status = xf_event_KeyReleaseOrIgnore(xfc, &event->xkey, xfc->remote_app);
1285 break;
1286
1287 case FocusIn:
1288 status = xf_event_FocusIn(xfc, &event->xfocus, xfc->remote_app);
1289 break;
1290
1291 case FocusOut:
1292 status = xf_event_FocusOut(xfc, &event->xfocus, xfc->remote_app);
1293 break;
1294
1295 case EnterNotify:
1296 status = xf_event_EnterNotify(xfc, &event->xcrossing, xfc->remote_app);
1297 break;
1298
1299 case LeaveNotify:
1300 status = xf_event_LeaveNotify(xfc, &event->xcrossing, xfc->remote_app);
1301 break;
1302
1303 case NoExpose:
1304 break;
1305
1306 case GraphicsExpose:
1307 break;
1308
1309 case ConfigureNotify:
1310 status = xf_event_ConfigureNotify(xfc, &event->xconfigure, xfc->remote_app);
1311 break;
1312
1313 case MapNotify:
1314 status = xf_event_MapNotify(xfc, &event->xmap, xfc->remote_app);
1315 break;
1316
1317 case UnmapNotify:
1318 status = xf_event_UnmapNotify(xfc, &event->xunmap, xfc->remote_app);
1319 break;
1320
1321 case ReparentNotify:
1322 break;
1323
1324 case MappingNotify:
1325 status = xf_event_MappingNotify(xfc, &event->xmapping, xfc->remote_app);
1326 break;
1327
1328 case ClientMessage:
1329 status = xf_event_ClientMessage(xfc, &event->xclient, xfc->remote_app);
1330 break;
1331
1332 case PropertyNotify:
1333 status = xf_event_PropertyNotify(xfc, &event->xproperty, xfc->remote_app);
1334 break;
1335
1336 default:
1337 if (freerdp_settings_get_bool(settings, FreeRDP_SupportDisplayControl))
1338 xf_disp_handle_xevent(xfc, event);
1339
1340 break;
1341 }
1342
1343 xfWindow* window = xfc->window;
1344 xfFloatbar* floatbar = nullptr;
1345 if (window)
1346 floatbar = window->floatbar;
1347
1348 xf_cliprdr_handle_xevent(xfc, event);
1349 if (!xf_floatbar_check_event(floatbar, event) && !xf_floatbar_is_locked(floatbar))
1350 {
1351 if (xf_input_handle_event(xfc, event) < 0)
1352 return FALSE;
1353 }
1354
1355 LogDynAndXSync(xfc->log, xfc->display, FALSE);
1356 return status;
1357}
1358
1359BOOL xf_generic_RawButtonEvent_(xfContext* xfc, int button, BOOL app, BOOL down, const char* file,
1360 const char* fkt, size_t line)
1361{
1362 UINT16 flags = 0;
1363
1364 if (WLog_IsLevelActive(xfc->log, mouseLogLevel))
1365 WLog_PrintTextMessage(xfc->log, mouseLogLevel, line, file, fkt,
1366 "%s: button=%d, app=%d, down=%d", __func__, button, app, down);
1367
1368 if (app || (button < 0))
1369 return FALSE;
1370
1371 for (size_t i = 0; i < ARRAYSIZE(xfc->button_map); i++)
1372 {
1373 const button_map* cur = &xfc->button_map[i];
1374
1375 if (cur->button == (UINT32)button)
1376 {
1377 flags = cur->flags;
1378 break;
1379 }
1380 }
1381
1382 if (flags != 0)
1383 {
1384 if (flags & (PTR_FLAGS_WHEEL | PTR_FLAGS_HWHEEL))
1385 {
1386 if (down)
1387 freerdp_client_send_wheel_event(&xfc->common, flags);
1388 }
1389 else
1390 {
1391 BOOL extended = FALSE;
1392
1393 if (flags & (PTR_XFLAGS_BUTTON1 | PTR_XFLAGS_BUTTON2))
1394 {
1395 extended = TRUE;
1396
1397 if (down)
1398 flags |= PTR_XFLAGS_DOWN;
1399 }
1400 else if (flags & (PTR_FLAGS_BUTTON1 | PTR_FLAGS_BUTTON2 | PTR_FLAGS_BUTTON3))
1401 {
1402 if (down)
1403 flags |= PTR_FLAGS_DOWN;
1404 }
1405
1406 if (extended)
1407 freerdp_client_send_extended_button_event(&xfc->common, TRUE, flags, 0, 0);
1408 else
1409 freerdp_client_send_button_event(&xfc->common, TRUE, flags, 0, 0);
1410 }
1411 }
1412
1413 return TRUE;
1414}
1415
1416BOOL xf_event_update_screen(freerdp* instance)
1417{
1418 WINPR_ASSERT(instance);
1419
1420 xfContext* xfc = (xfContext*)instance->context;
1421 WINPR_ASSERT(xfc);
1422
1423 rdpSettings* settings = xfc->common.context.settings;
1424 WINPR_ASSERT(settings);
1425
1426 if (!xfc->exposeRequested)
1427 return TRUE;
1428 xfc->exposeRequested = false;
1429
1430 if (!xfc->remote_app)
1431 {
1432 if (xfc->common.context.gdi->gfx)
1433 {
1434 xf_OutputExpose(xfc, WINPR_ASSERTING_INT_CAST(uint32_t, xfc->exposedArea.x),
1435 WINPR_ASSERTING_INT_CAST(uint32_t, xfc->exposedArea.y),
1436 WINPR_ASSERTING_INT_CAST(uint32_t, xfc->exposedArea.w),
1437 WINPR_ASSERTING_INT_CAST(uint32_t, xfc->exposedArea.h));
1438 return TRUE;
1439 }
1440 xf_draw_screen(xfc, xfc->exposedArea.x, xfc->exposedArea.y, xfc->exposedArea.w,
1441 xfc->exposedArea.h);
1442 }
1443 else
1444 {
1445 xfAppWindow* appWindow = xf_AppWindowFromX11Window(xfc, xfc->exposedWindow);
1446 if (appWindow)
1447 xf_UpdateWindowArea(xfc, appWindow, xfc->exposedArea.x, xfc->exposedArea.y,
1448 xfc->exposedArea.w, xfc->exposedArea.h);
1449 xf_rail_return_window(appWindow, FALSE);
1450 }
1451 return TRUE;
1452}
WINPR_ATTR_NODISCARD FREERDP_API UINT32 freerdp_settings_get_uint32(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id)
Returns a UINT32 settings value.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.
This struct contains function pointer to initialize/free objects.
Definition collections.h:52
OBJECT_FREE_FN fnObjectFree
Definition collections.h:59
WINPR_ATTR_NODISCARD OBJECT_NEW_FN fnObjectNew
Definition collections.h:54