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