FreeRDP
xf_floatbar.c
1 
18 #include <X11/Xlib.h>
19 #include <X11/Xutil.h>
20 #include <X11/Xatom.h>
21 #include <X11/extensions/shape.h>
22 #include <X11/cursorfont.h>
23 
24 #include <winpr/assert.h>
25 
26 #include "xf_floatbar.h"
27 #include "resource/close.xbm"
28 #include "resource/lock.xbm"
29 #include "resource/unlock.xbm"
30 #include "resource/minimize.xbm"
31 #include "resource/restore.xbm"
32 
33 #include <freerdp/log.h>
34 #define TAG CLIENT_TAG("x11")
35 
36 #define FLOATBAR_HEIGHT 26
37 #define FLOATBAR_DEFAULT_WIDTH 576
38 #define FLOATBAR_MIN_WIDTH 200
39 #define FLOATBAR_BORDER 24
40 #define FLOATBAR_BUTTON_WIDTH 24
41 #define FLOATBAR_COLOR_BACKGROUND "RGB:31/6c/a9"
42 #define FLOATBAR_COLOR_BORDER "RGB:75/9a/c8"
43 #define FLOATBAR_COLOR_FOREGROUND "RGB:FF/FF/FF"
44 
45 #define XF_FLOATBAR_MODE_NONE 0
46 #define XF_FLOATBAR_MODE_DRAGGING 1
47 #define XF_FLOATBAR_MODE_RESIZE_LEFT 2
48 #define XF_FLOATBAR_MODE_RESIZE_RIGHT 3
49 
50 #define XF_FLOATBAR_BUTTON_CLOSE 1
51 #define XF_FLOATBAR_BUTTON_RESTORE 2
52 #define XF_FLOATBAR_BUTTON_MINIMIZE 3
53 #define XF_FLOATBAR_BUTTON_LOCKED 4
54 
55 typedef BOOL (*OnClick)(xfFloatbar*);
56 
57 typedef struct
58 {
59  int x;
60  int y;
61  int type;
62  bool focus;
63  bool clicked;
64  OnClick onclick;
65  Window handle;
66 } xfFloatbarButton;
67 
68 struct xf_floatbar
69 {
70  int x;
71  int y;
72  int width;
73  int height;
74  int mode;
75  int last_motion_x_root;
76  int last_motion_y_root;
77  BOOL locked;
78  xfFloatbarButton* buttons[4];
79  Window handle;
80  BOOL hasCursor;
81  xfContext* xfc;
82  DWORD flags;
83  BOOL created;
84  Window root_window;
85  char* title;
86  XFontSet fontSet;
87 };
88 
89 static xfFloatbarButton* xf_floatbar_new_button(xfFloatbar* floatbar, int type);
90 
91 static BOOL xf_floatbar_button_onclick_close(xfFloatbar* floatbar)
92 {
93  if (!floatbar)
94  return FALSE;
95 
96  return freerdp_abort_connect_context(&floatbar->xfc->common.context);
97 }
98 
99 static BOOL xf_floatbar_button_onclick_minimize(xfFloatbar* floatbar)
100 {
101  xfContext* xfc = NULL;
102 
103  if (!floatbar || !floatbar->xfc)
104  return FALSE;
105 
106  xfc = floatbar->xfc;
107  xf_SetWindowMinimized(xfc, xfc->window);
108  return TRUE;
109 }
110 
111 static BOOL xf_floatbar_button_onclick_restore(xfFloatbar* floatbar)
112 {
113  if (!floatbar)
114  return FALSE;
115 
116  xf_toggle_fullscreen(floatbar->xfc);
117  return TRUE;
118 }
119 
120 static BOOL xf_floatbar_button_onclick_locked(xfFloatbar* floatbar)
121 {
122  if (!floatbar)
123  return FALSE;
124 
125  floatbar->locked = (floatbar->locked) ? FALSE : TRUE;
126  return xf_floatbar_hide_and_show(floatbar);
127 }
128 
129 BOOL xf_floatbar_set_root_y(xfFloatbar* floatbar, int y)
130 {
131  if (!floatbar)
132  return FALSE;
133 
134  floatbar->last_motion_y_root = y;
135  return TRUE;
136 }
137 
138 BOOL xf_floatbar_hide_and_show(xfFloatbar* floatbar)
139 {
140  xfContext* xfc = NULL;
141 
142  if (!floatbar || !floatbar->xfc)
143  return FALSE;
144 
145  if (!floatbar->created)
146  return TRUE;
147 
148  xfc = floatbar->xfc;
149  WINPR_ASSERT(xfc);
150  WINPR_ASSERT(xfc->display);
151 
152  if (!floatbar->locked)
153  {
154  if ((floatbar->mode == XF_FLOATBAR_MODE_NONE) && (floatbar->last_motion_y_root > 10) &&
155  (floatbar->y > (FLOATBAR_HEIGHT * -1)))
156  {
157  floatbar->y = floatbar->y - 1;
158  XMoveWindow(xfc->display, floatbar->handle, floatbar->x, floatbar->y);
159  }
160  else if (floatbar->y < 0 && (floatbar->last_motion_y_root < 10))
161  {
162  floatbar->y = floatbar->y + 1;
163  XMoveWindow(xfc->display, floatbar->handle, floatbar->x, floatbar->y);
164  }
165  }
166 
167  return TRUE;
168 }
169 
170 static BOOL create_floatbar(xfFloatbar* floatbar)
171 {
172  xfContext* xfc = NULL;
173  Status status = 0;
174  XWindowAttributes attr = { 0 };
175 
176  WINPR_ASSERT(floatbar);
177  if (floatbar->created)
178  return TRUE;
179 
180  xfc = floatbar->xfc;
181  WINPR_ASSERT(xfc);
182  WINPR_ASSERT(xfc->display);
183 
184  status = XGetWindowAttributes(xfc->display, floatbar->root_window, &attr);
185  if (status == 0)
186  {
187  WLog_WARN(TAG, "XGetWindowAttributes failed");
188  return FALSE;
189  }
190  floatbar->x = attr.x + attr.width / 2 - FLOATBAR_DEFAULT_WIDTH / 2;
191  floatbar->y = 0;
192 
193  if (((floatbar->flags & 0x0004) == 0) && !floatbar->locked)
194  floatbar->y = -FLOATBAR_HEIGHT + 1;
195 
196  floatbar->handle =
197  XCreateWindow(xfc->display, floatbar->root_window, floatbar->x, 0, FLOATBAR_DEFAULT_WIDTH,
198  FLOATBAR_HEIGHT, 0, CopyFromParent, InputOutput, CopyFromParent, 0, NULL);
199  floatbar->width = FLOATBAR_DEFAULT_WIDTH;
200  floatbar->height = FLOATBAR_HEIGHT;
201  floatbar->mode = XF_FLOATBAR_MODE_NONE;
202  floatbar->buttons[0] = xf_floatbar_new_button(floatbar, XF_FLOATBAR_BUTTON_CLOSE);
203  floatbar->buttons[1] = xf_floatbar_new_button(floatbar, XF_FLOATBAR_BUTTON_RESTORE);
204  floatbar->buttons[2] = xf_floatbar_new_button(floatbar, XF_FLOATBAR_BUTTON_MINIMIZE);
205  floatbar->buttons[3] = xf_floatbar_new_button(floatbar, XF_FLOATBAR_BUTTON_LOCKED);
206  XSelectInput(xfc->display, floatbar->handle,
207  ExposureMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask |
208  FocusChangeMask | LeaveWindowMask | EnterWindowMask | StructureNotifyMask |
209  PropertyChangeMask);
210  floatbar->created = TRUE;
211  return TRUE;
212 }
213 
214 BOOL xf_floatbar_toggle_fullscreen(xfFloatbar* floatbar, bool fullscreen)
215 {
216  int size = 0;
217  bool visible = False;
218  xfContext* xfc = NULL;
219 
220  if (!floatbar || !floatbar->xfc)
221  return FALSE;
222 
223  xfc = floatbar->xfc;
224  WINPR_ASSERT(xfc->display);
225 
226  /* Only visible if enabled */
227  if (floatbar->flags & 0x0001)
228  {
229  /* Visible if fullscreen and flag visible in fullscreen mode */
230  visible |= ((floatbar->flags & 0x0010) != 0) && fullscreen;
231  /* Visible if window and flag visible in window mode */
232  visible |= ((floatbar->flags & 0x0020) != 0) && !fullscreen;
233  }
234 
235  if (visible)
236  {
237  if (!create_floatbar(floatbar))
238  return FALSE;
239 
240  XMapWindow(xfc->display, floatbar->handle);
241  size = ARRAYSIZE(floatbar->buttons);
242 
243  for (int i = 0; i < size; i++)
244  {
245  xfFloatbarButton* button = floatbar->buttons[i];
246  XMapWindow(xfc->display, button->handle);
247  }
248 
249  /* If default is hidden (and not sticky) don't show on fullscreen state changes */
250  if (((floatbar->flags & 0x0004) == 0) && !floatbar->locked)
251  floatbar->y = -FLOATBAR_HEIGHT + 1;
252 
253  xf_floatbar_hide_and_show(floatbar);
254  }
255  else if (floatbar->created)
256  {
257  XUnmapSubwindows(xfc->display, floatbar->handle);
258  XUnmapWindow(xfc->display, floatbar->handle);
259  }
260 
261  return TRUE;
262 }
263 
264 xfFloatbarButton* xf_floatbar_new_button(xfFloatbar* floatbar, int type)
265 {
266  xfFloatbarButton* button = NULL;
267 
268  WINPR_ASSERT(floatbar);
269  WINPR_ASSERT(floatbar->xfc);
270  WINPR_ASSERT(floatbar->xfc->display);
271  WINPR_ASSERT(floatbar->handle);
272 
273  button = (xfFloatbarButton*)calloc(1, sizeof(xfFloatbarButton));
274  button->type = type;
275 
276  switch (type)
277  {
278  case XF_FLOATBAR_BUTTON_CLOSE:
279  button->x = floatbar->width - FLOATBAR_BORDER - FLOATBAR_BUTTON_WIDTH * type;
280  button->onclick = xf_floatbar_button_onclick_close;
281  break;
282 
283  case XF_FLOATBAR_BUTTON_RESTORE:
284  button->x = floatbar->width - FLOATBAR_BORDER - FLOATBAR_BUTTON_WIDTH * type;
285  button->onclick = xf_floatbar_button_onclick_restore;
286  break;
287 
288  case XF_FLOATBAR_BUTTON_MINIMIZE:
289  button->x = floatbar->width - FLOATBAR_BORDER - FLOATBAR_BUTTON_WIDTH * type;
290  button->onclick = xf_floatbar_button_onclick_minimize;
291  break;
292 
293  case XF_FLOATBAR_BUTTON_LOCKED:
294  button->x = FLOATBAR_BORDER;
295  button->onclick = xf_floatbar_button_onclick_locked;
296  break;
297 
298  default:
299  break;
300  }
301 
302  button->y = 0;
303  button->focus = FALSE;
304  button->handle = XCreateWindow(floatbar->xfc->display, floatbar->handle, button->x, 0,
305  FLOATBAR_BUTTON_WIDTH, FLOATBAR_BUTTON_WIDTH, 0, CopyFromParent,
306  InputOutput, CopyFromParent, 0, NULL);
307  XSelectInput(floatbar->xfc->display, button->handle,
308  ExposureMask | ButtonPressMask | ButtonReleaseMask | FocusChangeMask |
309  LeaveWindowMask | EnterWindowMask | StructureNotifyMask);
310  return button;
311 }
312 
313 xfFloatbar* xf_floatbar_new(xfContext* xfc, Window window, const char* name, DWORD flags)
314 {
315  WINPR_ASSERT(xfc);
316  WINPR_ASSERT(xfc->display);
317  WINPR_ASSERT(name);
318 
319  /* Floatbar not enabled */
320  if ((flags & 0x0001) == 0)
321  return NULL;
322 
323  if (!xfc)
324  return NULL;
325 
326  /* Force disable with remote app */
327  if (xfc->remote_app)
328  return NULL;
329 
330  xfFloatbar* floatbar = (xfFloatbar*)calloc(1, sizeof(xfFloatbar));
331 
332  if (!floatbar)
333  return NULL;
334 
335  floatbar->title = _strdup(name);
336 
337  if (!floatbar->title)
338  goto fail;
339 
340  floatbar->root_window = window;
341  floatbar->flags = flags;
342  floatbar->xfc = xfc;
343  floatbar->locked = flags & 0x0002;
344  xf_floatbar_toggle_fullscreen(floatbar, FALSE);
345  char** missingList = NULL;
346  int missingCount = 0;
347  char* defString = NULL;
348  floatbar->fontSet = XCreateFontSet(floatbar->xfc->display, "-*-*-*-*-*-*-*-*-*-*-*-*-*-*",
349  &missingList, &missingCount, &defString);
350  if (floatbar->fontSet == NULL)
351  {
352  WLog_ERR(TAG, "Failed to create fontset");
353  }
354  XFreeStringList(missingList);
355  return floatbar;
356 fail:
357  WINPR_PRAGMA_DIAG_PUSH
358  WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
359  xf_floatbar_free(floatbar);
360  WINPR_PRAGMA_DIAG_POP
361  return NULL;
362 }
363 
364 static unsigned long xf_floatbar_get_color(xfFloatbar* floatbar, char* rgb_value)
365 {
366  XColor color;
367 
368  WINPR_ASSERT(floatbar);
369  WINPR_ASSERT(floatbar->xfc);
370 
371  Display* display = floatbar->xfc->display;
372  WINPR_ASSERT(display);
373 
374  Colormap cmap = DefaultColormap(display, XDefaultScreen(display));
375  XParseColor(display, cmap, rgb_value, &color);
376  XAllocColor(display, cmap, &color);
377  return color.pixel;
378 }
379 
380 static void xf_floatbar_event_expose(xfFloatbar* floatbar)
381 {
382  GC gc = NULL;
383  GC shape_gc = NULL;
384  Pixmap pmap = 0;
385  XPoint shape[5] = { 0 };
386  XPoint border[5] = { 0 };
387 
388  WINPR_ASSERT(floatbar);
389  WINPR_ASSERT(floatbar->xfc);
390 
391  Display* display = floatbar->xfc->display;
392  WINPR_ASSERT(display);
393 
394  /* create the pixmap that we'll use for shaping the window */
395  pmap = XCreatePixmap(display, floatbar->handle, floatbar->width, floatbar->height, 1);
396  gc = XCreateGC(display, floatbar->handle, 0, 0);
397  shape_gc = XCreateGC(display, pmap, 0, 0);
398  /* points for drawing the floatbar */
399  shape[0].x = 0;
400  shape[0].y = 0;
401  shape[1].x = floatbar->width;
402  shape[1].y = 0;
403  shape[2].x = shape[1].x - FLOATBAR_BORDER;
404  shape[2].y = FLOATBAR_HEIGHT;
405  shape[3].x = shape[0].x + FLOATBAR_BORDER;
406  shape[3].y = FLOATBAR_HEIGHT;
407  shape[4].x = shape[0].x;
408  shape[4].y = shape[0].y;
409  /* points for drawing the border of the floatbar */
410  border[0].x = shape[0].x;
411  border[0].y = shape[0].y - 1;
412  border[1].x = shape[1].x - 1;
413  border[1].y = shape[1].y - 1;
414  border[2].x = shape[2].x;
415  border[2].y = shape[2].y - 1;
416  border[3].x = shape[3].x - 1;
417  border[3].y = shape[3].y - 1;
418  border[4].x = border[0].x;
419  border[4].y = border[0].y;
420  /* Fill all pixels with 0 */
421  XSetForeground(display, shape_gc, 0);
422  XFillRectangle(display, pmap, shape_gc, 0, 0, floatbar->width, floatbar->height);
423  /* Fill all pixels which should be shown with 1 */
424  XSetForeground(display, shape_gc, 1);
425  XFillPolygon(display, pmap, shape_gc, shape, 5, 0, CoordModeOrigin);
426  XShapeCombineMask(display, floatbar->handle, ShapeBounding, 0, 0, pmap, ShapeSet);
427  /* draw the float bar */
428  XSetForeground(display, gc, xf_floatbar_get_color(floatbar, FLOATBAR_COLOR_BACKGROUND));
429  XFillPolygon(display, floatbar->handle, gc, shape, 4, 0, CoordModeOrigin);
430  /* draw an border for the floatbar */
431  XSetForeground(display, gc, xf_floatbar_get_color(floatbar, FLOATBAR_COLOR_BORDER));
432  XDrawLines(display, floatbar->handle, gc, border, 5, CoordModeOrigin);
433  /* draw the host name connected to (limit to maximum file name) */
434  const size_t len = strnlen(floatbar->title, MAX_PATH);
435  XSetForeground(display, gc, xf_floatbar_get_color(floatbar, FLOATBAR_COLOR_FOREGROUND));
436 
437  WINPR_ASSERT(len <= INT32_MAX / 2);
438  const int fx = floatbar->width / 2 - (int)len * 2;
439  if (floatbar->fontSet != NULL)
440  {
441  XmbDrawString(display, floatbar->handle, floatbar->fontSet, gc, fx, 15, floatbar->title,
442  (int)len);
443  }
444  else
445  {
446  XDrawString(display, floatbar->handle, gc, fx, 15, floatbar->title, (int)len);
447  }
448  XFreeGC(display, gc);
449  XFreeGC(display, shape_gc);
450 }
451 
452 static xfFloatbarButton* xf_floatbar_get_button(xfFloatbar* floatbar, Window window)
453 {
454  WINPR_ASSERT(floatbar);
455  const size_t size = ARRAYSIZE(floatbar->buttons);
456 
457  for (size_t i = 0; i < size; i++)
458  {
459  xfFloatbarButton* button = floatbar->buttons[i];
460  if (button->handle == window)
461  {
462  return button;
463  }
464  }
465 
466  return NULL;
467 }
468 
469 static void xf_floatbar_button_update_positon(xfFloatbar* floatbar)
470 {
471  xfFloatbarButton* button = NULL;
472  WINPR_ASSERT(floatbar);
473  xfContext* xfc = floatbar->xfc;
474  const size_t size = ARRAYSIZE(floatbar->buttons);
475 
476  for (size_t i = 0; i < size; i++)
477  {
478  button = floatbar->buttons[i];
479 
480  switch (button->type)
481  {
482  case XF_FLOATBAR_BUTTON_CLOSE:
483  button->x =
484  floatbar->width - FLOATBAR_BORDER - FLOATBAR_BUTTON_WIDTH * button->type;
485  break;
486 
487  case XF_FLOATBAR_BUTTON_RESTORE:
488  button->x =
489  floatbar->width - FLOATBAR_BORDER - FLOATBAR_BUTTON_WIDTH * button->type;
490  break;
491 
492  case XF_FLOATBAR_BUTTON_MINIMIZE:
493  button->x =
494  floatbar->width - FLOATBAR_BORDER - FLOATBAR_BUTTON_WIDTH * button->type;
495  break;
496 
497  default:
498  break;
499  }
500 
501  WINPR_ASSERT(xfc);
502  WINPR_ASSERT(xfc->display);
503  XMoveWindow(xfc->display, button->handle, button->x, button->y);
504  xf_floatbar_event_expose(floatbar);
505  }
506 }
507 
508 static void xf_floatbar_button_event_expose(xfFloatbar* floatbar, Window window)
509 {
510  xfFloatbarButton* button = xf_floatbar_get_button(floatbar, window);
511  static unsigned char* bits;
512  GC gc = NULL;
513  Pixmap pattern = 0;
514  xfContext* xfc = floatbar->xfc;
515 
516  if (!button)
517  return;
518 
519  WINPR_ASSERT(xfc);
520  WINPR_ASSERT(xfc->display);
521  WINPR_ASSERT(xfc->window);
522 
523  gc = XCreateGC(xfc->display, button->handle, 0, 0);
524  floatbar = xfc->window->floatbar;
525  WINPR_ASSERT(floatbar);
526 
527  switch (button->type)
528  {
529  case XF_FLOATBAR_BUTTON_CLOSE:
530  bits = close_bits;
531  break;
532 
533  case XF_FLOATBAR_BUTTON_RESTORE:
534  bits = restore_bits;
535  break;
536 
537  case XF_FLOATBAR_BUTTON_MINIMIZE:
538  bits = minimize_bits;
539  break;
540 
541  case XF_FLOATBAR_BUTTON_LOCKED:
542  if (floatbar->locked)
543  bits = lock_bits;
544  else
545  bits = unlock_bits;
546 
547  break;
548 
549  default:
550  break;
551  }
552 
553  pattern = XCreateBitmapFromData(xfc->display, button->handle, (const char*)bits,
554  FLOATBAR_BUTTON_WIDTH, FLOATBAR_BUTTON_WIDTH);
555 
556  if (!(button->focus))
557  XSetForeground(xfc->display, gc,
558  xf_floatbar_get_color(floatbar, FLOATBAR_COLOR_BACKGROUND));
559  else
560  XSetForeground(xfc->display, gc, xf_floatbar_get_color(floatbar, FLOATBAR_COLOR_BORDER));
561 
562  XSetBackground(xfc->display, gc, xf_floatbar_get_color(floatbar, FLOATBAR_COLOR_FOREGROUND));
563  XCopyPlane(xfc->display, pattern, button->handle, gc, 0, 0, FLOATBAR_BUTTON_WIDTH,
564  FLOATBAR_BUTTON_WIDTH, 0, 0, 1);
565  XFreePixmap(xfc->display, pattern);
566  XFreeGC(xfc->display, gc);
567 }
568 
569 static void xf_floatbar_button_event_buttonpress(xfFloatbar* floatbar, const XButtonEvent* event)
570 {
571  WINPR_ASSERT(event);
572  xfFloatbarButton* button = xf_floatbar_get_button(floatbar, event->window);
573 
574  if (button)
575  button->clicked = TRUE;
576 }
577 
578 static void xf_floatbar_button_event_buttonrelease(xfFloatbar* floatbar, const XButtonEvent* event)
579 {
580  xfFloatbarButton* button = NULL;
581 
582  WINPR_ASSERT(floatbar);
583  WINPR_ASSERT(event);
584 
585  button = xf_floatbar_get_button(floatbar, event->window);
586 
587  if (button)
588  {
589  if (button->clicked)
590  button->onclick(floatbar);
591  button->clicked = FALSE;
592  }
593 }
594 
595 static void xf_floatbar_event_buttonpress(xfFloatbar* floatbar, const XButtonEvent* event)
596 {
597  WINPR_ASSERT(floatbar);
598  WINPR_ASSERT(event);
599 
600  switch (event->button)
601  {
602  case Button1:
603  if (event->x <= FLOATBAR_BORDER)
604  floatbar->mode = XF_FLOATBAR_MODE_RESIZE_LEFT;
605  else if (event->x >= (floatbar->width - FLOATBAR_BORDER))
606  floatbar->mode = XF_FLOATBAR_MODE_RESIZE_RIGHT;
607  else
608  floatbar->mode = XF_FLOATBAR_MODE_DRAGGING;
609 
610  break;
611 
612  default:
613  break;
614  }
615 }
616 
617 static void xf_floatbar_event_buttonrelease(xfFloatbar* floatbar, const XButtonEvent* event)
618 {
619  WINPR_ASSERT(floatbar);
620  WINPR_ASSERT(event);
621 
622  switch (event->button)
623  {
624  case Button1:
625  floatbar->mode = XF_FLOATBAR_MODE_NONE;
626  break;
627 
628  default:
629  break;
630  }
631 }
632 
633 static void xf_floatbar_resize(xfFloatbar* floatbar, const XMotionEvent* event)
634 {
635  int x = 0;
636  int width = 0;
637  int movement = 0;
638 
639  WINPR_ASSERT(floatbar);
640  WINPR_ASSERT(event);
641 
642  xfContext* xfc = floatbar->xfc;
643  WINPR_ASSERT(xfc);
644  WINPR_ASSERT(xfc->display);
645 
646  /* calculate movement which happened on the root window */
647  movement = event->x_root - floatbar->last_motion_x_root;
648 
649  /* set x and width depending if movement happens on the left or right */
650  if (floatbar->mode == XF_FLOATBAR_MODE_RESIZE_LEFT)
651  {
652  x = floatbar->x + movement;
653  width = floatbar->width + movement * -1;
654  }
655  else
656  {
657  x = floatbar->x;
658  width = floatbar->width + movement;
659  }
660 
661  /* only resize and move window if still above minimum width */
662  if (FLOATBAR_MIN_WIDTH < width)
663  {
664  XMoveResizeWindow(xfc->display, floatbar->handle, x, 0, width, floatbar->height);
665  floatbar->x = x;
666  floatbar->width = width;
667  }
668 }
669 
670 static void xf_floatbar_dragging(xfFloatbar* floatbar, const XMotionEvent* event)
671 {
672  int x = 0;
673  int movement = 0;
674 
675  WINPR_ASSERT(floatbar);
676  WINPR_ASSERT(event);
677  xfContext* xfc = floatbar->xfc;
678  WINPR_ASSERT(xfc);
679  WINPR_ASSERT(xfc->window);
680  WINPR_ASSERT(xfc->display);
681 
682  /* calculate movement and new x position */
683  movement = event->x_root - floatbar->last_motion_x_root;
684  x = floatbar->x + movement;
685 
686  /* do nothing if floatbar would be moved out of the window */
687  if (x < 0 || (x + floatbar->width) > xfc->window->width)
688  return;
689 
690  /* move window to new x position */
691  XMoveWindow(xfc->display, floatbar->handle, x, 0);
692  /* update struct values for the next event */
693  floatbar->last_motion_x_root = floatbar->last_motion_x_root + movement;
694  floatbar->x = x;
695 }
696 
697 static void xf_floatbar_event_motionnotify(xfFloatbar* floatbar, const XMotionEvent* event)
698 {
699  int mode = 0;
700  Cursor cursor = 0;
701 
702  WINPR_ASSERT(floatbar);
703  WINPR_ASSERT(event);
704 
705  xfContext* xfc = floatbar->xfc;
706  WINPR_ASSERT(xfc);
707  WINPR_ASSERT(xfc->display);
708 
709  mode = floatbar->mode;
710  cursor = XCreateFontCursor(xfc->display, XC_arrow);
711 
712  if ((event->state & Button1Mask) && (mode > XF_FLOATBAR_MODE_DRAGGING))
713  {
714  xf_floatbar_resize(floatbar, event);
715  }
716  else if ((event->state & Button1Mask) && (mode == XF_FLOATBAR_MODE_DRAGGING))
717  {
718  xf_floatbar_dragging(floatbar, event);
719  }
720  else
721  {
722  if (event->x <= FLOATBAR_BORDER || event->x >= floatbar->width - FLOATBAR_BORDER)
723  cursor = XCreateFontCursor(xfc->display, XC_sb_h_double_arrow);
724  }
725 
726  XDefineCursor(xfc->display, xfc->window->handle, cursor);
727  XFreeCursor(xfc->display, cursor);
728  floatbar->last_motion_x_root = event->x_root;
729 }
730 
731 static void xf_floatbar_button_event_focusin(xfFloatbar* floatbar, const XAnyEvent* event)
732 {
733  xfFloatbarButton* button = NULL;
734 
735  WINPR_ASSERT(floatbar);
736  WINPR_ASSERT(event);
737 
738  button = xf_floatbar_get_button(floatbar, event->window);
739 
740  if (button)
741  {
742  button->focus = TRUE;
743  xf_floatbar_button_event_expose(floatbar, event->window);
744  }
745 }
746 
747 static void xf_floatbar_button_event_focusout(xfFloatbar* floatbar, const XAnyEvent* event)
748 {
749  xfFloatbarButton* button = NULL;
750 
751  WINPR_ASSERT(floatbar);
752  WINPR_ASSERT(event);
753 
754  button = xf_floatbar_get_button(floatbar, event->window);
755 
756  if (button)
757  {
758  button->focus = FALSE;
759  xf_floatbar_button_event_expose(floatbar, event->window);
760  }
761 }
762 
763 static void xf_floatbar_event_focusout(xfFloatbar* floatbar)
764 {
765  WINPR_ASSERT(floatbar);
766  xfContext* xfc = floatbar->xfc;
767  WINPR_ASSERT(xfc);
768 
769  if (xfc->pointer)
770  {
771  WINPR_ASSERT(xfc->window);
772  WINPR_ASSERT(xfc->pointer);
773  XDefineCursor(xfc->display, xfc->window->handle, xfc->pointer->cursor);
774  }
775 }
776 
777 BOOL xf_floatbar_check_event(xfFloatbar* floatbar, const XEvent* event)
778 {
779  if (!floatbar || !floatbar->xfc || !event)
780  return FALSE;
781 
782  if (!floatbar->created)
783  return FALSE;
784 
785  if (event->xany.window == floatbar->handle)
786  return TRUE;
787 
788  size_t size = ARRAYSIZE(floatbar->buttons);
789 
790  for (size_t i = 0; i < size; i++)
791  {
792  const xfFloatbarButton* button = floatbar->buttons[i];
793 
794  if (event->xany.window == button->handle)
795  return TRUE;
796  }
797 
798  return FALSE;
799 }
800 
801 BOOL xf_floatbar_event_process(xfFloatbar* floatbar, const XEvent* event)
802 {
803  if (!floatbar || !floatbar->xfc || !event)
804  return FALSE;
805 
806  if (!floatbar->created)
807  return FALSE;
808 
809  switch (event->type)
810  {
811  case Expose:
812  if (event->xexpose.window == floatbar->handle)
813  xf_floatbar_event_expose(floatbar);
814  else
815  xf_floatbar_button_event_expose(floatbar, event->xexpose.window);
816 
817  break;
818 
819  case MotionNotify:
820  xf_floatbar_event_motionnotify(floatbar, &event->xmotion);
821  break;
822 
823  case ButtonPress:
824  if (event->xany.window == floatbar->handle)
825  xf_floatbar_event_buttonpress(floatbar, &event->xbutton);
826  else
827  xf_floatbar_button_event_buttonpress(floatbar, &event->xbutton);
828 
829  break;
830 
831  case ButtonRelease:
832  if (event->xany.window == floatbar->handle)
833  xf_floatbar_event_buttonrelease(floatbar, &event->xbutton);
834  else
835  xf_floatbar_button_event_buttonrelease(floatbar, &event->xbutton);
836 
837  break;
838 
839  case EnterNotify:
840  case FocusIn:
841  if (event->xany.window != floatbar->handle)
842  xf_floatbar_button_event_focusin(floatbar, &event->xany);
843 
844  break;
845 
846  case LeaveNotify:
847  case FocusOut:
848  if (event->xany.window == floatbar->handle)
849  xf_floatbar_event_focusout(floatbar);
850  else
851  xf_floatbar_button_event_focusout(floatbar, &event->xany);
852 
853  break;
854 
855  case ConfigureNotify:
856  if (event->xany.window == floatbar->handle)
857  xf_floatbar_button_update_positon(floatbar);
858 
859  break;
860 
861  case PropertyNotify:
862  if (event->xany.window == floatbar->handle)
863  xf_floatbar_button_update_positon(floatbar);
864 
865  break;
866 
867  default:
868  break;
869  }
870 
871  return floatbar->handle == event->xany.window;
872 }
873 
874 static void xf_floatbar_button_free(xfContext* xfc, xfFloatbarButton* button)
875 {
876  if (!button)
877  return;
878 
879  if (button->handle)
880  {
881  WINPR_ASSERT(xfc);
882  WINPR_ASSERT(xfc->display);
883  XUnmapWindow(xfc->display, button->handle);
884  XDestroyWindow(xfc->display, button->handle);
885  }
886 
887  free(button);
888 }
889 
890 void xf_floatbar_free(xfFloatbar* floatbar)
891 {
892  size_t size = 0;
893  xfContext* xfc = NULL;
894 
895  if (!floatbar)
896  return;
897 
898  free(floatbar->title);
899  xfc = floatbar->xfc;
900  WINPR_ASSERT(xfc);
901 
902  size = ARRAYSIZE(floatbar->buttons);
903 
904  for (size_t i = 0; i < size; i++)
905  {
906  xf_floatbar_button_free(xfc, floatbar->buttons[i]);
907  floatbar->buttons[i] = NULL;
908  }
909 
910  if (floatbar->handle)
911  {
912  WINPR_ASSERT(xfc->display);
913  XUnmapWindow(xfc->display, floatbar->handle);
914  XDestroyWindow(xfc->display, floatbar->handle);
915  }
916 
917  free(floatbar);
918 }
919 
920 BOOL xf_floatbar_is_locked(xfFloatbar* floatbar)
921 {
922  if (!floatbar)
923  return FALSE;
924  return floatbar->mode != XF_FLOATBAR_MODE_NONE;
925 }