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