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 int x = 0;
369 int y = 0;
370 int w = 0;
371 int h = 0;
372 rdpSettings* settings = nullptr;
373
374 WINPR_ASSERT(xfc);
375 WINPR_ASSERT(event);
376
377 settings = xfc->common.context.settings;
378 WINPR_ASSERT(settings);
379
380 if (!app && (freerdp_settings_get_bool(settings, FreeRDP_SmartSizing) ||
381 freerdp_settings_get_bool(settings, FreeRDP_MultiTouchGestures)))
382 {
383 x = 0;
384 y = 0;
385 w = WINPR_ASSERTING_INT_CAST(int,
386 freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth));
387 h = WINPR_ASSERTING_INT_CAST(int,
388 freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight));
389 }
390 else
391 {
392 x = event->x;
393 y = event->y;
394 w = event->width;
395 h = event->height;
396 }
397
398 if (!app)
399 {
400 if (xfc->common.context.gdi->gfx)
401 {
402 xf_OutputExpose(
403 xfc, WINPR_ASSERTING_INT_CAST(uint32_t, x), WINPR_ASSERTING_INT_CAST(uint32_t, y),
404 WINPR_ASSERTING_INT_CAST(uint32_t, w), WINPR_ASSERTING_INT_CAST(uint32_t, h));
405 return TRUE;
406 }
407 xf_draw_screen(xfc, x, y, w, h);
408 }
409 else
410 {
411 xfAppWindow* appWindow = xf_AppWindowFromX11Window(xfc, event->window);
412 if (appWindow)
413 xf_UpdateWindowArea(xfc, appWindow, x, y, w, h);
414 xf_rail_return_window(appWindow);
415 }
416
417 return TRUE;
418}
419
420static BOOL xf_event_VisibilityNotify(xfContext* xfc, const XVisibilityEvent* event, BOOL app)
421{
422 WINPR_UNUSED(app);
423 xfc->unobscured = event->state == VisibilityUnobscured;
424 return TRUE;
425}
426
427BOOL xf_generic_MotionNotify_(xfContext* xfc, int x, int y, Window window, BOOL app,
428 const char* file, const char* fkt, size_t line)
429{
430 Window childWindow = None;
431
432 if (WLog_IsLevelActive(xfc->log, mouseLogLevel))
433 WLog_PrintTextMessage(xfc->log, mouseLogLevel, line, file, fkt,
434 "%s: x=%d, y=%d, window=0x%08lx, app=%d", __func__, x, y, window,
435 app);
436
437 WINPR_ASSERT(xfc);
438 WINPR_ASSERT(xfc->common.context.settings);
439
440 if (app)
441 {
442 /* make sure window exists */
443 xfAppWindow* appWindow = xf_AppWindowFromX11Window(xfc, window);
444 xf_rail_return_window(appWindow);
445 if (!appWindow)
446 return TRUE;
447
448 /* Translate to desktop coordinates */
449 XTranslateCoordinates(xfc->display, window, RootWindowOfScreen(xfc->screen), x, y, &x, &y,
450 &childWindow);
451 }
452
453 xf_event_adjust_coordinates(xfc, &x, &y);
454 freerdp_client_send_button_event(&xfc->common, FALSE, PTR_FLAGS_MOVE, x, y);
455
456 if (xfc->fullscreen && !app)
457 {
458 if (xfc->window)
459 XSetInputFocus(xfc->display, xfc->window->handle, RevertToPointerRoot, CurrentTime);
460 }
461
462 return TRUE;
463}
464
465BOOL xf_generic_RawMotionNotify_(xfContext* xfc, int x, int y, WINPR_ATTR_UNUSED Window window,
466 BOOL app, const char* file, const char* fkt, size_t line)
467{
468 WINPR_ASSERT(xfc);
469
470 if (WLog_IsLevelActive(xfc->log, mouseLogLevel))
471 WLog_PrintTextMessage(xfc->log, mouseLogLevel, line, file, fkt,
472 "%s: x=%d, y=%d, window=0x%08lx, app=%d", __func__, x, y, window,
473 app);
474
475 if (app)
476 {
477 WLog_Print(xfc->log, WLOG_ERROR,
478 "Relative mouse input is not supported with remoate app mode!");
479 return FALSE;
480 }
481
482 return freerdp_client_send_button_event(&xfc->common, TRUE, PTR_FLAGS_MOVE, x, y);
483}
484
485static BOOL xf_event_MotionNotify(xfContext* xfc, const XMotionEvent* event, BOOL app)
486{
487 WINPR_ASSERT(xfc);
488
489 if (xfc->window)
490 xf_floatbar_set_root_y(xfc->window->floatbar, event->y);
491
492 if (xfc->xi_event || xfc->xi_rawevent || (xfc->common.mouse_grabbed && xf_use_rel_mouse(xfc)))
493 return TRUE;
494
495 return xf_generic_MotionNotify(xfc, event->x, event->y, event->window, app);
496}
497
498BOOL xf_generic_ButtonEvent_(xfContext* xfc, int x, int y, int button, Window window, BOOL app,
499 BOOL down, const char* file, const char* fkt, size_t line)
500{
501 UINT16 flags = 0;
502 Window childWindow = None;
503
504 if (WLog_IsLevelActive(xfc->log, mouseLogLevel))
505 WLog_PrintTextMessage(xfc->log, mouseLogLevel, line, file, fkt,
506 "%s: x=%d, y=%d, button=%d, window=0x%08lx, app=%d, down=%d",
507 __func__, x, y, button, window, app, down);
508
509 WINPR_ASSERT(xfc);
510 if (button < 0)
511 return FALSE;
512
513 for (size_t i = 0; i < ARRAYSIZE(xfc->button_map); i++)
514 {
515 const button_map* cur = &xfc->button_map[i];
516
517 if (cur->button == (UINT32)button)
518 {
519 flags = cur->flags;
520 break;
521 }
522 }
523
524 if (flags != 0)
525 {
526 if (flags & (PTR_FLAGS_WHEEL | PTR_FLAGS_HWHEEL))
527 {
528 if (down)
529 freerdp_client_send_wheel_event(&xfc->common, flags);
530 }
531 else
532 {
533 BOOL extended = FALSE;
534
535 if (flags & (PTR_XFLAGS_BUTTON1 | PTR_XFLAGS_BUTTON2))
536 {
537 extended = TRUE;
538
539 if (down)
540 flags |= PTR_XFLAGS_DOWN;
541 }
542 else if (flags & (PTR_FLAGS_BUTTON1 | PTR_FLAGS_BUTTON2 | PTR_FLAGS_BUTTON3))
543 {
544 if (down)
545 flags |= PTR_FLAGS_DOWN;
546 }
547
548 if (app)
549 {
550 /* make sure window exists */
551 xfAppWindow* appWindow = xf_AppWindowFromX11Window(xfc, window);
552 xf_rail_return_window(appWindow);
553 if (!appWindow)
554 return TRUE;
555
556 /* Translate to desktop coordinates */
557 XTranslateCoordinates(xfc->display, window, RootWindowOfScreen(xfc->screen), x, y,
558 &x, &y, &childWindow);
559 }
560
561 xf_event_adjust_coordinates(xfc, &x, &y);
562
563 if (extended)
564 freerdp_client_send_extended_button_event(&xfc->common, FALSE, flags, x, y);
565 else
566 freerdp_client_send_button_event(&xfc->common, FALSE, flags, x, y);
567 }
568 }
569
570 return TRUE;
571}
572
573static BOOL xf_grab_mouse(xfContext* xfc)
574{
575 WINPR_ASSERT(xfc);
576
577 if (!xfc->window)
578 return FALSE;
579
580 if (freerdp_settings_get_bool(xfc->common.context.settings, FreeRDP_GrabMouse))
581 {
582 XGrabPointer(xfc->display, xfc->window->handle, False,
583 ButtonPressMask | ButtonReleaseMask | PointerMotionMask | FocusChangeMask |
584 EnterWindowMask | LeaveWindowMask,
585 GrabModeAsync, GrabModeAsync, xfc->window->handle, None, CurrentTime);
586 xfc->common.mouse_grabbed = TRUE;
587 }
588 return TRUE;
589}
590
591static BOOL xf_grab_kbd(xfContext* xfc)
592{
593 WINPR_ASSERT(xfc);
594
595 if (!xfc->window)
596 return FALSE;
597
598 XGrabKeyboard(xfc->display, xfc->window->handle, TRUE, GrabModeAsync, GrabModeAsync,
599 CurrentTime);
600 return TRUE;
601}
602
603static BOOL xf_event_ButtonPress(xfContext* xfc, const XButtonEvent* event, BOOL app)
604{
605 xf_grab_mouse(xfc);
606
607 if (xfc->xi_event || xfc->xi_rawevent || (xfc->common.mouse_grabbed && xf_use_rel_mouse(xfc)))
608 return TRUE;
609 if (!app && xfc_is_floatbar_window(xfc, event->window))
610 return TRUE;
611 return xf_generic_ButtonEvent(xfc, event->x, event->y,
612 WINPR_ASSERTING_INT_CAST(int, event->button), event->window, app,
613 TRUE);
614}
615
616static BOOL xf_event_ButtonRelease(xfContext* xfc, const XButtonEvent* event, BOOL app)
617{
618 xf_grab_mouse(xfc);
619
620 if (xfc->xi_event || xfc->xi_rawevent || (xfc->common.mouse_grabbed && xf_use_rel_mouse(xfc)))
621 return TRUE;
622 return xf_generic_ButtonEvent(xfc, event->x, event->y,
623 WINPR_ASSERTING_INT_CAST(int, event->button), event->window, app,
624 FALSE);
625}
626
627static BOOL xf_event_KeyPress(xfContext* xfc, const XKeyEvent* event, BOOL app)
628{
629 KeySym keysym = 0;
630 char str[256] = WINPR_C_ARRAY_INIT;
631 union
632 {
633 const XKeyEvent* cev;
634 XKeyEvent* ev;
635 } cnv;
636 cnv.cev = event;
637 WINPR_UNUSED(app);
638 XLookupString(cnv.ev, str, sizeof(str), &keysym, nullptr);
639 xf_keyboard_key_press(xfc, event, keysym);
640 return TRUE;
641}
642
643static BOOL xf_event_KeyRelease(xfContext* xfc, const XKeyEvent* event, BOOL app)
644{
645 KeySym keysym = 0;
646 char str[256] = WINPR_C_ARRAY_INIT;
647 union
648 {
649 const XKeyEvent* cev;
650 XKeyEvent* ev;
651 } cnv;
652 cnv.cev = event;
653
654 WINPR_UNUSED(app);
655 XLookupString(cnv.ev, str, sizeof(str), &keysym, nullptr);
656 xf_keyboard_key_release(xfc, event, keysym);
657 return TRUE;
658}
659
660/* Release a key, but ignore the event in case of autorepeat.
661 */
662static BOOL xf_event_KeyReleaseOrIgnore(xfContext* xfc, const XKeyEvent* event, BOOL app)
663{
664 WINPR_ASSERT(xfc);
665 WINPR_ASSERT(event);
666
667 if ((event->type == KeyRelease) && XEventsQueued(xfc->display, QueuedAfterReading))
668 {
669 XEvent nev = WINPR_C_ARRAY_INIT;
670 XPeekEvent(xfc->display, &nev);
671
672 if ((nev.type == KeyPress) && (nev.xkey.time == event->time) &&
673 (nev.xkey.keycode == event->keycode))
674 {
675 /* Key wasn’t actually released */
676 return TRUE;
677 }
678 }
679
680 return xf_event_KeyRelease(xfc, event, app);
681}
682
683static BOOL xf_event_FocusIn(xfContext* xfc, const XFocusInEvent* event, BOOL app)
684{
685 if (event->mode == NotifyGrab)
686 return TRUE;
687
688 xfc->focused = TRUE;
689
690 if (xfc->mouse_active && !app)
691 {
692 xf_grab_mouse(xfc);
693 if (!xf_grab_kbd(xfc))
694 return FALSE;
695 }
696
697 /* Release all keys, should already be done at FocusOut but might be missed
698 * if the WM decided to use an alternate event order */
699 if (!app)
700 xf_keyboard_release_all_keypress(xfc);
701 else
702 {
703 if (!xf_rail_send_activate(xfc, event->window, TRUE))
704 return FALSE;
705 }
706
707 xf_pointer_update_scale(xfc);
708
709 if (app)
710 {
711 xfAppWindow* appWindow = xf_AppWindowFromX11Window(xfc, event->window);
712
713 /* Update the server with any window changes that occurred while the window was not focused.
714 */
715 if (appWindow)
716 xf_rail_adjust_position(xfc, appWindow);
717 xf_rail_return_window(appWindow);
718 }
719
720 xf_keyboard_focus_in(xfc);
721 return TRUE;
722}
723
724static BOOL xf_event_FocusOut(xfContext* xfc, const XFocusOutEvent* event, BOOL app)
725{
726 if (event->mode == NotifyUngrab)
727 return TRUE;
728
729 xfc->focused = FALSE;
730
731 if (event->mode == NotifyWhileGrabbed)
732 XUngrabKeyboard(xfc->display, CurrentTime);
733
734 xf_keyboard_release_all_keypress(xfc);
735 if (app)
736 return xf_rail_send_activate(xfc, event->window, FALSE);
737
738 return TRUE;
739}
740
741static BOOL xf_event_MappingNotify(xfContext* xfc, const XMappingEvent* event, BOOL app)
742{
743 WINPR_UNUSED(app);
744
745 switch (event->request)
746 {
747 case MappingModifier:
748 return xf_keyboard_update_modifier_map(xfc);
749 case MappingKeyboard:
750 WLog_Print(xfc->log, WLOG_TRACE, "[%d] MappingKeyboard", event->request);
751 return xf_keyboard_init(xfc);
752 case MappingPointer:
753 WLog_Print(xfc->log, WLOG_TRACE, "[%d] MappingPointer", event->request);
754 xf_button_map_init(xfc);
755 return TRUE;
756 default:
757 WLog_Print(xfc->log, WLOG_WARN,
758 "[%d] Unsupported MappingNotify::request, must be one "
759 "of[MappingModifier(%d), MappingKeyboard(%d), MappingPointer(%d)]",
760 event->request, MappingModifier, MappingKeyboard, MappingPointer);
761 return FALSE;
762 }
763}
764
765static BOOL xf_event_ClientMessage(xfContext* xfc, const XClientMessageEvent* event, BOOL app)
766{
767 if ((event->message_type == xfc->WM_PROTOCOLS) &&
768 ((Atom)event->data.l[0] == xfc->WM_DELETE_WINDOW))
769 {
770 if (app)
771 {
772 BOOL rc = TRUE;
773 xfAppWindow* appWindow = xf_AppWindowFromX11Window(xfc, event->window);
774
775 if (appWindow)
776 rc = xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_CLOSE);
777 xf_rail_return_window(appWindow);
778 return rc;
779 }
780 else
781 {
782 DEBUG_X11("Main window closed");
783 return FALSE;
784 }
785 }
786
787 return TRUE;
788}
789
790static BOOL xf_event_EnterNotify(xfContext* xfc, const XEnterWindowEvent* event, BOOL app)
791{
792 if (!app)
793 {
794 if (!xfc->window)
795 return FALSE;
796
797 xfc->mouse_active = TRUE;
798
799 if (xfc->fullscreen)
800 XSetInputFocus(xfc->display, xfc->window->handle, RevertToPointerRoot, CurrentTime);
801
802 if (xfc->focused)
803 xf_grab_kbd(xfc);
804 }
805 else
806 {
807 xfAppWindow* appWindow = xf_AppWindowFromX11Window(xfc, event->window);
808
809 /* keep track of which window has focus so that we can apply pointer updates */
810 xfc->appWindow = appWindow;
811 xf_rail_return_window(appWindow);
812 }
813
814 return TRUE;
815}
816
817static BOOL xf_event_LeaveNotify(xfContext* xfc, const XLeaveWindowEvent* event, BOOL app)
818{
819 if (event->mode == NotifyGrab || event->mode == NotifyUngrab)
820 return TRUE;
821 if (!app)
822 {
823 xfc->mouse_active = FALSE;
824 XUngrabKeyboard(xfc->display, CurrentTime);
825 }
826 else
827 {
828 xfAppWindow* appWindow = xf_AppWindowFromX11Window(xfc, event->window);
829
830 /* keep track of which window has focus so that we can apply pointer updates */
831 if (xfc->appWindow == appWindow)
832 xfc->appWindow = nullptr;
833 xf_rail_return_window(appWindow);
834 }
835 return TRUE;
836}
837
838static BOOL xf_event_ConfigureNotify(xfContext* xfc, const XConfigureEvent* event, BOOL app)
839{
840 Window childWindow = None;
841 xfAppWindow* appWindow = nullptr;
842
843 WINPR_ASSERT(xfc);
844 WINPR_ASSERT(event);
845
846 const rdpSettings* settings = xfc->common.context.settings;
847 WINPR_ASSERT(settings);
848
849 WLog_Print(xfc->log, WLOG_DEBUG, "x=%" PRId32 ", y=%" PRId32 ", w=%" PRId32 ", h=%" PRId32,
850 event->x, event->y, event->width, event->height);
851
852 if (!app)
853 {
854 if (!xfc->window)
855 return FALSE;
856
857 if (xfc->window->left != event->x)
858 xfc->window->left = event->x;
859
860 if (xfc->window->top != event->y)
861 xfc->window->top = event->y;
862
863 if (xfc->window->width != event->width || xfc->window->height != event->height)
864 {
865 xfc->window->width = event->width;
866 xfc->window->height = event->height;
867#ifdef WITH_XRENDER
868 xfc->offset_x = 0;
869 xfc->offset_y = 0;
870
871 if (freerdp_settings_get_bool(settings, FreeRDP_SmartSizing) ||
872 freerdp_settings_get_bool(settings, FreeRDP_MultiTouchGestures))
873 {
874 xfc->scaledWidth = xfc->window->width;
875 xfc->scaledHeight = xfc->window->height;
876 xf_draw_screen(
877 xfc, 0, 0,
878 WINPR_ASSERTING_INT_CAST(
879 int32_t, freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth)),
880 WINPR_ASSERTING_INT_CAST(
881 int32_t, freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight)));
882 }
883 else
884 {
885 xfc->scaledWidth = WINPR_ASSERTING_INT_CAST(
886 int, freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth));
887 xfc->scaledHeight = WINPR_ASSERTING_INT_CAST(
888 int, freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight));
889 }
890
891#endif
892 }
893
894 if (freerdp_settings_get_bool(settings, FreeRDP_DynamicResolutionUpdate))
895 {
896 const int alignedWidth = (xfc->window->width / 2) * 2;
897 const int alignedHeight = (xfc->window->height / 2) * 2;
898 /* ask the server to resize using the display channel */
899 xf_disp_handle_configureNotify(xfc, alignedWidth, alignedHeight);
900 }
901 }
902 else
903 {
904 appWindow = xf_AppWindowFromX11Window(xfc, event->window);
905
906 if (appWindow)
907 {
908 /*
909 * ConfigureNotify coordinates are expressed relative to the window parent.
910 * Translate these to root window coordinates.
911 */
912 XTranslateCoordinates(xfc->display, appWindow->handle, RootWindowOfScreen(xfc->screen),
913 0, 0, &appWindow->x, &appWindow->y, &childWindow);
914 appWindow->width = event->width;
915 appWindow->height = event->height;
916
917 xf_AppWindowResize(xfc, appWindow);
918
919 /*
920 * Additional checks for not in a local move and not ignoring configure to send
921 * position update to server, also should the window not be focused then do not
922 * send to server yet (i.e. resizing using window decoration).
923 * The server will be updated when the window gets refocused.
924 */
925 if (appWindow->decorations)
926 {
927 /* moving resizing using window decoration */
928 xf_rail_adjust_position(xfc, appWindow);
929 }
930 else
931 {
932 if ((!event->send_event || appWindow->local_move.state == LMS_NOT_ACTIVE) &&
933 !appWindow->rail_ignore_configure && xfc->focused)
934 xf_rail_adjust_position(xfc, appWindow);
935 }
936 }
937 xf_rail_return_window(appWindow);
938 }
939 return xf_pointer_update_scale(xfc);
940}
941
942static BOOL xf_event_MapNotify(xfContext* xfc, const XMapEvent* event, BOOL app)
943{
944 WINPR_ASSERT(xfc);
945 if (!app)
946 {
947 if (!gdi_send_suppress_output(xfc->common.context.gdi, FALSE))
948 return FALSE;
949 }
950 else
951 {
952 xfAppWindow* appWindow = xf_AppWindowFromX11Window(xfc, event->window);
953
954 if (appWindow)
955 {
956 /* local restore event */
957 /* This is now handled as part of the PropertyNotify
958 * Doing this here would inhibit the ability to restore a maximized window
959 * that is minimized back to the maximized state
960 */
961 // xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_RESTORE);
962 appWindow->is_mapped = TRUE;
963 }
964 xf_rail_return_window(appWindow);
965 }
966
967 return TRUE;
968}
969
970static BOOL xf_event_UnmapNotify(xfContext* xfc, const XUnmapEvent* event, BOOL app)
971{
972 WINPR_ASSERT(xfc);
973 WINPR_ASSERT(event);
974
975 if (!app)
976 xf_keyboard_release_all_keypress(xfc);
977
978 if (!app)
979 return gdi_send_suppress_output(xfc->common.context.gdi, TRUE);
980
981 {
982 xfAppWindow* appWindow = xf_AppWindowFromX11Window(xfc, event->window);
983
984 if (appWindow)
985 appWindow->is_mapped = FALSE;
986 xf_rail_return_window(appWindow);
987 }
988
989 return TRUE;
990}
991
992static BOOL xf_event_PropertyNotify(xfContext* xfc, const XPropertyEvent* event, BOOL app)
993{
994 BOOL rc = TRUE;
995 WINPR_ASSERT(xfc);
996 WINPR_ASSERT(event);
997
998 /*
999 * This section handles sending the appropriate commands to the rail server
1000 * when the window has been minimized, maximized, restored locally
1001 * ie. not using the buttons on the rail window itself
1002 */
1003 if (((event->atom == xfc->NET_WM_STATE) && (event->state != PropertyDelete)) ||
1004 ((event->atom == xfc->WM_STATE) && (event->state != PropertyDelete)))
1005 {
1006 BOOL status = FALSE;
1007 BOOL minimized = FALSE;
1008 BOOL minimizedChanged = FALSE;
1009 unsigned long nitems = 0;
1010 unsigned long bytes = 0;
1011 unsigned char* prop = nullptr;
1012 xfAppWindow* appWindow = nullptr;
1013
1014 if (app)
1015 {
1016 appWindow = xf_AppWindowFromX11Window(xfc, event->window);
1017
1018 if (!appWindow)
1019 goto fail;
1020 }
1021
1022 if (event->atom == xfc->NET_WM_STATE)
1023 {
1024 status = xf_GetWindowProperty(xfc, event->window, xfc->NET_WM_STATE, 12, &nitems,
1025 &bytes, &prop);
1026
1027 if (status)
1028 {
1029 if (appWindow)
1030 {
1031 appWindow->maxVert = FALSE;
1032 appWindow->maxHorz = FALSE;
1033 }
1034 for (unsigned long i = 0; i < nitems; i++)
1035 {
1036 if ((Atom)((UINT16**)prop)[i] ==
1037 Logging_XInternAtom(xfc->log, xfc->display, "_NET_WM_STATE_MAXIMIZED_VERT",
1038 False))
1039 {
1040 if (appWindow)
1041 appWindow->maxVert = TRUE;
1042 }
1043
1044 if ((Atom)((UINT16**)prop)[i] ==
1045 Logging_XInternAtom(xfc->log, xfc->display, "_NET_WM_STATE_MAXIMIZED_HORZ",
1046 False))
1047 {
1048 if (appWindow)
1049 appWindow->maxHorz = TRUE;
1050 }
1051 }
1052
1053 XFree(prop);
1054 }
1055 }
1056
1057 if (event->atom == xfc->WM_STATE)
1058 {
1059 status =
1060 xf_GetWindowProperty(xfc, event->window, xfc->WM_STATE, 1, &nitems, &bytes, &prop);
1061
1062 if (status)
1063 {
1064 /* If the window is in the iconic state */
1065 if (((UINT32)*prop == 3) && !IsGnome())
1066 {
1067 minimized = TRUE;
1068 if (appWindow)
1069 appWindow->minimized = TRUE;
1070 }
1071 else
1072 {
1073 minimized = FALSE;
1074 if (appWindow)
1075 appWindow->minimized = FALSE;
1076 }
1077
1078 minimizedChanged = TRUE;
1079 XFree(prop);
1080 }
1081 }
1082
1083 if (app)
1084 {
1085 WINPR_ASSERT(appWindow);
1086 if (appWindow->maxVert && appWindow->maxHorz && !appWindow->minimized)
1087 {
1088 if (appWindow->rail_state != WINDOW_SHOW_MAXIMIZED)
1089 {
1090 appWindow->rail_state = WINDOW_SHOW_MAXIMIZED;
1091 rc = xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_MAXIMIZE);
1092 }
1093 }
1094 else if (appWindow->minimized)
1095 {
1096 if (appWindow->rail_state != WINDOW_SHOW_MINIMIZED)
1097 {
1098 appWindow->rail_state = WINDOW_SHOW_MINIMIZED;
1099 rc = xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_MINIMIZE);
1100 }
1101 }
1102 else
1103 {
1104 if (appWindow->rail_state != WINDOW_SHOW && appWindow->rail_state != WINDOW_HIDE)
1105 {
1106 appWindow->rail_state = WINDOW_SHOW;
1107 rc = xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_RESTORE);
1108 }
1109 }
1110 }
1111 else if (minimizedChanged)
1112 rc = gdi_send_suppress_output(xfc->common.context.gdi, minimized);
1113
1114 fail:
1115 xf_rail_return_window(appWindow);
1116 }
1117
1118 return rc;
1119}
1120
1121static BOOL xf_event_suppress_events(xfContext* xfc, xfAppWindow* appWindow, const XEvent* event)
1122{
1123 if (!xfc->remote_app)
1124 return FALSE;
1125
1126 switch (appWindow->local_move.state)
1127 {
1128 case LMS_NOT_ACTIVE:
1129
1130 /* No local move in progress, nothing to do */
1131
1132 /* Prevent Configure from happening during indeterminant state of Horz or Vert Max only
1133 */
1134 if ((event->type == ConfigureNotify) && appWindow->rail_ignore_configure)
1135 {
1136 appWindow->rail_ignore_configure = FALSE;
1137 return TRUE;
1138 }
1139
1140 break;
1141
1142 case LMS_STARTING:
1143
1144 /* Local move initiated by RDP server, but we have not yet seen any updates from the X
1145 * server */
1146 switch (event->type)
1147 {
1148 case ConfigureNotify:
1149 /* Starting to see move events from the X server. Local move is now in progress.
1150 */
1151 appWindow->local_move.state = LMS_ACTIVE;
1152 /* Allow these events to be processed during move to keep our state up to date.
1153 */
1154 break;
1155
1156 case ButtonPress:
1157 case ButtonRelease:
1158 case KeyPress:
1159 case KeyRelease:
1160 case UnmapNotify:
1161 /*
1162 * A button release event means the X window server did not grab the
1163 * mouse before the user released it. In this case we must cancel the
1164 * local move. The event will be processed below as normal, below.
1165 */
1166 break;
1167
1168 case VisibilityNotify:
1169 case PropertyNotify:
1170 case Expose:
1171 /* Allow these events to pass */
1172 break;
1173
1174 default:
1175 /* Eat any other events */
1176 return TRUE;
1177 }
1178
1179 break;
1180
1181 case LMS_ACTIVE:
1182
1183 /* Local move is in progress */
1184 switch (event->type)
1185 {
1186 case ConfigureNotify:
1187 case VisibilityNotify:
1188 case PropertyNotify:
1189 case Expose:
1190 case GravityNotify:
1191 /* Keep us up to date on position */
1192 break;
1193
1194 default:
1195 /* Any other event terminates move */
1196 xf_rail_end_local_move(xfc, appWindow);
1197 break;
1198 }
1199
1200 break;
1201
1202 case LMS_TERMINATING:
1203 /* Already sent RDP end move to server. Allow events to pass. */
1204 break;
1205 default:
1206 break;
1207 }
1208
1209 return FALSE;
1210}
1211
1212BOOL xf_event_process(freerdp* instance, const XEvent* event)
1213{
1214 BOOL status = TRUE;
1215
1216 WINPR_ASSERT(instance);
1217 WINPR_ASSERT(event);
1218
1219 xfContext* xfc = (xfContext*)instance->context;
1220 WINPR_ASSERT(xfc);
1221
1222 rdpSettings* settings = xfc->common.context.settings;
1223 WINPR_ASSERT(settings);
1224
1225 if (xfc->remote_app)
1226 {
1227 xfAppWindow* appWindow = xf_AppWindowFromX11Window(xfc, event->xany.window);
1228
1229 if (appWindow)
1230 {
1231 /* Update "current" window for cursor change orders */
1232 xfc->appWindow = appWindow;
1233
1234 const BOOL rc = xf_event_suppress_events(xfc, appWindow, event);
1235 xf_rail_return_window(appWindow);
1236 if (rc)
1237 return TRUE;
1238 }
1239 }
1240
1241 if (xfc->window)
1242 {
1243 xfFloatbar* floatbar = xfc->window->floatbar;
1244 if (xf_floatbar_check_event(floatbar, event))
1245 {
1246 xf_floatbar_event_process(floatbar, event);
1247 return TRUE;
1248 }
1249
1250 if (xf_floatbar_is_locked(floatbar))
1251 {
1252 /* Filter input events, floatbar is locked do not forward anything to the session */
1253 switch (event->type)
1254 {
1255 case MotionNotify:
1256 case ButtonPress:
1257 case ButtonRelease:
1258 case KeyPress:
1259 case KeyRelease:
1260 case FocusIn:
1261 case FocusOut:
1262 case EnterNotify:
1263 case LeaveNotify:
1264 return TRUE;
1265 default:
1266 break;
1267 }
1268 }
1269 }
1270
1271 xf_event_execute_action_script(xfc, event);
1272
1273 if (event->type != MotionNotify)
1274 {
1275 DEBUG_X11("%s Event(%d): wnd=0x%08lX", x11_event_string(event->type), event->type,
1276 (unsigned long)event->xany.window);
1277 }
1278
1279 switch (event->type)
1280 {
1281 case Expose:
1282 status = xf_event_Expose(xfc, &event->xexpose, xfc->remote_app);
1283 break;
1284
1285 case VisibilityNotify:
1286 status = xf_event_VisibilityNotify(xfc, &event->xvisibility, xfc->remote_app);
1287 break;
1288
1289 case MotionNotify:
1290 status = xf_event_MotionNotify(xfc, &event->xmotion, xfc->remote_app);
1291 break;
1292
1293 case ButtonPress:
1294 status = xf_event_ButtonPress(xfc, &event->xbutton, xfc->remote_app);
1295 break;
1296
1297 case ButtonRelease:
1298 status = xf_event_ButtonRelease(xfc, &event->xbutton, xfc->remote_app);
1299 break;
1300
1301 case KeyPress:
1302 status = xf_event_KeyPress(xfc, &event->xkey, xfc->remote_app);
1303 break;
1304
1305 case KeyRelease:
1306 status = xf_event_KeyReleaseOrIgnore(xfc, &event->xkey, xfc->remote_app);
1307 break;
1308
1309 case FocusIn:
1310 status = xf_event_FocusIn(xfc, &event->xfocus, xfc->remote_app);
1311 break;
1312
1313 case FocusOut:
1314 status = xf_event_FocusOut(xfc, &event->xfocus, xfc->remote_app);
1315 break;
1316
1317 case EnterNotify:
1318 status = xf_event_EnterNotify(xfc, &event->xcrossing, xfc->remote_app);
1319 break;
1320
1321 case LeaveNotify:
1322 status = xf_event_LeaveNotify(xfc, &event->xcrossing, xfc->remote_app);
1323 break;
1324
1325 case NoExpose:
1326 break;
1327
1328 case GraphicsExpose:
1329 break;
1330
1331 case ConfigureNotify:
1332 status = xf_event_ConfigureNotify(xfc, &event->xconfigure, xfc->remote_app);
1333 break;
1334
1335 case MapNotify:
1336 status = xf_event_MapNotify(xfc, &event->xmap, xfc->remote_app);
1337 break;
1338
1339 case UnmapNotify:
1340 status = xf_event_UnmapNotify(xfc, &event->xunmap, xfc->remote_app);
1341 break;
1342
1343 case ReparentNotify:
1344 break;
1345
1346 case MappingNotify:
1347 status = xf_event_MappingNotify(xfc, &event->xmapping, xfc->remote_app);
1348 break;
1349
1350 case ClientMessage:
1351 status = xf_event_ClientMessage(xfc, &event->xclient, xfc->remote_app);
1352 break;
1353
1354 case PropertyNotify:
1355 status = xf_event_PropertyNotify(xfc, &event->xproperty, xfc->remote_app);
1356 break;
1357
1358 default:
1359 if (freerdp_settings_get_bool(settings, FreeRDP_SupportDisplayControl))
1360 xf_disp_handle_xevent(xfc, event);
1361
1362 break;
1363 }
1364
1365 xfWindow* window = xfc->window;
1366 xfFloatbar* floatbar = nullptr;
1367 if (window)
1368 floatbar = window->floatbar;
1369
1370 xf_cliprdr_handle_xevent(xfc, event);
1371 if (!xf_floatbar_check_event(floatbar, event) && !xf_floatbar_is_locked(floatbar))
1372 xf_input_handle_event(xfc, event);
1373
1374 LogDynAndXSync(xfc->log, xfc->display, FALSE);
1375 return status;
1376}
1377
1378BOOL xf_generic_RawButtonEvent_(xfContext* xfc, int button, BOOL app, BOOL down, const char* file,
1379 const char* fkt, size_t line)
1380{
1381 UINT16 flags = 0;
1382
1383 if (WLog_IsLevelActive(xfc->log, mouseLogLevel))
1384 WLog_PrintTextMessage(xfc->log, mouseLogLevel, line, file, fkt,
1385 "%s: button=%d, app=%d, down=%d", __func__, button, app, down);
1386
1387 if (app || (button < 0))
1388 return FALSE;
1389
1390 for (size_t i = 0; i < ARRAYSIZE(xfc->button_map); i++)
1391 {
1392 const button_map* cur = &xfc->button_map[i];
1393
1394 if (cur->button == (UINT32)button)
1395 {
1396 flags = cur->flags;
1397 break;
1398 }
1399 }
1400
1401 if (flags != 0)
1402 {
1403 if (flags & (PTR_FLAGS_WHEEL | PTR_FLAGS_HWHEEL))
1404 {
1405 if (down)
1406 freerdp_client_send_wheel_event(&xfc->common, flags);
1407 }
1408 else
1409 {
1410 BOOL extended = FALSE;
1411
1412 if (flags & (PTR_XFLAGS_BUTTON1 | PTR_XFLAGS_BUTTON2))
1413 {
1414 extended = TRUE;
1415
1416 if (down)
1417 flags |= PTR_XFLAGS_DOWN;
1418 }
1419 else if (flags & (PTR_FLAGS_BUTTON1 | PTR_FLAGS_BUTTON2 | PTR_FLAGS_BUTTON3))
1420 {
1421 if (down)
1422 flags |= PTR_FLAGS_DOWN;
1423 }
1424
1425 if (extended)
1426 freerdp_client_send_extended_button_event(&xfc->common, TRUE, flags, 0, 0);
1427 else
1428 freerdp_client_send_button_event(&xfc->common, TRUE, flags, 0, 0);
1429 }
1430 }
1431
1432 return TRUE;
1433}
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:58
OBJECT_NEW_FN fnObjectNew
Definition collections.h:53