FreeRDP
Loading...
Searching...
No Matches
wf_event.c
1
22#include <freerdp/config.h>
23
24#include <stdio.h>
25
26#include <winpr/assert.h>
27#include <winpr/sysinfo.h>
28#include <freerdp/freerdp.h>
29
30#include "wf_client.h"
31
32#include "wf_gdi.h"
33#include "wf_event.h"
34
35#include <freerdp/event.h>
36
37#include <windowsx.h>
38
39static HWND g_focus_hWnd = nullptr;
40static HWND g_main_hWnd = nullptr;
41static HWND g_parent_hWnd = nullptr;
42
43#define RESIZE_MIN_DELAY 200 /* minimum delay in ms between two resizes */
44
45static BOOL wf_scale_blt(wfContext* wfc, HDC hdc, int x, int y, int w, int h, HDC hdcSrc, int x1,
46 int y1, DWORD rop);
47static BOOL wf_scale_mouse_event(wfContext* wfc, UINT16 flags, INT32 x, INT32 y);
48#if (_WIN32_WINNT >= 0x0500)
49static BOOL wf_scale_mouse_event_ex(wfContext* wfc, UINT16 flags, UINT16 buttonMask, INT32 x,
50 INT32 y);
51#endif
52
53static BOOL g_flipping_in = FALSE;
54static BOOL g_flipping_out = FALSE;
55
56static BOOL g_keystates[256] = WINPR_C_ARRAY_INIT;
57
58static BOOL ctrl_down(void)
59{
60 return g_keystates[VK_CONTROL] || g_keystates[VK_LCONTROL] || g_keystates[VK_RCONTROL];
61}
62
63static BOOL alt_ctrl_down(void)
64{
65 const BOOL altDown = g_keystates[VK_MENU] || g_keystates[VK_LMENU] || g_keystates[VK_RMENU];
66 return altDown && ctrl_down();
67}
68
69LRESULT CALLBACK wf_ll_kbd_proc(int nCode, WPARAM wParam, LPARAM lParam)
70{
71 DWORD ext_proc_id = 0;
72
73 wfContext* wfc = nullptr;
74 DWORD rdp_scancode;
75 BOOL keystate;
76 rdpInput* input;
77 PKBDLLHOOKSTRUCT p;
78
79 DEBUG_KBD("Low-level keyboard hook, hWnd %X nCode %X wParam %X", g_focus_hWnd, nCode, wParam);
80
81 if (g_flipping_in)
82 {
83 if (!alt_ctrl_down())
84 g_flipping_in = FALSE;
85
86 return CallNextHookEx(nullptr, nCode, wParam, lParam);
87 }
88
89 if (g_parent_hWnd && g_main_hWnd)
90 {
91 wfc = (wfContext*)GetWindowLongPtr(g_main_hWnd, GWLP_USERDATA);
92 GUITHREADINFO gui_thread_info;
93 gui_thread_info.cbSize = sizeof(GUITHREADINFO);
94 HWND fg_win_hwnd = GetForegroundWindow();
95 DWORD fg_win_thread_id = GetWindowThreadProcessId(fg_win_hwnd, &ext_proc_id);
96 BOOL result = GetGUIThreadInfo(fg_win_thread_id, &gui_thread_info);
97 if (gui_thread_info.hwndFocus != wfc->hWndParent)
98 {
99 g_focus_hWnd = nullptr;
100 return CallNextHookEx(nullptr, nCode, wParam, lParam);
101 }
102
103 g_focus_hWnd = g_main_hWnd;
104 }
105
106 if (g_focus_hWnd && (nCode == HC_ACTION))
107 {
108 switch (wParam)
109 {
110 case WM_KEYDOWN:
111 case WM_SYSKEYDOWN:
112 case WM_KEYUP:
113 case WM_SYSKEYUP:
114 if (!wfc)
115 wfc = (wfContext*)GetWindowLongPtr(g_focus_hWnd, GWLP_USERDATA);
116 p = (PKBDLLHOOKSTRUCT)lParam;
117
118 if (!wfc || !p)
119 return 1;
120
121 input = wfc->common.context.input;
122 rdp_scancode = MAKE_RDP_SCANCODE((BYTE)p->scanCode, p->flags & LLKHF_EXTENDED);
123 keystate = g_keystates[p->scanCode & 0xFF];
124
125 switch (wParam)
126 {
127 case WM_KEYDOWN:
128 case WM_SYSKEYDOWN:
129 g_keystates[p->scanCode & 0xFF] = TRUE;
130 break;
131 case WM_KEYUP:
132 case WM_SYSKEYUP:
133 default:
134 g_keystates[p->scanCode & 0xFF] = FALSE;
135 break;
136 }
137 DEBUG_KBD("keydown %d scanCode 0x%08lX flags 0x%08lX vkCode 0x%08lX",
138 (wParam == WM_KEYDOWN), p->scanCode, p->flags, p->vkCode);
139
140 if (wfc->fullscreen_toggle && (p->vkCode == VK_RETURN || p->vkCode == VK_CANCEL))
141 {
142 if (alt_ctrl_down())
143 {
144 if (wParam == WM_KEYDOWN)
145 {
146 wf_toggle_fullscreen(wfc);
147 return 1;
148 }
149 }
150 }
151
152 if (rdp_scancode == RDP_SCANCODE_NUMLOCK_EXTENDED)
153 {
154 /* Windows sends NumLock as extended - rdp doesn't */
155 DEBUG_KBD("hack: NumLock (x45) should not be extended");
156 rdp_scancode = RDP_SCANCODE_NUMLOCK;
157 }
158 else if (rdp_scancode == RDP_SCANCODE_NUMLOCK)
159 {
160 /* Windows sends Pause as if it was a RDP NumLock (handled above).
161 * It must however be sent as a one-shot Ctrl+NumLock */
162 if (wParam == WM_KEYDOWN)
163 {
164 DEBUG_KBD("Pause, sent as Ctrl+NumLock");
165 freerdp_input_send_keyboard_event_ex(input, TRUE, FALSE,
166 RDP_SCANCODE_LCONTROL);
167 freerdp_input_send_keyboard_event_ex(input, TRUE, FALSE,
168 RDP_SCANCODE_NUMLOCK);
169 freerdp_input_send_keyboard_event_ex(input, FALSE, FALSE,
170 RDP_SCANCODE_LCONTROL);
171 freerdp_input_send_keyboard_event_ex(input, FALSE, FALSE,
172 RDP_SCANCODE_NUMLOCK);
173 }
174 else
175 {
176 DEBUG_KBD("Pause up");
177 }
178
179 return 1;
180 }
181 else if (rdp_scancode == RDP_SCANCODE_RSHIFT_EXTENDED)
182 {
183 DEBUG_KBD("right shift (x36) should not be extended");
184 rdp_scancode = RDP_SCANCODE_RSHIFT;
185 }
186
187 freerdp_input_send_keyboard_event_ex(input, !(p->flags & LLKHF_UP), keystate,
188 rdp_scancode);
189
190 if (p->vkCode == VK_NUMLOCK || p->vkCode == VK_CAPITAL || p->vkCode == VK_SCROLL ||
191 p->vkCode == VK_KANA)
192 DEBUG_KBD(
193 "lock keys are processed on client side too to toggle their indicators");
194 else
195 return 1;
196
197 break;
198 default:
199 break;
200 }
201 }
202
203 if (g_flipping_out)
204 {
205 if (!alt_ctrl_down())
206 {
207 g_flipping_out = FALSE;
208 g_focus_hWnd = nullptr;
209 }
210 }
211
212 return CallNextHookEx(nullptr, nCode, wParam, lParam);
213}
214
215void wf_event_focus_in(wfContext* wfc)
216{
217 UINT16 syncFlags;
218 rdpInput* input;
219 POINT pt;
220 RECT rc;
221 input = wfc->common.context.input;
222 syncFlags = 0;
223
224 if (GetKeyState(VK_NUMLOCK))
225 syncFlags |= KBD_SYNC_NUM_LOCK;
226
227 if (GetKeyState(VK_CAPITAL))
228 syncFlags |= KBD_SYNC_CAPS_LOCK;
229
230 if (GetKeyState(VK_SCROLL))
231 syncFlags |= KBD_SYNC_SCROLL_LOCK;
232
233 if (GetKeyState(VK_KANA))
234 syncFlags |= KBD_SYNC_KANA_LOCK;
235
236 input->FocusInEvent(input, syncFlags);
237 /* send pointer position if the cursor is currently inside our client area */
238 GetCursorPos(&pt);
239 ScreenToClient(wfc->hwnd, &pt);
240 GetClientRect(wfc->hwnd, &rc);
241
242 if (pt.x >= rc.left && pt.x < rc.right && pt.y >= rc.top && pt.y < rc.bottom)
243 input->MouseEvent(input, PTR_FLAGS_MOVE, (UINT16)pt.x, (UINT16)pt.y);
244}
245
246static BOOL wf_event_process_WM_MOUSEWHEEL(wfContext* wfc, HWND hWnd, UINT Msg, WPARAM wParam,
247 LPARAM lParam, BOOL horizontal, INT32 x, INT32 y)
248{
249 int delta;
250 UINT16 flags = 0;
251 rdpInput* input;
252
253 WINPR_ASSERT(wfc);
254
255 input = wfc->common.context.input;
256 WINPR_ASSERT(input);
257
258 DefWindowProc(hWnd, Msg, wParam, lParam);
259 delta = ((signed short)HIWORD(wParam)); /* GET_WHEEL_DELTA_WPARAM(wParam); */
260
261 if (horizontal)
262 flags |= PTR_FLAGS_HWHEEL;
263 else
264 flags |= PTR_FLAGS_WHEEL;
265
266 if (delta < 0)
267 {
268 flags |= PTR_FLAGS_WHEEL_NEGATIVE;
269 /* 9bit twos complement, delta already negative */
270 delta = 0x100 + delta;
271 }
272
273 flags |= delta;
274 return wf_scale_mouse_event(wfc, flags, x, y);
275}
276
277static void wf_sizing(wfContext* wfc, WPARAM wParam, LPARAM lParam)
278{
279 rdpSettings* settings = wfc->common.context.settings;
280 // Holding the CTRL key down while resizing the window will force the desktop aspect ratio.
281 LPRECT rect;
282
283 if ((freerdp_settings_get_bool(settings, FreeRDP_SmartSizing) ||
284 freerdp_settings_get_bool(settings, FreeRDP_DynamicResolutionUpdate)) &&
285 ctrl_down())
286 {
287 rect = (LPRECT)wParam;
288
289 switch (lParam)
290 {
291 case WMSZ_LEFT:
292 case WMSZ_RIGHT:
293 case WMSZ_BOTTOMRIGHT:
294 // Adjust height
295 rect->bottom =
296 rect->top + freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight) *
297 (rect->right - rect->left) /
298 freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth);
299 break;
300
301 case WMSZ_TOP:
302 case WMSZ_BOTTOM:
303 case WMSZ_TOPRIGHT:
304 // Adjust width
305 rect->right =
306 rect->left + freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth) *
307 (rect->bottom - rect->top) /
308 freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight);
309 break;
310
311 case WMSZ_BOTTOMLEFT:
312 case WMSZ_TOPLEFT:
313 // adjust width
314 rect->left =
315 rect->right - (freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth) *
316 (rect->bottom - rect->top) /
317 freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight));
318 break;
319 }
320 }
321}
322
323static void wf_send_resize(wfContext* wfc)
324{
325 RECT windowRect;
326 int targetWidth = wfc->client_width;
327 int targetHeight = wfc->client_height;
328 rdpSettings* settings = wfc->common.context.settings;
329
330 if (freerdp_settings_get_bool(settings, FreeRDP_DynamicResolutionUpdate) &&
331 wfc->disp != nullptr)
332 {
333 if (GetTickCount64() - wfc->lastSentDate > RESIZE_MIN_DELAY)
334 {
335 if (wfc->fullscreen)
336 {
337 GetWindowRect(wfc->hwnd, &windowRect);
338 targetWidth = windowRect.right - windowRect.left;
339 targetHeight = windowRect.bottom - windowRect.top;
340 }
341 if (freerdp_settings_get_uint32(settings, FreeRDP_SmartSizingWidth) != targetWidth ||
342 freerdp_settings_get_uint32(settings, FreeRDP_SmartSizingHeight) != targetHeight)
343 {
344 DISPLAY_CONTROL_MONITOR_LAYOUT layout = WINPR_C_ARRAY_INIT;
345
346 layout.Flags = DISPLAY_CONTROL_MONITOR_PRIMARY;
347 layout.Top = layout.Left = 0;
348 layout.Width = targetWidth;
349 layout.Height = targetHeight;
350 layout.Orientation =
351 freerdp_settings_get_uint16(settings, FreeRDP_DesktopOrientation);
352 layout.DesktopScaleFactor =
353 freerdp_settings_get_uint32(settings, FreeRDP_DesktopScaleFactor);
354 layout.DeviceScaleFactor =
355 freerdp_settings_get_uint32(settings, FreeRDP_DeviceScaleFactor);
356 layout.PhysicalWidth = targetWidth;
357 layout.PhysicalHeight = targetHeight;
358
359 if (IFCALLRESULT(CHANNEL_RC_OK, wfc->disp->SendMonitorLayout, wfc->disp, 1,
360 &layout) != CHANNEL_RC_OK)
361 {
362 WLog_ERR("", "SendMonitorLayout failed.");
363 }
364 (void)freerdp_settings_set_uint32(settings, FreeRDP_SmartSizingWidth, targetWidth);
365 (void)freerdp_settings_set_uint32(settings, FreeRDP_SmartSizingHeight,
366 targetHeight);
367 }
368 wfc->lastSentDate = GetTickCount64();
369 }
370 }
371}
372
373LRESULT CALLBACK wf_event_proc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
374{
375 HDC hdc = WINPR_C_ARRAY_INIT;
376 PAINTSTRUCT ps = WINPR_C_ARRAY_INIT;
377 BOOL processed = FALSE;
378 RECT windowRect = WINPR_C_ARRAY_INIT;
379 MINMAXINFO* minmax = nullptr;
380 SCROLLINFO si = WINPR_C_ARRAY_INIT;
381 processed = TRUE;
382 LONG_PTR ptr = GetWindowLongPtr(hWnd, GWLP_USERDATA);
383 wfContext* wfc = (wfContext*)ptr;
384
385 if (wfc != nullptr)
386 {
387 rdpInput* input = wfc->common.context.input;
388 rdpSettings* settings = wfc->common.context.settings;
389
390 if (!g_parent_hWnd && wfc->hWndParent)
391 g_parent_hWnd = wfc->hWndParent;
392
393 if (!g_main_hWnd)
394 g_main_hWnd = wfc->hwnd;
395
396 switch (Msg)
397 {
398 case WM_MOVE:
399 if (!wfc->disablewindowtracking)
400 {
401 int x = (int)(short)LOWORD(lParam);
402 int y = (int)(short)HIWORD(lParam);
403 wfc->client_x = x;
404 wfc->client_y = y;
405 }
406
407 break;
408
409 case WM_GETMINMAXINFO:
410 if (freerdp_settings_get_bool(settings, FreeRDP_SmartSizing) ||
411 (freerdp_settings_get_bool(settings, FreeRDP_DynamicResolutionUpdate)))
412 {
413 processed = FALSE;
414 }
415 else
416 {
417 // Set maximum window size for resizing
418 minmax = (MINMAXINFO*)lParam;
419
420 // always use the last determined canvas diff, because it could be
421 // that the window is minimized when this gets called
422 // wf_update_canvas_diff(wfc);
423
424 if (!wfc->fullscreen)
425 {
426 // add window decoration
427 minmax->ptMaxTrackSize.x =
428 freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth) +
429 wfc->diff.x;
430 minmax->ptMaxTrackSize.y =
431 freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight) +
432 wfc->diff.y;
433 }
434 }
435
436 break;
437
438 case WM_SIZING:
439 wf_sizing(wfc, lParam, wParam);
440 break;
441
442 case WM_SIZE:
443 GetWindowRect(wfc->hwnd, &windowRect);
444
445 if (!wfc->fullscreen)
446 {
447 wfc->client_width = LOWORD(lParam);
448 wfc->client_height = HIWORD(lParam);
449 wfc->client_x = windowRect.left;
450 wfc->client_y = windowRect.top;
451 }
452 else
453 {
454 wfc->wasMaximized = TRUE;
455 wf_send_resize(wfc);
456 }
457
458 if (wfc->client_width && wfc->client_height)
459 {
460 wf_size_scrollbars(wfc, LOWORD(lParam), HIWORD(lParam));
461
462 // Workaround: when the window is maximized, the call to "ShowScrollBars"
463 // returns TRUE but has no effect.
464 if (wParam == SIZE_MAXIMIZED && !wfc->fullscreen)
465 {
466 SetWindowPos(wfc->hwnd, HWND_TOP, 0, 0, windowRect.right - windowRect.left,
467 windowRect.bottom - windowRect.top,
468 SWP_NOMOVE | SWP_FRAMECHANGED);
469 wfc->wasMaximized = TRUE;
470 wf_send_resize(wfc);
471 }
472 else if (wParam == SIZE_RESTORED && !wfc->fullscreen && wfc->wasMaximized)
473 {
474 wfc->wasMaximized = FALSE;
475 wf_send_resize(wfc);
476 }
477 else if (wParam == SIZE_MINIMIZED)
478 {
479 g_focus_hWnd = nullptr;
480 }
481 }
482
483 break;
484
485 case WM_EXITSIZEMOVE:
486 wf_size_scrollbars(wfc, wfc->client_width, wfc->client_height);
487 wf_send_resize(wfc);
488 break;
489
490 case WM_ERASEBKGND:
491 /* Say we handled it - prevents flickering */
492 return (LRESULT)1;
493
494 case WM_PAINT:
495 {
496 hdc = BeginPaint(hWnd, &ps);
497 const int x = ps.rcPaint.left;
498 const int y = ps.rcPaint.top;
499 const int w = ps.rcPaint.right - ps.rcPaint.left + 1;
500 const int h = ps.rcPaint.bottom - ps.rcPaint.top + 1;
501 wf_scale_blt(wfc, hdc, x, y, w, h, wfc->primary->hdc,
502 x - wfc->offset_x + wfc->xCurrentScroll,
503 y - wfc->offset_y + wfc->yCurrentScroll, SRCCOPY);
504 EndPaint(hWnd, &ps);
505 }
506 break;
507#if (_WIN32_WINNT >= 0x0500)
508
509 case WM_XBUTTONDOWN:
510 wf_scale_mouse_event_ex(wfc, PTR_XFLAGS_DOWN, GET_XBUTTON_WPARAM(wParam),
511 GET_X_LPARAM(lParam) - wfc->offset_x,
512 GET_Y_LPARAM(lParam) - wfc->offset_y);
513 break;
514
515 case WM_XBUTTONUP:
516 wf_scale_mouse_event_ex(wfc, 0, GET_XBUTTON_WPARAM(wParam),
517 GET_X_LPARAM(lParam) - wfc->offset_x,
518 GET_Y_LPARAM(lParam) - wfc->offset_y);
519 break;
520#endif
521
522 case WM_MBUTTONDOWN:
523 wf_scale_mouse_event(wfc, PTR_FLAGS_DOWN | PTR_FLAGS_BUTTON3,
524 GET_X_LPARAM(lParam) - wfc->offset_x,
525 GET_Y_LPARAM(lParam) - wfc->offset_y);
526 break;
527
528 case WM_MBUTTONUP:
529 wf_scale_mouse_event(wfc, PTR_FLAGS_BUTTON3, GET_X_LPARAM(lParam) - wfc->offset_x,
530 GET_Y_LPARAM(lParam) - wfc->offset_y);
531 break;
532
533 case WM_LBUTTONDOWN:
534 wf_scale_mouse_event(wfc, PTR_FLAGS_DOWN | PTR_FLAGS_BUTTON1,
535 GET_X_LPARAM(lParam) - wfc->offset_x,
536 GET_Y_LPARAM(lParam) - wfc->offset_y);
537 SetCapture(wfc->hwnd);
538 break;
539
540 case WM_LBUTTONUP:
541 wf_scale_mouse_event(wfc, PTR_FLAGS_BUTTON1, GET_X_LPARAM(lParam) - wfc->offset_x,
542 GET_Y_LPARAM(lParam) - wfc->offset_y);
543 ReleaseCapture();
544 break;
545
546 case WM_RBUTTONDOWN:
547 wf_scale_mouse_event(wfc, PTR_FLAGS_DOWN | PTR_FLAGS_BUTTON2,
548 GET_X_LPARAM(lParam) - wfc->offset_x,
549 GET_Y_LPARAM(lParam) - wfc->offset_y);
550 break;
551
552 case WM_RBUTTONUP:
553 wf_scale_mouse_event(wfc, PTR_FLAGS_BUTTON2, GET_X_LPARAM(lParam) - wfc->offset_x,
554 GET_Y_LPARAM(lParam) - wfc->offset_y);
555 break;
556
557 case WM_MOUSEMOVE:
558 wf_scale_mouse_event(wfc, PTR_FLAGS_MOVE, GET_X_LPARAM(lParam) - wfc->offset_x,
559 GET_Y_LPARAM(lParam) - wfc->offset_y);
560 break;
561#if (_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)
562
563 case WM_MOUSEWHEEL:
564 wf_event_process_WM_MOUSEWHEEL(wfc, hWnd, Msg, wParam, lParam, FALSE,
565 GET_X_LPARAM(lParam) - wfc->offset_x,
566 GET_Y_LPARAM(lParam) - wfc->offset_y);
567 break;
568#endif
569#if (_WIN32_WINNT >= 0x0600)
570
571 case WM_MOUSEHWHEEL:
572 wf_event_process_WM_MOUSEWHEEL(wfc, hWnd, Msg, wParam, lParam, TRUE,
573 GET_X_LPARAM(lParam) - wfc->offset_x,
574 GET_Y_LPARAM(lParam) - wfc->offset_y);
575 break;
576#endif
577
578 case WM_SETCURSOR:
579 if (LOWORD(lParam) == HTCLIENT)
580 SetCursor(wfc->cursor);
581 else
582 DefWindowProc(hWnd, Msg, wParam, lParam);
583
584 break;
585
586 case WM_HSCROLL:
587 {
588 int xDelta; // xDelta = new_pos - current_pos
589 int xNewPos; // new position
590 int yDelta = 0;
591
592 switch (LOWORD(wParam))
593 {
594 // User clicked the scroll bar shaft left of the scroll box.
595 case SB_PAGEUP:
596 xNewPos = wfc->xCurrentScroll - 50;
597 break;
598
599 // User clicked the scroll bar shaft right of the scroll box.
600 case SB_PAGEDOWN:
601 xNewPos = wfc->xCurrentScroll + 50;
602 break;
603
604 // User clicked the left arrow.
605 case SB_LINEUP:
606 xNewPos = wfc->xCurrentScroll - 5;
607 break;
608
609 // User clicked the right arrow.
610 case SB_LINEDOWN:
611 xNewPos = wfc->xCurrentScroll + 5;
612 break;
613
614 // User dragged the scroll box.
615 case SB_THUMBPOSITION:
616 xNewPos = HIWORD(wParam);
617 break;
618
619 // user is dragging the scrollbar
620 case SB_THUMBTRACK:
621 xNewPos = HIWORD(wParam);
622 break;
623
624 default:
625 xNewPos = wfc->xCurrentScroll;
626 }
627
628 // New position must be between 0 and the screen width.
629 xNewPos = MAX(0, xNewPos);
630 xNewPos = MIN(wfc->xMaxScroll, xNewPos);
631
632 // If the current position does not change, do not scroll.
633 if (xNewPos == wfc->xCurrentScroll)
634 break;
635
636 // Determine the amount scrolled (in pixels).
637 xDelta = xNewPos - wfc->xCurrentScroll;
638 // Reset the current scroll position.
639 wfc->xCurrentScroll = xNewPos;
640 // Scroll the window. (The system repaints most of the
641 // client area when ScrollWindowEx is called; however, it is
642 // necessary to call UpdateWindow in order to repaint the
643 // rectangle of pixels that were invalidated.)
644 ScrollWindowEx(wfc->hwnd, -xDelta, -yDelta, (CONST RECT*)nullptr,
645 (CONST RECT*)nullptr, (HRGN) nullptr, (PRECT) nullptr,
646 SW_INVALIDATE);
647 UpdateWindow(wfc->hwnd);
648 // Reset the scroll bar.
649 si.cbSize = sizeof(si);
650 si.fMask = SIF_POS;
651 si.nPos = wfc->xCurrentScroll;
652 SetScrollInfo(wfc->hwnd, SB_HORZ, &si, TRUE);
653 }
654 break;
655
656 case WM_VSCROLL:
657 {
658 int xDelta = 0;
659 int yDelta; // yDelta = new_pos - current_pos
660 int yNewPos; // new position
661
662 switch (LOWORD(wParam))
663 {
664 // User clicked the scroll bar shaft above the scroll box.
665 case SB_PAGEUP:
666 yNewPos = wfc->yCurrentScroll - 50;
667 break;
668
669 // User clicked the scroll bar shaft below the scroll box.
670 case SB_PAGEDOWN:
671 yNewPos = wfc->yCurrentScroll + 50;
672 break;
673
674 // User clicked the top arrow.
675 case SB_LINEUP:
676 yNewPos = wfc->yCurrentScroll - 5;
677 break;
678
679 // User clicked the bottom arrow.
680 case SB_LINEDOWN:
681 yNewPos = wfc->yCurrentScroll + 5;
682 break;
683
684 // User dragged the scroll box.
685 case SB_THUMBPOSITION:
686 yNewPos = HIWORD(wParam);
687 break;
688
689 // user is dragging the scrollbar
690 case SB_THUMBTRACK:
691 yNewPos = HIWORD(wParam);
692 break;
693
694 default:
695 yNewPos = wfc->yCurrentScroll;
696 }
697
698 // New position must be between 0 and the screen height.
699 yNewPos = MAX(0, yNewPos);
700 yNewPos = MIN(wfc->yMaxScroll, yNewPos);
701
702 // If the current position does not change, do not scroll.
703 if (yNewPos == wfc->yCurrentScroll)
704 break;
705
706 // Determine the amount scrolled (in pixels).
707 yDelta = yNewPos - wfc->yCurrentScroll;
708 // Reset the current scroll position.
709 wfc->yCurrentScroll = yNewPos;
710 // Scroll the window. (The system repaints most of the
711 // client area when ScrollWindowEx is called; however, it is
712 // necessary to call UpdateWindow in order to repaint the
713 // rectangle of pixels that were invalidated.)
714 ScrollWindowEx(wfc->hwnd, -xDelta, -yDelta, (CONST RECT*)nullptr,
715 (CONST RECT*)nullptr, (HRGN) nullptr, (PRECT) nullptr,
716 SW_INVALIDATE);
717 UpdateWindow(wfc->hwnd);
718 // Reset the scroll bar.
719 si.cbSize = sizeof(si);
720 si.fMask = SIF_POS;
721 si.nPos = wfc->yCurrentScroll;
722 SetScrollInfo(wfc->hwnd, SB_VERT, &si, TRUE);
723 }
724 break;
725
726 case WM_SYSCOMMAND:
727 {
728 if (wParam == SYSCOMMAND_ID_SMARTSIZING)
729 {
730 HMENU hMenu = GetSystemMenu(wfc->hwnd, FALSE);
731 const BOOL rc = freerdp_settings_get_bool(settings, FreeRDP_SmartSizing);
732 (void)freerdp_settings_set_bool(settings, FreeRDP_SmartSizing, !rc);
733 CheckMenuItem(hMenu, SYSCOMMAND_ID_SMARTSIZING,
734 freerdp_settings_get_bool(settings, FreeRDP_SmartSizing)
735 ? MF_CHECKED
736 : MF_UNCHECKED);
737 if (!freerdp_settings_get_bool(settings, FreeRDP_SmartSizing))
738 {
739 SetWindowPos(wfc->hwnd, HWND_TOP, -1, -1,
740 freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth) +
741 wfc->diff.x,
742 freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight) +
743 wfc->diff.y,
744 SWP_NOMOVE);
745 }
746 else
747 {
748 wf_size_scrollbars(wfc, wfc->client_width, wfc->client_height);
749 wf_send_resize(wfc);
750 }
751 }
752 else if (wParam == SYSCOMMAND_ID_REQUEST_CONTROL)
753 {
754 freerdp_client_encomsp_set_control(wfc->common.encomsp, TRUE);
755 }
756 else
757 {
758 processed = FALSE;
759 }
760 }
761 break;
762
763 default:
764 processed = FALSE;
765 break;
766 }
767 }
768 else
769 {
770 processed = FALSE;
771 }
772
773 if (processed)
774 return 0;
775
776 switch (Msg)
777 {
778 case WM_DESTROY:
779 PostQuitMessage(WM_QUIT);
780 break;
781
782 case WM_SETFOCUS:
783 DEBUG_KBD("getting focus %X", hWnd);
784
785 (void)freerdp_settings_set_bool(wfc->common.context.settings, FreeRDP_SuspendInput,
786 FALSE);
787
788 if (alt_ctrl_down())
789 g_flipping_in = TRUE;
790
791 g_focus_hWnd = hWnd;
792 freerdp_set_focus(wfc->common.context.instance);
793 wf_event_focus_in(wfc);
794 break;
795
796 case WM_KILLFOCUS:
797 (void)freerdp_settings_set_bool(wfc->common.context.settings, FreeRDP_SuspendInput,
798 TRUE);
799
800 if (g_focus_hWnd == hWnd && wfc && !wfc->fullscreen)
801 {
802 DEBUG_KBD("losing focus %X", hWnd);
803
804 if (alt_ctrl_down())
805 g_flipping_out = TRUE;
806 else
807 g_focus_hWnd = nullptr;
808 }
809
810 break;
811
812 case WM_ACTIVATE:
813 {
814 int activate = (int)(short)LOWORD(wParam);
815 BOOL minimized_flag = (BOOL)HIWORD(wParam);
816
817 if (activate != WA_INACTIVE && !minimized_flag)
818 {
819 if (alt_ctrl_down())
820 g_flipping_in = TRUE;
821
822 g_focus_hWnd = hWnd;
823 }
824 else
825 {
826 if (alt_ctrl_down())
827 g_flipping_out = TRUE;
828 else
829 g_focus_hWnd = nullptr;
830 }
831 }
832
833 default:
834 return DefWindowProc(hWnd, Msg, wParam, lParam);
835 break;
836 }
837
838 return 0;
839}
840
841BOOL wf_scale_blt(wfContext* wfc, HDC hdc, int x, int y, int w, int h, HDC hdcSrc, int x1, int y1,
842 DWORD rop)
843{
844 UINT32 ww, wh, dw, dh;
845 WINPR_ASSERT(wfc);
846
847 rdpSettings* settings = wfc->common.context.settings;
848 WINPR_ASSERT(settings);
849
850 if (!wfc->client_width)
851 wfc->client_width = freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth);
852
853 if (!wfc->client_height)
854 wfc->client_height = freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight);
855
856 ww = wfc->client_width;
857 wh = wfc->client_height;
858 dw = freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth);
859 dh = freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight);
860
861 if (!ww)
862 ww = dw;
863
864 if (!wh)
865 wh = dh;
866
867 if (wfc->fullscreen || !freerdp_settings_get_bool(settings, FreeRDP_SmartSizing) ||
868 (ww == dw && wh == dh))
869 {
870 return BitBlt(hdc, x, y, w, h, wfc->primary->hdc, x1, y1, SRCCOPY);
871 }
872 else
873 {
874 SetStretchBltMode(hdc, HALFTONE);
875 SetBrushOrgEx(hdc, 0, 0, nullptr);
876 return StretchBlt(hdc, 0, 0, ww, wh, wfc->primary->hdc, 0, 0, dw, dh, SRCCOPY);
877 }
878
879 return TRUE;
880}
881
882static BOOL wf_scale_mouse_pos(wfContext* wfc, INT32 x, INT32 y, UINT16* px, UINT16* py)
883{
884 int ww, wh, dw, dh;
885 rdpSettings* settings;
886
887 if (!wfc || !px || !py)
888 return FALSE;
889
890 settings = wfc->common.context.settings;
891
892 if (!settings)
893 return FALSE;
894
895 if (!wfc->client_width)
896 wfc->client_width = freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth);
897
898 if (!wfc->client_height)
899 wfc->client_height = freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight);
900
901 ww = wfc->client_width;
902 wh = wfc->client_height;
903 dw = freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth);
904 dh = freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight);
905
906 if (!freerdp_settings_get_bool(settings, FreeRDP_SmartSizing) || ((ww == dw) && (wh == dh)))
907 {
908 x += wfc->xCurrentScroll;
909 y += wfc->yCurrentScroll;
910 }
911 else
912 {
913 x = x * dw / ww + wfc->xCurrentScroll;
914 y = y * dh / wh + wfc->yCurrentScroll;
915 }
916
917 *px = MIN(UINT16_MAX, MAX(0, x));
918 *py = MIN(UINT16_MAX, MAX(0, y));
919
920 return TRUE;
921}
922
923static BOOL wf_pub_mouse_event(wfContext* wfc, UINT16 flags, UINT16 x, UINT16 y)
924{
925 MouseEventEventArgs eventArgs = WINPR_C_ARRAY_INIT;
926
927 eventArgs.flags = flags;
928 eventArgs.x = x;
929 eventArgs.y = y;
930 PubSub_OnMouseEvent(wfc->common.context.pubSub, &wfc->common.context, &eventArgs);
931 return TRUE;
932}
933
934static BOOL wf_scale_mouse_event(wfContext* wfc, UINT16 flags, INT32 x, INT32 y)
935{
936 UINT16 px, py;
937
938 WINPR_ASSERT(wfc);
939
940 if (!wf_scale_mouse_pos(wfc, x, y, &px, &py))
941 return FALSE;
942
943 /* Fix: correct return value check (TRUE means success). */
944 if (!freerdp_client_send_button_event(&wfc->common, FALSE, flags, px, py))
945 return FALSE;
946
947 return wf_pub_mouse_event(wfc, flags, px, py);
948}
949
950#if (_WIN32_WINNT >= 0x0500)
951static BOOL wf_scale_mouse_event_ex(wfContext* wfc, UINT16 flags, UINT16 buttonMask, INT32 x,
952 INT32 y)
953{
954 UINT16 px, py;
955
956 WINPR_ASSERT(wfc);
957
958 if (buttonMask & XBUTTON1)
959 flags |= PTR_XFLAGS_BUTTON1;
960
961 if (buttonMask & XBUTTON2)
962 flags |= PTR_XFLAGS_BUTTON2;
963
964 if (!wf_scale_mouse_pos(wfc, x, y, &px, &py))
965 return FALSE;
966
967 /* Fix: correct return value check for extended mouse events. */
968 if (!freerdp_client_send_extended_button_event(&wfc->common, FALSE, flags, px, py))
969 return FALSE;
970
971 return wf_pub_mouse_event(wfc, flags, px, py);
972}
973#endif
974
975BOOL wf_keyboard_set_indicators(rdpContext* context, UINT16 led_flags)
976{
977 wfContext* wfc = (wfContext*)context;
978 BYTE keyState[256] = WINPR_C_ARRAY_INIT;
979
980 if (!wfc || !GetKeyboardState(keyState))
981 return FALSE;
982
983 if (led_flags & KBD_SYNC_NUM_LOCK)
984 keyState[VK_NUMLOCK] |= 1;
985 else
986 keyState[VK_NUMLOCK] &= ~1;
987
988 if (led_flags & KBD_SYNC_CAPS_LOCK)
989 keyState[VK_CAPITAL] |= 1;
990 else
991 keyState[VK_CAPITAL] &= ~1;
992
993 if (led_flags & KBD_SYNC_SCROLL_LOCK)
994 keyState[VK_SCROLL] |= 1;
995 else
996 keyState[VK_SCROLL] &= ~1;
997
998 if (led_flags & KBD_SYNC_KANA_LOCK)
999 keyState[VK_KANA] |= 1;
1000 else
1001 keyState[VK_KANA] &= ~1;
1002
1003 return SetKeyboardState(keyState);
1004}
FREERDP_API BOOL freerdp_settings_set_uint32(rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id, UINT32 val)
Sets a UINT32 settings value.
FREERDP_API BOOL freerdp_settings_set_bool(rdpSettings *settings, FreeRDP_Settings_Keys_Bool id, BOOL val)
Sets a BOOL settings value.
WINPR_ATTR_NODISCARD FREERDP_API UINT16 freerdp_settings_get_uint16(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt16 id)
Returns a UINT16 settings value.
WINPR_ATTR_NODISCARD FREERDP_API UINT32 freerdp_settings_get_uint32(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id)
Returns a UINT32 settings value.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.