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