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