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