22#include <freerdp/config.h>
26#include <winpr/assert.h>
27#include <winpr/sysinfo.h>
28#include <freerdp/freerdp.h>
35#include <freerdp/event.h>
39static HWND g_focus_hWnd =
nullptr;
40static HWND g_main_hWnd =
nullptr;
41static HWND g_parent_hWnd =
nullptr;
43#define RESIZE_MIN_DELAY 200
45static BOOL wf_scale_blt(wfContext* wfc, HDC hdc,
int x,
int y,
int w,
int h, HDC hdcSrc,
int x1,
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,
53static BOOL g_flipping_in = FALSE;
54static BOOL g_flipping_out = FALSE;
56static BOOL g_keystates[256] = WINPR_C_ARRAY_INIT;
58static BOOL ctrl_down(
void)
60 return g_keystates[VK_CONTROL] || g_keystates[VK_LCONTROL] || g_keystates[VK_RCONTROL];
63static BOOL alt_ctrl_down(
void)
65 const BOOL altDown = g_keystates[VK_MENU] || g_keystates[VK_LMENU] || g_keystates[VK_RMENU];
66 return altDown && ctrl_down();
69LRESULT CALLBACK wf_ll_kbd_proc(
int nCode, WPARAM wParam, LPARAM lParam)
71 DWORD ext_proc_id = 0;
73 wfContext* wfc =
nullptr;
79 DEBUG_KBD(
"Low-level keyboard hook, hWnd %X nCode %X wParam %X", g_focus_hWnd, nCode, wParam);
84 g_flipping_in = FALSE;
86 return CallNextHookEx(
nullptr, nCode, wParam, lParam);
89 if (g_parent_hWnd && g_main_hWnd)
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)
99 g_focus_hWnd =
nullptr;
100 return CallNextHookEx(
nullptr, nCode, wParam, lParam);
103 g_focus_hWnd = g_main_hWnd;
106 if (g_focus_hWnd && (nCode == HC_ACTION))
115 wfc = (wfContext*)GetWindowLongPtr(g_focus_hWnd, GWLP_USERDATA);
116 p = (PKBDLLHOOKSTRUCT)lParam;
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];
129 g_keystates[p->scanCode & 0xFF] = TRUE;
134 g_keystates[p->scanCode & 0xFF] = FALSE;
137 DEBUG_KBD(
"keydown %d scanCode 0x%08lX flags 0x%08lX vkCode 0x%08lX",
138 (wParam == WM_KEYDOWN), p->scanCode, p->flags, p->vkCode);
140 if (wfc->fullscreen_toggle && (p->vkCode == VK_RETURN || p->vkCode == VK_CANCEL))
144 if (wParam == WM_KEYDOWN)
146 wf_toggle_fullscreen(wfc);
152 if (rdp_scancode == RDP_SCANCODE_NUMLOCK_EXTENDED)
155 DEBUG_KBD(
"hack: NumLock (x45) should not be extended");
156 rdp_scancode = RDP_SCANCODE_NUMLOCK;
158 else if (rdp_scancode == RDP_SCANCODE_NUMLOCK)
162 if (wParam == WM_KEYDOWN)
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);
176 DEBUG_KBD(
"Pause up");
181 else if (rdp_scancode == RDP_SCANCODE_RSHIFT_EXTENDED)
183 DEBUG_KBD(
"right shift (x36) should not be extended");
184 rdp_scancode = RDP_SCANCODE_RSHIFT;
187 freerdp_input_send_keyboard_event_ex(input, !(p->flags & LLKHF_UP), keystate,
190 if (p->vkCode == VK_NUMLOCK || p->vkCode == VK_CAPITAL || p->vkCode == VK_SCROLL ||
191 p->vkCode == VK_KANA)
193 "lock keys are processed on client side too to toggle their indicators");
205 if (!alt_ctrl_down())
207 g_flipping_out = FALSE;
208 g_focus_hWnd =
nullptr;
212 return CallNextHookEx(
nullptr, nCode, wParam, lParam);
215void wf_event_focus_in(wfContext* wfc)
221 input = wfc->common.context.input;
224 if (GetKeyState(VK_NUMLOCK))
225 syncFlags |= KBD_SYNC_NUM_LOCK;
227 if (GetKeyState(VK_CAPITAL))
228 syncFlags |= KBD_SYNC_CAPS_LOCK;
230 if (GetKeyState(VK_SCROLL))
231 syncFlags |= KBD_SYNC_SCROLL_LOCK;
233 if (GetKeyState(VK_KANA))
234 syncFlags |= KBD_SYNC_KANA_LOCK;
236 input->FocusInEvent(input, syncFlags);
239 ScreenToClient(wfc->hwnd, &pt);
240 GetClientRect(wfc->hwnd, &rc);
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);
246static BOOL wf_event_process_WM_MOUSEWHEEL(wfContext* wfc, HWND hWnd, UINT Msg, WPARAM wParam,
247 LPARAM lParam, BOOL horizontal, INT32 x, INT32 y)
255 input = wfc->common.context.input;
258 DefWindowProc(hWnd, Msg, wParam, lParam);
259 delta = ((
signed short)HIWORD(wParam));
262 flags |= PTR_FLAGS_HWHEEL;
264 flags |= PTR_FLAGS_WHEEL;
268 flags |= PTR_FLAGS_WHEEL_NEGATIVE;
270 delta = 0x100 + delta;
274 return wf_scale_mouse_event(wfc, flags, x, y);
277static void wf_sizing(wfContext* wfc, WPARAM wParam, LPARAM lParam)
279 rdpSettings* settings = wfc->common.context.settings;
287 rect = (LPRECT)wParam;
293 case WMSZ_BOTTOMRIGHT:
297 (rect->right - rect->left) /
307 (rect->bottom - rect->top) /
311 case WMSZ_BOTTOMLEFT:
316 (rect->bottom - rect->top) /
323static void wf_send_resize(wfContext* wfc)
326 int targetWidth = wfc->client_width;
327 int targetHeight = wfc->client_height;
328 rdpSettings* settings = wfc->common.context.settings;
331 wfc->disp !=
nullptr)
333 if (GetTickCount64() - wfc->lastSentDate > RESIZE_MIN_DELAY)
337 GetWindowRect(wfc->hwnd, &windowRect);
338 targetWidth = windowRect.right - windowRect.left;
339 targetHeight = windowRect.bottom - windowRect.top;
346 layout.Flags = DISPLAY_CONTROL_MONITOR_PRIMARY;
347 layout.Top = layout.Left = 0;
348 layout.Width = targetWidth;
349 layout.Height = targetHeight;
352 layout.DesktopScaleFactor =
354 layout.DeviceScaleFactor =
356 layout.PhysicalWidth = targetWidth;
357 layout.PhysicalHeight = targetHeight;
359 if (IFCALLRESULT(CHANNEL_RC_OK, wfc->disp->SendMonitorLayout, wfc->disp, 1,
360 &layout) != CHANNEL_RC_OK)
362 WLog_ERR(
"",
"SendMonitorLayout failed.");
368 wfc->lastSentDate = GetTickCount64();
373LRESULT CALLBACK wf_event_proc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
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;
382 LONG_PTR ptr = GetWindowLongPtr(hWnd, GWLP_USERDATA);
383 wfContext* wfc = (wfContext*)ptr;
387 rdpInput* input = wfc->common.context.input;
388 rdpSettings* settings = wfc->common.context.settings;
390 if (!g_parent_hWnd && wfc->hWndParent)
391 g_parent_hWnd = wfc->hWndParent;
394 g_main_hWnd = wfc->hwnd;
399 if (!wfc->disablewindowtracking)
401 int x = (int)(
short)LOWORD(lParam);
402 int y = (int)(
short)HIWORD(lParam);
409 case WM_GETMINMAXINFO:
418 minmax = (MINMAXINFO*)lParam;
424 if (!wfc->fullscreen)
427 minmax->ptMaxTrackSize.x =
430 minmax->ptMaxTrackSize.y =
439 wf_sizing(wfc, lParam, wParam);
443 GetWindowRect(wfc->hwnd, &windowRect);
445 if (!wfc->fullscreen)
447 wfc->client_width = LOWORD(lParam);
448 wfc->client_height = HIWORD(lParam);
449 wfc->client_x = windowRect.left;
450 wfc->client_y = windowRect.top;
454 wfc->wasMaximized = TRUE;
458 if (wfc->client_width && wfc->client_height)
460 wf_size_scrollbars(wfc, LOWORD(lParam), HIWORD(lParam));
464 if (wParam == SIZE_MAXIMIZED && !wfc->fullscreen)
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;
472 else if (wParam == SIZE_RESTORED && !wfc->fullscreen && wfc->wasMaximized)
474 wfc->wasMaximized = FALSE;
477 else if (wParam == SIZE_MINIMIZED)
479 g_focus_hWnd =
nullptr;
485 case WM_EXITSIZEMOVE:
486 wf_size_scrollbars(wfc, wfc->client_width, wfc->client_height);
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);
507#if (_WIN32_WINNT >= 0x0500)
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);
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);
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);
529 wf_scale_mouse_event(wfc, PTR_FLAGS_BUTTON3, GET_X_LPARAM(lParam) - wfc->offset_x,
530 GET_Y_LPARAM(lParam) - wfc->offset_y);
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);
541 wf_scale_mouse_event(wfc, PTR_FLAGS_BUTTON1, GET_X_LPARAM(lParam) - wfc->offset_x,
542 GET_Y_LPARAM(lParam) - wfc->offset_y);
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);
553 wf_scale_mouse_event(wfc, PTR_FLAGS_BUTTON2, GET_X_LPARAM(lParam) - wfc->offset_x,
554 GET_Y_LPARAM(lParam) - wfc->offset_y);
558 wf_scale_mouse_event(wfc, PTR_FLAGS_MOVE, GET_X_LPARAM(lParam) - wfc->offset_x,
559 GET_Y_LPARAM(lParam) - wfc->offset_y);
561#if (_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)
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);
569#if (_WIN32_WINNT >= 0x0600)
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);
579 if (LOWORD(lParam) == HTCLIENT)
580 SetCursor(wfc->cursor);
582 DefWindowProc(hWnd, Msg, wParam, lParam);
592 switch (LOWORD(wParam))
596 xNewPos = wfc->xCurrentScroll - 50;
601 xNewPos = wfc->xCurrentScroll + 50;
606 xNewPos = wfc->xCurrentScroll - 5;
611 xNewPos = wfc->xCurrentScroll + 5;
615 case SB_THUMBPOSITION:
616 xNewPos = HIWORD(wParam);
621 xNewPos = HIWORD(wParam);
625 xNewPos = wfc->xCurrentScroll;
629 xNewPos = MAX(0, xNewPos);
630 xNewPos = MIN(wfc->xMaxScroll, xNewPos);
633 if (xNewPos == wfc->xCurrentScroll)
637 xDelta = xNewPos - wfc->xCurrentScroll;
639 wfc->xCurrentScroll = xNewPos;
644 ScrollWindowEx(wfc->hwnd, -xDelta, -yDelta, (CONST RECT*)
nullptr,
645 (CONST RECT*)
nullptr, (HRGN)
nullptr, (PRECT)
nullptr,
647 UpdateWindow(wfc->hwnd);
649 si.cbSize =
sizeof(si);
651 si.nPos = wfc->xCurrentScroll;
652 SetScrollInfo(wfc->hwnd, SB_HORZ, &si, TRUE);
662 switch (LOWORD(wParam))
666 yNewPos = wfc->yCurrentScroll - 50;
671 yNewPos = wfc->yCurrentScroll + 50;
676 yNewPos = wfc->yCurrentScroll - 5;
681 yNewPos = wfc->yCurrentScroll + 5;
685 case SB_THUMBPOSITION:
686 yNewPos = HIWORD(wParam);
691 yNewPos = HIWORD(wParam);
695 yNewPos = wfc->yCurrentScroll;
699 yNewPos = MAX(0, yNewPos);
700 yNewPos = MIN(wfc->yMaxScroll, yNewPos);
703 if (yNewPos == wfc->yCurrentScroll)
707 yDelta = yNewPos - wfc->yCurrentScroll;
709 wfc->yCurrentScroll = yNewPos;
714 ScrollWindowEx(wfc->hwnd, -xDelta, -yDelta, (CONST RECT*)
nullptr,
715 (CONST RECT*)
nullptr, (HRGN)
nullptr, (PRECT)
nullptr,
717 UpdateWindow(wfc->hwnd);
719 si.cbSize =
sizeof(si);
721 si.nPos = wfc->yCurrentScroll;
722 SetScrollInfo(wfc->hwnd, SB_VERT, &si, TRUE);
728 if (wParam == SYSCOMMAND_ID_SMARTSIZING)
730 HMENU hMenu = GetSystemMenu(wfc->hwnd, FALSE);
733 CheckMenuItem(hMenu, SYSCOMMAND_ID_SMARTSIZING,
739 SetWindowPos(wfc->hwnd, HWND_TOP, -1, -1,
748 wf_size_scrollbars(wfc, wfc->client_width, wfc->client_height);
752 else if (wParam == SYSCOMMAND_ID_REQUEST_CONTROL)
754 freerdp_client_encomsp_set_control(wfc->common.encomsp, TRUE);
779 PostQuitMessage(WM_QUIT);
783 DEBUG_KBD(
"getting focus %X", hWnd);
789 g_flipping_in = TRUE;
792 freerdp_set_focus(wfc->common.context.instance);
793 wf_event_focus_in(wfc);
800 if (g_focus_hWnd == hWnd && wfc && !wfc->fullscreen)
802 DEBUG_KBD(
"losing focus %X", hWnd);
805 g_flipping_out = TRUE;
807 g_focus_hWnd =
nullptr;
814 int activate = (int)(
short)LOWORD(wParam);
815 BOOL minimized_flag = (BOOL)HIWORD(wParam);
817 if (activate != WA_INACTIVE && !minimized_flag)
820 g_flipping_in = TRUE;
827 g_flipping_out = TRUE;
829 g_focus_hWnd =
nullptr;
834 return DefWindowProc(hWnd, Msg, wParam, lParam);
841BOOL wf_scale_blt(wfContext* wfc, HDC hdc,
int x,
int y,
int w,
int h, HDC hdcSrc,
int x1,
int y1,
844 UINT32 ww, wh, dw, dh;
847 rdpSettings* settings = wfc->common.context.settings;
848 WINPR_ASSERT(settings);
850 if (!wfc->client_width)
853 if (!wfc->client_height)
856 ww = wfc->client_width;
857 wh = wfc->client_height;
868 (ww == dw && wh == dh))
870 return BitBlt(hdc, x, y, w, h, wfc->primary->hdc, x1, y1, SRCCOPY);
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);
882static BOOL wf_scale_mouse_pos(wfContext* wfc, INT32 x, INT32 y, UINT16* px, UINT16* py)
885 rdpSettings* settings;
887 if (!wfc || !px || !py)
890 settings = wfc->common.context.settings;
895 if (!wfc->client_width)
898 if (!wfc->client_height)
901 ww = wfc->client_width;
902 wh = wfc->client_height;
908 x += wfc->xCurrentScroll;
909 y += wfc->yCurrentScroll;
913 x = x * dw / ww + wfc->xCurrentScroll;
914 y = y * dh / wh + wfc->yCurrentScroll;
917 *px = MIN(UINT16_MAX, MAX(0, x));
918 *py = MIN(UINT16_MAX, MAX(0, y));
923static BOOL wf_pub_mouse_event(wfContext* wfc, UINT16 flags, UINT16 x, UINT16 y)
925 MouseEventEventArgs eventArgs = WINPR_C_ARRAY_INIT;
927 eventArgs.flags = flags;
930 PubSub_OnMouseEvent(wfc->common.context.pubSub, &wfc->common.context, &eventArgs);
934static BOOL wf_scale_mouse_event(wfContext* wfc, UINT16 flags, INT32 x, INT32 y)
940 if (!wf_scale_mouse_pos(wfc, x, y, &px, &py))
944 if (!freerdp_client_send_button_event(&wfc->common, FALSE, flags, px, py))
947 return wf_pub_mouse_event(wfc, flags, px, py);
950#if (_WIN32_WINNT >= 0x0500)
951static BOOL wf_scale_mouse_event_ex(wfContext* wfc, UINT16 flags, UINT16 buttonMask, INT32 x,
958 if (buttonMask & XBUTTON1)
959 flags |= PTR_XFLAGS_BUTTON1;
961 if (buttonMask & XBUTTON2)
962 flags |= PTR_XFLAGS_BUTTON2;
964 if (!wf_scale_mouse_pos(wfc, x, y, &px, &py))
968 if (!freerdp_client_send_extended_button_event(&wfc->common, FALSE, flags, px, py))
971 return wf_pub_mouse_event(wfc, flags, px, py);
975BOOL wf_keyboard_set_indicators(rdpContext* context, UINT16 led_flags)
977 wfContext* wfc = (wfContext*)context;
978 BYTE keyState[256] = WINPR_C_ARRAY_INIT;
980 if (!wfc || !GetKeyboardState(keyState))
983 if (led_flags & KBD_SYNC_NUM_LOCK)
984 keyState[VK_NUMLOCK] |= 1;
986 keyState[VK_NUMLOCK] &= ~1;
988 if (led_flags & KBD_SYNC_CAPS_LOCK)
989 keyState[VK_CAPITAL] |= 1;
991 keyState[VK_CAPITAL] &= ~1;
993 if (led_flags & KBD_SYNC_SCROLL_LOCK)
994 keyState[VK_SCROLL] |= 1;
996 keyState[VK_SCROLL] &= ~1;
998 if (led_flags & KBD_SYNC_KANA_LOCK)
999 keyState[VK_KANA] |= 1;
1001 keyState[VK_KANA] &= ~1;
1003 return SetKeyboardState(keyState);
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.