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