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