22 #include <freerdp/config.h>
26 #include <winpr/assert.h>
27 #include <winpr/sysinfo.h>
28 #include <freerdp/freerdp.h>
30 #include "wf_client.h"
35 #include <freerdp/event.h>
39 static HWND g_focus_hWnd = NULL;
40 static HWND g_main_hWnd = NULL;
41 static HWND g_parent_hWnd = NULL;
43 #define RESIZE_MIN_DELAY 200
45 static BOOL wf_scale_blt(wfContext* wfc, HDC hdc,
int x,
int y,
int w,
int h, HDC hdcSrc,
int x1,
47 static BOOL wf_scale_mouse_event(wfContext* wfc, UINT16 flags, INT32 x, INT32 y);
48 #if (_WIN32_WINNT >= 0x0500)
49 static BOOL wf_scale_mouse_event_ex(wfContext* wfc, UINT16 flags, UINT16 buttonMask, INT32 x,
53 static BOOL g_flipping_in = FALSE;
54 static BOOL g_flipping_out = FALSE;
56 static BOOL g_keystates[256] = { 0 };
58 static BOOL ctrl_down(
void)
60 return g_keystates[VK_CONTROL] || g_keystates[VK_LCONTROL] || g_keystates[VK_RCONTROL];
63 static 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();
69 LRESULT CALLBACK wf_ll_kbd_proc(
int nCode, WPARAM wParam, LPARAM lParam)
71 DWORD ext_proc_id = 0;
73 wfContext* wfc = NULL;
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(NULL, 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)
100 return CallNextHookEx(NULL, 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;
212 return CallNextHookEx(NULL, nCode, wParam, lParam);
215 void 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);
246 static 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);
277 static 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) /
323 static 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;
332 if (GetTickCount64() - wfc->lastSentDate > RESIZE_MIN_DELAY)
336 GetWindowRect(wfc->hwnd, &windowRect);
337 targetWidth = windowRect.right - windowRect.left;
338 targetHeight = windowRect.bottom - windowRect.top;
345 layout.Flags = DISPLAY_CONTROL_MONITOR_PRIMARY;
346 layout.Top = layout.Left = 0;
347 layout.Width = targetWidth;
348 layout.Height = targetHeight;
351 layout.DesktopScaleFactor =
353 layout.DeviceScaleFactor =
355 layout.PhysicalWidth = targetWidth;
356 layout.PhysicalHeight = targetHeight;
358 if (IFCALLRESULT(CHANNEL_RC_OK, wfc->disp->SendMonitorLayout, wfc->disp, 1,
359 &layout) != CHANNEL_RC_OK)
361 WLog_ERR(
"",
"SendMonitorLayout failed.");
367 wfc->lastSentDate = GetTickCount64();
372 LRESULT CALLBACK wf_event_proc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
375 PAINTSTRUCT ps = { 0 };
376 BOOL processed = FALSE;
377 RECT windowRect = { 0 };
378 MINMAXINFO* minmax = NULL;
379 SCROLLINFO si = { 0 };
381 LONG_PTR ptr = GetWindowLongPtr(hWnd, GWLP_USERDATA);
382 wfContext* wfc = (wfContext*)ptr;
386 rdpInput* input = wfc->common.context.input;
387 rdpSettings* settings = wfc->common.context.settings;
389 if (!g_parent_hWnd && wfc->hWndParent)
390 g_parent_hWnd = wfc->hWndParent;
393 g_main_hWnd = wfc->hwnd;
398 if (!wfc->disablewindowtracking)
400 int x = (int)(
short)LOWORD(lParam);
401 int y = (int)(
short)HIWORD(lParam);
408 case WM_GETMINMAXINFO:
417 minmax = (MINMAXINFO*)lParam;
423 if (!wfc->fullscreen)
426 minmax->ptMaxTrackSize.x =
429 minmax->ptMaxTrackSize.y =
438 wf_sizing(wfc, lParam, wParam);
442 GetWindowRect(wfc->hwnd, &windowRect);
444 if (!wfc->fullscreen)
446 wfc->client_width = LOWORD(lParam);
447 wfc->client_height = HIWORD(lParam);
448 wfc->client_x = windowRect.left;
449 wfc->client_y = windowRect.top;
453 wfc->wasMaximized = TRUE;
457 if (wfc->client_width && wfc->client_height)
459 wf_size_scrollbars(wfc, LOWORD(lParam), HIWORD(lParam));
463 if (wParam == SIZE_MAXIMIZED && !wfc->fullscreen)
465 SetWindowPos(wfc->hwnd, HWND_TOP, 0, 0, windowRect.right - windowRect.left,
466 windowRect.bottom - windowRect.top,
467 SWP_NOMOVE | SWP_FRAMECHANGED);
468 wfc->wasMaximized = TRUE;
471 else if (wParam == SIZE_RESTORED && !wfc->fullscreen && wfc->wasMaximized)
473 wfc->wasMaximized = FALSE;
476 else if (wParam == SIZE_MINIMIZED)
484 case WM_EXITSIZEMOVE:
485 wf_size_scrollbars(wfc, wfc->client_width, wfc->client_height);
495 hdc = BeginPaint(hWnd, &ps);
496 const int x = ps.rcPaint.left;
497 const int y = ps.rcPaint.top;
498 const int w = ps.rcPaint.right - ps.rcPaint.left + 1;
499 const int h = ps.rcPaint.bottom - ps.rcPaint.top + 1;
500 wf_scale_blt(wfc, hdc, x, y, w, h, wfc->primary->hdc,
501 x - wfc->offset_x + wfc->xCurrentScroll,
502 y - wfc->offset_y + wfc->yCurrentScroll, SRCCOPY);
506 #if (_WIN32_WINNT >= 0x0500)
509 wf_scale_mouse_event_ex(wfc, PTR_XFLAGS_DOWN, GET_XBUTTON_WPARAM(wParam),
510 GET_X_LPARAM(lParam) - wfc->offset_x,
511 GET_Y_LPARAM(lParam) - wfc->offset_y);
515 wf_scale_mouse_event_ex(wfc, 0, GET_XBUTTON_WPARAM(wParam),
516 GET_X_LPARAM(lParam) - wfc->offset_x,
517 GET_Y_LPARAM(lParam) - wfc->offset_y);
522 wf_scale_mouse_event(wfc, PTR_FLAGS_DOWN | PTR_FLAGS_BUTTON3,
523 GET_X_LPARAM(lParam) - wfc->offset_x,
524 GET_Y_LPARAM(lParam) - wfc->offset_y);
528 wf_scale_mouse_event(wfc, PTR_FLAGS_BUTTON3, GET_X_LPARAM(lParam) - wfc->offset_x,
529 GET_Y_LPARAM(lParam) - wfc->offset_y);
533 wf_scale_mouse_event(wfc, PTR_FLAGS_DOWN | PTR_FLAGS_BUTTON1,
534 GET_X_LPARAM(lParam) - wfc->offset_x,
535 GET_Y_LPARAM(lParam) - wfc->offset_y);
536 SetCapture(wfc->hwnd);
540 wf_scale_mouse_event(wfc, PTR_FLAGS_BUTTON1, GET_X_LPARAM(lParam) - wfc->offset_x,
541 GET_Y_LPARAM(lParam) - wfc->offset_y);
546 wf_scale_mouse_event(wfc, PTR_FLAGS_DOWN | PTR_FLAGS_BUTTON2,
547 GET_X_LPARAM(lParam) - wfc->offset_x,
548 GET_Y_LPARAM(lParam) - wfc->offset_y);
552 wf_scale_mouse_event(wfc, PTR_FLAGS_BUTTON2, GET_X_LPARAM(lParam) - wfc->offset_x,
553 GET_Y_LPARAM(lParam) - wfc->offset_y);
557 wf_scale_mouse_event(wfc, PTR_FLAGS_MOVE, GET_X_LPARAM(lParam) - wfc->offset_x,
558 GET_Y_LPARAM(lParam) - wfc->offset_y);
560 #if (_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)
563 wf_event_process_WM_MOUSEWHEEL(wfc, hWnd, Msg, wParam, lParam, FALSE,
564 GET_X_LPARAM(lParam) - wfc->offset_x,
565 GET_Y_LPARAM(lParam) - wfc->offset_y);
568 #if (_WIN32_WINNT >= 0x0600)
571 wf_event_process_WM_MOUSEWHEEL(wfc, hWnd, Msg, wParam, lParam, TRUE,
572 GET_X_LPARAM(lParam) - wfc->offset_x,
573 GET_Y_LPARAM(lParam) - wfc->offset_y);
578 if (LOWORD(lParam) == HTCLIENT)
579 SetCursor(wfc->cursor);
581 DefWindowProc(hWnd, Msg, wParam, lParam);
591 switch (LOWORD(wParam))
595 xNewPos = wfc->xCurrentScroll - 50;
600 xNewPos = wfc->xCurrentScroll + 50;
605 xNewPos = wfc->xCurrentScroll - 5;
610 xNewPos = wfc->xCurrentScroll + 5;
614 case SB_THUMBPOSITION:
615 xNewPos = HIWORD(wParam);
620 xNewPos = HIWORD(wParam);
624 xNewPos = wfc->xCurrentScroll;
628 xNewPos = MAX(0, xNewPos);
629 xNewPos = MIN(wfc->xMaxScroll, xNewPos);
632 if (xNewPos == wfc->xCurrentScroll)
636 xDelta = xNewPos - wfc->xCurrentScroll;
638 wfc->xCurrentScroll = xNewPos;
643 ScrollWindowEx(wfc->hwnd, -xDelta, -yDelta, (CONST RECT*)NULL, (CONST RECT*)NULL,
644 (HRGN)NULL, (PRECT)NULL, SW_INVALIDATE);
645 UpdateWindow(wfc->hwnd);
647 si.cbSize =
sizeof(si);
649 si.nPos = wfc->xCurrentScroll;
650 SetScrollInfo(wfc->hwnd, SB_HORZ, &si, TRUE);
660 switch (LOWORD(wParam))
664 yNewPos = wfc->yCurrentScroll - 50;
669 yNewPos = wfc->yCurrentScroll + 50;
674 yNewPos = wfc->yCurrentScroll - 5;
679 yNewPos = wfc->yCurrentScroll + 5;
683 case SB_THUMBPOSITION:
684 yNewPos = HIWORD(wParam);
689 yNewPos = HIWORD(wParam);
693 yNewPos = wfc->yCurrentScroll;
697 yNewPos = MAX(0, yNewPos);
698 yNewPos = MIN(wfc->yMaxScroll, yNewPos);
701 if (yNewPos == wfc->yCurrentScroll)
705 yDelta = yNewPos - wfc->yCurrentScroll;
707 wfc->yCurrentScroll = yNewPos;
712 ScrollWindowEx(wfc->hwnd, -xDelta, -yDelta, (CONST RECT*)NULL, (CONST RECT*)NULL,
713 (HRGN)NULL, (PRECT)NULL, SW_INVALIDATE);
714 UpdateWindow(wfc->hwnd);
716 si.cbSize =
sizeof(si);
718 si.nPos = wfc->yCurrentScroll;
719 SetScrollInfo(wfc->hwnd, SB_VERT, &si, TRUE);
725 if (wParam == SYSCOMMAND_ID_SMARTSIZING)
727 HMENU hMenu = GetSystemMenu(wfc->hwnd, FALSE);
730 CheckMenuItem(hMenu, SYSCOMMAND_ID_SMARTSIZING,
736 SetWindowPos(wfc->hwnd, HWND_TOP, -1, -1,
745 wf_size_scrollbars(wfc, wfc->client_width, wfc->client_height);
749 else if (wParam == SYSCOMMAND_ID_REQUEST_CONTROL)
751 freerdp_client_encomsp_set_control(wfc->common.encomsp, TRUE);
776 PostQuitMessage(WM_QUIT);
780 DEBUG_KBD(
"getting focus %X", hWnd);
786 g_flipping_in = TRUE;
789 freerdp_set_focus(wfc->common.context.instance);
796 if (g_focus_hWnd == hWnd && wfc && !wfc->fullscreen)
798 DEBUG_KBD(
"loosing focus %X", hWnd);
801 g_flipping_out = TRUE;
810 int activate = (int)(
short)LOWORD(wParam);
811 BOOL minimized_flag = (BOOL)HIWORD(wParam);
813 if (activate != WA_INACTIVE && !minimized_flag)
816 g_flipping_in = TRUE;
823 g_flipping_out = TRUE;
830 return DefWindowProc(hWnd, Msg, wParam, lParam);
837 BOOL wf_scale_blt(wfContext* wfc, HDC hdc,
int x,
int y,
int w,
int h, HDC hdcSrc,
int x1,
int y1,
840 UINT32 ww, wh, dw, dh;
843 rdpSettings* settings = wfc->common.context.settings;
844 WINPR_ASSERT(settings);
846 if (!wfc->client_width)
849 if (!wfc->client_height)
852 ww = wfc->client_width;
853 wh = wfc->client_height;
864 (ww == dw && wh == dh))
866 return BitBlt(hdc, x, y, w, h, wfc->primary->hdc, x1, y1, SRCCOPY);
870 SetStretchBltMode(hdc, HALFTONE);
871 SetBrushOrgEx(hdc, 0, 0, NULL);
872 return StretchBlt(hdc, 0, 0, ww, wh, wfc->primary->hdc, 0, 0, dw, dh, SRCCOPY);
878 static BOOL wf_scale_mouse_pos(wfContext* wfc, INT32 x, INT32 y, UINT16* px, UINT16* py)
881 rdpSettings* settings;
883 if (!wfc || !px || !py)
886 settings = wfc->common.context.settings;
891 if (!wfc->client_width)
894 if (!wfc->client_height)
897 ww = wfc->client_width;
898 wh = wfc->client_height;
904 x += wfc->xCurrentScroll;
905 y += wfc->yCurrentScroll;
909 x = x * dw / ww + wfc->xCurrentScroll;
910 y = y * dh / wh + wfc->yCurrentScroll;
913 *px = MIN(UINT16_MAX, MAX(0, x));
914 *py = MIN(UINT16_MAX, MAX(0, y));
919 static BOOL wf_pub_mouse_event(wfContext* wfc, UINT16 flags, UINT16 x, UINT16 y)
921 MouseEventEventArgs eventArgs = { 0 };
923 eventArgs.flags = flags;
926 PubSub_OnMouseEvent(wfc->common.context.pubSub, &wfc->common.context, &eventArgs);
930 static BOOL wf_scale_mouse_event(wfContext* wfc, UINT16 flags, INT32 x, INT32 y)
936 if (!wf_scale_mouse_pos(wfc, x, y, &px, &py))
939 if (freerdp_client_send_button_event(&wfc->common, FALSE, flags, px, py))
942 return wf_pub_mouse_event(wfc, flags, px, py);
945 #if (_WIN32_WINNT >= 0x0500)
946 static BOOL wf_scale_mouse_event_ex(wfContext* wfc, UINT16 flags, UINT16 buttonMask, INT32 x,
953 if (buttonMask & XBUTTON1)
954 flags |= PTR_XFLAGS_BUTTON1;
956 if (buttonMask & XBUTTON2)
957 flags |= PTR_XFLAGS_BUTTON2;
959 if (!wf_scale_mouse_pos(wfc, x, y, &px, &py))
962 if (freerdp_client_send_extended_button_event(&wfc->common, FALSE, flags, px, py))
965 return wf_pub_mouse_event(wfc, flags, px, py);
FREERDP_API UINT32 freerdp_settings_get_uint32(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id)
Returns a UINT32 settings value.
FREERDP_API BOOL freerdp_settings_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.
FREERDP_API UINT16 freerdp_settings_get_uint16(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt16 id)
Returns a UINT16 settings value.
FREERDP_API BOOL freerdp_settings_set_uint32(rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id, UINT32 param)
Sets a UINT32 settings value.
FREERDP_API BOOL freerdp_settings_set_bool(rdpSettings *settings, FreeRDP_Settings_Keys_Bool id, BOOL param)
Sets a BOOL settings value.