19 #include <X11/Xutil.h>
20 #include <X11/Xatom.h>
21 #include <X11/extensions/shape.h>
22 #include <X11/cursorfont.h>
24 #include <winpr/assert.h>
26 #include "xf_floatbar.h"
27 #include "resource/close.xbm"
28 #include "resource/lock.xbm"
29 #include "resource/unlock.xbm"
30 #include "resource/minimize.xbm"
31 #include "resource/restore.xbm"
33 #include <freerdp/log.h>
34 #define TAG CLIENT_TAG("x11")
36 #define FLOATBAR_HEIGHT 26
37 #define FLOATBAR_DEFAULT_WIDTH 576
38 #define FLOATBAR_MIN_WIDTH 200
39 #define FLOATBAR_BORDER 24
40 #define FLOATBAR_BUTTON_WIDTH 24
41 #define FLOATBAR_COLOR_BACKGROUND "RGB:31/6c/a9"
42 #define FLOATBAR_COLOR_BORDER "RGB:75/9a/c8"
43 #define FLOATBAR_COLOR_FOREGROUND "RGB:FF/FF/FF"
45 #define XF_FLOATBAR_MODE_NONE 0
46 #define XF_FLOATBAR_MODE_DRAGGING 1
47 #define XF_FLOATBAR_MODE_RESIZE_LEFT 2
48 #define XF_FLOATBAR_MODE_RESIZE_RIGHT 3
50 #define XF_FLOATBAR_BUTTON_CLOSE 1
51 #define XF_FLOATBAR_BUTTON_RESTORE 2
52 #define XF_FLOATBAR_BUTTON_MINIMIZE 3
53 #define XF_FLOATBAR_BUTTON_LOCKED 4
55 typedef BOOL (*OnClick)(xfFloatbar*);
75 int last_motion_x_root;
76 int last_motion_y_root;
78 xfFloatbarButton* buttons[4];
89 static xfFloatbarButton* xf_floatbar_new_button(xfFloatbar* floatbar,
int type);
91 static BOOL xf_floatbar_button_onclick_close(xfFloatbar* floatbar)
96 return freerdp_abort_connect_context(&floatbar->xfc->common.context);
99 static BOOL xf_floatbar_button_onclick_minimize(xfFloatbar* floatbar)
101 xfContext* xfc = NULL;
103 if (!floatbar || !floatbar->xfc)
107 xf_SetWindowMinimized(xfc, xfc->window);
111 static BOOL xf_floatbar_button_onclick_restore(xfFloatbar* floatbar)
116 xf_toggle_fullscreen(floatbar->xfc);
120 static BOOL xf_floatbar_button_onclick_locked(xfFloatbar* floatbar)
125 floatbar->locked = (floatbar->locked) ? FALSE : TRUE;
126 return xf_floatbar_hide_and_show(floatbar);
129 BOOL xf_floatbar_set_root_y(xfFloatbar* floatbar,
int y)
134 floatbar->last_motion_y_root = y;
138 BOOL xf_floatbar_hide_and_show(xfFloatbar* floatbar)
140 xfContext* xfc = NULL;
142 if (!floatbar || !floatbar->xfc)
145 if (!floatbar->created)
150 WINPR_ASSERT(xfc->display);
152 if (!floatbar->locked)
154 if ((floatbar->mode == XF_FLOATBAR_MODE_NONE) && (floatbar->last_motion_y_root > 10) &&
155 (floatbar->y > (FLOATBAR_HEIGHT * -1)))
157 floatbar->y = floatbar->y - 1;
158 XMoveWindow(xfc->display, floatbar->handle, floatbar->x, floatbar->y);
160 else if (floatbar->y < 0 && (floatbar->last_motion_y_root < 10))
162 floatbar->y = floatbar->y + 1;
163 XMoveWindow(xfc->display, floatbar->handle, floatbar->x, floatbar->y);
170 static BOOL create_floatbar(xfFloatbar* floatbar)
172 xfContext* xfc = NULL;
174 XWindowAttributes attr = { 0 };
176 WINPR_ASSERT(floatbar);
177 if (floatbar->created)
182 WINPR_ASSERT(xfc->display);
184 status = XGetWindowAttributes(xfc->display, floatbar->root_window, &attr);
187 WLog_WARN(TAG,
"XGetWindowAttributes failed");
190 floatbar->x = attr.x + attr.width / 2 - FLOATBAR_DEFAULT_WIDTH / 2;
193 if (((floatbar->flags & 0x0004) == 0) && !floatbar->locked)
194 floatbar->y = -FLOATBAR_HEIGHT + 1;
197 XCreateWindow(xfc->display, floatbar->root_window, floatbar->x, 0, FLOATBAR_DEFAULT_WIDTH,
198 FLOATBAR_HEIGHT, 0, CopyFromParent, InputOutput, CopyFromParent, 0, NULL);
199 floatbar->width = FLOATBAR_DEFAULT_WIDTH;
200 floatbar->height = FLOATBAR_HEIGHT;
201 floatbar->mode = XF_FLOATBAR_MODE_NONE;
202 floatbar->buttons[0] = xf_floatbar_new_button(floatbar, XF_FLOATBAR_BUTTON_CLOSE);
203 floatbar->buttons[1] = xf_floatbar_new_button(floatbar, XF_FLOATBAR_BUTTON_RESTORE);
204 floatbar->buttons[2] = xf_floatbar_new_button(floatbar, XF_FLOATBAR_BUTTON_MINIMIZE);
205 floatbar->buttons[3] = xf_floatbar_new_button(floatbar, XF_FLOATBAR_BUTTON_LOCKED);
206 XSelectInput(xfc->display, floatbar->handle,
207 ExposureMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask |
208 FocusChangeMask | LeaveWindowMask | EnterWindowMask | StructureNotifyMask |
210 floatbar->created = TRUE;
214 BOOL xf_floatbar_toggle_fullscreen(xfFloatbar* floatbar,
bool fullscreen)
217 bool visible = False;
218 xfContext* xfc = NULL;
220 if (!floatbar || !floatbar->xfc)
224 WINPR_ASSERT(xfc->display);
227 if (floatbar->flags & 0x0001)
230 visible |= ((floatbar->flags & 0x0010) != 0) && fullscreen;
232 visible |= ((floatbar->flags & 0x0020) != 0) && !fullscreen;
237 if (!create_floatbar(floatbar))
240 XMapWindow(xfc->display, floatbar->handle);
241 size = ARRAYSIZE(floatbar->buttons);
243 for (
int i = 0; i < size; i++)
245 xfFloatbarButton* button = floatbar->buttons[i];
246 XMapWindow(xfc->display, button->handle);
250 if (((floatbar->flags & 0x0004) == 0) && !floatbar->locked)
251 floatbar->y = -FLOATBAR_HEIGHT + 1;
253 xf_floatbar_hide_and_show(floatbar);
255 else if (floatbar->created)
257 XUnmapSubwindows(xfc->display, floatbar->handle);
258 XUnmapWindow(xfc->display, floatbar->handle);
264 xfFloatbarButton* xf_floatbar_new_button(xfFloatbar* floatbar,
int type)
266 xfFloatbarButton* button = NULL;
268 WINPR_ASSERT(floatbar);
269 WINPR_ASSERT(floatbar->xfc);
270 WINPR_ASSERT(floatbar->xfc->display);
271 WINPR_ASSERT(floatbar->handle);
273 button = (xfFloatbarButton*)calloc(1,
sizeof(xfFloatbarButton));
278 case XF_FLOATBAR_BUTTON_CLOSE:
279 button->x = floatbar->width - FLOATBAR_BORDER - FLOATBAR_BUTTON_WIDTH * type;
280 button->onclick = xf_floatbar_button_onclick_close;
283 case XF_FLOATBAR_BUTTON_RESTORE:
284 button->x = floatbar->width - FLOATBAR_BORDER - FLOATBAR_BUTTON_WIDTH * type;
285 button->onclick = xf_floatbar_button_onclick_restore;
288 case XF_FLOATBAR_BUTTON_MINIMIZE:
289 button->x = floatbar->width - FLOATBAR_BORDER - FLOATBAR_BUTTON_WIDTH * type;
290 button->onclick = xf_floatbar_button_onclick_minimize;
293 case XF_FLOATBAR_BUTTON_LOCKED:
294 button->x = FLOATBAR_BORDER;
295 button->onclick = xf_floatbar_button_onclick_locked;
303 button->focus = FALSE;
304 button->handle = XCreateWindow(floatbar->xfc->display, floatbar->handle, button->x, 0,
305 FLOATBAR_BUTTON_WIDTH, FLOATBAR_BUTTON_WIDTH, 0, CopyFromParent,
306 InputOutput, CopyFromParent, 0, NULL);
307 XSelectInput(floatbar->xfc->display, button->handle,
308 ExposureMask | ButtonPressMask | ButtonReleaseMask | FocusChangeMask |
309 LeaveWindowMask | EnterWindowMask | StructureNotifyMask);
313 xfFloatbar* xf_floatbar_new(xfContext* xfc, Window window,
const char* name, DWORD flags)
316 WINPR_ASSERT(xfc->display);
320 if ((flags & 0x0001) == 0)
330 xfFloatbar* floatbar = (xfFloatbar*)calloc(1,
sizeof(xfFloatbar));
335 floatbar->title = _strdup(name);
337 if (!floatbar->title)
340 floatbar->root_window = window;
341 floatbar->flags = flags;
343 floatbar->locked = flags & 0x0002;
344 xf_floatbar_toggle_fullscreen(floatbar, FALSE);
345 char** missingList = NULL;
346 int missingCount = 0;
347 char* defString = NULL;
348 floatbar->fontSet = XCreateFontSet(floatbar->xfc->display,
"-*-*-*-*-*-*-*-*-*-*-*-*-*-*",
349 &missingList, &missingCount, &defString);
350 if (floatbar->fontSet == NULL)
352 WLog_ERR(TAG,
"Failed to create fontset");
354 XFreeStringList(missingList);
357 WINPR_PRAGMA_DIAG_PUSH
358 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
359 xf_floatbar_free(floatbar);
360 WINPR_PRAGMA_DIAG_POP
364 static unsigned long xf_floatbar_get_color(xfFloatbar* floatbar,
char* rgb_value)
368 WINPR_ASSERT(floatbar);
369 WINPR_ASSERT(floatbar->xfc);
371 Display* display = floatbar->xfc->display;
372 WINPR_ASSERT(display);
374 Colormap cmap = DefaultColormap(display, XDefaultScreen(display));
375 XParseColor(display, cmap, rgb_value, &color);
376 XAllocColor(display, cmap, &color);
380 static void xf_floatbar_event_expose(xfFloatbar* floatbar)
385 XPoint shape[5] = { 0 };
386 XPoint border[5] = { 0 };
388 WINPR_ASSERT(floatbar);
389 WINPR_ASSERT(floatbar->xfc);
391 Display* display = floatbar->xfc->display;
392 WINPR_ASSERT(display);
395 pmap = XCreatePixmap(display, floatbar->handle, floatbar->width, floatbar->height, 1);
396 gc = XCreateGC(display, floatbar->handle, 0, 0);
397 shape_gc = XCreateGC(display, pmap, 0, 0);
401 shape[1].x = floatbar->width;
403 shape[2].x = shape[1].x - FLOATBAR_BORDER;
404 shape[2].y = FLOATBAR_HEIGHT;
405 shape[3].x = shape[0].x + FLOATBAR_BORDER;
406 shape[3].y = FLOATBAR_HEIGHT;
407 shape[4].x = shape[0].x;
408 shape[4].y = shape[0].y;
410 border[0].x = shape[0].x;
411 border[0].y = shape[0].y - 1;
412 border[1].x = shape[1].x - 1;
413 border[1].y = shape[1].y - 1;
414 border[2].x = shape[2].x;
415 border[2].y = shape[2].y - 1;
416 border[3].x = shape[3].x - 1;
417 border[3].y = shape[3].y - 1;
418 border[4].x = border[0].x;
419 border[4].y = border[0].y;
421 XSetForeground(display, shape_gc, 0);
422 XFillRectangle(display, pmap, shape_gc, 0, 0, floatbar->width, floatbar->height);
424 XSetForeground(display, shape_gc, 1);
425 XFillPolygon(display, pmap, shape_gc, shape, 5, 0, CoordModeOrigin);
426 XShapeCombineMask(display, floatbar->handle, ShapeBounding, 0, 0, pmap, ShapeSet);
428 XSetForeground(display, gc, xf_floatbar_get_color(floatbar, FLOATBAR_COLOR_BACKGROUND));
429 XFillPolygon(display, floatbar->handle, gc, shape, 4, 0, CoordModeOrigin);
431 XSetForeground(display, gc, xf_floatbar_get_color(floatbar, FLOATBAR_COLOR_BORDER));
432 XDrawLines(display, floatbar->handle, gc, border, 5, CoordModeOrigin);
434 const size_t len = strnlen(floatbar->title, MAX_PATH);
435 XSetForeground(display, gc, xf_floatbar_get_color(floatbar, FLOATBAR_COLOR_FOREGROUND));
437 WINPR_ASSERT(len <= INT32_MAX / 2);
438 const int fx = floatbar->width / 2 - (int)len * 2;
439 if (floatbar->fontSet != NULL)
441 XmbDrawString(display, floatbar->handle, floatbar->fontSet, gc, fx, 15, floatbar->title,
446 XDrawString(display, floatbar->handle, gc, fx, 15, floatbar->title, (
int)len);
448 XFreeGC(display, gc);
449 XFreeGC(display, shape_gc);
452 static xfFloatbarButton* xf_floatbar_get_button(xfFloatbar* floatbar, Window window)
454 WINPR_ASSERT(floatbar);
455 const size_t size = ARRAYSIZE(floatbar->buttons);
457 for (
size_t i = 0; i < size; i++)
459 xfFloatbarButton* button = floatbar->buttons[i];
460 if (button->handle == window)
469 static void xf_floatbar_button_update_positon(xfFloatbar* floatbar)
471 xfFloatbarButton* button = NULL;
472 WINPR_ASSERT(floatbar);
473 xfContext* xfc = floatbar->xfc;
474 const size_t size = ARRAYSIZE(floatbar->buttons);
476 for (
size_t i = 0; i < size; i++)
478 button = floatbar->buttons[i];
480 switch (button->type)
482 case XF_FLOATBAR_BUTTON_CLOSE:
484 floatbar->width - FLOATBAR_BORDER - FLOATBAR_BUTTON_WIDTH * button->type;
487 case XF_FLOATBAR_BUTTON_RESTORE:
489 floatbar->width - FLOATBAR_BORDER - FLOATBAR_BUTTON_WIDTH * button->type;
492 case XF_FLOATBAR_BUTTON_MINIMIZE:
494 floatbar->width - FLOATBAR_BORDER - FLOATBAR_BUTTON_WIDTH * button->type;
502 WINPR_ASSERT(xfc->display);
503 XMoveWindow(xfc->display, button->handle, button->x, button->y);
504 xf_floatbar_event_expose(floatbar);
508 static void xf_floatbar_button_event_expose(xfFloatbar* floatbar, Window window)
510 xfFloatbarButton* button = xf_floatbar_get_button(floatbar, window);
511 static unsigned char* bits;
514 xfContext* xfc = floatbar->xfc;
520 WINPR_ASSERT(xfc->display);
521 WINPR_ASSERT(xfc->window);
523 gc = XCreateGC(xfc->display, button->handle, 0, 0);
524 floatbar = xfc->window->floatbar;
525 WINPR_ASSERT(floatbar);
527 switch (button->type)
529 case XF_FLOATBAR_BUTTON_CLOSE:
533 case XF_FLOATBAR_BUTTON_RESTORE:
537 case XF_FLOATBAR_BUTTON_MINIMIZE:
538 bits = minimize_bits;
541 case XF_FLOATBAR_BUTTON_LOCKED:
542 if (floatbar->locked)
553 pattern = XCreateBitmapFromData(xfc->display, button->handle, (
const char*)bits,
554 FLOATBAR_BUTTON_WIDTH, FLOATBAR_BUTTON_WIDTH);
556 if (!(button->focus))
557 XSetForeground(xfc->display, gc,
558 xf_floatbar_get_color(floatbar, FLOATBAR_COLOR_BACKGROUND));
560 XSetForeground(xfc->display, gc, xf_floatbar_get_color(floatbar, FLOATBAR_COLOR_BORDER));
562 XSetBackground(xfc->display, gc, xf_floatbar_get_color(floatbar, FLOATBAR_COLOR_FOREGROUND));
563 XCopyPlane(xfc->display, pattern, button->handle, gc, 0, 0, FLOATBAR_BUTTON_WIDTH,
564 FLOATBAR_BUTTON_WIDTH, 0, 0, 1);
565 XFreePixmap(xfc->display, pattern);
566 XFreeGC(xfc->display, gc);
569 static void xf_floatbar_button_event_buttonpress(xfFloatbar* floatbar,
const XButtonEvent* event)
572 xfFloatbarButton* button = xf_floatbar_get_button(floatbar, event->window);
575 button->clicked = TRUE;
578 static void xf_floatbar_button_event_buttonrelease(xfFloatbar* floatbar,
const XButtonEvent* event)
580 xfFloatbarButton* button = NULL;
582 WINPR_ASSERT(floatbar);
585 button = xf_floatbar_get_button(floatbar, event->window);
590 button->onclick(floatbar);
591 button->clicked = FALSE;
595 static void xf_floatbar_event_buttonpress(xfFloatbar* floatbar,
const XButtonEvent* event)
597 WINPR_ASSERT(floatbar);
600 switch (event->button)
603 if (event->x <= FLOATBAR_BORDER)
604 floatbar->mode = XF_FLOATBAR_MODE_RESIZE_LEFT;
605 else if (event->x >= (floatbar->width - FLOATBAR_BORDER))
606 floatbar->mode = XF_FLOATBAR_MODE_RESIZE_RIGHT;
608 floatbar->mode = XF_FLOATBAR_MODE_DRAGGING;
617 static void xf_floatbar_event_buttonrelease(xfFloatbar* floatbar,
const XButtonEvent* event)
619 WINPR_ASSERT(floatbar);
622 switch (event->button)
625 floatbar->mode = XF_FLOATBAR_MODE_NONE;
633 static void xf_floatbar_resize(xfFloatbar* floatbar,
const XMotionEvent* event)
639 WINPR_ASSERT(floatbar);
642 xfContext* xfc = floatbar->xfc;
644 WINPR_ASSERT(xfc->display);
647 movement =
event->x_root - floatbar->last_motion_x_root;
650 if (floatbar->mode == XF_FLOATBAR_MODE_RESIZE_LEFT)
652 x = floatbar->x + movement;
653 width = floatbar->width + movement * -1;
658 width = floatbar->width + movement;
662 if (FLOATBAR_MIN_WIDTH < width)
664 XMoveResizeWindow(xfc->display, floatbar->handle, x, 0, width, floatbar->height);
666 floatbar->width = width;
670 static void xf_floatbar_dragging(xfFloatbar* floatbar,
const XMotionEvent* event)
675 WINPR_ASSERT(floatbar);
677 xfContext* xfc = floatbar->xfc;
679 WINPR_ASSERT(xfc->window);
680 WINPR_ASSERT(xfc->display);
683 movement =
event->x_root - floatbar->last_motion_x_root;
684 x = floatbar->x + movement;
687 if (x < 0 || (x + floatbar->width) > xfc->window->width)
691 XMoveWindow(xfc->display, floatbar->handle, x, 0);
693 floatbar->last_motion_x_root = floatbar->last_motion_x_root + movement;
697 static void xf_floatbar_event_motionnotify(xfFloatbar* floatbar,
const XMotionEvent* event)
702 WINPR_ASSERT(floatbar);
705 xfContext* xfc = floatbar->xfc;
707 WINPR_ASSERT(xfc->display);
709 mode = floatbar->mode;
710 cursor = XCreateFontCursor(xfc->display, XC_arrow);
712 if ((event->state & Button1Mask) && (mode > XF_FLOATBAR_MODE_DRAGGING))
714 xf_floatbar_resize(floatbar, event);
716 else if ((event->state & Button1Mask) && (mode == XF_FLOATBAR_MODE_DRAGGING))
718 xf_floatbar_dragging(floatbar, event);
722 if (event->x <= FLOATBAR_BORDER || event->x >= floatbar->width - FLOATBAR_BORDER)
723 cursor = XCreateFontCursor(xfc->display, XC_sb_h_double_arrow);
726 XDefineCursor(xfc->display, xfc->window->handle, cursor);
727 XFreeCursor(xfc->display, cursor);
728 floatbar->last_motion_x_root =
event->x_root;
731 static void xf_floatbar_button_event_focusin(xfFloatbar* floatbar,
const XAnyEvent* event)
733 xfFloatbarButton* button = NULL;
735 WINPR_ASSERT(floatbar);
738 button = xf_floatbar_get_button(floatbar, event->window);
742 button->focus = TRUE;
743 xf_floatbar_button_event_expose(floatbar, event->window);
747 static void xf_floatbar_button_event_focusout(xfFloatbar* floatbar,
const XAnyEvent* event)
749 xfFloatbarButton* button = NULL;
751 WINPR_ASSERT(floatbar);
754 button = xf_floatbar_get_button(floatbar, event->window);
758 button->focus = FALSE;
759 xf_floatbar_button_event_expose(floatbar, event->window);
763 static void xf_floatbar_event_focusout(xfFloatbar* floatbar)
765 WINPR_ASSERT(floatbar);
766 xfContext* xfc = floatbar->xfc;
771 WINPR_ASSERT(xfc->window);
772 WINPR_ASSERT(xfc->pointer);
773 XDefineCursor(xfc->display, xfc->window->handle, xfc->pointer->cursor);
777 BOOL xf_floatbar_check_event(xfFloatbar* floatbar,
const XEvent* event)
779 if (!floatbar || !floatbar->xfc || !event)
782 if (!floatbar->created)
785 if (event->xany.window == floatbar->handle)
788 size_t size = ARRAYSIZE(floatbar->buttons);
790 for (
size_t i = 0; i < size; i++)
792 const xfFloatbarButton* button = floatbar->buttons[i];
794 if (event->xany.window == button->handle)
801 BOOL xf_floatbar_event_process(xfFloatbar* floatbar,
const XEvent* event)
803 if (!floatbar || !floatbar->xfc || !event)
806 if (!floatbar->created)
812 if (event->xexpose.window == floatbar->handle)
813 xf_floatbar_event_expose(floatbar);
815 xf_floatbar_button_event_expose(floatbar, event->xexpose.window);
820 xf_floatbar_event_motionnotify(floatbar, &event->xmotion);
824 if (event->xany.window == floatbar->handle)
825 xf_floatbar_event_buttonpress(floatbar, &event->xbutton);
827 xf_floatbar_button_event_buttonpress(floatbar, &event->xbutton);
832 if (event->xany.window == floatbar->handle)
833 xf_floatbar_event_buttonrelease(floatbar, &event->xbutton);
835 xf_floatbar_button_event_buttonrelease(floatbar, &event->xbutton);
841 if (event->xany.window != floatbar->handle)
842 xf_floatbar_button_event_focusin(floatbar, &event->xany);
848 if (event->xany.window == floatbar->handle)
849 xf_floatbar_event_focusout(floatbar);
851 xf_floatbar_button_event_focusout(floatbar, &event->xany);
855 case ConfigureNotify:
856 if (event->xany.window == floatbar->handle)
857 xf_floatbar_button_update_positon(floatbar);
862 if (event->xany.window == floatbar->handle)
863 xf_floatbar_button_update_positon(floatbar);
871 return floatbar->handle ==
event->xany.window;
874 static void xf_floatbar_button_free(xfContext* xfc, xfFloatbarButton* button)
882 WINPR_ASSERT(xfc->display);
883 XUnmapWindow(xfc->display, button->handle);
884 XDestroyWindow(xfc->display, button->handle);
890 void xf_floatbar_free(xfFloatbar* floatbar)
893 xfContext* xfc = NULL;
898 free(floatbar->title);
902 size = ARRAYSIZE(floatbar->buttons);
904 for (
size_t i = 0; i < size; i++)
906 xf_floatbar_button_free(xfc, floatbar->buttons[i]);
907 floatbar->buttons[i] = NULL;
910 if (floatbar->handle)
912 WINPR_ASSERT(xfc->display);
913 XUnmapWindow(xfc->display, floatbar->handle);
914 XDestroyWindow(xfc->display, floatbar->handle);
920 BOOL xf_floatbar_is_locked(xfFloatbar* floatbar)
924 return floatbar->mode != XF_FLOATBAR_MODE_NONE;