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