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