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