/** * FreeRDP: A Remote Desktop Protocol Implementation * Event Handling * * Copyright 2009-2011 Jay Sorg * Copyright 2010-2011 Vic Lee * Copyright 2010-2011 Marc-Andre Moreau * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include "wf_client.h" #include "wf_gdi.h" #include "wf_event.h" #include #include static HWND g_focus_hWnd = NULL; static HWND g_main_hWnd = NULL; static HWND g_parent_hWnd = NULL; #define RESIZE_MIN_DELAY 200 /* minimum delay in ms between two resizes */ static BOOL wf_scale_blt(wfContext* wfc, HDC hdc, int x, int y, int w, int h, HDC hdcSrc, int x1, int y1, DWORD rop); static BOOL wf_scale_mouse_event(wfContext* wfc, UINT16 flags, INT32 x, INT32 y); #if (_WIN32_WINNT >= 0x0500) static BOOL wf_scale_mouse_event_ex(wfContext* wfc, UINT16 flags, UINT16 buttonMask, INT32 x, INT32 y); #endif static BOOL g_flipping_in = FALSE; static BOOL g_flipping_out = FALSE; static BOOL g_keystates[256] = { 0 }; static BOOL ctrl_down(void) { return g_keystates[VK_CONTROL] || g_keystates[VK_LCONTROL] || g_keystates[VK_RCONTROL]; } static BOOL alt_ctrl_down(void) { const BOOL altDown = g_keystates[VK_MENU] || g_keystates[VK_LMENU] || g_keystates[VK_RMENU]; return altDown && ctrl_down(); } LRESULT CALLBACK wf_ll_kbd_proc(int nCode, WPARAM wParam, LPARAM lParam) { DWORD ext_proc_id = 0; wfContext* wfc = NULL; DWORD rdp_scancode; BOOL keystate; rdpInput* input; PKBDLLHOOKSTRUCT p; DEBUG_KBD("Low-level keyboard hook, hWnd %X nCode %X wParam %X", g_focus_hWnd, nCode, wParam); if (g_flipping_in) { if (!alt_ctrl_down()) g_flipping_in = FALSE; return CallNextHookEx(NULL, nCode, wParam, lParam); } if (g_parent_hWnd && g_main_hWnd) { wfc = (wfContext*)GetWindowLongPtr(g_main_hWnd, GWLP_USERDATA); GUITHREADINFO gui_thread_info; gui_thread_info.cbSize = sizeof(GUITHREADINFO); HWND fg_win_hwnd = GetForegroundWindow(); DWORD fg_win_thread_id = GetWindowThreadProcessId(fg_win_hwnd, &ext_proc_id); BOOL result = GetGUIThreadInfo(fg_win_thread_id, &gui_thread_info); if (gui_thread_info.hwndFocus != wfc->hWndParent) { g_focus_hWnd = NULL; return CallNextHookEx(NULL, nCode, wParam, lParam); } g_focus_hWnd = g_main_hWnd; } if (g_focus_hWnd && (nCode == HC_ACTION)) { switch (wParam) { case WM_KEYDOWN: case WM_SYSKEYDOWN: case WM_KEYUP: case WM_SYSKEYUP: if (!wfc) wfc = (wfContext*)GetWindowLongPtr(g_focus_hWnd, GWLP_USERDATA); p = (PKBDLLHOOKSTRUCT)lParam; if (!wfc || !p) return 1; input = wfc->common.context.input; rdp_scancode = MAKE_RDP_SCANCODE((BYTE)p->scanCode, p->flags & LLKHF_EXTENDED); keystate = g_keystates[p->scanCode & 0xFF]; switch (wParam) { case WM_KEYDOWN: case WM_SYSKEYDOWN: g_keystates[p->scanCode & 0xFF] = TRUE; break; case WM_KEYUP: case WM_SYSKEYUP: default: g_keystates[p->scanCode & 0xFF] = FALSE; break; } DEBUG_KBD("keydown %d scanCode 0x%08lX flags 0x%08lX vkCode 0x%08lX", (wParam == WM_KEYDOWN), p->scanCode, p->flags, p->vkCode); if (wfc->fullscreen_toggle && (p->vkCode == VK_RETURN || p->vkCode == VK_CANCEL)) { if (alt_ctrl_down()) { if (wParam == WM_KEYDOWN) { wf_toggle_fullscreen(wfc); return 1; } } } if (rdp_scancode == RDP_SCANCODE_NUMLOCK_EXTENDED) { /* Windows sends NumLock as extended - rdp doesn't */ DEBUG_KBD("hack: NumLock (x45) should not be extended"); rdp_scancode = RDP_SCANCODE_NUMLOCK; } else if (rdp_scancode == RDP_SCANCODE_NUMLOCK) { /* Windows sends Pause as if it was a RDP NumLock (handled above). * It must however be sent as a one-shot Ctrl+NumLock */ if (wParam == WM_KEYDOWN) { DEBUG_KBD("Pause, sent as Ctrl+NumLock"); freerdp_input_send_keyboard_event_ex(input, TRUE, FALSE, RDP_SCANCODE_LCONTROL); freerdp_input_send_keyboard_event_ex(input, TRUE, FALSE, RDP_SCANCODE_NUMLOCK); freerdp_input_send_keyboard_event_ex(input, FALSE, FALSE, RDP_SCANCODE_LCONTROL); freerdp_input_send_keyboard_event_ex(input, FALSE, FALSE, RDP_SCANCODE_NUMLOCK); } else { DEBUG_KBD("Pause up"); } return 1; } else if (rdp_scancode == RDP_SCANCODE_RSHIFT_EXTENDED) { DEBUG_KBD("right shift (x36) should not be extended"); rdp_scancode = RDP_SCANCODE_RSHIFT; } freerdp_input_send_keyboard_event_ex(input, !(p->flags & LLKHF_UP), keystate, rdp_scancode); if (p->vkCode == VK_NUMLOCK || p->vkCode == VK_CAPITAL || p->vkCode == VK_SCROLL || p->vkCode == VK_KANA) DEBUG_KBD( "lock keys are processed on client side too to toggle their indicators"); else return 1; break; default: break; } } if (g_flipping_out) { if (!alt_ctrl_down()) { g_flipping_out = FALSE; g_focus_hWnd = NULL; } } return CallNextHookEx(NULL, nCode, wParam, lParam); } void wf_event_focus_in(wfContext* wfc) { UINT16 syncFlags; rdpInput* input; POINT pt; RECT rc; input = wfc->common.context.input; syncFlags = 0; if (GetKeyState(VK_NUMLOCK)) syncFlags |= KBD_SYNC_NUM_LOCK; if (GetKeyState(VK_CAPITAL)) syncFlags |= KBD_SYNC_CAPS_LOCK; if (GetKeyState(VK_SCROLL)) syncFlags |= KBD_SYNC_SCROLL_LOCK; if (GetKeyState(VK_KANA)) syncFlags |= KBD_SYNC_KANA_LOCK; input->FocusInEvent(input, syncFlags); /* send pointer position if the cursor is currently inside our client area */ GetCursorPos(&pt); ScreenToClient(wfc->hwnd, &pt); GetClientRect(wfc->hwnd, &rc); if (pt.x >= rc.left && pt.x < rc.right && pt.y >= rc.top && pt.y < rc.bottom) input->MouseEvent(input, PTR_FLAGS_MOVE, (UINT16)pt.x, (UINT16)pt.y); } static BOOL wf_event_process_WM_MOUSEWHEEL(wfContext* wfc, HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam, BOOL horizontal, INT32 x, INT32 y) { int delta; UINT16 flags = 0; rdpInput* input; WINPR_ASSERT(wfc); input = wfc->common.context.input; WINPR_ASSERT(input); DefWindowProc(hWnd, Msg, wParam, lParam); delta = ((signed short)HIWORD(wParam)); /* GET_WHEEL_DELTA_WPARAM(wParam); */ if (horizontal) flags |= PTR_FLAGS_HWHEEL; else flags |= PTR_FLAGS_WHEEL; if (delta < 0) { flags |= PTR_FLAGS_WHEEL_NEGATIVE; /* 9bit twos complement, delta already negative */ delta = 0x100 + delta; } flags |= delta; return wf_scale_mouse_event(wfc, flags, x, y); } static void wf_sizing(wfContext* wfc, WPARAM wParam, LPARAM lParam) { rdpSettings* settings = wfc->common.context.settings; // Holding the CTRL key down while resizing the window will force the desktop aspect ratio. LPRECT rect; if ((freerdp_settings_get_bool(settings, FreeRDP_SmartSizing) || freerdp_settings_get_bool(settings, FreeRDP_DynamicResolutionUpdate)) && ctrl_down()) { rect = (LPRECT)wParam; switch (lParam) { case WMSZ_LEFT: case WMSZ_RIGHT: case WMSZ_BOTTOMRIGHT: // Adjust height rect->bottom = rect->top + freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight) * (rect->right - rect->left) / freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth); break; case WMSZ_TOP: case WMSZ_BOTTOM: case WMSZ_TOPRIGHT: // Adjust width rect->right = rect->left + freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth) * (rect->bottom - rect->top) / freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight); break; case WMSZ_BOTTOMLEFT: case WMSZ_TOPLEFT: // adjust width rect->left = rect->right - (freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth) * (rect->bottom - rect->top) / freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight)); break; } } } static void wf_send_resize(wfContext* wfc) { RECT windowRect; int targetWidth = wfc->client_width; int targetHeight = wfc->client_height; rdpSettings* settings = wfc->common.context.settings; if (freerdp_settings_get_bool(settings, FreeRDP_DynamicResolutionUpdate) && wfc->disp != NULL) { if (GetTickCount64() - wfc->lastSentDate > RESIZE_MIN_DELAY) { if (wfc->fullscreen) { GetWindowRect(wfc->hwnd, &windowRect); targetWidth = windowRect.right - windowRect.left; targetHeight = windowRect.bottom - windowRect.top; } if (freerdp_settings_get_uint32(settings, FreeRDP_SmartSizingWidth) != targetWidth || freerdp_settings_get_uint32(settings, FreeRDP_SmartSizingHeight) != targetHeight) { DISPLAY_CONTROL_MONITOR_LAYOUT layout = { 0 }; layout.Flags = DISPLAY_CONTROL_MONITOR_PRIMARY; layout.Top = layout.Left = 0; layout.Width = targetWidth; layout.Height = targetHeight; layout.Orientation = freerdp_settings_get_uint16(settings, FreeRDP_DesktopOrientation); layout.DesktopScaleFactor = freerdp_settings_get_uint32(settings, FreeRDP_DesktopScaleFactor); layout.DeviceScaleFactor = freerdp_settings_get_uint32(settings, FreeRDP_DeviceScaleFactor); layout.PhysicalWidth = targetWidth; layout.PhysicalHeight = targetHeight; if (IFCALLRESULT(CHANNEL_RC_OK, wfc->disp->SendMonitorLayout, wfc->disp, 1, &layout) != CHANNEL_RC_OK) { WLog_ERR("", "SendMonitorLayout failed."); } freerdp_settings_set_uint32(settings, FreeRDP_SmartSizingWidth, targetWidth); freerdp_settings_set_uint32(settings, FreeRDP_SmartSizingHeight, targetHeight); } wfc->lastSentDate = GetTickCount64(); } } } LRESULT CALLBACK wf_event_proc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) { HDC hdc; LONG_PTR ptr; wfContext* wfc; int x, y, w, h; PAINTSTRUCT ps; BOOL processed; RECT windowRect; MINMAXINFO* minmax; SCROLLINFO si; processed = TRUE; ptr = GetWindowLongPtr(hWnd, GWLP_USERDATA); wfc = (wfContext*)ptr; if (wfc != NULL) { rdpInput* input = wfc->common.context.input; rdpSettings* settings = wfc->common.context.settings; if (!g_parent_hWnd && wfc->hWndParent) g_parent_hWnd = wfc->hWndParent; if (!g_main_hWnd) g_main_hWnd = wfc->hwnd; switch (Msg) { case WM_MOVE: if (!wfc->disablewindowtracking) { int x = (int)(short)LOWORD(lParam); int y = (int)(short)HIWORD(lParam); wfc->client_x = x; wfc->client_y = y; } break; case WM_GETMINMAXINFO: if (freerdp_settings_get_bool(wfc->common.context.settings, FreeRDP_SmartSizing) || (freerdp_settings_get_bool(wfc->common.context.settings, FreeRDP_DynamicResolutionUpdate))) { processed = FALSE; } else { // Set maximum window size for resizing minmax = (MINMAXINFO*)lParam; // always use the last determined canvas diff, because it could be // that the window is minimized when this gets called // wf_update_canvas_diff(wfc); if (!wfc->fullscreen) { // add window decoration minmax->ptMaxTrackSize.x = freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth) + wfc->diff.x; minmax->ptMaxTrackSize.y = freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight) + wfc->diff.y; } } break; case WM_SIZING: wf_sizing(wfc, lParam, wParam); break; case WM_SIZE: GetWindowRect(wfc->hwnd, &windowRect); if (!wfc->fullscreen) { wfc->client_width = LOWORD(lParam); wfc->client_height = HIWORD(lParam); wfc->client_x = windowRect.left; wfc->client_y = windowRect.top; } else { wfc->wasMaximized = TRUE; wf_send_resize(wfc); } if (wfc->client_width && wfc->client_height) { wf_size_scrollbars(wfc, LOWORD(lParam), HIWORD(lParam)); // Workaround: when the window is maximized, the call to "ShowScrollBars" // returns TRUE but has no effect. if (wParam == SIZE_MAXIMIZED && !wfc->fullscreen) { SetWindowPos(wfc->hwnd, HWND_TOP, 0, 0, windowRect.right - windowRect.left, windowRect.bottom - windowRect.top, SWP_NOMOVE | SWP_FRAMECHANGED); wfc->wasMaximized = TRUE; wf_send_resize(wfc); } else if (wParam == SIZE_RESTORED && !wfc->fullscreen && wfc->wasMaximized) { wfc->wasMaximized = FALSE; wf_send_resize(wfc); } else if (wParam == SIZE_MINIMIZED) { g_focus_hWnd = NULL; } } break; case WM_EXITSIZEMOVE: wf_size_scrollbars(wfc, wfc->client_width, wfc->client_height); wf_send_resize(wfc); break; case WM_ERASEBKGND: /* Say we handled it - prevents flickering */ return (LRESULT)1; case WM_PAINT: hdc = BeginPaint(hWnd, &ps); x = ps.rcPaint.left; y = ps.rcPaint.top; w = ps.rcPaint.right - ps.rcPaint.left + 1; h = ps.rcPaint.bottom - ps.rcPaint.top + 1; wf_scale_blt(wfc, hdc, x, y, w, h, wfc->primary->hdc, x - wfc->offset_x + wfc->xCurrentScroll, y - wfc->offset_y + wfc->yCurrentScroll, SRCCOPY); EndPaint(hWnd, &ps); break; #if (_WIN32_WINNT >= 0x0500) case WM_XBUTTONDOWN: wf_scale_mouse_event_ex(wfc, PTR_XFLAGS_DOWN, GET_XBUTTON_WPARAM(wParam), GET_X_LPARAM(lParam) - wfc->offset_x, GET_Y_LPARAM(lParam) - wfc->offset_y); break; case WM_XBUTTONUP: wf_scale_mouse_event_ex(wfc, 0, GET_XBUTTON_WPARAM(wParam), GET_X_LPARAM(lParam) - wfc->offset_x, GET_Y_LPARAM(lParam) - wfc->offset_y); break; #endif case WM_MBUTTONDOWN: wf_scale_mouse_event(wfc, PTR_FLAGS_DOWN | PTR_FLAGS_BUTTON3, GET_X_LPARAM(lParam) - wfc->offset_x, GET_Y_LPARAM(lParam) - wfc->offset_y); break; case WM_MBUTTONUP: wf_scale_mouse_event(wfc, PTR_FLAGS_BUTTON3, GET_X_LPARAM(lParam) - wfc->offset_x, GET_Y_LPARAM(lParam) - wfc->offset_y); break; case WM_LBUTTONDOWN: wf_scale_mouse_event(wfc, PTR_FLAGS_DOWN | PTR_FLAGS_BUTTON1, GET_X_LPARAM(lParam) - wfc->offset_x, GET_Y_LPARAM(lParam) - wfc->offset_y); SetCapture(wfc->hwnd); break; case WM_LBUTTONUP: wf_scale_mouse_event(wfc, PTR_FLAGS_BUTTON1, GET_X_LPARAM(lParam) - wfc->offset_x, GET_Y_LPARAM(lParam) - wfc->offset_y); ReleaseCapture(); break; case WM_RBUTTONDOWN: wf_scale_mouse_event(wfc, PTR_FLAGS_DOWN | PTR_FLAGS_BUTTON2, GET_X_LPARAM(lParam) - wfc->offset_x, GET_Y_LPARAM(lParam) - wfc->offset_y); break; case WM_RBUTTONUP: wf_scale_mouse_event(wfc, PTR_FLAGS_BUTTON2, GET_X_LPARAM(lParam) - wfc->offset_x, GET_Y_LPARAM(lParam) - wfc->offset_y); break; case WM_MOUSEMOVE: wf_scale_mouse_event(wfc, PTR_FLAGS_MOVE, GET_X_LPARAM(lParam) - wfc->offset_x, GET_Y_LPARAM(lParam) - wfc->offset_y); break; #if (_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400) case WM_MOUSEWHEEL: wf_event_process_WM_MOUSEWHEEL(wfc, hWnd, Msg, wParam, lParam, FALSE, GET_X_LPARAM(lParam) - wfc->offset_x, GET_Y_LPARAM(lParam) - wfc->offset_y); break; #endif #if (_WIN32_WINNT >= 0x0600) case WM_MOUSEHWHEEL: wf_event_process_WM_MOUSEWHEEL(wfc, hWnd, Msg, wParam, lParam, TRUE, GET_X_LPARAM(lParam) - wfc->offset_x, GET_Y_LPARAM(lParam) - wfc->offset_y); break; #endif case WM_SETCURSOR: if (LOWORD(lParam) == HTCLIENT) SetCursor(wfc->cursor); else DefWindowProc(hWnd, Msg, wParam, lParam); break; case WM_HSCROLL: { int xDelta; // xDelta = new_pos - current_pos int xNewPos; // new position int yDelta = 0; switch (LOWORD(wParam)) { // User clicked the scroll bar shaft left of the scroll box. case SB_PAGEUP: xNewPos = wfc->xCurrentScroll - 50; break; // User clicked the scroll bar shaft right of the scroll box. case SB_PAGEDOWN: xNewPos = wfc->xCurrentScroll + 50; break; // User clicked the left arrow. case SB_LINEUP: xNewPos = wfc->xCurrentScroll - 5; break; // User clicked the right arrow. case SB_LINEDOWN: xNewPos = wfc->xCurrentScroll + 5; break; // User dragged the scroll box. case SB_THUMBPOSITION: xNewPos = HIWORD(wParam); break; // user is dragging the scrollbar case SB_THUMBTRACK: xNewPos = HIWORD(wParam); break; default: xNewPos = wfc->xCurrentScroll; } // New position must be between 0 and the screen width. xNewPos = MAX(0, xNewPos); xNewPos = MIN(wfc->xMaxScroll, xNewPos); // If the current position does not change, do not scroll. if (xNewPos == wfc->xCurrentScroll) break; // Determine the amount scrolled (in pixels). xDelta = xNewPos - wfc->xCurrentScroll; // Reset the current scroll position. wfc->xCurrentScroll = xNewPos; // Scroll the window. (The system repaints most of the // client area when ScrollWindowEx is called; however, it is // necessary to call UpdateWindow in order to repaint the // rectangle of pixels that were invalidated.) ScrollWindowEx(wfc->hwnd, -xDelta, -yDelta, (CONST RECT*)NULL, (CONST RECT*)NULL, (HRGN)NULL, (PRECT)NULL, SW_INVALIDATE); UpdateWindow(wfc->hwnd); // Reset the scroll bar. si.cbSize = sizeof(si); si.fMask = SIF_POS; si.nPos = wfc->xCurrentScroll; SetScrollInfo(wfc->hwnd, SB_HORZ, &si, TRUE); } break; case WM_VSCROLL: { int xDelta = 0; int yDelta; // yDelta = new_pos - current_pos int yNewPos; // new position switch (LOWORD(wParam)) { // User clicked the scroll bar shaft above the scroll box. case SB_PAGEUP: yNewPos = wfc->yCurrentScroll - 50; break; // User clicked the scroll bar shaft below the scroll box. case SB_PAGEDOWN: yNewPos = wfc->yCurrentScroll + 50; break; // User clicked the top arrow. case SB_LINEUP: yNewPos = wfc->yCurrentScroll - 5; break; // User clicked the bottom arrow. case SB_LINEDOWN: yNewPos = wfc->yCurrentScroll + 5; break; // User dragged the scroll box. case SB_THUMBPOSITION: yNewPos = HIWORD(wParam); break; // user is dragging the scrollbar case SB_THUMBTRACK: yNewPos = HIWORD(wParam); break; default: yNewPos = wfc->yCurrentScroll; } // New position must be between 0 and the screen height. yNewPos = MAX(0, yNewPos); yNewPos = MIN(wfc->yMaxScroll, yNewPos); // If the current position does not change, do not scroll. if (yNewPos == wfc->yCurrentScroll) break; // Determine the amount scrolled (in pixels). yDelta = yNewPos - wfc->yCurrentScroll; // Reset the current scroll position. wfc->yCurrentScroll = yNewPos; // Scroll the window. (The system repaints most of the // client area when ScrollWindowEx is called; however, it is // necessary to call UpdateWindow in order to repaint the // rectangle of pixels that were invalidated.) ScrollWindowEx(wfc->hwnd, -xDelta, -yDelta, (CONST RECT*)NULL, (CONST RECT*)NULL, (HRGN)NULL, (PRECT)NULL, SW_INVALIDATE); UpdateWindow(wfc->hwnd); // Reset the scroll bar. si.cbSize = sizeof(si); si.fMask = SIF_POS; si.nPos = wfc->yCurrentScroll; SetScrollInfo(wfc->hwnd, SB_VERT, &si, TRUE); } break; case WM_SYSCOMMAND: { if (wParam == SYSCOMMAND_ID_SMARTSIZING) { rdpSettings* settings = wfc->common.context.settings; HMENU hMenu = GetSystemMenu(wfc->hwnd, FALSE); const BOOL rc = freerdp_settings_get_bool(settings, FreeRDP_SmartSizing); freerdp_settings_set_bool(settings, FreeRDP_SmartSizing, !rc); CheckMenuItem(hMenu, SYSCOMMAND_ID_SMARTSIZING, freerdp_settings_get_bool(settings, FreeRDP_SmartSizing) ? MF_CHECKED : MF_UNCHECKED); if (!freerdp_settings_get_bool(settings, FreeRDP_SmartSizing)) { SetWindowPos(wfc->hwnd, HWND_TOP, -1, -1, freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth) + wfc->diff.x, freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight) + wfc->diff.y, SWP_NOMOVE); } else { wf_size_scrollbars(wfc, wfc->client_width, wfc->client_height); wf_send_resize(wfc); } } else if (wParam == SYSCOMMAND_ID_REQUEST_CONTROL) { freerdp_client_encomsp_set_control(wfc->common.encomsp, TRUE); } else { processed = FALSE; } } break; default: processed = FALSE; break; } } else { processed = FALSE; } if (processed) return 0; switch (Msg) { case WM_DESTROY: PostQuitMessage(WM_QUIT); break; case WM_SETFOCUS: DEBUG_KBD("getting focus %X", hWnd); freerdp_settings_set_bool(wfc->common.context.settings, FreeRDP_SuspendInput, FALSE); if (alt_ctrl_down()) g_flipping_in = TRUE; g_focus_hWnd = hWnd; freerdp_set_focus(wfc->common.context.instance); break; case WM_KILLFOCUS: freerdp_settings_set_bool(wfc->common.context.settings, FreeRDP_SuspendInput, TRUE); if (g_focus_hWnd == hWnd && wfc && !wfc->fullscreen) { DEBUG_KBD("loosing focus %X", hWnd); if (alt_ctrl_down()) g_flipping_out = TRUE; else g_focus_hWnd = NULL; } break; case WM_ACTIVATE: { int activate = (int)(short)LOWORD(wParam); BOOL minimized_flag = (BOOL)HIWORD(wParam); if (activate != WA_INACTIVE && !minimized_flag) { if (alt_ctrl_down()) g_flipping_in = TRUE; g_focus_hWnd = hWnd; } else { if (alt_ctrl_down()) g_flipping_out = TRUE; else g_focus_hWnd = NULL; } } default: return DefWindowProc(hWnd, Msg, wParam, lParam); break; } return 0; } BOOL wf_scale_blt(wfContext* wfc, HDC hdc, int x, int y, int w, int h, HDC hdcSrc, int x1, int y1, DWORD rop) { rdpSettings* settings; UINT32 ww, wh, dw, dh; WINPR_ASSERT(wfc); settings = wfc->common.context.settings; WINPR_ASSERT(settings); if (!wfc->client_width) wfc->client_width = freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth); if (!wfc->client_height) wfc->client_height = freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight); ww = wfc->client_width; wh = wfc->client_height; dw = freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth); dh = freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight); if (!ww) ww = dw; if (!wh) wh = dh; if (wfc->fullscreen || !freerdp_settings_get_bool(settings, FreeRDP_SmartSizing) || (ww == dw && wh == dh)) { return BitBlt(hdc, x, y, w, h, wfc->primary->hdc, x1, y1, SRCCOPY); } else { SetStretchBltMode(hdc, HALFTONE); SetBrushOrgEx(hdc, 0, 0, NULL); return StretchBlt(hdc, 0, 0, ww, wh, wfc->primary->hdc, 0, 0, dw, dh, SRCCOPY); } return TRUE; } static BOOL wf_scale_mouse_pos(wfContext* wfc, INT32 x, INT32 y, UINT16* px, UINT16* py) { int ww, wh, dw, dh; rdpSettings* settings; if (!wfc || !px || !py) return FALSE; settings = wfc->common.context.settings; if (!settings) return FALSE; if (!wfc->client_width) wfc->client_width = freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth); if (!wfc->client_height) wfc->client_height = freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight); ww = wfc->client_width; wh = wfc->client_height; dw = freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth); dh = freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight); if (!freerdp_settings_get_bool(settings, FreeRDP_SmartSizing) || ((ww == dw) && (wh == dh))) { x += wfc->xCurrentScroll; y += wfc->yCurrentScroll; } else { x = x * dw / ww + wfc->xCurrentScroll; y = y * dh / wh + wfc->yCurrentScroll; } *px = MIN(UINT16_MAX, MAX(0, x)); *py = MIN(UINT16_MAX, MAX(0, y)); return TRUE; } static BOOL wf_pub_mouse_event(wfContext* wfc, UINT16 flags, UINT16 x, UINT16 y) { MouseEventEventArgs eventArgs = { 0 }; eventArgs.flags = flags; eventArgs.x = x; eventArgs.y = y; PubSub_OnMouseEvent(wfc->common.context.pubSub, &wfc->common.context, &eventArgs); return TRUE; } static BOOL wf_scale_mouse_event(wfContext* wfc, UINT16 flags, INT32 x, INT32 y) { UINT16 px, py; WINPR_ASSERT(wfc); if (!wf_scale_mouse_pos(wfc, x, y, &px, &py)) return FALSE; if (freerdp_client_send_button_event(&wfc->common, FALSE, flags, px, py)) return FALSE; return wf_pub_mouse_event(wfc, flags, px, py); } #if (_WIN32_WINNT >= 0x0500) static BOOL wf_scale_mouse_event_ex(wfContext* wfc, UINT16 flags, UINT16 buttonMask, INT32 x, INT32 y) { UINT16 px, py; WINPR_ASSERT(wfc); if (buttonMask & XBUTTON1) flags |= PTR_XFLAGS_BUTTON1; if (buttonMask & XBUTTON2) flags |= PTR_XFLAGS_BUTTON2; if (!wf_scale_mouse_pos(wfc, x, y, &px, &py)) return FALSE; if (freerdp_client_send_extended_button_event(&wfc->common, FALSE, flags, px, py)) return FALSE; return wf_pub_mouse_event(wfc, flags, px, py); } #endif