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