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