19#include "sdl_win32.hpp"
20#include "sdl_utils.hpp"
26#include <winpr/library.h>
27#include <winpr/platform.h>
28#include <freerdp/log.h>
32#define TAG CLIENT_TAG("sdl.win32")
38static HWND g_sizeMoveHwnd =
nullptr;
40static int g_sizingEdge = 0;
42static std::mutex g_sizeMoveLock;
44static const wchar_t kFrameProp[] = L
"FreeRDP.RailNativeFrame";
45static const wchar_t kIdProp[] = L
"FreeRDP.RailWindowId";
49 std::scoped_lock lock(g_sizeMoveLock);
53[[nodiscard]]
static SDL_WindowID sdl_win32_window_id(HWND hwnd)
55 return static_cast<SDL_WindowID
>(
reinterpret_cast<uintptr_t
>(GetPropW(hwnd, kIdProp)));
58[[nodiscard]]
static LRESULT sdl_win32_frame_next(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
60 auto next =
reinterpret_cast<WNDPROC
>(GetPropW(hwnd, kFrameProp));
61 return next ? CallWindowProcW(next, hwnd, msg, wParam, lParam)
62 : DefWindowProcW(hwnd, msg, wParam, lParam);
65[[nodiscard]]
static HWND sdl_win32_hwnd(SDL_Window* window)
67 if (!window || !sdl::utils::isWindowsDriver())
69 return static_cast<HWND
>(SDL_GetPointerProperty(SDL_GetWindowProperties(window),
70 SDL_PROP_WINDOW_WIN32_HWND_POINTER,
nullptr));
73bool sdl_win32_restack_windows(
const std::vector<SDL_Window*>& topToBottom)
80 std::vector<Entry> stack;
81 stack.reserve(topToBottom.size());
82 for (
auto* window : topToBottom)
84 HWND hwnd = sdl_win32_hwnd(window);
86 stack.push_back({ hwnd, (GetWindowLongPtr(hwnd, GWL_EXSTYLE) & WS_EX_TOPMOST) != 0 });
91 HDWP dwp = BeginDeferWindowPos(
static_cast<int>(stack.size() - 1));
95 HWND prevTopmost =
nullptr;
96 HWND prevNormal =
nullptr;
97 for (
const auto& e : stack)
99 HWND& prev = e.topmost ? prevTopmost : prevNormal;
102 dwp = DeferWindowPos(dwp, e.hwnd, prev, 0, 0, 0, 0,
103 SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOOWNERZORDER);
109 return EndDeferWindowPos(dwp) != FALSE;
112bool sdl_win32_can_move_size(SDL_Window* window,
bool move,
int wmszEdge)
114 return sdl_win32_hwnd(window) &&
115 (move || ((wmszEdge >= WMSZ_LEFT) && (wmszEdge <= WMSZ_BOTTOMRIGHT)));
118void sdl_win32_run_move_size(SDL_Window* window,
bool move,
int wmszEdge)
120 HWND hwnd = sdl_win32_hwnd(window);
124 const int button = GetSystemMetrics(SM_SWAPBUTTON) ? VK_RBUTTON : VK_LBUTTON;
125 if (GetAsyncKeyState(button) >= 0)
128 if (!ReleaseCapture())
129 WLog_DBG(TAG,
"ReleaseCapture failed: %" PRIu32,
static_cast<uint32_t
>(GetLastError()));
130 const WPARAM cmd = move ? (SC_MOVE | HTCAPTION) : (SC_SIZE | static_cast<WPARAM>(wmszEdge));
131 SendMessage(hwnd, WM_SYSCOMMAND, cmd, 0);
134[[nodiscard]]
static bool sdl_win32_frame_insets(HWND hwnd, RECT& wr, RECT& insets)
138 if (!GetWindowRect(hwnd, &wr) || !GetClientRect(hwnd, &cr) || !ClientToScreen(hwnd, &origin))
140 insets.left = origin.x - wr.left;
141 insets.top = origin.y - wr.top;
142 insets.right = (wr.right - wr.left) - insets.left - cr.right;
143 insets.bottom = (wr.bottom - wr.top) - insets.top - cr.bottom;
147static LRESULT CALLBACK sdl_win32_frame_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
155 return sdl_win32_frame_next(hwnd, msg, wParam, -1);
158 if (wParam && !IsZoomed(hwnd))
160 auto* params =
reinterpret_cast<NCCALCSIZE_PARAMS*
>(lParam);
161 const LONG top = params->rgrc[0].top;
162 const LRESULT rc = sdl_win32_frame_next(hwnd, msg, wParam, lParam);
163 params->rgrc[0].top = top;
170 const LRESULT hit = sdl_win32_frame_next(hwnd, msg, wParam, lParam);
171 if (IsZoomed(hwnd) || ((hit != HTCLIENT) && (hit != HTLEFT) && (hit != HTRIGHT)))
175 const int frame = sdl_win32_frame_insets(hwnd, wr, in) ? in.left : 0;
178 const int x = GET_X_LPARAM(lParam);
179 if (GET_Y_LPARAM(lParam) >= wr.top + frame)
181 if ((hit == HTLEFT) || (x < wr.left + 2 * frame))
183 return ((hit == HTRIGHT) || (x >= wr.right - 2 * frame)) ? HTTOPRIGHT : HTTOP;
185 case WM_GETMINMAXINFO:
188 const LRESULT rc = sdl_win32_frame_next(hwnd, msg, wParam, lParam);
191 if (!IsZoomed(hwnd) && sdl_win32_frame_insets(hwnd, wr, in) && (in.top == 0))
193 auto* info =
reinterpret_cast<MINMAXINFO*
>(lParam);
194 info->ptMinTrackSize.y = std::max<LONG>(info->ptMinTrackSize.y - in.bottom, 0);
198 SDL_Window* window = SDL_GetWindowFromID(sdl_win32_window_id(hwnd));
199 if (window && SDL_GetWindowMaximumSize(window, &maxW, &maxH) && (maxH > 0))
200 info->ptMaxTrackSize.y -= in.bottom;
204 case WM_STYLECHANGING:
206 if (wParam ==
static_cast<WPARAM
>(GWL_STYLE))
207 reinterpret_cast<STYLESTRUCT*
>(lParam)->styleNew &= ~WS_CAPTION;
209 case WM_ENTERSIZEMOVE:
210 g_sizeMoveHwnd = hwnd;
215 const auto handler = sdl_win32_size_move_handler();
216 if ((g_sizingEdge == 0) && handler.sizing)
218 g_sizingEdge =
static_cast<int>(wParam);
219 handler.sizing(handler.userdata, sdl_win32_window_id(hwnd), g_sizingEdge,
false);
225 if (hwnd == g_sizeMoveHwnd)
227 const auto handler = sdl_win32_size_move_handler();
228 const LRESULT rc = sdl_win32_frame_next(hwnd, msg, wParam, lParam);
230 handler.redraw(handler.userdata);
234 case WM_EXITSIZEMOVE:
236 g_sizeMoveHwnd =
nullptr;
237 const LRESULT rc = sdl_win32_frame_next(hwnd, msg, wParam, lParam);
238 const int edge = g_sizingEdge;
240 const auto handler = sdl_win32_size_move_handler();
241 if ((edge != 0) && handler.sizing)
242 handler.sizing(handler.userdata, sdl_win32_window_id(hwnd), edge,
true);
248 if (hwnd == g_sizeMoveHwnd)
250 g_sizeMoveHwnd =
nullptr;
253 RemovePropW(hwnd, kIdProp);
254 auto next =
reinterpret_cast<WNDPROC
>(RemovePropW(hwnd, kFrameProp));
255 if (next && !SetWindowLongPtrW(hwnd, GWLP_WNDPROC,
reinterpret_cast<LONG_PTR
>(next)))
256 WLog_WARN(TAG,
"restoring the window procedure failed: %" PRIu32,
257 static_cast<uint32_t
>(GetLastError()));
258 return next ? CallWindowProcW(next, hwnd, msg, wParam, lParam)
259 : DefWindowProcW(hwnd, msg, wParam, lParam);
264 return sdl_win32_frame_next(hwnd, msg, wParam, lParam);
268static void sdl_win32_plain_frame(HWND hwnd)
270 using SetAttrFn = HRESULT(WINAPI*)(HWND, DWORD, LPCVOID, DWORD);
271 static const auto setAttr = []
273 HMODULE dwm = LoadLibraryExW(L
"dwmapi.dll",
nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
274 return dwm ? GetProcAddressAs(dwm,
"DwmSetWindowAttribute", SetAttrFn) : nullptr;
278 const DWORD none = 0xFFFFFFFE;
279 const DWORD borderColor = 34;
280 if (FAILED(setAttr(hwnd, borderColor, &none,
sizeof(none))))
281 WLog_DBG(TAG,
"DWMWA_BORDER_COLOR not supported");
282 const DWORD square = 1;
283 const DWORD cornerPref = 33;
284 if (FAILED(setAttr(hwnd, cornerPref, &square,
sizeof(square))))
285 WLog_DBG(TAG,
"DWMWA_WINDOW_CORNER_PREFERENCE not supported");
288bool sdl_win32_use_native_frame(SDL_Window* window)
290 HWND hwnd = sdl_win32_hwnd(window);
293 if (!GetPropW(hwnd, kFrameProp))
297 SetWindowLongPtrW(hwnd, GWLP_WNDPROC,
reinterpret_cast<LONG_PTR
>(sdl_win32_frame_proc));
300 if (!SetPropW(hwnd, kFrameProp,
reinterpret_cast<HANDLE
>(next)))
302 if (!SetWindowLongPtrW(hwnd, GWLP_WNDPROC, next))
303 WLog_WARN(TAG,
"restoring the window procedure failed: %" PRIu32,
304 static_cast<uint32_t
>(GetLastError()));
307 const auto id =
static_cast<uintptr_t
>(SDL_GetWindowID(window));
308 if (!SetPropW(hwnd, kIdProp,
reinterpret_cast<HANDLE
>(
id)))
309 WLog_WARN(TAG,
"SetPropW failed: %" PRIu32,
static_cast<uint32_t
>(GetLastError()));
311 sdl_win32_plain_frame(hwnd);
312 SetLastError(ERROR_SUCCESS);
313 if (!SetWindowLongPtr(hwnd, GWL_STYLE, GetWindowLongPtr(hwnd, GWL_STYLE) & ~WS_CAPTION) &&
314 (GetLastError() != ERROR_SUCCESS))
316 return SetWindowPos(hwnd,
nullptr, 0, 0, 0, 0,
317 SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
318 SWP_NOACTIVATE) != FALSE;
323 std::scoped_lock lock(g_sizeMoveLock);
324 g_sizeMove = handler;
327bool sdl_win32_set_client_rect(SDL_Window* window,
const SDL_Rect& rect)
329 HWND hwnd = sdl_win32_hwnd(window);
330 if (!hwnd || !GetPropW(hwnd, kFrameProp))
335 if (!sdl_win32_frame_insets(hwnd, wr, in))
337 return SetWindowPos(hwnd,
nullptr, rect.x - in.left, rect.y - in.top,
338 rect.w + in.left + in.right, rect.h + in.top + in.bottom,
339 SWP_NOZORDER | SWP_NOACTIVATE) != FALSE;
342bool sdl_win32_send_left_button_release(SDL_Window* window)
344 HWND hwnd = sdl_win32_hwnd(window);
346 if (!hwnd || !GetCursorPos(&pt) || !ScreenToClient(hwnd, &pt))
348 const LPARAM pos = MAKELPARAM(
static_cast<WORD
>(pt.x),
static_cast<WORD
>(pt.y));
349 return PostMessage(hwnd, WM_LBUTTONUP, 0, pos) != FALSE;
354bool sdl_win32_restack_windows(WINPR_ATTR_UNUSED
const std::vector<SDL_Window*>& topToBottom)
359bool sdl_win32_can_move_size(WINPR_ATTR_UNUSED SDL_Window* window, WINPR_ATTR_UNUSED
bool move,
360 WINPR_ATTR_UNUSED
int wmszEdge)
365void sdl_win32_run_move_size(WINPR_ATTR_UNUSED SDL_Window* window, WINPR_ATTR_UNUSED
bool move,
366 WINPR_ATTR_UNUSED
int wmszEdge)
370bool sdl_win32_use_native_frame(WINPR_ATTR_UNUSED SDL_Window* window)
379bool sdl_win32_set_client_rect(WINPR_ATTR_UNUSED SDL_Window* window,
380 WINPR_ATTR_UNUSED
const SDL_Rect& rect)
385bool sdl_win32_send_left_button_release(WINPR_ATTR_UNUSED SDL_Window* window)