/** * FreeRDP: A Remote Desktop Protocol Implementation * X11 Windows * * Licensed under the Apache License, Version 2.0 (the "License");n * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include "xf_floatbar.h" #include "xf_utils.h" #include "resource/close.xbm" #include "resource/lock.xbm" #include "resource/unlock.xbm" #include "resource/minimize.xbm" #include "resource/restore.xbm" #include #define TAG CLIENT_TAG("x11") #define FLOATBAR_HEIGHT 26 #define FLOATBAR_DEFAULT_WIDTH 576 #define FLOATBAR_MIN_WIDTH 200 #define FLOATBAR_BORDER 24 #define FLOATBAR_BUTTON_WIDTH 24 #define FLOATBAR_COLOR_BACKGROUND "RGB:31/6c/a9" #define FLOATBAR_COLOR_BORDER "RGB:75/9a/c8" #define FLOATBAR_COLOR_FOREGROUND "RGB:FF/FF/FF" #define XF_FLOATBAR_MODE_NONE 0 #define XF_FLOATBAR_MODE_DRAGGING 1 #define XF_FLOATBAR_MODE_RESIZE_LEFT 2 #define XF_FLOATBAR_MODE_RESIZE_RIGHT 3 #define XF_FLOATBAR_BUTTON_CLOSE 1 #define XF_FLOATBAR_BUTTON_RESTORE 2 #define XF_FLOATBAR_BUTTON_MINIMIZE 3 #define XF_FLOATBAR_BUTTON_LOCKED 4 typedef BOOL (*OnClick)(xfFloatbar*); typedef struct { int x; int y; int type; bool focus; bool clicked; OnClick onclick; Window handle; } xfFloatbarButton; struct xf_floatbar { int x; int y; int width; int height; int mode; int last_motion_x_root; int last_motion_y_root; BOOL locked; xfFloatbarButton* buttons[4]; Window handle; BOOL hasCursor; xfContext* xfc; DWORD flags; BOOL created; Window root_window; char* title; XFontSet fontSet; }; static xfFloatbarButton* xf_floatbar_new_button(xfFloatbar* floatbar, int type); static BOOL xf_floatbar_button_onclick_close(xfFloatbar* floatbar) { if (!floatbar) return FALSE; return freerdp_abort_connect_context(&floatbar->xfc->common.context); } static BOOL xf_floatbar_button_onclick_minimize(xfFloatbar* floatbar) { xfContext* xfc = nullptr; if (!floatbar || !floatbar->xfc) return FALSE; xfc = floatbar->xfc; xf_SetWindowMinimized(xfc, xfc->window); return TRUE; } static BOOL xf_floatbar_button_onclick_restore(xfFloatbar* floatbar) { if (!floatbar) return FALSE; xf_toggle_fullscreen(floatbar->xfc); return TRUE; } static BOOL xf_floatbar_button_onclick_locked(xfFloatbar* floatbar) { if (!floatbar) return FALSE; floatbar->locked = !((floatbar->locked)); return xf_floatbar_hide_and_show(floatbar); } BOOL xf_floatbar_set_root_y(xfFloatbar* floatbar, int y) { if (!floatbar) return FALSE; floatbar->last_motion_y_root = y; return TRUE; } BOOL xf_floatbar_hide_and_show(xfFloatbar* floatbar) { xfContext* xfc = nullptr; if (!floatbar || !floatbar->xfc) return FALSE; if (!floatbar->created) return TRUE; xfc = floatbar->xfc; WINPR_ASSERT(xfc); WINPR_ASSERT(xfc->display); if (!floatbar->locked) { if ((floatbar->mode == XF_FLOATBAR_MODE_NONE) && (floatbar->last_motion_y_root > 10) && (floatbar->y > (FLOATBAR_HEIGHT * -1))) { floatbar->y = floatbar->y - 1; LogDynAndXMoveWindow(xfc->log, xfc->display, floatbar->handle, floatbar->x, floatbar->y); } else if (floatbar->y < 0 && (floatbar->last_motion_y_root < 10)) { floatbar->y = floatbar->y + 1; LogDynAndXMoveWindow(xfc->log, xfc->display, floatbar->handle, floatbar->x, floatbar->y); } } return TRUE; } static BOOL create_floatbar(xfFloatbar* floatbar) { xfContext* xfc = nullptr; Status status = 0; XWindowAttributes attr = WINPR_C_ARRAY_INIT; WINPR_ASSERT(floatbar); if (floatbar->created) return TRUE; xfc = floatbar->xfc; WINPR_ASSERT(xfc); WINPR_ASSERT(xfc->display); status = XGetWindowAttributes(xfc->display, floatbar->root_window, &attr); if (status == 0) { WLog_WARN(TAG, "XGetWindowAttributes failed"); return FALSE; } floatbar->x = attr.x + attr.width / 2 - FLOATBAR_DEFAULT_WIDTH / 2; floatbar->y = 0; if (((floatbar->flags & 0x0004) == 0) && !floatbar->locked) floatbar->y = -FLOATBAR_HEIGHT + 1; floatbar->handle = LogDynAndXCreateWindow( xfc->log, xfc->display, floatbar->root_window, floatbar->x, 0, FLOATBAR_DEFAULT_WIDTH, FLOATBAR_HEIGHT, 0, CopyFromParent, InputOutput, CopyFromParent, 0, nullptr); floatbar->width = FLOATBAR_DEFAULT_WIDTH; floatbar->height = FLOATBAR_HEIGHT; floatbar->mode = XF_FLOATBAR_MODE_NONE; floatbar->buttons[0] = xf_floatbar_new_button(floatbar, XF_FLOATBAR_BUTTON_CLOSE); floatbar->buttons[1] = xf_floatbar_new_button(floatbar, XF_FLOATBAR_BUTTON_RESTORE); floatbar->buttons[2] = xf_floatbar_new_button(floatbar, XF_FLOATBAR_BUTTON_MINIMIZE); floatbar->buttons[3] = xf_floatbar_new_button(floatbar, XF_FLOATBAR_BUTTON_LOCKED); XSelectInput(xfc->display, floatbar->handle, ExposureMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | FocusChangeMask | LeaveWindowMask | EnterWindowMask | StructureNotifyMask | PropertyChangeMask); floatbar->created = TRUE; return TRUE; } BOOL xf_floatbar_toggle_fullscreen(xfFloatbar* floatbar, bool fullscreen) { int size = 0; bool visible = False; xfContext* xfc = nullptr; if (!floatbar || !floatbar->xfc) return FALSE; xfc = floatbar->xfc; WINPR_ASSERT(xfc->display); /* Only visible if enabled */ if (floatbar->flags & 0x0001) { /* Visible if fullscreen and flag visible in fullscreen mode */ visible |= ((floatbar->flags & 0x0010) != 0) && fullscreen; /* Visible if window and flag visible in window mode */ visible |= ((floatbar->flags & 0x0020) != 0) && !fullscreen; } if (visible) { if (!create_floatbar(floatbar)) return FALSE; LogDynAndXMapWindow(xfc->log, xfc->display, floatbar->handle); size = ARRAYSIZE(floatbar->buttons); for (int i = 0; i < size; i++) { xfFloatbarButton* button = floatbar->buttons[i]; LogDynAndXMapWindow(xfc->log, xfc->display, button->handle); } /* If default is hidden (and not sticky) don't show on fullscreen state changes */ if (((floatbar->flags & 0x0004) == 0) && !floatbar->locked) floatbar->y = -FLOATBAR_HEIGHT + 1; xf_floatbar_hide_and_show(floatbar); } else if (floatbar->created) { XUnmapSubwindows(xfc->display, floatbar->handle); LogDynAndXUnmapWindow(xfc->log, xfc->display, floatbar->handle); } return TRUE; } xfFloatbarButton* xf_floatbar_new_button(xfFloatbar* floatbar, int type) { xfFloatbarButton* button = nullptr; WINPR_ASSERT(floatbar); WINPR_ASSERT(floatbar->xfc); WINPR_ASSERT(floatbar->xfc->display); WINPR_ASSERT(floatbar->handle); button = (xfFloatbarButton*)calloc(1, sizeof(xfFloatbarButton)); button->type = type; switch (type) { case XF_FLOATBAR_BUTTON_CLOSE: button->x = floatbar->width - FLOATBAR_BORDER - FLOATBAR_BUTTON_WIDTH * type; button->onclick = xf_floatbar_button_onclick_close; break; case XF_FLOATBAR_BUTTON_RESTORE: button->x = floatbar->width - FLOATBAR_BORDER - FLOATBAR_BUTTON_WIDTH * type; button->onclick = xf_floatbar_button_onclick_restore; break; case XF_FLOATBAR_BUTTON_MINIMIZE: button->x = floatbar->width - FLOATBAR_BORDER - FLOATBAR_BUTTON_WIDTH * type; button->onclick = xf_floatbar_button_onclick_minimize; break; case XF_FLOATBAR_BUTTON_LOCKED: button->x = FLOATBAR_BORDER; button->onclick = xf_floatbar_button_onclick_locked; break; default: break; } button->y = 0; button->focus = FALSE; button->handle = LogDynAndXCreateWindow(floatbar->xfc->log, floatbar->xfc->display, floatbar->handle, button->x, 0, FLOATBAR_BUTTON_WIDTH, FLOATBAR_BUTTON_WIDTH, 0, CopyFromParent, InputOutput, CopyFromParent, 0, nullptr); XSelectInput(floatbar->xfc->display, button->handle, ExposureMask | ButtonPressMask | ButtonReleaseMask | FocusChangeMask | LeaveWindowMask | EnterWindowMask | StructureNotifyMask); return button; } xfFloatbar* xf_floatbar_new(xfContext* xfc, Window window, const char* title, DWORD flags) { WINPR_ASSERT(xfc); WINPR_ASSERT(xfc->display); WINPR_ASSERT(title); /* Floatbar not enabled */ if ((flags & 0x0001) == 0) return nullptr; if (!xfc) return nullptr; /* Force disable with remote app */ if (xfc->remote_app) return nullptr; xfFloatbar* floatbar = (xfFloatbar*)calloc(1, sizeof(xfFloatbar)); if (!floatbar) return nullptr; floatbar->title = _strdup(title); if (!floatbar->title) goto fail; floatbar->root_window = window; floatbar->flags = flags; floatbar->xfc = xfc; floatbar->locked = (flags & 0x0002) != 0; xf_floatbar_toggle_fullscreen(floatbar, FALSE); { char** missingList = nullptr; int missingCount = 0; char* defString = nullptr; floatbar->fontSet = XCreateFontSet(floatbar->xfc->display, "-*-*-*-*-*-*-*-*-*-*-*-*-*-*", &missingList, &missingCount, &defString); if (floatbar->fontSet == nullptr) { WLog_ERR(TAG, "Failed to create fontset"); } XFreeStringList(missingList); } return floatbar; fail: WINPR_PRAGMA_DIAG_PUSH WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC xf_floatbar_free(floatbar); WINPR_PRAGMA_DIAG_POP return nullptr; } static unsigned long xf_floatbar_get_color(xfFloatbar* floatbar, char* rgb_value) { XColor color; WINPR_ASSERT(floatbar); WINPR_ASSERT(floatbar->xfc); Display* display = floatbar->xfc->display; WINPR_ASSERT(display); Colormap cmap = DefaultColormap(display, XDefaultScreen(display)); XParseColor(display, cmap, rgb_value, &color); XAllocColor(display, cmap, &color); return color.pixel; } static void xf_floatbar_event_expose(xfFloatbar* floatbar) { GC gc = nullptr; GC shape_gc = nullptr; Pixmap pmap = 0; XPoint shape[5] = WINPR_C_ARRAY_INIT; XPoint border[5] = WINPR_C_ARRAY_INIT; WINPR_ASSERT(floatbar); WINPR_ASSERT(floatbar->xfc); Display* display = floatbar->xfc->display; WINPR_ASSERT(display); /* create the pixmap that we'll use for shaping the window */ pmap = LogDynAndXCreatePixmap(floatbar->xfc->log, display, floatbar->handle, WINPR_ASSERTING_INT_CAST(uint32_t, floatbar->width), WINPR_ASSERTING_INT_CAST(uint32_t, floatbar->height), 1); gc = LogDynAndXCreateGC(floatbar->xfc->log, display, floatbar->handle, 0, 0); shape_gc = LogDynAndXCreateGC(floatbar->xfc->log, display, pmap, 0, 0); /* points for drawing the floatbar */ shape[0].x = 0; shape[0].y = 0; shape[1].x = WINPR_ASSERTING_INT_CAST(short, floatbar->width); shape[1].y = 0; shape[2].x = WINPR_ASSERTING_INT_CAST(short, shape[1].x - FLOATBAR_BORDER); shape[2].y = FLOATBAR_HEIGHT; shape[3].x = WINPR_ASSERTING_INT_CAST(short, shape[0].x + FLOATBAR_BORDER); shape[3].y = FLOATBAR_HEIGHT; shape[4].x = shape[0].x; shape[4].y = shape[0].y; /* points for drawing the border of the floatbar */ border[0].x = shape[0].x; border[0].y = WINPR_ASSERTING_INT_CAST(short, shape[0].y - 1); border[1].x = WINPR_ASSERTING_INT_CAST(short, shape[1].x - 1); border[1].y = WINPR_ASSERTING_INT_CAST(short, shape[1].y - 1); border[2].x = shape[2].x; border[2].y = WINPR_ASSERTING_INT_CAST(short, shape[2].y - 1); border[3].x = WINPR_ASSERTING_INT_CAST(short, shape[3].x - 1); border[3].y = WINPR_ASSERTING_INT_CAST(short, shape[3].y - 1); border[4].x = border[0].x; border[4].y = border[0].y; /* Fill all pixels with 0 */ LogDynAndXSetForeground(floatbar->xfc->log, display, shape_gc, 0); LogDynAndXFillRectangle(floatbar->xfc->log, display, pmap, shape_gc, 0, 0, WINPR_ASSERTING_INT_CAST(uint32_t, floatbar->width), WINPR_ASSERTING_INT_CAST(uint32_t, floatbar->height)); /* Fill all pixels which should be shown with 1 */ LogDynAndXSetForeground(floatbar->xfc->log, display, shape_gc, 1); XFillPolygon(display, pmap, shape_gc, shape, 5, 0, CoordModeOrigin); XShapeCombineMask(display, floatbar->handle, ShapeBounding, 0, 0, pmap, ShapeSet); /* draw the float bar */ LogDynAndXSetForeground(floatbar->xfc->log, display, gc, xf_floatbar_get_color(floatbar, FLOATBAR_COLOR_BACKGROUND)); XFillPolygon(display, floatbar->handle, gc, shape, 4, 0, CoordModeOrigin); /* draw an border for the floatbar */ LogDynAndXSetForeground(floatbar->xfc->log, display, gc, xf_floatbar_get_color(floatbar, FLOATBAR_COLOR_BORDER)); XDrawLines(display, floatbar->handle, gc, border, 5, CoordModeOrigin); /* draw the host name connected to (limit to maximum file name) */ const size_t len = strnlen(floatbar->title, MAX_PATH); LogDynAndXSetForeground(floatbar->xfc->log, display, gc, xf_floatbar_get_color(floatbar, FLOATBAR_COLOR_FOREGROUND)); WINPR_ASSERT(len <= INT32_MAX / 2); const int fx = floatbar->width / 2 - (int)len * 2; if (floatbar->fontSet != nullptr) { XmbDrawString(display, floatbar->handle, floatbar->fontSet, gc, fx, 15, floatbar->title, (int)len); } else { XDrawString(display, floatbar->handle, gc, fx, 15, floatbar->title, (int)len); } LogDynAndXFreeGC(floatbar->xfc->log, display, gc); LogDynAndXFreeGC(floatbar->xfc->log, display, shape_gc); } static xfFloatbarButton* xf_floatbar_get_button(xfFloatbar* floatbar, Window window) { WINPR_ASSERT(floatbar); const size_t size = ARRAYSIZE(floatbar->buttons); for (size_t i = 0; i < size; i++) { xfFloatbarButton* button = floatbar->buttons[i]; if (button->handle == window) { return button; } } return nullptr; } static void xf_floatbar_button_update_positon(xfFloatbar* floatbar) { xfFloatbarButton* button = nullptr; WINPR_ASSERT(floatbar); xfContext* xfc = floatbar->xfc; const size_t size = ARRAYSIZE(floatbar->buttons); for (size_t i = 0; i < size; i++) { button = floatbar->buttons[i]; switch (button->type) { case XF_FLOATBAR_BUTTON_CLOSE: button->x = floatbar->width - FLOATBAR_BORDER - FLOATBAR_BUTTON_WIDTH * button->type; break; case XF_FLOATBAR_BUTTON_RESTORE: button->x = floatbar->width - FLOATBAR_BORDER - FLOATBAR_BUTTON_WIDTH * button->type; break; case XF_FLOATBAR_BUTTON_MINIMIZE: button->x = floatbar->width - FLOATBAR_BORDER - FLOATBAR_BUTTON_WIDTH * button->type; break; default: break; } WINPR_ASSERT(xfc); WINPR_ASSERT(xfc->display); LogDynAndXMoveWindow(xfc->log, xfc->display, button->handle, button->x, button->y); xf_floatbar_event_expose(floatbar); } } static void xf_floatbar_button_event_expose(xfFloatbar* floatbar, Window window) { xfFloatbarButton* button = xf_floatbar_get_button(floatbar, window); static unsigned char* bits; GC gc = nullptr; Pixmap pattern = 0; xfContext* xfc = floatbar->xfc; if (!button) return; WINPR_ASSERT(xfc); WINPR_ASSERT(xfc->display); WINPR_ASSERT(xfc->window); gc = LogDynAndXCreateGC(xfc->log, xfc->display, button->handle, 0, 0); floatbar = xfc->window->floatbar; WINPR_ASSERT(floatbar); switch (button->type) { case XF_FLOATBAR_BUTTON_CLOSE: bits = close_bits; break; case XF_FLOATBAR_BUTTON_RESTORE: bits = restore_bits; break; case XF_FLOATBAR_BUTTON_MINIMIZE: bits = minimize_bits; break; case XF_FLOATBAR_BUTTON_LOCKED: if (floatbar->locked) bits = lock_bits; else bits = unlock_bits; break; default: break; } pattern = XCreateBitmapFromData(xfc->display, button->handle, (const char*)bits, FLOATBAR_BUTTON_WIDTH, FLOATBAR_BUTTON_WIDTH); if (!(button->focus)) LogDynAndXSetForeground(floatbar->xfc->log, xfc->display, gc, xf_floatbar_get_color(floatbar, FLOATBAR_COLOR_BACKGROUND)); else LogDynAndXSetForeground(floatbar->xfc->log, xfc->display, gc, xf_floatbar_get_color(floatbar, FLOATBAR_COLOR_BORDER)); LogDynAndXSetBackground(xfc->log, xfc->display, gc, xf_floatbar_get_color(floatbar, FLOATBAR_COLOR_FOREGROUND)); XCopyPlane(xfc->display, pattern, button->handle, gc, 0, 0, FLOATBAR_BUTTON_WIDTH, FLOATBAR_BUTTON_WIDTH, 0, 0, 1); LogDynAndXFreePixmap(xfc->log, xfc->display, pattern); LogDynAndXFreeGC(xfc->log, xfc->display, gc); } static void xf_floatbar_button_event_buttonpress(xfFloatbar* floatbar, const XButtonEvent* event) { WINPR_ASSERT(event); xfFloatbarButton* button = xf_floatbar_get_button(floatbar, event->window); if (button) button->clicked = TRUE; } static void xf_floatbar_button_event_buttonrelease(xfFloatbar* floatbar, const XButtonEvent* event) { xfFloatbarButton* button = nullptr; WINPR_ASSERT(floatbar); WINPR_ASSERT(event); button = xf_floatbar_get_button(floatbar, event->window); if (button) { if (button->clicked) button->onclick(floatbar); button->clicked = FALSE; } } static void xf_floatbar_event_buttonpress(xfFloatbar* floatbar, const XButtonEvent* event) { WINPR_ASSERT(floatbar); WINPR_ASSERT(event); switch (event->button) { case Button1: if (event->x <= FLOATBAR_BORDER) floatbar->mode = XF_FLOATBAR_MODE_RESIZE_LEFT; else if (event->x >= (floatbar->width - FLOATBAR_BORDER)) floatbar->mode = XF_FLOATBAR_MODE_RESIZE_RIGHT; else floatbar->mode = XF_FLOATBAR_MODE_DRAGGING; break; default: break; } } static void xf_floatbar_event_buttonrelease(xfFloatbar* floatbar, const XButtonEvent* event) { WINPR_ASSERT(floatbar); WINPR_ASSERT(event); switch (event->button) { case Button1: floatbar->mode = XF_FLOATBAR_MODE_NONE; break; default: break; } } static void xf_floatbar_resize(xfFloatbar* floatbar, const XMotionEvent* event) { int x = 0; int width = 0; int movement = 0; WINPR_ASSERT(floatbar); WINPR_ASSERT(event); xfContext* xfc = floatbar->xfc; WINPR_ASSERT(xfc); WINPR_ASSERT(xfc->display); /* calculate movement which happened on the root window */ movement = event->x_root - floatbar->last_motion_x_root; /* set x and width depending if movement happens on the left or right */ if (floatbar->mode == XF_FLOATBAR_MODE_RESIZE_LEFT) { x = floatbar->x + movement; width = floatbar->width + movement * -1; } else { x = floatbar->x; width = floatbar->width + movement; } /* only resize and move window if still above minimum width */ if (FLOATBAR_MIN_WIDTH < width) { LogDynAndXMoveResizeWindow(xfc->log, xfc->display, floatbar->handle, x, 0, WINPR_ASSERTING_INT_CAST(uint32_t, width), WINPR_ASSERTING_INT_CAST(uint32_t, floatbar->height)); floatbar->x = x; floatbar->width = width; } } static void xf_floatbar_dragging(xfFloatbar* floatbar, const XMotionEvent* event) { int x = 0; int movement = 0; WINPR_ASSERT(floatbar); WINPR_ASSERT(event); xfContext* xfc = floatbar->xfc; WINPR_ASSERT(xfc); WINPR_ASSERT(xfc->window); WINPR_ASSERT(xfc->display); /* calculate movement and new x position */ movement = event->x_root - floatbar->last_motion_x_root; x = floatbar->x + movement; /* do nothing if floatbar would be moved out of the window */ if (x < 0 || (x + floatbar->width) > xfc->window->width) return; /* move window to new x position */ LogDynAndXMoveWindow(xfc->log, xfc->display, floatbar->handle, x, 0); /* update struct values for the next event */ floatbar->last_motion_x_root = floatbar->last_motion_x_root + movement; floatbar->x = x; } static void xf_floatbar_event_motionnotify(xfFloatbar* floatbar, const XMotionEvent* event) { int mode = 0; Cursor cursor = 0; WINPR_ASSERT(floatbar); WINPR_ASSERT(event); xfContext* xfc = floatbar->xfc; WINPR_ASSERT(xfc); WINPR_ASSERT(xfc->display); mode = floatbar->mode; cursor = XCreateFontCursor(xfc->display, XC_arrow); if ((event->state & Button1Mask) && (mode > XF_FLOATBAR_MODE_DRAGGING)) { xf_floatbar_resize(floatbar, event); } else if ((event->state & Button1Mask) && (mode == XF_FLOATBAR_MODE_DRAGGING)) { xf_floatbar_dragging(floatbar, event); } else { if (event->x <= FLOATBAR_BORDER || event->x >= floatbar->width - FLOATBAR_BORDER) cursor = XCreateFontCursor(xfc->display, XC_sb_h_double_arrow); } XDefineCursor(xfc->display, xfc->window->handle, cursor); XFreeCursor(xfc->display, cursor); floatbar->last_motion_x_root = event->x_root; } static void xf_floatbar_button_event_focusin(xfFloatbar* floatbar, const XAnyEvent* event) { xfFloatbarButton* button = nullptr; WINPR_ASSERT(floatbar); WINPR_ASSERT(event); button = xf_floatbar_get_button(floatbar, event->window); if (button) { button->focus = TRUE; xf_floatbar_button_event_expose(floatbar, event->window); } } static void xf_floatbar_button_event_focusout(xfFloatbar* floatbar, const XAnyEvent* event) { xfFloatbarButton* button = nullptr; WINPR_ASSERT(floatbar); WINPR_ASSERT(event); button = xf_floatbar_get_button(floatbar, event->window); if (button) { button->focus = FALSE; xf_floatbar_button_event_expose(floatbar, event->window); } } static void xf_floatbar_event_focusout(xfFloatbar* floatbar) { WINPR_ASSERT(floatbar); xfContext* xfc = floatbar->xfc; WINPR_ASSERT(xfc); if (xfc->pointer) { WINPR_ASSERT(xfc->window); WINPR_ASSERT(xfc->pointer); XDefineCursor(xfc->display, xfc->window->handle, xfc->pointer->cursor); } } BOOL xf_floatbar_check_event(xfFloatbar* floatbar, const XEvent* event) { if (!floatbar || !floatbar->xfc || !event) return FALSE; if (!floatbar->created) return FALSE; if (event->xany.window == floatbar->handle) return TRUE; size_t size = ARRAYSIZE(floatbar->buttons); for (size_t i = 0; i < size; i++) { const xfFloatbarButton* button = floatbar->buttons[i]; if (event->xany.window == button->handle) return TRUE; } return FALSE; } BOOL xf_floatbar_event_process(xfFloatbar* floatbar, const XEvent* event) { if (!floatbar || !floatbar->xfc || !event) return FALSE; if (!floatbar->created) return FALSE; switch (event->type) { case Expose: if (event->xexpose.window == floatbar->handle) xf_floatbar_event_expose(floatbar); else xf_floatbar_button_event_expose(floatbar, event->xexpose.window); break; case MotionNotify: xf_floatbar_event_motionnotify(floatbar, &event->xmotion); break; case ButtonPress: if (event->xany.window == floatbar->handle) xf_floatbar_event_buttonpress(floatbar, &event->xbutton); else xf_floatbar_button_event_buttonpress(floatbar, &event->xbutton); break; case ButtonRelease: if (event->xany.window == floatbar->handle) xf_floatbar_event_buttonrelease(floatbar, &event->xbutton); else xf_floatbar_button_event_buttonrelease(floatbar, &event->xbutton); break; case EnterNotify: case FocusIn: if (event->xany.window != floatbar->handle) xf_floatbar_button_event_focusin(floatbar, &event->xany); break; case LeaveNotify: case FocusOut: if (event->xany.window == floatbar->handle) xf_floatbar_event_focusout(floatbar); else xf_floatbar_button_event_focusout(floatbar, &event->xany); break; case ConfigureNotify: if (event->xany.window == floatbar->handle) xf_floatbar_button_update_positon(floatbar); break; case PropertyNotify: if (event->xany.window == floatbar->handle) xf_floatbar_button_update_positon(floatbar); break; default: break; } return floatbar->handle == event->xany.window; } static void xf_floatbar_button_free(xfContext* xfc, xfFloatbarButton* button) { if (!button) return; if (button->handle) { WINPR_ASSERT(xfc); WINPR_ASSERT(xfc->display); LogDynAndXUnmapWindow(xfc->log, xfc->display, button->handle); LogDynAndXDestroyWindow(xfc->log, xfc->display, button->handle); } free(button); } void xf_floatbar_free(xfFloatbar* floatbar) { size_t size = 0; xfContext* xfc = nullptr; if (!floatbar) return; free(floatbar->title); xfc = floatbar->xfc; WINPR_ASSERT(xfc); size = ARRAYSIZE(floatbar->buttons); for (size_t i = 0; i < size; i++) { xf_floatbar_button_free(xfc, floatbar->buttons[i]); floatbar->buttons[i] = nullptr; } if (floatbar->handle) { WINPR_ASSERT(xfc->display); LogDynAndXUnmapWindow(xfc->log, xfc->display, floatbar->handle); LogDynAndXDestroyWindow(xfc->log, xfc->display, floatbar->handle); } free(floatbar); } BOOL xf_floatbar_is_locked(xfFloatbar* floatbar) { if (!floatbar) return FALSE; return floatbar->mode != XF_FLOATBAR_MODE_NONE; } BOOL xf_floatbar_is_window(xfFloatbar* floatbar, Window window) { if (!floatbar) return FALSE; return floatbar->handle == window; } BOOL xfc_is_floatbar_window(xfContext* xfc, Window window) { if (!xfc || !xfc->window) return FALSE; return xf_floatbar_is_window(xfc->window->floatbar, window); }