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