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