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