FreeRDP
Loading...
Searching...
No Matches
wf_floatbar.c
1
20#include <winpr/crt.h>
21#include <winpr/windows.h>
22
23#include "wf_client.h"
24#include "wf_floatbar.h"
25
26#include "resource/resource.h"
27#include "wf_gdi.h"
28#ifdef _MSC_VER
29#pragma comment(lib, "Msimg32.lib")
30#endif
31
32#define TAG CLIENT_TAG("windows.floatbar")
33
34/* TIMERs */
35#define TIMER_HIDE 1
36#define TIMER_ANIMAT_SHOW 2
37#define TIMER_ANIMAT_HIDE 3
38
39/* Button Type */
40#define BUTTON_LOCKPIN 0
41#define BUTTON_MINIMIZE 1
42#define BUTTON_RESTORE 2
43#define BUTTON_CLOSE 3
44#define BTN_MAX 4
45
46/* bmp size */
47#define BACKGROUND_W 576
48#define BACKGROUND_H 27
49#define BUTTON_OFFSET 5
50#define BUTTON_Y 2
51#define BUTTON_WIDTH 23
52#define BUTTON_HEIGHT 21
53#define BUTTON_SPACING 1
54#define RESTORE_DRAG_BAR_MULTIPLIER 2
55#define RESIZE_GRIP_WIDTH 8
56#define MIN_LABEL_WIDTH 260
57
58#define LOCK_X (BACKGROUND_H + BUTTON_OFFSET)
59#define CLOSE_X ((BACKGROUND_W - (BACKGROUND_H + BUTTON_OFFSET)) - BUTTON_WIDTH)
60#define RESTORE_X (CLOSE_X - (BUTTON_WIDTH + BUTTON_SPACING))
61#define MINIMIZE_X (RESTORE_X - (BUTTON_WIDTH + BUTTON_SPACING))
62#define TEXT_X (BACKGROUND_H + ((BUTTON_WIDTH + BUTTON_SPACING) * 3) + 5)
63#define MIN_FLOATBAR_WIDTH ((TEXT_X * 2) + MIN_LABEL_WIDTH)
64
65#define DRAG_NONE 0
66#define DRAG_MOVE 1
67#define DRAG_RESIZE_LEFT 2
68#define DRAG_RESIZE_RIGHT 3
69
70typedef struct
71{
72 wfFloatBar* floatbar;
73 int type;
74 int x, y, h, w;
75 int active;
76 HBITMAP bmp;
77 HBITMAP bmp_act;
78
79 /* Lock Specified */
80 HBITMAP locked_bmp;
81 HBITMAP locked_bmp_act;
82 HBITMAP unlocked_bmp;
83 HBITMAP unlocked_bmp_act;
84} Button;
85
86struct s_FloatBar
87{
88 HINSTANCE root_window;
89 DWORD flags;
90 HWND parent;
91 HWND hwnd;
92 RECT rect;
93 LONG width;
94 LONG height;
95 LONG offset;
96 wfContext* wfc;
97 Button* buttons[BTN_MAX];
98 BOOL shown;
99 BOOL locked;
100 HDC hdcmem;
101 RECT textRect;
102 UINT_PTR animating;
103};
104
105static BOOL floatbar_kill_timers(wfFloatBar* floatbar)
106{
107 UINT_PTR timers[] = { TIMER_HIDE, TIMER_ANIMAT_HIDE, TIMER_ANIMAT_SHOW };
108
109 if (!floatbar)
110 return FALSE;
111
112 for (size_t x = 0; x < ARRAYSIZE(timers); x++)
113 KillTimer(floatbar->hwnd, timers[x]);
114
115 floatbar->animating = 0;
116 return TRUE;
117}
118
119static BOOL floatbar_animation(wfFloatBar* const floatbar, const BOOL show)
120{
121 UINT_PTR timer = show ? TIMER_ANIMAT_SHOW : TIMER_ANIMAT_HIDE;
122
123 if (!floatbar)
124 return FALSE;
125
126 if (floatbar->shown == show)
127 return TRUE;
128
129 if (floatbar->animating == timer)
130 return TRUE;
131
132 floatbar->animating = timer;
133
134 if (SetTimer(floatbar->hwnd, timer, USER_TIMER_MINIMUM, nullptr) == 0)
135 {
136 DWORD err = GetLastError();
137 WLog_ERR(TAG, "SetTimer failed with %08" PRIx32, err);
138 return FALSE;
139 }
140
141 return TRUE;
142}
143
144static BOOL floatbar_trigger_hide(wfFloatBar* floatbar)
145{
146 if (!floatbar_kill_timers(floatbar))
147 return FALSE;
148
149 if (!floatbar->locked && floatbar->shown)
150 {
151 if (SetTimer(floatbar->hwnd, TIMER_HIDE, 3000, nullptr) == 0)
152 {
153 DWORD err = GetLastError();
154 WLog_ERR(TAG, "SetTimer failed with %08" PRIx32, err);
155 return FALSE;
156 }
157 }
158
159 return TRUE;
160}
161
162static BOOL floatbar_hide(wfFloatBar* floatbar)
163{
164 if (!floatbar_kill_timers(floatbar))
165 return FALSE;
166
167 floatbar->offset = floatbar->height - 2;
168
169 if (!MoveWindow(floatbar->hwnd, floatbar->rect.left, -floatbar->offset, floatbar->width,
170 floatbar->height, TRUE))
171 {
172 DWORD err = GetLastError();
173 WLog_ERR(TAG, "MoveWindow failed with %08" PRIx32, err);
174 return FALSE;
175 }
176
177 floatbar->shown = FALSE;
178
179 if (!floatbar_trigger_hide(floatbar))
180 return FALSE;
181
182 return TRUE;
183}
184
185static BOOL floatbar_show(wfFloatBar* floatbar)
186{
187 if (!floatbar_kill_timers(floatbar))
188 return FALSE;
189
190 floatbar->offset = 0;
191
192 if (!MoveWindow(floatbar->hwnd, floatbar->rect.left, -floatbar->offset, floatbar->width,
193 floatbar->height, TRUE))
194 {
195 DWORD err = GetLastError();
196 WLog_ERR(TAG, "MoveWindow failed with %08" PRIx32, err);
197 return FALSE;
198 }
199
200 floatbar->shown = TRUE;
201
202 if (!floatbar_trigger_hide(floatbar))
203 return FALSE;
204
205 return TRUE;
206}
207
208static BOOL button_set_locked(Button* button, BOOL locked)
209{
210 if (locked)
211 {
212 button->bmp = button->locked_bmp;
213 button->bmp_act = button->locked_bmp_act;
214 }
215 else
216 {
217 button->bmp = button->unlocked_bmp;
218 button->bmp_act = button->unlocked_bmp_act;
219 }
220
221 InvalidateRect(button->floatbar->hwnd, nullptr, FALSE);
222 UpdateWindow(button->floatbar->hwnd);
223 return TRUE;
224}
225
226static BOOL update_locked_state(wfFloatBar* floatbar)
227{
228 Button* button;
229
230 if (!floatbar)
231 return FALSE;
232
233 button = floatbar->buttons[3];
234
235 if (!button_set_locked(button, floatbar->locked))
236 return FALSE;
237
238 return TRUE;
239}
240
241static int button_hit(Button* const button)
242{
243 wfFloatBar* const floatbar = button->floatbar;
244
245 switch (button->type)
246 {
247 case BUTTON_LOCKPIN:
248 floatbar->locked = !floatbar->locked;
249 update_locked_state(floatbar);
250 break;
251
252 case BUTTON_MINIMIZE:
253 ShowWindow(floatbar->parent, SW_MINIMIZE);
254 break;
255
256 case BUTTON_RESTORE:
257 wf_toggle_fullscreen(floatbar->wfc);
258 break;
259
260 case BUTTON_CLOSE:
261 SendMessage(floatbar->parent, WM_DESTROY, 0, 0);
262 break;
263
264 default:
265 return 0;
266 }
267
268 return 0;
269}
270
271static int button_paint(const Button* const button, const HDC hdc)
272{
273 if (button != nullptr)
274 {
275 wfFloatBar* floatbar = button->floatbar;
276 BLENDFUNCTION bf;
277 SelectObject(floatbar->hdcmem, button->active ? button->bmp_act : button->bmp);
278 bf.BlendOp = AC_SRC_OVER;
279 bf.BlendFlags = 0;
280 bf.SourceConstantAlpha = 255;
281 bf.AlphaFormat = AC_SRC_ALPHA;
282 AlphaBlend(hdc, button->x, button->y, button->w, button->h, floatbar->hdcmem, 0, 0,
283 button->w, button->h, bf);
284 }
285
286 return 0;
287}
288
289static Button* floatbar_create_button(wfFloatBar* const floatbar, const int type, const int resid,
290 const int resid_act, const int x, const int y, const int h,
291 const int w)
292{
293 Button* button = (Button*)calloc(1, sizeof(Button));
294
295 if (!button)
296 return nullptr;
297
298 button->floatbar = floatbar;
299 button->type = type;
300 button->x = x;
301 button->y = y;
302 button->w = w;
303 button->h = h;
304 button->active = FALSE;
305 button->bmp = (HBITMAP)LoadImage(floatbar->root_window, MAKEINTRESOURCE(resid), IMAGE_BITMAP, 0,
306 0, LR_DEFAULTCOLOR);
307 button->bmp_act = (HBITMAP)LoadImage(floatbar->root_window, MAKEINTRESOURCE(resid_act),
308 IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR);
309 return button;
310}
311
312static Button* floatbar_create_lock_button(wfFloatBar* const floatbar, const int unlock_resid,
313 const int unlock_resid_act, const int lock_resid,
314 const int lock_resid_act, const int x, const int y,
315 const int h, const int w)
316{
317 Button* button = floatbar_create_button(floatbar, BUTTON_LOCKPIN, unlock_resid,
318 unlock_resid_act, x, y, h, w);
319
320 if (!button)
321 return nullptr;
322
323 button->unlocked_bmp = button->bmp;
324 button->unlocked_bmp_act = button->bmp_act;
325 button->locked_bmp = (HBITMAP)LoadImage(floatbar->wfc->hInstance, MAKEINTRESOURCE(lock_resid),
326 IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR);
327 button->locked_bmp_act =
328 (HBITMAP)LoadImage(floatbar->wfc->hInstance, MAKEINTRESOURCE(lock_resid_act), IMAGE_BITMAP,
329 0, 0, LR_DEFAULTCOLOR);
330 return button;
331}
332
333static Button* floatbar_get_button(const wfFloatBar* const floatbar, const int x, const int y)
334{
335 if ((y > BUTTON_Y) && (y < BUTTON_Y + BUTTON_HEIGHT))
336 {
337 for (int i = 0; i < BTN_MAX; i++)
338 {
339 if ((floatbar->buttons[i] != nullptr) && (x > floatbar->buttons[i]->x) &&
340 (x < floatbar->buttons[i]->x + floatbar->buttons[i]->w))
341 {
342 return floatbar->buttons[i];
343 }
344 }
345 }
346
347 return nullptr;
348}
349
350static Button* floatbar_get_button_by_type(const wfFloatBar* const floatbar, const int type)
351{
352 if (!floatbar)
353 return nullptr;
354
355 for (int i = 0; i < BTN_MAX; i++)
356 {
357 Button* button = floatbar->buttons[i];
358
359 if (button && (button->type == type))
360 return button;
361 }
362
363 return nullptr;
364}
365
366static LONG floatbar_parent_width(const wfFloatBar* const floatbar)
367{
368 RECT rect = WINPR_C_ARRAY_INIT;
369
370 if (!floatbar || !floatbar->parent || !GetWindowRect(floatbar->parent, &rect))
371 return GetSystemMetrics(SM_CXSCREEN);
372
373 return rect.right - rect.left;
374}
375
376static BOOL floatbar_update_layout(wfFloatBar* floatbar)
377{
378 if (!floatbar)
379 return FALSE;
380
381 Button* close = floatbar_get_button_by_type(floatbar, BUTTON_CLOSE);
382 Button* restore = floatbar_get_button_by_type(floatbar, BUTTON_RESTORE);
383 Button* minimize = floatbar_get_button_by_type(floatbar, BUTTON_MINIMIZE);
384
385 if (close)
386 close->x = (floatbar->width - (BACKGROUND_H + BUTTON_OFFSET)) - BUTTON_WIDTH;
387
388 if (restore && close)
389 restore->x = close->x - (BUTTON_WIDTH + BUTTON_SPACING);
390
391 if (minimize && restore)
392 minimize->x = restore->x - (BUTTON_WIDTH + BUTTON_SPACING);
393
394 SetRect(&floatbar->textRect, TEXT_X, 0, floatbar->width - TEXT_X, floatbar->height);
395 return TRUE;
396}
397
398static BOOL floatbar_update_region(wfFloatBar* floatbar)
399{
400 if (!floatbar || !floatbar->hwnd)
401 return FALSE;
402
403 HRGN hRgn = CreateRectRgn(0, 0, floatbar->width, floatbar->height);
404 if (!hRgn)
405 return FALSE;
406
407 if (!SetWindowRgn(floatbar->hwnd, hRgn, TRUE))
408 {
409 DeleteObject(hRgn);
410 return FALSE;
411 }
412
413 return TRUE;
414}
415
416static BOOL floatbar_resize(wfFloatBar* floatbar, LONG left, LONG width)
417{
418 if (!floatbar)
419 return FALSE;
420
421 LONG parentWidth = floatbar_parent_width(floatbar);
422
423 if (width < MIN_FLOATBAR_WIDTH)
424 width = MIN_FLOATBAR_WIDTH;
425
426 if (width > parentWidth)
427 width = parentWidth;
428
429 if (left < 0)
430 {
431 width += left;
432 left = 0;
433 }
434
435 if (left + width > parentWidth)
436 width = parentWidth - left;
437
438 if (width < MIN_FLOATBAR_WIDTH)
439 {
440 width = MIN_FLOATBAR_WIDTH;
441 left = parentWidth - width;
442
443 if (left < 0)
444 left = 0;
445 }
446
447 floatbar->rect.left = left;
448 floatbar->width = width;
449
450 if (!floatbar_update_layout(floatbar))
451 return FALSE;
452
453 if (!MoveWindow(floatbar->hwnd, floatbar->rect.left,
454 -WINPR_ASSERTING_INT_CAST(int, floatbar->offset), floatbar->width,
455 floatbar->height, TRUE))
456 {
457 DWORD err = GetLastError();
458 WLog_ERR(TAG, "MoveWindow failed with %08" PRIx32, err);
459 return FALSE;
460 }
461
462 if (!floatbar_update_region(floatbar))
463 return FALSE;
464
465 InvalidateRect(floatbar->hwnd, nullptr, FALSE);
466 return TRUE;
467}
468
469static int floatbar_get_resize_mode(const wfFloatBar* const floatbar, const int x, const int y)
470{
471 if (!floatbar || (y < 0) || (y > floatbar->height))
472 return DRAG_NONE;
473
474 if (floatbar_get_button(floatbar, x, y))
475 return DRAG_NONE;
476
477 if (x <= RESIZE_GRIP_WIDTH)
478 return DRAG_RESIZE_LEFT;
479
480 if (x >= floatbar->width - RESIZE_GRIP_WIDTH)
481 return DRAG_RESIZE_RIGHT;
482
483 return DRAG_NONE;
484}
485
486static BOOL floatbar_update_hover(wfFloatBar* floatbar, const Button* activeButton)
487{
488 BOOL changed = FALSE;
489
490 if (!floatbar)
491 return FALSE;
492
493 for (int i = 0; i < BTN_MAX; i++)
494 {
495 Button* button = floatbar->buttons[i];
496 const int active = (button == activeButton) ? TRUE : FALSE;
497
498 if (button && (button->active != active))
499 {
500 button->active = active;
501 changed = TRUE;
502 }
503 }
504
505 if (changed)
506 {
507 InvalidateRect(floatbar->hwnd, nullptr, FALSE);
508 UpdateWindow(floatbar->hwnd);
509 }
510
511 return TRUE;
512}
513
514static BOOL floatbar_paint(wfFloatBar* const floatbar, const HDC hdc)
515{
516 if (!floatbar)
517 return FALSE;
518
519 /* paint background */
520 GRADIENT_RECT gradientRect = { 0, 1 };
521 COLORREF rgbTop = RGB(117, 154, 198);
522 COLORREF rgbBottom = RGB(6, 55, 120);
523 const int top = 0;
524 int bottom = BACKGROUND_H - 1;
525 int left = 0;
526 const int angleOffset = BACKGROUND_H - 1;
527
528 int right = floatbar->width - 1;
529 POINT pt[4];
530 pt[0].x = 0;
531 pt[0].y = 0;
532 pt[1].x = floatbar->width;
533 pt[1].y = 0;
534 pt[2].x = floatbar->width - BACKGROUND_H;
535 pt[2].y = BACKGROUND_H;
536 pt[3].x = BACKGROUND_H;
537 pt[3].y = BACKGROUND_H;
538 HRGN clipRgn = CreatePolygonRgn(pt, 4, ALTERNATE);
539 if (!clipRgn)
540 return FALSE;
541
542 SelectClipRgn(hdc, clipRgn);
543
544 TRIVERTEX triVertext[2];
545 triVertext[0].x = left;
546 triVertext[0].y = top;
547 triVertext[0].Red = GetRValue(rgbTop) << 8;
548 triVertext[0].Green = GetGValue(rgbTop) << 8;
549 triVertext[0].Blue = GetBValue(rgbTop) << 8;
550 triVertext[0].Alpha = 0x0000;
551 triVertext[1].x = right;
552 triVertext[1].y = bottom;
553 triVertext[1].Red = GetRValue(rgbBottom) << 8;
554 triVertext[1].Green = GetGValue(rgbBottom) << 8;
555 triVertext[1].Blue = GetBValue(rgbBottom) << 8;
556 triVertext[1].Alpha = 0x0000;
557
558 GradientFill(hdc, triVertext, 2, &gradientRect, 1, GRADIENT_FILL_RECT_V);
559 /* paint shadow */
560 HPEN hpen = CreatePen(PS_SOLID, 1, RGB(71, 71, 71));
561 HGDIOBJECT orig = SelectObject(hdc, hpen);
562 MoveToEx(hdc, left, top, nullptr);
563 LineTo(hdc, left + angleOffset, bottom);
564 LineTo(hdc, right - angleOffset, bottom);
565 LineTo(hdc, right + 1, top - 1);
566 DeleteObject(hpen);
567 hpen = CreatePen(PS_SOLID, 1, RGB(107, 141, 184));
568 SelectObject(hdc, hpen);
569 left += 1;
570 bottom -= 1;
571 right -= 1;
572 MoveToEx(hdc, left, top, nullptr);
573 LineTo(hdc, left + (angleOffset - 1), bottom);
574 LineTo(hdc, right - (angleOffset - 1), bottom);
575 LineTo(hdc, right + 1, top - 1);
576 DeleteObject(hpen);
577 SelectObject(hdc, orig);
578
579 const size_t wlen = wcslen(floatbar->wfc->window_title);
580 DrawText(hdc, floatbar->wfc->window_title, WINPR_ASSERTING_INT_CAST(int, wlen),
581 &floatbar->textRect,
582 DT_CENTER | DT_VCENTER | DT_END_ELLIPSIS | DT_NOPREFIX | DT_SINGLELINE);
583
584 /* paint buttons */
585
586 for (int i = 0; i < BTN_MAX; i++)
587 button_paint(floatbar->buttons[i], hdc);
588
589 SelectClipRgn(hdc, nullptr);
590 DeleteObject(clipRgn);
591 return TRUE;
592}
593
594static LRESULT CALLBACK floatbar_proc(const HWND hWnd, const UINT Msg, const WPARAM wParam,
595 const LPARAM lParam)
596{
597 static int drag_mode = DRAG_NONE;
598 static int lbtn_dwn = FALSE;
599 static int btn_dwn_x = 0;
600 static int btn_dwn_y = 0;
601 static int btn_dwn_screen_x = 0;
602 static LONG drag_start_left = 0;
603 static LONG drag_start_width = 0;
604 static wfFloatBar* floatbar = nullptr;
605 static TRACKMOUSEEVENT tme = WINPR_C_ARRAY_INIT;
606 PAINTSTRUCT ps = WINPR_C_ARRAY_INIT;
607 POINT pt = WINPR_C_ARRAY_INIT;
608 Button* button = nullptr;
609 HDC hdc = nullptr;
610 int pos_x = 0;
611 int pos_y = 0;
612 NONCLIENTMETRICS ncm = WINPR_C_ARRAY_INIT;
613
614 switch (Msg)
615 {
616 case WM_CREATE:
617 floatbar = ((wfFloatBar*)((CREATESTRUCT*)lParam)->lpCreateParams);
618 floatbar->hwnd = hWnd;
619 GetWindowRect(floatbar->hwnd, &floatbar->rect);
620 floatbar->width = floatbar->rect.right - floatbar->rect.left;
621 floatbar->height = floatbar->rect.bottom - floatbar->rect.top;
622 floatbar_update_layout(floatbar);
623 hdc = GetDC(hWnd);
624 floatbar->hdcmem = CreateCompatibleDC(hdc);
625 ReleaseDC(hWnd, hdc);
626 tme.cbSize = sizeof(TRACKMOUSEEVENT);
627 tme.dwFlags = TME_LEAVE;
628 tme.hwndTrack = hWnd;
629 tme.dwHoverTime = HOVER_DEFAULT;
630 // Use caption font, white, draw transparent
631 SetBkMode(hdc, TRANSPARENT);
632 SetTextColor(hdc, RGB(255, 255, 255));
633 ncm.cbSize = sizeof(NONCLIENTMETRICS);
634 SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &ncm, 0);
635 SelectObject(hdc, CreateFontIndirect(&ncm.lfCaptionFont));
636 floatbar_trigger_hide(floatbar);
637 break;
638
639 case WM_PAINT:
640 hdc = BeginPaint(hWnd, &ps);
641 floatbar_paint(floatbar, hdc);
642 EndPaint(hWnd, &ps);
643 break;
644
645 case WM_ERASEBKGND:
646 return TRUE;
647
648 case WM_LBUTTONDOWN:
649 pos_x = LOWORD(lParam);
650 pos_y = HIWORD(lParam);
651 button = floatbar_get_button(floatbar, pos_x, pos_y);
652
653 if (!button)
654 {
655 pt.x = pos_x;
656 pt.y = pos_y;
657 ClientToScreen(hWnd, &pt);
658 SetCapture(hWnd);
659 floatbar_kill_timers(floatbar);
660 drag_mode = floatbar_get_resize_mode(floatbar, pos_x, pos_y);
661 if (drag_mode == DRAG_NONE)
662 drag_mode = DRAG_MOVE;
663 else
664 SetCursor(LoadCursor(nullptr, IDC_SIZEWE));
665 btn_dwn_x = pos_x;
666 btn_dwn_y = pos_y;
667 btn_dwn_screen_x = pt.x;
668 drag_start_left = floatbar->rect.left;
669 drag_start_width = floatbar->width;
670 }
671 else
672 lbtn_dwn = TRUE;
673
674 break;
675
676 case WM_LBUTTONUP:
677 pos_x = LOWORD(lParam);
678 pos_y = HIWORD(lParam);
679 const BOOL was_dragging = drag_mode != DRAG_NONE;
680 drag_mode = DRAG_NONE;
681 ReleaseCapture();
682 if (was_dragging)
683 floatbar_trigger_hide(floatbar);
684
685 if (lbtn_dwn)
686 {
687 button = floatbar_get_button(floatbar, pos_x, pos_y);
688
689 if (button)
690 button_hit(button);
691
692 lbtn_dwn = FALSE;
693 }
694
695 break;
696
697 case WM_LBUTTONDBLCLK:
698 pos_x = LOWORD(lParam);
699 pos_y = HIWORD(lParam);
700
701 if (!floatbar_get_button(floatbar, pos_x, pos_y) && floatbar->wfc->fullscreen_toggle &&
702 floatbar->wfc->fullscreen)
703 wf_toggle_fullscreen(floatbar->wfc);
704 break;
705
706 case WM_MOUSEMOVE:
707 pos_x = LOWORD(lParam);
708 pos_y = HIWORD(lParam);
709
710 if (!floatbar->locked)
711 floatbar_animation(floatbar, TRUE);
712
713 if (drag_mode != DRAG_NONE)
714 {
715 // Use screen pos during left-side resize to sync new position and avoid jittering.
716 pt.x = pos_x;
717 pt.y = pos_y;
718 ClientToScreen(hWnd, &pt);
719
720 if (drag_mode == DRAG_MOVE)
721 {
722 const int dy = pos_y - btn_dwn_y;
723 const int dy_threshold = floatbar->height * RESTORE_DRAG_BAR_MULTIPLIER;
724
725 if (dy > dy_threshold && floatbar->wfc->fullscreen_toggle &&
726 floatbar->wfc->fullscreen)
727 {
728 drag_mode = DRAG_NONE;
729 ReleaseCapture();
730 wf_toggle_fullscreen(floatbar->wfc);
731 break;
732 }
733
734 const int xScreen = floatbar_parent_width(floatbar);
735 floatbar->rect.left = floatbar->rect.left + pos_x - btn_dwn_x;
736
737 if (floatbar->rect.left < 0)
738 floatbar->rect.left = 0;
739 else if (floatbar->rect.left > xScreen - floatbar->width)
740 floatbar->rect.left = xScreen - floatbar->width;
741
742 MoveWindow(hWnd, floatbar->rect.left, 0, floatbar->width, floatbar->height,
743 TRUE);
744 }
745 else
746 {
747 const int dx = pt.x - btn_dwn_screen_x;
748 LONG left = drag_start_left;
749 LONG width = drag_start_width;
750
751 if (drag_mode == DRAG_RESIZE_LEFT)
752 {
753 left = drag_start_left + dx;
754 width = drag_start_width - dx;
755
756 if (width < MIN_FLOATBAR_WIDTH)
757 {
758 width = MIN_FLOATBAR_WIDTH;
759 left = drag_start_left + drag_start_width - width;
760 }
761 }
762 else if (drag_mode == DRAG_RESIZE_RIGHT)
763 {
764 width = drag_start_width + dx;
765 }
766
767 floatbar_resize(floatbar, left, width);
768 }
769 break;
770 }
771 else
772 {
773 button = floatbar_get_button(floatbar, pos_x, pos_y);
774 floatbar_update_hover(floatbar, button);
775 }
776
777 TrackMouseEvent(&tme);
778 break;
779
780 case WM_CAPTURECHANGED:
781 if (drag_mode != DRAG_NONE)
782 floatbar_trigger_hide(floatbar);
783
784 drag_mode = DRAG_NONE;
785 break;
786
787 case WM_SETCURSOR:
788 if (LOWORD(lParam) == HTCLIENT)
789 {
790 GetCursorPos(&pt);
791 ScreenToClient(hWnd, &pt);
792
793 if ((drag_mode == DRAG_RESIZE_LEFT) || (drag_mode == DRAG_RESIZE_RIGHT) ||
794 (floatbar_get_resize_mode(floatbar, pt.x, pt.y) != DRAG_NONE))
795 SetCursor(LoadCursor(nullptr, IDC_SIZEWE));
796 else
797 SetCursor(LoadCursor(nullptr, IDC_ARROW));
798
799 return TRUE;
800 }
801
802 return DefWindowProc(hWnd, Msg, wParam, lParam);
803
804 case WM_MOUSELEAVE:
805 {
806 floatbar_update_hover(floatbar, nullptr);
807 floatbar_trigger_hide(floatbar);
808 break;
809 }
810
811 case WM_TIMER:
812 switch (wParam)
813 {
814 case TIMER_HIDE:
815 floatbar_animation(floatbar, FALSE);
816 break;
817
818 case TIMER_ANIMAT_SHOW:
819 {
820 floatbar->offset--;
821 MoveWindow(floatbar->hwnd, floatbar->rect.left, -floatbar->offset,
822 floatbar->width, floatbar->height, TRUE);
823
824 if (floatbar->offset <= 0)
825 floatbar_show(floatbar);
826
827 break;
828 }
829
830 case TIMER_ANIMAT_HIDE:
831 {
832 floatbar->offset++;
833 MoveWindow(floatbar->hwnd, floatbar->rect.left, -floatbar->offset,
834 floatbar->width, floatbar->height, TRUE);
835
836 if (floatbar->offset >= floatbar->height - 2)
837 floatbar_hide(floatbar);
838
839 break;
840 }
841
842 default:
843 break;
844 }
845
846 break;
847
848 case WM_DESTROY:
849 DeleteDC(floatbar->hdcmem);
850 PostQuitMessage(0);
851 break;
852
853 default:
854 return DefWindowProc(hWnd, Msg, wParam, lParam);
855 }
856
857 return 0;
858}
859
860static BOOL floatbar_window_create(wfFloatBar* floatbar)
861{
862 WNDCLASSEX wnd_cls;
863 HWND barWnd;
864 RECT rect;
865 LONG x;
866
867 if (!floatbar)
868 return FALSE;
869
870 if (!GetWindowRect(floatbar->parent, &rect))
871 return FALSE;
872
873 x = (rect.right - rect.left - BACKGROUND_W) / 2;
874 wnd_cls.cbSize = sizeof(WNDCLASSEX);
875 wnd_cls.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC | CS_DBLCLKS;
876 wnd_cls.lpfnWndProc = floatbar_proc;
877 wnd_cls.cbClsExtra = 0;
878 wnd_cls.cbWndExtra = 0;
879 wnd_cls.hIcon = LoadIcon(nullptr, IDI_APPLICATION);
880 wnd_cls.hCursor = LoadCursor(floatbar->root_window, IDC_ARROW);
881 wnd_cls.hbrBackground = nullptr;
882 wnd_cls.lpszMenuName = nullptr;
883 wnd_cls.lpszClassName = L"floatbar";
884 wnd_cls.hInstance = floatbar->root_window;
885 wnd_cls.hIconSm = LoadIcon(nullptr, IDI_APPLICATION);
886 RegisterClassEx(&wnd_cls);
887 barWnd =
888 CreateWindowEx(WS_EX_TOPMOST, L"floatbar", L"floatbar", WS_CHILD, x, 0, BACKGROUND_W,
889 BACKGROUND_H, floatbar->parent, nullptr, floatbar->root_window, floatbar);
890
891 if (barWnd == nullptr)
892 return FALSE;
893
894 return floatbar_update_region(floatbar);
895}
896
897void wf_floatbar_free(wfFloatBar* floatbar)
898{
899 if (!floatbar)
900 return;
901
902 free(floatbar);
903}
904
905wfFloatBar* wf_floatbar_new(wfContext* wfc, HINSTANCE window, DWORD flags)
906{
907 wfFloatBar* floatbar;
908
909 /* Floatbar not enabled */
910 if ((flags & 0x0001) == 0)
911 return nullptr;
912
913 if (!wfc)
914 return nullptr;
915
916 // TODO: Disable for remote app
917 floatbar = (wfFloatBar*)calloc(1, sizeof(wfFloatBar));
918
919 if (!floatbar)
920 return nullptr;
921
922 floatbar->root_window = window;
923 floatbar->flags = flags;
924 floatbar->wfc = wfc;
925 floatbar->locked = (flags & 0x0002) != 0;
926 floatbar->shown = (flags & 0x0006) != 0; /* If it is loked or shown show it */
927 floatbar->hwnd = nullptr;
928 floatbar->parent = wfc->hwnd;
929 floatbar->hdcmem = nullptr;
930
931 if (wfc->fullscreen_toggle)
932 {
933 floatbar->buttons[0] =
934 floatbar_create_button(floatbar, BUTTON_MINIMIZE, IDB_MINIMIZE, IDB_MINIMIZE_ACT,
935 MINIMIZE_X, BUTTON_Y, BUTTON_HEIGHT, BUTTON_WIDTH);
936 floatbar->buttons[1] =
937 floatbar_create_button(floatbar, BUTTON_RESTORE, IDB_RESTORE, IDB_RESTORE_ACT,
938 RESTORE_X, BUTTON_Y, BUTTON_HEIGHT, BUTTON_WIDTH);
939 }
940 else
941 {
942 floatbar->buttons[0] = nullptr;
943 floatbar->buttons[1] = nullptr;
944 }
945
946 floatbar->buttons[2] = floatbar_create_button(floatbar, BUTTON_CLOSE, IDB_CLOSE, IDB_CLOSE_ACT,
947 CLOSE_X, BUTTON_Y, BUTTON_HEIGHT, BUTTON_WIDTH);
948 floatbar->buttons[3] =
949 floatbar_create_lock_button(floatbar, IDB_UNLOCK, IDB_UNLOCK_ACT, IDB_LOCK, IDB_LOCK_ACT,
950 LOCK_X, BUTTON_Y, BUTTON_HEIGHT, BUTTON_WIDTH);
951
952 if (!floatbar_window_create(floatbar))
953 goto fail;
954
955 if (!update_locked_state(floatbar))
956 goto fail;
957
958 if (!wf_floatbar_toggle_fullscreen(
959 floatbar, freerdp_settings_get_bool(wfc->common.context.settings, FreeRDP_Fullscreen)))
960 goto fail;
961
962 return floatbar;
963fail:
964 wf_floatbar_free(floatbar);
965 return nullptr;
966}
967
968BOOL wf_floatbar_toggle_fullscreen(wfFloatBar* floatbar, BOOL fullscreen)
969{
970 BOOL show_fs, show_wn;
971
972 if (!floatbar)
973 return FALSE;
974
975 show_fs = (floatbar->flags & 0x0010) != 0;
976 show_wn = (floatbar->flags & 0x0020) != 0;
977
978 if ((show_fs && fullscreen) || (show_wn && !fullscreen))
979 {
980 ShowWindow(floatbar->hwnd, SW_SHOWNORMAL);
981 Sleep(10);
982
983 if (floatbar->shown)
984 floatbar_show(floatbar);
985 else
986 floatbar_hide(floatbar);
987 }
988 else
989 {
990 ShowWindow(floatbar->hwnd, SW_HIDE);
991 }
992
993 return TRUE;
994}
995
996BOOL wf_floatbar_reset_position(wfFloatBar* floatbar)
997{
998 RECT rect = WINPR_C_ARRAY_INIT;
999
1000 if (!floatbar || !floatbar->parent || !floatbar->hwnd)
1001 return FALSE;
1002
1003 const int y = -WINPR_ASSERTING_INT_CAST(int, floatbar->offset);
1004
1005 if (!GetWindowRect(floatbar->parent, &rect))
1006 return FALSE;
1007
1008 floatbar->rect.left = ((rect.right - rect.left) - floatbar->width) / 2;
1009 if (floatbar->rect.left < 0)
1010 floatbar->rect.left = 0;
1011
1012 if (!MoveWindow(floatbar->hwnd, floatbar->rect.left, y, floatbar->width, floatbar->height,
1013 TRUE))
1014 {
1015 DWORD err = GetLastError();
1016 WLog_ERR(TAG, "MoveWindow failed with %08" PRIx32, err);
1017 return FALSE;
1018 }
1019
1020 return TRUE;
1021}
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.