Atlas - SDL_windowswindow.c

Home / ext / SDL / src / video / windows Lines: 9 | Size: 87884 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)]
[FILE BEGIN]
1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2025 Sam Lantinga <[email protected]> 4 5 This software is provided 'as-is', without any express or implied 6 warranty. In no event will the authors be held liable for any damages 7 arising from the use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, 10 including commercial applications, and to alter it and redistribute it 11 freely, subject to the following restrictions: 12 13 1. The origin of this software must not be misrepresented; you must not 14 claim that you wrote the original software. If you use this software 15 in a product, an acknowledgment in the product documentation would be 16 appreciated but is not required. 17 2. Altered source versions must be plainly marked as such, and must not be 18 misrepresented as being the original software. 19 3. This notice may not be removed or altered from any source distribution. 20*/ 21#include "SDL_internal.h" 22 23#ifdef SDL_VIDEO_DRIVER_WINDOWS 24 25#include "../../core/windows/SDL_windows.h" 26 27#include "../../SDL_hints_c.h" 28#include "../../events/SDL_dropevents_c.h" 29#include "../../events/SDL_keyboard_c.h" 30#include "../../events/SDL_mouse_c.h" 31#include "../../events/SDL_windowevents_c.h" 32#include "../SDL_pixels_c.h" 33#include "../SDL_sysvideo.h" 34 35#include "SDL_windowsvideo.h" 36#include "SDL_windowskeyboard.h" 37#include "SDL_windowswindow.h" 38 39// Dropfile support 40#include <shellapi.h> 41 42#if !(defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES)) 43#include <shobjidl.h> 44#endif 45 46// Windows CE compatibility 47#ifndef SWP_NOCOPYBITS 48#define SWP_NOCOPYBITS 0 49#endif 50 51/* An undocumented message to create a popup system menu 52 * - wParam is always 0 53 * - lParam = MAKELONG(x, y) where x and y are the screen coordinates where the menu should be displayed 54 */ 55#ifndef WM_POPUPSYSTEMMENU 56#define WM_POPUPSYSTEMMENU 0x313 57#endif 58 59// #define HIGHDPI_DEBUG 60 61/* For borderless Windows, still want the following flag: 62 - WS_MINIMIZEBOX: window will respond to Windows minimize commands sent to all windows, such as windows key + m, shaking title bar, etc. 63 Additionally, non-fullscreen windows can add: 64 - WS_CAPTION: this seems to enable the Windows minimize animation 65 - WS_SYSMENU: enables system context menu on task bar 66 This will also cause the task bar to overlap the window and other windowed behaviors, so only use this for windows that shouldn't appear to be fullscreen 67 - WS_THICKFRAME: allows hit-testing to resize window (doesn't actually add a frame to a borderless window). 68 - WS_MAXIMIZEBOX: window will respond to Windows maximize commands sent to all windows, and the window will fill the usable desktop area rather than the whole screen 69 */ 70 71#define STYLE_BASIC (WS_CLIPSIBLINGS | WS_CLIPCHILDREN) 72#define STYLE_FULLSCREEN (WS_POPUP | WS_MINIMIZEBOX) 73#define STYLE_BORDERLESS (WS_POPUP | WS_MINIMIZEBOX) 74#define STYLE_BORDERLESS_WINDOWED (WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX) 75#define STYLE_NORMAL (WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX) 76#define STYLE_RESIZABLE (WS_THICKFRAME | WS_MAXIMIZEBOX) 77#define STYLE_MASK (STYLE_FULLSCREEN | STYLE_BORDERLESS | STYLE_NORMAL | STYLE_RESIZABLE) 78 79static DWORD GetWindowStyle(SDL_Window *window) 80{ 81 DWORD style = 0; 82 83 if (SDL_WINDOW_IS_POPUP(window)) { 84 style |= WS_POPUP; 85 } else if (window->flags & SDL_WINDOW_FULLSCREEN) { 86 style |= STYLE_FULLSCREEN; 87 } else { 88 if (window->flags & SDL_WINDOW_BORDERLESS) { 89 /* This behavior more closely matches other platform where the window is borderless 90 but still interacts with the window manager (e.g. task bar shows above it, it can 91 be resized to fit within usable desktop area, etc.) 92 */ 93 if (SDL_GetHintBoolean("SDL_BORDERLESS_WINDOWED_STYLE", true)) { 94 style |= STYLE_BORDERLESS_WINDOWED; 95 } else { 96 style |= STYLE_BORDERLESS; 97 } 98 } else { 99 style |= STYLE_NORMAL; 100 } 101 102 /* The WS_MAXIMIZEBOX style flag needs to be retained for as long as the window is maximized, 103 * or restoration from minimized can fail, and leaving maximized can result in an odd size. 104 */ 105 if (window->flags & SDL_WINDOW_RESIZABLE) { 106 /* You can have a borderless resizable window, but Windows doesn't always draw it correctly, 107 see https://bugzilla.libsdl.org/show_bug.cgi?id=4466 108 */ 109 if (!(window->flags & SDL_WINDOW_BORDERLESS) || 110 SDL_GetHintBoolean("SDL_BORDERLESS_RESIZABLE_STYLE", true)) { 111 style |= STYLE_RESIZABLE; 112 } 113 } 114 115 if (window->internal && window->internal->force_ws_maximizebox) { 116 /* Even if the resizable flag is cleared, WS_MAXIMIZEBOX is still needed as long 117 * as the window is maximized, or de-maximizing or minimizing and restoring the 118 * maximized window can result in the window disappearing or being the wrong size. 119 */ 120 style |= WS_MAXIMIZEBOX; 121 } 122 123 // Need to set initialize minimize style, or when we call ShowWindow with WS_MINIMIZE it will activate a random window 124 if (window->flags & SDL_WINDOW_MINIMIZED) { 125 style |= WS_MINIMIZE; 126 } 127 } 128 return style; 129} 130 131static DWORD GetWindowStyleEx(SDL_Window *window) 132{ 133 DWORD style = 0; 134 135 if (SDL_WINDOW_IS_POPUP(window) || (window->flags & SDL_WINDOW_UTILITY)) { 136 style |= WS_EX_TOOLWINDOW; 137 } 138 if (SDL_WINDOW_IS_POPUP(window) || (window->flags & SDL_WINDOW_NOT_FOCUSABLE)) { 139 style |= WS_EX_NOACTIVATE; 140 } 141 return style; 142} 143 144#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) 145static ITaskbarList3 *GetTaskbarList(SDL_Window *window) 146{ 147 const SDL_WindowData *data = window->internal; 148 SDL_assert(data->taskbar_button_created); 149 if (!data->videodata->taskbar_list) { 150 HRESULT ret = CoCreateInstance(&CLSID_TaskbarList, NULL, CLSCTX_ALL, &IID_ITaskbarList3, (LPVOID *)&data->videodata->taskbar_list); 151 if (FAILED(ret)) { 152 WIN_SetErrorFromHRESULT("Unable to create taskbar list", ret); 153 return NULL; 154 } 155 ITaskbarList3 *taskbarlist = data->videodata->taskbar_list; 156 ret = taskbarlist->lpVtbl->HrInit(taskbarlist); 157 if (FAILED(ret)) { 158 taskbarlist->lpVtbl->Release(taskbarlist); 159 data->videodata->taskbar_list = NULL; 160 WIN_SetErrorFromHRESULT("Unable to initialize taskbar list", ret); 161 return NULL; 162 } 163 } 164 return data->videodata->taskbar_list; 165} 166#endif 167 168/** 169 * Returns arguments to pass to SetWindowPos - the window rect, including frame, in Windows coordinates. 170 * Can be called before we have a HWND. 171 */ 172static bool WIN_AdjustWindowRectWithStyle(SDL_Window *window, DWORD style, DWORD styleEx, BOOL menu, int *x, int *y, int *width, int *height, SDL_WindowRect rect_type) 173{ 174 SDL_VideoData *videodata = SDL_GetVideoDevice() ? SDL_GetVideoDevice()->internal : NULL; 175 RECT rect; 176 177 // Client rect, in points 178 switch (rect_type) { 179 case SDL_WINDOWRECT_CURRENT: 180 SDL_RelativeToGlobalForWindow(window, window->x, window->y, x, y); 181 *width = window->w; 182 *height = window->h; 183 break; 184 case SDL_WINDOWRECT_WINDOWED: 185 SDL_RelativeToGlobalForWindow(window, window->windowed.x, window->windowed.y, x, y); 186 *width = window->windowed.w; 187 *height = window->windowed.h; 188 break; 189 case SDL_WINDOWRECT_FLOATING: 190 SDL_RelativeToGlobalForWindow(window, window->floating.x, window->floating.y, x, y); 191 *width = window->floating.w; 192 *height = window->floating.h; 193 break; 194 case SDL_WINDOWRECT_PENDING: 195 SDL_RelativeToGlobalForWindow(window, window->pending.x, window->pending.y, x, y); 196 *width = window->pending.w; 197 *height = window->pending.h; 198 break; 199 default: 200 // Should never be here 201 SDL_assert_release(false); 202 *width = 0; 203 *height = 0; 204 break; 205 } 206 207 /* Copy the client size in pixels into this rect structure, 208 which we'll then adjust with AdjustWindowRectEx */ 209 rect.left = 0; 210 rect.top = 0; 211 rect.right = *width; 212 rect.bottom = *height; 213 214 /* borderless windows will have WM_NCCALCSIZE return 0 for the non-client area. When this happens, it looks like windows will send a resize message 215 expanding the window client area to the previous window + chrome size, so shouldn't need to adjust the window size for the set styles. 216 */ 217 if (!(window->flags & SDL_WINDOW_BORDERLESS) && !SDL_WINDOW_IS_POPUP(window)) { 218#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES) 219 AdjustWindowRectEx(&rect, style, menu, 0); 220#else 221 if (WIN_IsPerMonitorV2DPIAware(SDL_GetVideoDevice())) { 222 /* With per-monitor v2, the window border/titlebar size depend on the DPI, so we need to call AdjustWindowRectExForDpi instead of 223 AdjustWindowRectEx. */ 224 if (videodata) { 225 UINT frame_dpi; 226 SDL_WindowData *data = window->internal; 227 frame_dpi = (data && videodata->GetDpiForWindow) ? videodata->GetDpiForWindow(data->hwnd) : USER_DEFAULT_SCREEN_DPI; 228 if (videodata->AdjustWindowRectExForDpi(&rect, style, menu, styleEx, frame_dpi) == 0) { 229 return WIN_SetError("AdjustWindowRectExForDpi()"); 230 } 231 } 232 } else { 233 if (AdjustWindowRectEx(&rect, style, menu, styleEx) == 0) { 234 return WIN_SetError("AdjustWindowRectEx()"); 235 } 236 } 237#endif 238 } 239 240 // Final rect in Windows screen space, including the frame 241 *x += rect.left; 242 *y += rect.top; 243 *width = (rect.right - rect.left); 244 *height = (rect.bottom - rect.top); 245 246#ifdef HIGHDPI_DEBUG 247 SDL_Log("WIN_AdjustWindowRectWithStyle: in: %d, %d, %dx%d, returning: %d, %d, %dx%d, used dpi %d for frame calculation", 248 (rect_type == SDL_WINDOWRECT_FLOATING ? window->floating.x : rect_type == SDL_WINDOWRECT_WINDOWED ? window->windowed.x : window->x), 249 (rect_type == SDL_WINDOWRECT_FLOATING ? window->floating.y : rect_type == SDL_WINDOWRECT_WINDOWED ? window->windowed.y : window->y), 250 (rect_type == SDL_WINDOWRECT_FLOATING ? window->floating.w : rect_type == SDL_WINDOWRECT_WINDOWED ? window->windowed.w : window->w), 251 (rect_type == SDL_WINDOWRECT_FLOATING ? window->floating.h : rect_type == SDL_WINDOWRECT_WINDOWED ? window->windowed.h : window->h), 252 *x, *y, *width, *height, frame_dpi); 253#endif 254 return true; 255} 256 257bool WIN_AdjustWindowRect(SDL_Window *window, int *x, int *y, int *width, int *height, SDL_WindowRect rect_type) 258{ 259 SDL_WindowData *data = window->internal; 260 HWND hwnd = data->hwnd; 261 DWORD style, styleEx; 262 BOOL menu; 263 264 style = GetWindowLong(hwnd, GWL_STYLE); 265 styleEx = GetWindowLong(hwnd, GWL_EXSTYLE); 266#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES) 267 menu = FALSE; 268#else 269 menu = (style & WS_CHILDWINDOW) ? FALSE : (GetMenu(hwnd) != NULL); 270#endif 271 return WIN_AdjustWindowRectWithStyle(window, style, styleEx, menu, x, y, width, height, rect_type); 272} 273 274bool WIN_AdjustWindowRectForHWND(HWND hwnd, LPRECT lpRect, UINT frame_dpi) 275{ 276 SDL_VideoDevice *videodevice = SDL_GetVideoDevice(); 277 SDL_VideoData *videodata = videodevice ? videodevice->internal : NULL; 278 DWORD style, styleEx; 279 BOOL menu; 280 281 style = GetWindowLong(hwnd, GWL_STYLE); 282 styleEx = GetWindowLong(hwnd, GWL_EXSTYLE); 283#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES) 284 menu = FALSE; 285#else 286 menu = (style & WS_CHILDWINDOW) ? FALSE : (GetMenu(hwnd) != NULL); 287#endif 288 289#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES) 290 AdjustWindowRectEx(lpRect, style, menu, styleEx); 291#else 292 if (WIN_IsPerMonitorV2DPIAware(videodevice)) { 293 // With per-monitor v2, the window border/titlebar size depend on the DPI, so we need to call AdjustWindowRectExForDpi instead of AdjustWindowRectEx. 294 if (!frame_dpi) { 295 frame_dpi = videodata->GetDpiForWindow ? videodata->GetDpiForWindow(hwnd) : USER_DEFAULT_SCREEN_DPI; 296 } 297 if (!videodata->AdjustWindowRectExForDpi(lpRect, style, menu, styleEx, frame_dpi)) { 298 return WIN_SetError("AdjustWindowRectExForDpi()"); 299 } 300 } else { 301 if (!AdjustWindowRectEx(lpRect, style, menu, styleEx)) { 302 return WIN_SetError("AdjustWindowRectEx()"); 303 } 304 } 305#endif 306 return true; 307} 308 309bool WIN_SetWindowPositionInternal(SDL_Window *window, UINT flags, SDL_WindowRect rect_type) 310{ 311 SDL_Window *child_window; 312 SDL_WindowData *data = window->internal; 313 HWND hwnd = data->hwnd; 314 HWND top; 315 int x, y; 316 int w, h; 317 bool result = true; 318 319 // Figure out what the window area will be 320 if (SDL_ShouldAllowTopmost() && (window->flags & SDL_WINDOW_ALWAYS_ON_TOP)) { 321 top = HWND_TOPMOST; 322 } else { 323 top = HWND_NOTOPMOST; 324 } 325 326 WIN_AdjustWindowRect(window, &x, &y, &w, &h, rect_type); 327 328 data->expected_resize = true; 329 if (SetWindowPos(hwnd, top, x, y, w, h, flags) == 0) { 330 result = WIN_SetError("SetWindowPos()"); 331 } 332 data->expected_resize = false; 333 334 // Update any child windows 335 for (child_window = window->first_child; child_window; child_window = child_window->next_sibling) { 336 if (!child_window->internal) { 337 // This child window is not yet fully initialized. 338 continue; 339 } 340 if (!WIN_SetWindowPositionInternal(child_window, flags, SDL_WINDOWRECT_CURRENT)) { 341 result = false; 342 } 343 } 344 return result; 345} 346 347static SDL_WindowEraseBackgroundMode GetEraseBackgroundModeHint(void) 348{ 349 const char *hint = SDL_GetHint(SDL_HINT_WINDOWS_ERASE_BACKGROUND_MODE); 350 if (!hint) 351 return SDL_ERASEBACKGROUNDMODE_INITIAL; 352 353 if (SDL_strstr(hint, "never")) 354 return SDL_ERASEBACKGROUNDMODE_NEVER; 355 356 if (SDL_strstr(hint, "initial")) 357 return SDL_ERASEBACKGROUNDMODE_INITIAL; 358 359 if (SDL_strstr(hint, "always")) 360 return SDL_ERASEBACKGROUNDMODE_ALWAYS; 361 362 int mode = SDL_GetStringInteger(hint, 1); 363 if (mode < 0 || mode > 2) { 364 SDL_Log("GetEraseBackgroundModeHint: invalid value for SDL_HINT_WINDOWS_ERASE_BACKGROUND_MODE. Fallback to default"); 365 return SDL_ERASEBACKGROUNDMODE_INITIAL; 366 } 367 368 return (SDL_WindowEraseBackgroundMode)mode; 369} 370 371static bool SetupWindowData(SDL_VideoDevice *_this, SDL_Window *window, HWND hwnd, HWND parent) 372{ 373 SDL_VideoData *videodata = _this->internal; 374 SDL_WindowData *data; 375 376 // Allocate the window data 377 data = (SDL_WindowData *)SDL_calloc(1, sizeof(*data)); 378 if (!data) { 379 return false; 380 } 381 data->window = window; 382 data->hwnd = hwnd; 383 data->parent = parent; 384#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES) 385 data->hdc = (HDC)data->hwnd; 386#else 387 data->hdc = GetDC(hwnd); 388#endif 389 data->hinstance = (HINSTANCE)GetWindowLongPtr(hwnd, GWLP_HINSTANCE); 390 data->mouse_button_flags = (WPARAM)-1; 391 data->last_pointer_update = (LPARAM)-1; 392 data->videodata = videodata; 393 data->initializing = true; 394 data->hint_erase_background_mode = GetEraseBackgroundModeHint(); 395 396 397 // WIN_WarpCursor() jitters by +1, and remote desktop warp wobble is +/- 1 398#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) 399 LONG remote_desktop_adjustment = GetSystemMetrics(SM_REMOTESESSION) ? 2 : 0; 400 data->cursor_ctrlock_rect.left = 0 - remote_desktop_adjustment; 401 data->cursor_ctrlock_rect.top = 0; 402 data->cursor_ctrlock_rect.right = 1 + remote_desktop_adjustment; 403 data->cursor_ctrlock_rect.bottom = 1; 404#endif 405 406 if (SDL_GetHintBoolean("SDL_WINDOW_RETAIN_CONTENT", false)) { 407 data->copybits_flag = 0; 408 } else { 409 data->copybits_flag = SWP_NOCOPYBITS; 410 } 411 412#ifdef HIGHDPI_DEBUG 413 SDL_Log("SetupWindowData: initialized data->scaling_dpi to %d", data->scaling_dpi); 414#endif 415 416#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) 417 // Associate the data with the window 418 if (!SetProp(hwnd, TEXT("SDL_WindowData"), data)) { 419 ReleaseDC(hwnd, data->hdc); 420 SDL_free(data); 421 return WIN_SetError("SetProp() failed"); 422 } 423#endif 424 425 window->internal = data; 426 427 // Set up the window proc function 428 if (window->flags & SDL_WINDOW_EXTERNAL) { 429#ifdef GWLP_WNDPROC 430 data->wndproc = (WNDPROC)GetWindowLongPtr(hwnd, GWLP_WNDPROC); 431 if (data->wndproc != WIN_WindowProc) { 432 SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)WIN_WindowProc); 433 } 434#else 435 data->wndproc = (WNDPROC)GetWindowLong(hwnd, GWL_WNDPROC); 436 if (data->wndproc != WIN_WindowProc) { 437 SetWindowLong(hwnd, GWL_WNDPROC, (LONG_PTR)WIN_WindowProc); 438 } 439#endif 440 } else { 441 // We set up our window proc function at window creation. 442 // If someone has set hooks to modify it, leave it alone. 443 } 444 445 // Fill in the SDL window with the window state 446 { 447 DWORD style = GetWindowLong(hwnd, GWL_STYLE); 448 if (style & WS_VISIBLE) { 449 window->flags &= ~SDL_WINDOW_HIDDEN; 450 } else { 451 window->flags |= SDL_WINDOW_HIDDEN; 452 } 453 if (style & WS_POPUP) { 454 window->flags |= SDL_WINDOW_BORDERLESS; 455 } else { 456 window->flags &= ~SDL_WINDOW_BORDERLESS; 457 } 458 if (style & WS_THICKFRAME) { 459 window->flags |= SDL_WINDOW_RESIZABLE; 460 } else if (!(style & WS_POPUP)) { 461 window->flags &= ~SDL_WINDOW_RESIZABLE; 462 } 463#ifdef WS_MAXIMIZE 464 if (style & WS_MAXIMIZE) { 465 window->flags |= SDL_WINDOW_MAXIMIZED; 466 } else 467#endif 468 { 469 window->flags &= ~SDL_WINDOW_MAXIMIZED; 470 } 471#ifdef WS_MINIMIZE 472 if (style & WS_MINIMIZE) { 473 window->flags |= SDL_WINDOW_MINIMIZED; 474 } else 475#endif 476 { 477 window->flags &= ~SDL_WINDOW_MINIMIZED; 478 } 479 } 480 if (!(window->flags & SDL_WINDOW_MINIMIZED)) { 481 RECT rect; 482 if (GetClientRect(hwnd, &rect) && WIN_WindowRectValid(&rect)) { 483 int w = rect.right; 484 int h = rect.bottom; 485 486 if (window->flags & SDL_WINDOW_EXTERNAL) { 487 window->floating.w = window->windowed.w = window->w = w; 488 window->floating.h = window->windowed.h = window->h = h; 489 } else if ((window->windowed.w && window->windowed.w != w) || (window->windowed.h && window->windowed.h != h)) { 490 // We tried to create a window larger than the desktop and Windows didn't allow it. Override! 491 int x, y; 492 // Figure out what the window area will be 493 WIN_AdjustWindowRect(window, &x, &y, &w, &h, SDL_WINDOWRECT_FLOATING); 494 data->expected_resize = true; 495 SetWindowPos(hwnd, NULL, x, y, w, h, data->copybits_flag | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOACTIVATE); 496 data->expected_resize = false; 497 } else { 498 window->w = w; 499 window->h = h; 500 } 501 } 502 } 503#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) 504 if (!(window->flags & SDL_WINDOW_MINIMIZED)) { 505 POINT point; 506 point.x = 0; 507 point.y = 0; 508 if (ClientToScreen(hwnd, &point)) { 509 if (window->flags & SDL_WINDOW_EXTERNAL) { 510 window->floating.x = window->windowed.x = point.x; 511 window->floating.y = window->windowed.y = point.y; 512 } 513 window->x = point.x; 514 window->y = point.y; 515 } 516 } 517 518 WIN_UpdateWindowICCProfile(window, false); 519#endif 520 521#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES) 522 window->flags |= SDL_WINDOW_INPUT_FOCUS; 523 SDL_SetKeyboardFocus(window); 524#else 525 if (GetFocus() == hwnd) { 526 window->flags |= SDL_WINDOW_INPUT_FOCUS; 527 SDL_SetKeyboardFocus(window); 528 WIN_UpdateClipCursor(window); 529 } 530#endif 531 532 if (window->flags & SDL_WINDOW_ALWAYS_ON_TOP) { 533 WIN_SetWindowAlwaysOnTop(_this, window, true); 534 } else { 535 WIN_SetWindowAlwaysOnTop(_this, window, false); 536 } 537 538#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) 539 // Enable multi-touch 540 if (videodata->RegisterTouchWindow) { 541 videodata->RegisterTouchWindow(hwnd, (TWF_FINETOUCH | TWF_WANTPALM)); 542 } 543#endif 544 545 if (data->parent && !window->parent) { 546 data->destroy_parent_with_window = true; 547 } 548 549 data->initializing = false; 550 551#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) 552 if (window->flags & SDL_WINDOW_EXTERNAL) { 553 // Query the title from the existing window 554 LPTSTR title; 555 int titleLen; 556 bool isstack; 557 558 titleLen = GetWindowTextLength(hwnd); 559 title = SDL_small_alloc(TCHAR, titleLen + 1, &isstack); 560 if (title) { 561 titleLen = GetWindowText(hwnd, title, titleLen + 1); 562 } else { 563 titleLen = 0; 564 } 565 if (titleLen > 0) { 566 window->title = WIN_StringToUTF8(title); 567 } 568 if (title) { 569 SDL_small_free(title, isstack); 570 } 571 } 572#endif // !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) 573 574 SDL_PropertiesID props = SDL_GetWindowProperties(window); 575 SDL_SetPointerProperty(props, SDL_PROP_WINDOW_WIN32_HWND_POINTER, data->hwnd); 576 SDL_SetPointerProperty(props, SDL_PROP_WINDOW_WIN32_HDC_POINTER, data->hdc); 577 SDL_SetPointerProperty(props, SDL_PROP_WINDOW_WIN32_INSTANCE_POINTER, data->hinstance); 578 579 // All done! 580 return true; 581} 582 583static void CleanupWindowData(SDL_VideoDevice *_this, SDL_Window *window) 584{ 585 SDL_WindowData *data = window->internal; 586 587 if (data) { 588#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) 589 if (data->drop_target) { 590 WIN_AcceptDragAndDrop(window, false); 591 } 592 SDL_free(data->ICMFileName); 593 if (data->keyboard_hook) { 594 UnhookWindowsHookEx(data->keyboard_hook); 595 } 596 if (data->hicon) { 597 DestroyIcon(data->hicon); 598 } 599 ReleaseDC(data->hwnd, data->hdc); 600 RemoveProp(data->hwnd, TEXT("SDL_WindowData")); 601#endif 602 if (!(window->flags & SDL_WINDOW_EXTERNAL)) { 603 DestroyWindow(data->hwnd); 604 if (data->destroy_parent_with_window && data->parent) { 605 DestroyWindow(data->parent); 606 } 607 } else { 608 // Restore any original event handler... 609 if (data->wndproc) { 610#ifdef GWLP_WNDPROC 611 SetWindowLongPtr(data->hwnd, GWLP_WNDPROC, 612 (LONG_PTR)data->wndproc); 613#else 614 SetWindowLong(data->hwnd, GWL_WNDPROC, 615 (LONG_PTR)data->wndproc); 616#endif 617 } 618 } 619 SDL_free(data); 620 } 621 window->internal = NULL; 622} 623 624static void WIN_ConstrainPopup(SDL_Window *window, bool output_to_pending) 625{ 626 // Possibly clamp popup windows to the output borders 627 if (SDL_WINDOW_IS_POPUP(window)) { 628 SDL_Window *w; 629 SDL_DisplayID displayID; 630 SDL_Rect rect; 631 int abs_x = window->last_position_pending ? window->pending.x : window->floating.x; 632 int abs_y = window->last_position_pending ? window->pending.y : window->floating.y; 633 const int width = window->last_size_pending ? window->pending.w : window->floating.w; 634 const int height = window->last_size_pending ? window->pending.h : window->floating.h; 635 int offset_x = 0, offset_y = 0; 636 637 if (window->constrain_popup) { 638 // Calculate the total offset from the parents 639 for (w = window->parent; SDL_WINDOW_IS_POPUP(w); w = w->parent) { 640 offset_x += w->x; 641 offset_y += w->y; 642 } 643 644 offset_x += w->x; 645 offset_y += w->y; 646 abs_x += offset_x; 647 abs_y += offset_y; 648 649 // Constrain the popup window to the display of the toplevel parent 650 displayID = SDL_GetDisplayForWindow(w); 651 SDL_GetDisplayBounds(displayID, &rect); 652 if (abs_x + width > rect.x + rect.w) { 653 abs_x -= (abs_x + width) - (rect.x + rect.w); 654 } 655 if (abs_y + height > rect.y + rect.h) { 656 abs_y -= (abs_y + height) - (rect.y + rect.h); 657 } 658 abs_x = SDL_max(abs_x, rect.x); 659 abs_y = SDL_max(abs_y, rect.y); 660 } 661 662 if (output_to_pending) { 663 window->pending.x = abs_x - offset_x; 664 window->pending.y = abs_y - offset_y; 665 window->pending.w = width; 666 window->pending.h = height; 667 } else { 668 window->floating.x = abs_x - offset_x; 669 window->floating.y = abs_y - offset_y; 670 window->floating.w = width; 671 window->floating.h = height; 672 } 673 } 674} 675 676static void WIN_SetKeyboardFocus(SDL_Window *window, bool set_active_focus) 677{ 678 SDL_Window *toplevel = window; 679 680 // Find the topmost parent 681 while (SDL_WINDOW_IS_POPUP(toplevel)) { 682 toplevel = toplevel->parent; 683 } 684 685 toplevel->keyboard_focus = window; 686 687 if (set_active_focus && !window->is_hiding && !window->is_destroying) { 688 SDL_SetKeyboardFocus(window); 689 } 690} 691 692bool WIN_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID create_props) 693{ 694 SDL_VideoData *videodata = _this->internal; 695 HWND hwnd = (HWND)SDL_GetPointerProperty(create_props, SDL_PROP_WINDOW_CREATE_WIN32_HWND_POINTER, SDL_GetPointerProperty(create_props, "sdl2-compat.external_window", NULL)); 696 HWND parent = NULL; 697 if (hwnd) { 698 window->flags |= SDL_WINDOW_EXTERNAL; 699 700 if (!SetupWindowData(_this, window, hwnd, parent)) { 701 return false; 702 } 703 } else { 704 DWORD style = STYLE_BASIC; 705 DWORD styleEx = 0; 706 int x, y; 707 int w, h; 708 709 if (window->flags & SDL_WINDOW_UTILITY) { 710 parent = CreateWindow(SDL_Appname, TEXT(""), STYLE_BASIC, 0, 0, 32, 32, NULL, NULL, SDL_Instance, NULL); 711 } else if (window->parent) { 712 parent = window->parent->internal->hwnd; 713 } 714 715 style |= GetWindowStyle(window); 716 styleEx |= GetWindowStyleEx(window); 717 718 // Figure out what the window area will be 719 WIN_ConstrainPopup(window, false); 720 WIN_AdjustWindowRectWithStyle(window, style, styleEx, FALSE, &x, &y, &w, &h, SDL_WINDOWRECT_FLOATING); 721 722 hwnd = CreateWindowEx(styleEx, SDL_Appname, TEXT(""), style, 723 x, y, w, h, parent, NULL, SDL_Instance, NULL); 724 if (!hwnd) { 725 return WIN_SetError("Couldn't create window"); 726 } 727 728 WIN_UpdateDarkModeForHWND(hwnd); 729 730 WIN_PumpEventsForHWND(_this, hwnd); 731 732 if (!SetupWindowData(_this, window, hwnd, parent)) { 733 DestroyWindow(hwnd); 734 if (parent) { 735 DestroyWindow(parent); 736 } 737 return false; 738 } 739 740#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) 741 // Ensure that the IME isn't active on the new window until explicitly requested. 742 WIN_StopTextInput(_this, window); 743#endif 744 745 // Inform Windows of the frame change so we can respond to WM_NCCALCSIZE 746 SetWindowPos(hwnd, NULL, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOACTIVATE); 747 748 if (window->flags & SDL_WINDOW_MINIMIZED) { 749 /* TODO: We have to clear SDL_WINDOW_HIDDEN here to ensure the window flags match the window state. The 750 window is already shown after this and windows with WS_MINIMIZE do not generate a WM_SHOWWINDOW. This 751 means you can't currently create a window that is initially hidden and is minimized when shown. 752 */ 753 window->flags &= ~SDL_WINDOW_HIDDEN; 754 ShowWindow(hwnd, SW_SHOWMINNOACTIVE); 755 } 756 } 757 758#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) 759 // FIXME: does not work on all hardware configurations with different renders (i.e. hybrid GPUs) 760 if (window->flags & SDL_WINDOW_TRANSPARENT) { 761 if (videodata->DwmEnableBlurBehindWindow) { 762 /* The region indicates which part of the window will be blurred and rest will be transparent. This 763 is because the alpha value of the window will be used for non-blurred areas 764 We can use (-1, -1, 0, 0) boundary to make sure no pixels are being blurred 765 */ 766 HRGN rgn = CreateRectRgn(-1, -1, 0, 0); 767 DWM_BLURBEHIND bb; 768 bb.flags = (DWM_BB_ENABLE | DWM_BB_BLURREGION); 769 bb.enable = TRUE; 770 bb.blur_region = rgn; 771 bb.transition_on_maxed = FALSE; 772 videodata->DwmEnableBlurBehindWindow(hwnd, &bb); 773 DeleteObject(rgn); 774 } 775 } 776 777 HWND share_hwnd = (HWND)SDL_GetPointerProperty(create_props, SDL_PROP_WINDOW_CREATE_WIN32_PIXEL_FORMAT_HWND_POINTER, NULL); 778 if (share_hwnd) { 779 HDC hdc = GetDC(share_hwnd); 780 int pixel_format = GetPixelFormat(hdc); 781 PIXELFORMATDESCRIPTOR pfd; 782 783 SDL_zero(pfd); 784 DescribePixelFormat(hdc, pixel_format, sizeof(pfd), &pfd); 785 ReleaseDC(share_hwnd, hdc); 786 787 if (!SetPixelFormat(window->internal->hdc, pixel_format, &pfd)) { 788 WIN_DestroyWindow(_this, window); 789 return WIN_SetError("SetPixelFormat()"); 790 } 791 } else { 792#endif // !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) 793 if (!(window->flags & SDL_WINDOW_OPENGL)) { 794 return true; 795 } 796 797 // The rest of this macro mess is for OpenGL or OpenGL ES windows 798#ifdef SDL_VIDEO_OPENGL_ES2 799 if ((_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES || 800 SDL_GetHintBoolean(SDL_HINT_VIDEO_FORCE_EGL, false)) 801#ifdef SDL_VIDEO_OPENGL_WGL 802 && (!_this->gl_data || WIN_GL_UseEGL(_this)) 803#endif // SDL_VIDEO_OPENGL_WGL 804 ) { 805#ifdef SDL_VIDEO_OPENGL_EGL 806 if (!WIN_GLES_SetupWindow(_this, window)) { 807 WIN_DestroyWindow(_this, window); 808 return false; 809 } 810 return true; 811#else 812 return SDL_SetError("Could not create GLES window surface (EGL support not configured)"); 813#endif // SDL_VIDEO_OPENGL_EGL 814 } 815#endif // SDL_VIDEO_OPENGL_ES2 816 817#ifdef SDL_VIDEO_OPENGL_WGL 818 if (!WIN_GL_SetupWindow(_this, window)) { 819 WIN_DestroyWindow(_this, window); 820 return false; 821 } 822#else 823 return SDL_SetError("Could not create GL window (WGL support not configured)"); 824#endif 825#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) 826 } 827#endif // !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) 828 829 return true; 830} 831 832void WIN_SetWindowTitle(SDL_VideoDevice *_this, SDL_Window *window) 833{ 834#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) 835 HWND hwnd = window->internal->hwnd; 836 LPTSTR title = WIN_UTF8ToString(window->title); 837 SetWindowText(hwnd, title); 838 SDL_free(title); 839#endif 840} 841 842bool WIN_SetWindowIcon(SDL_VideoDevice *_this, SDL_Window *window, SDL_Surface *icon) 843{ 844#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) 845 SDL_WindowData *data = window->internal; 846 HWND hwnd = data->hwnd; 847 HICON hicon = NULL; 848 BYTE *icon_bmp; 849 int icon_len, mask_len, row_len, y; 850 BITMAPINFOHEADER *bmi; 851 Uint8 *dst; 852 bool isstack; 853 bool result = true; 854 855 // Create temporary buffer for ICONIMAGE structure 856 SDL_COMPILE_TIME_ASSERT(WIN_SetWindowIcon_uses_BITMAPINFOHEADER_to_prepare_an_ICONIMAGE, sizeof(BITMAPINFOHEADER) == 40); 857 mask_len = (icon->h * (icon->w + 7) / 8); 858 icon_len = sizeof(BITMAPINFOHEADER) + icon->h * icon->w * sizeof(Uint32) + mask_len; 859 icon_bmp = SDL_small_alloc(BYTE, icon_len, &isstack); 860 if (!icon_bmp) { 861 return false; 862 } 863 864 // Write the BITMAPINFO header 865 bmi = (BITMAPINFOHEADER *)icon_bmp; 866 bmi->biSize = SDL_Swap32LE(sizeof(BITMAPINFOHEADER)); 867 bmi->biWidth = SDL_Swap32LE(icon->w); 868 bmi->biHeight = SDL_Swap32LE(icon->h * 2); 869 bmi->biPlanes = SDL_Swap16LE(1); 870 bmi->biBitCount = SDL_Swap16LE(32); 871 bmi->biCompression = SDL_Swap32LE(BI_RGB); 872 bmi->biSizeImage = SDL_Swap32LE(icon->h * icon->w * sizeof(Uint32)); 873 bmi->biXPelsPerMeter = SDL_Swap32LE(0); 874 bmi->biYPelsPerMeter = SDL_Swap32LE(0); 875 bmi->biClrUsed = SDL_Swap32LE(0); 876 bmi->biClrImportant = SDL_Swap32LE(0); 877 878 // Write the pixels upside down into the bitmap buffer 879 SDL_assert(icon->format == SDL_PIXELFORMAT_ARGB8888); 880 dst = &icon_bmp[sizeof(BITMAPINFOHEADER)]; 881 row_len = icon->w * sizeof(Uint32); 882 y = icon->h; 883 while (y--) { 884 Uint8 *src = (Uint8 *)icon->pixels + y * icon->pitch; 885 SDL_memcpy(dst, src, row_len); 886 dst += row_len; 887 } 888 889 // Write the mask 890 SDL_memset(icon_bmp + icon_len - mask_len, 0xFF, mask_len); 891 892 hicon = CreateIconFromResource(icon_bmp, icon_len, TRUE, 0x00030000); 893 894 SDL_small_free(icon_bmp, isstack); 895 896 if (!hicon) { 897 return SDL_SetError("SetWindowIcon() failed, error %08X", (unsigned int)GetLastError()); 898 } 899 900 if (data->hicon) { 901 DestroyIcon(data->hicon); 902 } 903 data->hicon = hicon; 904 905 // Set the icon for the window 906 SendMessage(hwnd, WM_SETICON, ICON_SMALL, (LPARAM)hicon); 907 908 // Set the icon in the task manager (should we do this?) 909 SendMessage(hwnd, WM_SETICON, ICON_BIG, (LPARAM)hicon); 910 return result; 911#else 912 return SDL_Unsupported(); 913#endif 914} 915 916bool WIN_SetWindowPosition(SDL_VideoDevice *_this, SDL_Window *window) 917{ 918 /* HighDPI support: removed SWP_NOSIZE. If the move results in a DPI change, we need to allow 919 * the window to resize (e.g. AdjustWindowRectExForDpi frame sizes are different). 920 */ 921 if (!(window->flags & SDL_WINDOW_FULLSCREEN)) { 922 if (!(window->flags & (SDL_WINDOW_MAXIMIZED | SDL_WINDOW_MINIMIZED))) { 923 WIN_ConstrainPopup(window, true); 924 return WIN_SetWindowPositionInternal(window, 925 window->internal->copybits_flag | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOSIZE | 926 SWP_NOACTIVATE, SDL_WINDOWRECT_PENDING); 927 } 928 } else { 929 return SDL_UpdateFullscreenMode(window, SDL_FULLSCREEN_OP_ENTER, true); 930 } 931 932 return true; 933} 934 935void WIN_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window) 936{ 937 if (!(window->flags & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_MAXIMIZED))) { 938 WIN_SetWindowPositionInternal(window, window->internal->copybits_flag | SWP_NOMOVE | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOACTIVATE, SDL_WINDOWRECT_PENDING); 939 } else { 940 // Can't resize the window 941 window->last_size_pending = false; 942 } 943} 944 945bool WIN_GetWindowBordersSize(SDL_VideoDevice *_this, SDL_Window *window, int *top, int *left, int *bottom, int *right) 946{ 947#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES) 948 HWND hwnd = window->internal->hwnd; 949 RECT rcClient; 950 951 /* rcClient stores the size of the inner window, while rcWindow stores the outer size relative to the top-left 952 * screen position; so the top/left values of rcClient are always {0,0} and bottom/right are {height,width} */ 953 GetClientRect(hwnd, &rcClient); 954 955 *top = rcClient.top; 956 *left = rcClient.left; 957 *bottom = rcClient.bottom; 958 *right = rcClient.right; 959 960 return true; 961#else // !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) 962 HWND hwnd = window->internal->hwnd; 963 RECT rcClient, rcWindow; 964 POINT ptDiff; 965 966 /* rcClient stores the size of the inner window, while rcWindow stores the outer size relative to the top-left 967 * screen position; so the top/left values of rcClient are always {0,0} and bottom/right are {height,width} */ 968 if (!GetClientRect(hwnd, &rcClient)) { 969 return SDL_SetError("GetClientRect() failed, error %08X", (unsigned int)GetLastError()); 970 } 971 972 if (!GetWindowRect(hwnd, &rcWindow)) { 973 return SDL_SetError("GetWindowRect() failed, error %08X", (unsigned int)GetLastError()); 974 } 975 976 /* convert the top/left values to make them relative to 977 * the window; they will end up being slightly negative */ 978 ptDiff.y = rcWindow.top; 979 ptDiff.x = rcWindow.left; 980 981 if (!ScreenToClient(hwnd, &ptDiff)) { 982 return SDL_SetError("ScreenToClient() failed, error %08X", (unsigned int)GetLastError()); 983 } 984 985 rcWindow.top = ptDiff.y; 986 rcWindow.left = ptDiff.x; 987 988 /* convert the bottom/right values to make them relative to the window, 989 * these will be slightly bigger than the inner width/height */ 990 ptDiff.y = rcWindow.bottom; 991 ptDiff.x = rcWindow.right; 992 993 if (!ScreenToClient(hwnd, &ptDiff)) { 994 return SDL_SetError("ScreenToClient() failed, error %08X", (unsigned int)GetLastError()); 995 } 996 997 rcWindow.bottom = ptDiff.y; 998 rcWindow.right = ptDiff.x; 999 1000 /* Now that both the inner and outer rects use the same coordinate system we can subtract them to get the border size. 1001 * Keep in mind that the top/left coordinates of rcWindow are negative because the border lies slightly before {0,0}, 1002 * so switch them around because SDL3 wants them in positive. */ 1003 *top = rcClient.top - rcWindow.top; 1004 *left = rcClient.left - rcWindow.left; 1005 *bottom = rcWindow.bottom - rcClient.bottom; 1006 *right = rcWindow.right - rcClient.right; 1007 1008 return true; 1009#endif // !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) 1010} 1011 1012void WIN_GetWindowSizeInPixels(SDL_VideoDevice *_this, SDL_Window *window, int *w, int *h) 1013{ 1014 const SDL_WindowData *data = window->internal; 1015 HWND hwnd = data->hwnd; 1016 RECT rect; 1017 1018 if (GetClientRect(hwnd, &rect) && WIN_WindowRectValid(&rect)) { 1019 *w = rect.right; 1020 *h = rect.bottom; 1021 } else if (window->last_pixel_w && window->last_pixel_h) { 1022 *w = window->last_pixel_w; 1023 *h = window->last_pixel_h; 1024 } else { 1025 // Probably created minimized, use the restored size 1026 *w = window->floating.w; 1027 *h = window->floating.h; 1028 } 1029} 1030 1031void WIN_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window) 1032{ 1033 SDL_WindowData *data = window->internal; 1034 HWND hwnd = data->hwnd; 1035 DWORD style; 1036 1037 bool bActivate = SDL_GetHintBoolean(SDL_HINT_WINDOW_ACTIVATE_WHEN_SHOWN, true); 1038 1039 if (SDL_WINDOW_IS_POPUP(window)) { 1040 // Update our position in case our parent moved while we were hidden 1041 WIN_SetWindowPosition(_this, window); 1042 } 1043 1044 // If the window isn't borderless and will be fullscreen, use the borderless style to hide the initial borders. 1045 if (window->pending_flags & SDL_WINDOW_FULLSCREEN) { 1046 if (!(window->flags & SDL_WINDOW_BORDERLESS)) { 1047 window->flags |= SDL_WINDOW_BORDERLESS; 1048 style = GetWindowLong(hwnd, GWL_STYLE); 1049 style &= ~STYLE_MASK; 1050 style |= GetWindowStyle(window); 1051 SetWindowLong(hwnd, GWL_STYLE, style); 1052 window->flags &= ~SDL_WINDOW_BORDERLESS; 1053 } 1054 } 1055 style = GetWindowLong(hwnd, GWL_EXSTYLE); 1056 if (style & WS_EX_NOACTIVATE) { 1057 bActivate = false; 1058 } 1059 1060 data->showing_window = true; 1061 if (bActivate) { 1062 ShowWindow(hwnd, SW_SHOW); 1063 } else { 1064 // Use SetWindowPos instead of ShowWindow to avoid activating the parent window if this is a child window 1065 SetWindowPos(hwnd, NULL, 0, 0, 0, 0, window->internal->copybits_flag | SWP_SHOWWINDOW | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER); 1066 } 1067 data->showing_window = false; 1068 1069 if ((window->flags & SDL_WINDOW_POPUP_MENU) && !(window->flags & SDL_WINDOW_NOT_FOCUSABLE) && bActivate) { 1070 WIN_SetKeyboardFocus(window, true); 1071 } 1072 if (window->flags & SDL_WINDOW_MODAL) { 1073 WIN_SetWindowModal(_this, window, true); 1074 } 1075} 1076 1077void WIN_HideWindow(SDL_VideoDevice *_this, SDL_Window *window) 1078{ 1079 HWND hwnd = window->internal->hwnd; 1080 1081 if (window->flags & SDL_WINDOW_MODAL) { 1082 WIN_SetWindowModal(_this, window, false); 1083 } 1084 1085 ShowWindow(hwnd, SW_HIDE); 1086 1087 // Transfer keyboard focus back to the parent from a grabbing popup. 1088 if ((window->flags & SDL_WINDOW_POPUP_MENU) && !(window->flags & SDL_WINDOW_NOT_FOCUSABLE)) { 1089 SDL_Window *new_focus; 1090 const bool set_focus = SDL_ShouldRelinquishPopupFocus(window, &new_focus); 1091 WIN_SetKeyboardFocus(new_focus, set_focus); 1092 } 1093} 1094 1095void WIN_RaiseWindow(SDL_VideoDevice *_this, SDL_Window *window) 1096{ 1097#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) 1098 /* If desired, raise the window more forcefully. 1099 * Technique taken from http://stackoverflow.com/questions/916259/ . 1100 * Specifically, http://stackoverflow.com/a/34414846 . 1101 * 1102 * The issue is that Microsoft has gone through a lot of trouble to make it 1103 * nearly impossible to programmatically move a window to the foreground, 1104 * for "security" reasons. Apparently, the following song-and-dance gets 1105 * around their objections. */ 1106 bool bForce = SDL_GetHintBoolean(SDL_HINT_FORCE_RAISEWINDOW, false); 1107 bool bActivate = SDL_GetHintBoolean(SDL_HINT_WINDOW_ACTIVATE_WHEN_RAISED, true); 1108 1109 HWND hCurWnd = NULL; 1110 DWORD dwMyID = 0u; 1111 DWORD dwCurID = 0u; 1112 1113 SDL_WindowData *data = window->internal; 1114 HWND hwnd = data->hwnd; 1115 if (bForce) { 1116 hCurWnd = GetForegroundWindow(); 1117 dwMyID = GetCurrentThreadId(); 1118 dwCurID = GetWindowThreadProcessId(hCurWnd, NULL); 1119 ShowWindow(hwnd, SW_RESTORE); 1120 AttachThreadInput(dwCurID, dwMyID, TRUE); 1121 SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE); 1122 if (!SDL_ShouldAllowTopmost() || !(window->flags & SDL_WINDOW_ALWAYS_ON_TOP)) { 1123 SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE); 1124 } 1125 } 1126 if (bActivate) { 1127 SetForegroundWindow(hwnd); 1128 if ((window->flags & SDL_WINDOW_POPUP_MENU) && !(window->flags & SDL_WINDOW_NOT_FOCUSABLE)) { 1129 WIN_SetKeyboardFocus(window, window->parent == SDL_GetKeyboardFocus()); 1130 } 1131 } else { 1132 SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0, data->copybits_flag | SWP_NOMOVE | SWP_NOSIZE | SWP_NOOWNERZORDER | SWP_NOACTIVATE); 1133 } 1134 if (bForce) { 1135 AttachThreadInput(dwCurID, dwMyID, FALSE); 1136 SetFocus(hwnd); 1137 SetActiveWindow(hwnd); 1138 } 1139#endif // !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) 1140} 1141 1142void WIN_MaximizeWindow(SDL_VideoDevice *_this, SDL_Window *window) 1143{ 1144 SDL_WindowData *data = window->internal; 1145 1146 if (!(window->flags & SDL_WINDOW_FULLSCREEN)) { 1147 HWND hwnd = data->hwnd; 1148 data->expected_resize = true; 1149 ShowWindow(hwnd, SW_MAXIMIZE); 1150 data->expected_resize = false; 1151 1152 /* Clamp the maximized window size to the max window size. 1153 * This is automatic if maximizing from the window controls. 1154 */ 1155 if (window->max_w || window->max_h) { 1156 int fx, fy, fw, fh; 1157 1158 window->windowed.w = window->max_w ? SDL_min(window->w, window->max_w) : window->windowed.w; 1159 window->windowed.h = window->max_h ? SDL_min(window->h, window->max_h) : window->windowed.h; 1160 WIN_AdjustWindowRect(window, &fx, &fy, &fw, &fh, SDL_WINDOWRECT_WINDOWED); 1161 1162 data->expected_resize = true; 1163 SetWindowPos(hwnd, HWND_TOP, fx, fy, fw, fh, data->copybits_flag | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOACTIVATE); 1164 data->expected_resize = false; 1165 } 1166 } else { 1167 data->windowed_mode_was_maximized = true; 1168 } 1169} 1170 1171void WIN_MinimizeWindow(SDL_VideoDevice *_this, SDL_Window *window) 1172{ 1173 HWND hwnd = window->internal->hwnd; 1174 ShowWindow(hwnd, SW_MINIMIZE); 1175} 1176 1177void WIN_SetWindowBordered(SDL_VideoDevice *_this, SDL_Window *window, bool bordered) 1178{ 1179 SDL_WindowData *data = window->internal; 1180 HWND hwnd = data->hwnd; 1181 DWORD style; 1182 1183 style = GetWindowLong(hwnd, GWL_STYLE); 1184 style &= ~STYLE_MASK; 1185 style |= GetWindowStyle(window); 1186 1187 data->in_border_change = true; 1188 SetWindowLong(hwnd, GWL_STYLE, style); 1189 WIN_SetWindowPositionInternal(window, data->copybits_flag | SWP_FRAMECHANGED | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOACTIVATE, SDL_WINDOWRECT_CURRENT); 1190 data->in_border_change = false; 1191} 1192 1193void WIN_SetWindowResizable(SDL_VideoDevice *_this, SDL_Window *window, bool resizable) 1194{ 1195 SDL_WindowData *data = window->internal; 1196 HWND hwnd = data->hwnd; 1197 DWORD style; 1198 1199 style = GetWindowLong(hwnd, GWL_STYLE); 1200 style &= ~STYLE_MASK; 1201 style |= GetWindowStyle(window); 1202 1203 SetWindowLong(hwnd, GWL_STYLE, style); 1204} 1205 1206void WIN_SetWindowAlwaysOnTop(SDL_VideoDevice *_this, SDL_Window *window, bool on_top) 1207{ 1208 WIN_SetWindowPositionInternal(window, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOOWNERZORDER, SDL_WINDOWRECT_CURRENT); 1209} 1210 1211void WIN_RestoreWindow(SDL_VideoDevice *_this, SDL_Window *window) 1212{ 1213 SDL_WindowData *data = window->internal; 1214 if (!(window->flags & SDL_WINDOW_FULLSCREEN)) { 1215 if (!data->showing_window || window->flags & (SDL_WINDOW_MAXIMIZED | SDL_WINDOW_MINIMIZED)) { 1216 HWND hwnd = data->hwnd; 1217 data->expected_resize = true; 1218 ShowWindow(hwnd, SW_RESTORE); 1219 data->expected_resize = false; 1220 } 1221 } else { 1222 data->windowed_mode_was_maximized = false; 1223 } 1224} 1225 1226static void WIN_UpdateCornerRoundingForHWND(SDL_VideoDevice *_this, HWND hwnd, DWM_WINDOW_CORNER_PREFERENCE cornerPref) 1227{ 1228#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) 1229 SDL_VideoData *videodata = _this->internal; 1230 if (videodata->DwmSetWindowAttribute) { 1231 videodata->DwmSetWindowAttribute(hwnd, DWMWA_WINDOW_CORNER_PREFERENCE, &cornerPref, sizeof(cornerPref)); 1232 } 1233#endif 1234} 1235 1236static void WIN_UpdateBorderColorForHWND(SDL_VideoDevice *_this, HWND hwnd, COLORREF colorRef) 1237{ 1238#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) 1239 SDL_VideoData *videodata = _this->internal; 1240 if (videodata->DwmSetWindowAttribute) { 1241 videodata->DwmSetWindowAttribute(hwnd, DWMWA_BORDER_COLOR, &colorRef, sizeof(colorRef)); 1242 } 1243#endif 1244} 1245 1246/** 1247 * Reconfigures the window to fill the given display, if fullscreen is true, otherwise restores the window. 1248 */ 1249SDL_FullscreenResult WIN_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *display, SDL_FullscreenOp fullscreen) 1250{ 1251#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) 1252 SDL_DisplayData *displaydata = display->internal; 1253 SDL_WindowData *data = window->internal; 1254 HWND hwnd = data ? data->hwnd : NULL; 1255 MONITORINFO minfo; 1256 DWORD style, styleEx; 1257 HWND top; 1258 int x, y; 1259 int w, h; 1260 bool enterMaximized = false; 1261 1262#ifdef HIGHDPI_DEBUG 1263 SDL_Log("WIN_SetWindowFullscreen: %d", (int)fullscreen); 1264#endif 1265 1266 /* Early out if already not in fullscreen, or the styling on 1267 * external windows may end up being overridden. 1268 */ 1269 if (!(window->flags & SDL_WINDOW_FULLSCREEN) && !fullscreen) { 1270 return SDL_FULLSCREEN_SUCCEEDED; 1271 } 1272 1273 if (SDL_ShouldAllowTopmost() && (window->flags & SDL_WINDOW_ALWAYS_ON_TOP)) { 1274 top = HWND_TOPMOST; 1275 } else { 1276 top = HWND_NOTOPMOST; 1277 } 1278 1279 /* Use GetMonitorInfo instead of WIN_GetDisplayBounds because we want the 1280 monitor bounds in Windows coordinates (pixels) rather than SDL coordinates (points). */ 1281 SDL_zero(minfo); 1282 minfo.cbSize = sizeof(MONITORINFO); 1283 if (!GetMonitorInfo(displaydata->MonitorHandle, &minfo)) { 1284 SDL_SetError("GetMonitorInfo failed"); 1285 return SDL_FULLSCREEN_FAILED; 1286 } 1287 1288 SDL_SendWindowEvent(window, fullscreen ? SDL_EVENT_WINDOW_ENTER_FULLSCREEN : SDL_EVENT_WINDOW_LEAVE_FULLSCREEN, 0, 0); 1289 style = GetWindowLong(hwnd, GWL_STYLE); 1290 style &= ~STYLE_MASK; 1291 style |= GetWindowStyle(window); 1292 styleEx = GetWindowLong(hwnd, GWL_EXSTYLE); 1293 1294 if (fullscreen) { 1295 x = minfo.rcMonitor.left; 1296 y = minfo.rcMonitor.top; 1297 w = minfo.rcMonitor.right - minfo.rcMonitor.left; 1298 h = minfo.rcMonitor.bottom - minfo.rcMonitor.top; 1299 1300 /* Unset the maximized flag. This fixes 1301 https://bugzilla.libsdl.org/show_bug.cgi?id=3215 1302 */ 1303 if (style & WS_MAXIMIZE) { 1304 data->windowed_mode_was_maximized = true; 1305 style &= ~WS_MAXIMIZE; 1306 } 1307 1308 // Disable corner rounding & border color (Windows 11+) so the window fills the full screen 1309 WIN_UpdateCornerRoundingForHWND(_this, hwnd, DWMWCP_DONOTROUND); 1310 WIN_UpdateBorderColorForHWND(_this, hwnd, DWMWA_COLOR_NONE); 1311 } else { 1312 BOOL menu; 1313 1314 WIN_UpdateCornerRoundingForHWND(_this, hwnd, DWMWCP_DEFAULT); 1315 WIN_UpdateBorderColorForHWND(_this, hwnd, DWMWA_COLOR_DEFAULT); 1316 1317 /* Restore window-maximization state, as applicable. 1318 Special care is taken to *not* do this if and when we're 1319 alt-tab'ing away (to some other window; as indicated by 1320 in_window_deactivation), otherwise 1321 https://bugzilla.libsdl.org/show_bug.cgi?id=3215 can reproduce! 1322 */ 1323 if (data->windowed_mode_was_maximized && !data->in_window_deactivation) { 1324 enterMaximized = true; 1325 data->disable_move_size_events = true; 1326 } 1327 1328 menu = (style & WS_CHILDWINDOW) ? FALSE : (GetMenu(hwnd) != NULL); 1329 WIN_AdjustWindowRectWithStyle(window, style, styleEx, menu, 1330 &x, &y, 1331 &w, &h, 1332 SDL_WINDOWRECT_FLOATING); 1333 data->windowed_mode_was_maximized = false; 1334 1335 /* A window may have been maximized by dragging it to the top of another display, in which case the floating 1336 * position may be out-of-date. If the window is being restored to maximized, and the maximized and floating 1337 * position are on different displays, try to center the window on the maximized display for restoration, which 1338 * mimics native Windows behavior. 1339 */ 1340 if (enterMaximized) { 1341 const SDL_Point windowed_point = { window->windowed.x, window->windowed.y }; 1342 const SDL_Point floating_point = { window->floating.x, window->floating.y }; 1343 const SDL_DisplayID floating_display = SDL_GetDisplayForPoint(&floating_point); 1344 const SDL_DisplayID windowed_display = SDL_GetDisplayForPoint(&windowed_point); 1345 1346 if (floating_display != windowed_display) { 1347 SDL_Rect bounds; 1348 1349 SDL_zero(bounds); 1350 SDL_GetDisplayUsableBounds(windowed_display, &bounds); 1351 if (w < bounds.w) { 1352 x = bounds.x + (bounds.w - w) / 2; 1353 } else { 1354 x = bounds.x; 1355 } 1356 if (h < bounds.h) { 1357 y = bounds.y + (bounds.h - h) / 2; 1358 } else { 1359 y = bounds.y; 1360 } 1361 } 1362 } 1363 } 1364 1365 /* Always reset the window to the base floating size before possibly re-applying the maximized state, 1366 * otherwise, the base floating size can seemingly be lost in some cases. 1367 */ 1368 SetWindowLong(hwnd, GWL_STYLE, style); 1369 data->expected_resize = true; 1370 SetWindowPos(hwnd, top, x, y, w, h, data->copybits_flag | SWP_NOACTIVATE); 1371 data->expected_resize = false; 1372 data->disable_move_size_events = false; 1373 1374 if (enterMaximized) { 1375 WIN_MaximizeWindow(_this, window); 1376 } 1377 1378 if (!fullscreen && data && data->hicon) { 1379 // Reset the icon for the window when returning from fullscreen mode 1380 SendMessage(hwnd, WM_SETICON, ICON_SMALL, 0); 1381 SendMessage(hwnd, WM_SETICON, ICON_SMALL, (LPARAM)data->hicon); 1382 } 1383 1384#ifdef HIGHDPI_DEBUG 1385 SDL_Log("WIN_SetWindowFullscreen: %d finished. Set window to %d,%d, %dx%d", (int)fullscreen, x, y, w, h); 1386#endif 1387 1388#endif // !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) 1389 return SDL_FULLSCREEN_SUCCEEDED; 1390} 1391 1392#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) 1393void WIN_UpdateWindowICCProfile(SDL_Window *window, bool send_event) 1394{ 1395 SDL_WindowData *data = window->internal; 1396 SDL_DisplayData *displaydata = SDL_GetDisplayDriverDataForWindow(window); 1397 1398 if (displaydata) { 1399 HDC hdc = CreateDCW(displaydata->DeviceName, NULL, NULL, NULL); 1400 if (hdc) { 1401 WCHAR fileName[MAX_PATH]; 1402 DWORD fileNameSize = SDL_arraysize(fileName); 1403 if (GetICMProfileW(hdc, &fileNameSize, fileName)) { 1404 // fileNameSize includes '\0' on return 1405 if (!data->ICMFileName || 1406 SDL_wcscmp(data->ICMFileName, fileName) != 0) { 1407 SDL_free(data->ICMFileName); 1408 data->ICMFileName = SDL_wcsdup(fileName); 1409 if (send_event) { 1410 SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_ICCPROF_CHANGED, 0, 0); 1411 } 1412 } 1413 } 1414 DeleteDC(hdc); 1415 } 1416 } 1417} 1418 1419void *WIN_GetWindowICCProfile(SDL_VideoDevice *_this, SDL_Window *window, size_t *size) 1420{ 1421 SDL_WindowData *data = window->internal; 1422 char *filename_utf8; 1423 void *iccProfileData = NULL; 1424 1425 filename_utf8 = WIN_StringToUTF8W(data->ICMFileName); 1426 if (filename_utf8) { 1427 iccProfileData = SDL_LoadFile(filename_utf8, size); 1428 if (!iccProfileData) { 1429 SDL_SetError("Could not open ICC profile"); 1430 } 1431 SDL_free(filename_utf8); 1432 } 1433 return iccProfileData; 1434} 1435 1436static void WIN_GrabKeyboard(SDL_Window *window) 1437{ 1438 SDL_WindowData *data = window->internal; 1439 HMODULE module; 1440 1441 if (data->keyboard_hook) { 1442 return; 1443 } 1444 1445 /* SetWindowsHookEx() needs to know which module contains the hook we 1446 want to install. This is complicated by the fact that SDL can be 1447 linked statically or dynamically. Fortunately XP and later provide 1448 this nice API that will go through the loaded modules and find the 1449 one containing our code. 1450 */ 1451 if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT | GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, 1452 (LPTSTR)WIN_KeyboardHookProc, 1453 &module)) { 1454 return; 1455 } 1456 1457 // Capture a snapshot of the current keyboard state before the hook 1458 if (!GetKeyboardState(data->videodata->pre_hook_key_state)) { 1459 return; 1460 } 1461 1462 /* To grab the keyboard, we have to install a low-level keyboard hook to 1463 intercept keys that would normally be captured by the OS. Intercepting 1464 all key events on the system is rather invasive, but it's what Microsoft 1465 actually documents that you do to capture these. 1466 */ 1467 data->keyboard_hook = SetWindowsHookEx(WH_KEYBOARD_LL, WIN_KeyboardHookProc, module, 0); 1468} 1469 1470void WIN_UngrabKeyboard(SDL_Window *window) 1471{ 1472 SDL_WindowData *data = window->internal; 1473 1474 if (data->keyboard_hook) { 1475 UnhookWindowsHookEx(data->keyboard_hook); 1476 data->keyboard_hook = NULL; 1477 } 1478} 1479 1480bool WIN_SetWindowMouseRect(SDL_VideoDevice *_this, SDL_Window *window) 1481{ 1482 WIN_UpdateClipCursor(window); 1483 return true; 1484} 1485 1486bool WIN_SetWindowMouseGrab(SDL_VideoDevice *_this, SDL_Window *window, bool grabbed) 1487{ 1488 WIN_UpdateClipCursor(window); 1489 return true; 1490} 1491 1492bool WIN_SetWindowKeyboardGrab(SDL_VideoDevice *_this, SDL_Window *window, bool grabbed) 1493{ 1494 if (grabbed) { 1495 WIN_GrabKeyboard(window); 1496 } else { 1497 WIN_UngrabKeyboard(window); 1498 } 1499 1500 return true; 1501} 1502#endif // !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) 1503 1504void WIN_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window) 1505{ 1506 CleanupWindowData(_this, window); 1507} 1508 1509#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) 1510void WIN_OnWindowEnter(SDL_VideoDevice *_this, SDL_Window *window) 1511{ 1512 SDL_WindowData *data = window->internal; 1513 1514 if (!data || !data->hwnd) { 1515 // The window wasn't fully initialized 1516 return; 1517 } 1518 1519 if (window->flags & SDL_WINDOW_ALWAYS_ON_TOP) { 1520 WIN_SetWindowPositionInternal(window, data->copybits_flag | SWP_NOSIZE | SWP_NOACTIVATE, SDL_WINDOWRECT_CURRENT); 1521 } 1522} 1523 1524static BOOL GetClientScreenRect(HWND hwnd, RECT *rect) 1525{ 1526 return GetClientRect(hwnd, rect) && // RECT( left , top , right , bottom ) 1527 ClientToScreen(hwnd, (LPPOINT)rect) && // POINT( left , top ) 1528 ClientToScreen(hwnd, (LPPOINT)rect + 1); // POINT( right , bottom ) 1529} 1530 1531void WIN_UnclipCursorForWindow(SDL_Window *window) { 1532 SDL_WindowData *data = window->internal; 1533 RECT rect; 1534 if (GetClipCursor(&rect) && SDL_memcmp(&rect, &data->cursor_clipped_rect, sizeof(rect)) == 0) { 1535 ClipCursor(NULL); 1536 SDL_zero(data->cursor_clipped_rect); 1537 } 1538} 1539 1540void WIN_UpdateClipCursor(SDL_Window *window) 1541{ 1542 SDL_WindowData *data = window->internal; 1543 if (data->in_title_click || data->focus_click_pending || data->postpone_clipcursor) { 1544 return; 1545 } 1546 1547 SDL_Rect mouse_rect = window->mouse_rect; 1548 bool win_mouse_rect = (mouse_rect.w > 0 && mouse_rect.h > 0); 1549 bool win_have_focus = (window->flags & SDL_WINDOW_INPUT_FOCUS); 1550 bool win_is_grabbed = (window->flags & SDL_WINDOW_MOUSE_GRABBED); 1551 bool win_in_relmode = (window->flags & SDL_WINDOW_MOUSE_RELATIVE_MODE); 1552 bool cursor_confine = win_in_relmode || win_is_grabbed || win_mouse_rect; 1553 1554 // This is verbatim translation of the old logic, 1555 // but I don't quite get what it's trying to do. 1556 // A clean-room implementation according to MSDN 1557 // documentation of GetClipCursor is provided in 1558 // a commented-out block below. 1559 if (!win_have_focus || !cursor_confine) { 1560 SDL_VideoDevice *videodevice = SDL_GetVideoDevice(); 1561 RECT current; 1562 if (!GetClipCursor(&current)) { 1563 return; 1564 } 1565 if (videodevice && ( 1566 current.left != videodevice->desktop_bounds.x || 1567 current.top != videodevice->desktop_bounds.y 1568 )) { 1569 POINT first, second; 1570 first.x = current.left; 1571 first.y = current.top; 1572 second.x = current.right - 1; 1573 second.y = current.bottom - 1; 1574 if (!PtInRect(&data->cursor_clipped_rect, first) || 1575 !PtInRect(&data->cursor_clipped_rect, second)) { 1576 return; 1577 } 1578 } 1579 ClipCursor(NULL); 1580 SDL_zero(data->cursor_clipped_rect); 1581 return; 1582 } 1583 1584 // if (!win_have_focus || !cursor_confine) { 1585 // RECT current; 1586 // SDL_VideoDevice *videodevice = SDL_GetVideoDevice(); 1587 // if (GetClipCursor(&current) && (!videodevice || 1588 // current.left != videodevice->desktop_bounds.x || 1589 // current.top != videodevice->desktop_bounds.y || 1590 // current.right != videodevice->desktop_bounds.x + videodevice->desktop_bounds.w || 1591 // current.bottom != videodevice->desktop_bounds.y + videodevice->desktop_bounds.h )) { 1592 // ClipCursor(NULL); 1593 // SDL_zero(data->cursor_clipped_rect); 1594 // } 1595 // return; 1596 // } 1597 1598 SDL_Mouse *mouse = SDL_GetMouse(); 1599 bool lock_to_ctr = (mouse->relative_mode && mouse->relative_mode_center); 1600 1601 RECT client; 1602 if (!GetClientScreenRect(data->hwnd, &client)) { 1603 return; 1604 } 1605 1606 RECT target = client; 1607 if (lock_to_ctr) { 1608 LONG cx = (client.left + client.right ) / 2; 1609 LONG cy = (client.top + client.bottom) / 2; 1610 target = data->cursor_ctrlock_rect; 1611 target.left += cx; 1612 target.right += cx; 1613 target.top += cy; 1614 target.bottom += cy; 1615 } else if (win_mouse_rect) { 1616 RECT custom, overlap; 1617 custom.left = client.left + mouse_rect.x; 1618 custom.top = client.top + mouse_rect.y; 1619 custom.right = client.left + mouse_rect.x + mouse_rect.w; 1620 custom.bottom = client.top + mouse_rect.y + mouse_rect.h; 1621 if (IntersectRect(&overlap, &client, &custom)) { 1622 target = overlap; 1623 } else if (!win_is_grabbed) { 1624 WIN_UnclipCursorForWindow(window); 1625 return; 1626 } 1627 } 1628 1629 if (GetClipCursor(&client) && 1630 0 != SDL_memcmp(&target, &client, sizeof(client)) && 1631 ClipCursor(&target)) { 1632 data->cursor_clipped_rect = target; // ClipCursor may fail if rect beyond screen 1633 } 1634} 1635 1636bool WIN_SetWindowHitTest(SDL_Window *window, bool enabled) 1637{ 1638 return true; // just succeed, the real work is done elsewhere. 1639} 1640#endif // !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) 1641 1642bool WIN_SetWindowOpacity(SDL_VideoDevice *_this, SDL_Window *window, float opacity) 1643{ 1644#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES) 1645 return false; 1646#else 1647 const SDL_WindowData *data = window->internal; 1648 HWND hwnd = data->hwnd; 1649 const LONG style = GetWindowLong(hwnd, GWL_EXSTYLE); 1650 1651 SDL_assert(style != 0); 1652 1653 if (opacity == 1.0f) { 1654 // want it fully opaque, just mark it unlayered if necessary. 1655 if (style & WS_EX_LAYERED) { 1656 if (SetWindowLong(hwnd, GWL_EXSTYLE, style & ~WS_EX_LAYERED) == 0) { 1657 return WIN_SetError("SetWindowLong()"); 1658 } 1659 } 1660 } else { 1661 const BYTE alpha = (BYTE)((int)(opacity * 255.0f)); 1662 // want it transparent, mark it layered if necessary. 1663 if (!(style & WS_EX_LAYERED)) { 1664 if (SetWindowLong(hwnd, GWL_EXSTYLE, style | WS_EX_LAYERED) == 0) { 1665 return WIN_SetError("SetWindowLong()"); 1666 } 1667 } 1668 1669 if (SetLayeredWindowAttributes(hwnd, 0, alpha, LWA_ALPHA) == 0) { 1670 return WIN_SetError("SetLayeredWindowAttributes()"); 1671 } 1672 } 1673 1674 return true; 1675#endif // !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) 1676} 1677 1678#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) 1679 1680static const char *SDLGetClipboardFormatName(UINT cf, char *text, int len) 1681{ 1682 switch (cf) { 1683 case CF_TEXT: 1684 return "CF_TEXT"; 1685 case CF_BITMAP: 1686 return "CF_BITMAP"; 1687 case CF_METAFILEPICT: 1688 return "CF_METAFILEPICT"; 1689 case CF_SYLK: 1690 return "CF_SYLK"; 1691 case CF_DIF: 1692 return "CF_DIF"; 1693 case CF_TIFF: 1694 return "CF_TIFF"; 1695 case CF_OEMTEXT: 1696 return "CF_OEMTEXT"; 1697 case CF_DIB: 1698 return "CF_DIB"; 1699 case CF_PALETTE: 1700 return "CF_PALETTE"; 1701 case CF_PENDATA: 1702 return "CF_PENDATA"; 1703 case CF_RIFF: 1704 return "CF_RIFF"; 1705 case CF_WAVE: 1706 return "CF_WAVE"; 1707 case CF_UNICODETEXT: 1708 return "CF_UNICODETEXT"; 1709 case CF_ENHMETAFILE: 1710 return "CF_ENHMETAFILE"; 1711 case CF_HDROP: 1712 return "CF_HDROP"; 1713 case CF_LOCALE: 1714 return "CF_LOCALE"; 1715 case CF_DIBV5: 1716 return "CF_DIBV5"; 1717 case CF_OWNERDISPLAY: 1718 return "CF_OWNERDISPLAY"; 1719 case CF_DSPTEXT: 1720 return "CF_DSPTEXT"; 1721 case CF_DSPBITMAP: 1722 return "CF_DSPBITMAP"; 1723 case CF_DSPMETAFILEPICT: 1724 return "CF_DSPMETAFILEPICT"; 1725 case CF_DSPENHMETAFILE: 1726 return "CF_DSPENHMETAFILE"; 1727 default: 1728 if (GetClipboardFormatNameA(cf, text, len)) { 1729 return text; 1730 } else { 1731 return NULL; 1732 } 1733 } 1734} 1735 1736static STDMETHODIMP_(ULONG) SDLDropTarget_AddRef(SDLDropTarget *target) 1737{ 1738 return ++target->refcount; 1739} 1740 1741static STDMETHODIMP_(ULONG) SDLDropTarget_Release(SDLDropTarget *target) 1742{ 1743 --target->refcount; 1744 if (target->refcount == 0) { 1745 SDL_free(target); 1746 return 0; 1747 } 1748 return target->refcount; 1749} 1750 1751static STDMETHODIMP SDLDropTarget_QueryInterface(SDLDropTarget *target, REFIID riid, PVOID *ppv) 1752{ 1753 if (ppv == NULL) { 1754 return E_INVALIDARG; 1755 } 1756 1757 *ppv = NULL; 1758 if (WIN_IsEqualIID(riid, &IID_IUnknown) || 1759 WIN_IsEqualIID(riid, &IID_IDropTarget)) { 1760 *ppv = (void *)target; 1761 } 1762 if (*ppv) { 1763 SDLDropTarget_AddRef(target); 1764 return S_OK; 1765 } 1766 return E_NOINTERFACE; 1767} 1768 1769static STDMETHODIMP SDLDropTarget_DragEnter(SDLDropTarget *target, 1770 IDataObject *pDataObject, DWORD grfKeyState, 1771 POINTL pt, DWORD *pdwEffect) 1772{ 1773 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT, 1774 ". In DragEnter at %ld, %ld", pt.x, pt.y); 1775 *pdwEffect = DROPEFFECT_COPY; 1776 POINT pnt = { pt.x, pt.y }; 1777 if (ScreenToClient(target->hwnd, &pnt)) { 1778 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT, 1779 ". In DragEnter at %ld, %ld => window %u at %ld, %ld", pt.x, pt.y, target->window->id, pnt.x, pnt.y); 1780 SDL_SendDropPosition(target->window, pnt.x, pnt.y); 1781 } else { 1782 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT, 1783 ". In DragEnter at %ld, %ld => nil, nil", pt.x, pt.y); 1784 } 1785 return S_OK; 1786} 1787 1788static STDMETHODIMP SDLDropTarget_DragOver(SDLDropTarget *target, 1789 DWORD grfKeyState, 1790 POINTL pt, DWORD *pdwEffect) 1791{ 1792 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT, 1793 ". In DragOver at %ld, %ld", pt.x, pt.y); 1794 *pdwEffect = DROPEFFECT_COPY; 1795 POINT pnt = { pt.x, pt.y }; 1796 if (ScreenToClient(target->hwnd, &pnt)) { 1797 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT, 1798 ". In DragOver at %ld, %ld => window %u at %ld, %ld", pt.x, pt.y, target->window->id, pnt.x, pnt.y); 1799 SDL_SendDropPosition(target->window, pnt.x, pnt.y); 1800 } else { 1801 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT, 1802 ". In DragOver at %ld, %ld => nil, nil", pt.x, pt.y); 1803 } 1804 return S_OK; 1805} 1806 1807static STDMETHODIMP SDLDropTarget_DragLeave(SDLDropTarget *target) 1808{ 1809 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT, 1810 ". In DragLeave"); 1811 SDL_SendDropComplete(target->window); 1812 return S_OK; 1813} 1814 1815static STDMETHODIMP SDLDropTarget_Drop(SDLDropTarget *target, 1816 IDataObject *pDataObject, DWORD grfKeyState, 1817 POINTL pt, DWORD *pdwEffect) 1818{ 1819 *pdwEffect = DROPEFFECT_COPY; 1820 POINT pnt = { pt.x, pt.y }; 1821 if (ScreenToClient(target->hwnd, &pnt)) { 1822 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT, 1823 ". In Drop at %ld, %ld => window %u at %ld, %ld", pt.x, pt.y, target->window->id, pnt.x, pnt.y); 1824 SDL_SendDropPosition(target->window, pnt.x, pnt.y); 1825 } else { 1826 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT, 1827 ". In Drop at %ld, %ld => nil, nil", pt.x, pt.y); 1828 } 1829 1830 { 1831 IEnumFORMATETC *pEnumFormatEtc; 1832 HRESULT hres; 1833 hres = pDataObject->lpVtbl->EnumFormatEtc(pDataObject, DATADIR_GET, &pEnumFormatEtc); 1834 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT, 1835 ". In Drop for EnumFormatEtc, HRESULT is %08lx", hres); 1836 if (hres == S_OK) { 1837 FORMATETC fetc; 1838 while (pEnumFormatEtc->lpVtbl->Next(pEnumFormatEtc, 1, &fetc, NULL) == S_OK) { 1839 char name[257] = { 0 }; 1840 const char *cfnm = SDLGetClipboardFormatName(fetc.cfFormat, name, 256); 1841 if (cfnm) { 1842 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT, 1843 ". In Drop, Supported format is %08x, '%s'", fetc.cfFormat, cfnm); 1844 } else { 1845 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT, 1846 ". In Drop, Supported format is %08x, Predefined", fetc.cfFormat); 1847 } 1848 } 1849 } 1850 } 1851 1852 { 1853 FORMATETC fetc; 1854 fetc.cfFormat = target->format_file; 1855 fetc.ptd = NULL; 1856 fetc.dwAspect = DVASPECT_CONTENT; 1857 fetc.lindex = -1; 1858 fetc.tymed = TYMED_HGLOBAL; 1859 const char *format_mime = "text/uri-list"; 1860 if (SUCCEEDED(pDataObject->lpVtbl->QueryGetData(pDataObject, &fetc))) { 1861 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT, 1862 ". In Drop File for QueryGetData, format %08x '%s', success", 1863 fetc.cfFormat, format_mime); 1864 STGMEDIUM med; 1865 HRESULT hres = pDataObject->lpVtbl->GetData(pDataObject, &fetc, &med); 1866 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT, 1867 ". In Drop File for GetData, format %08x '%s', HRESULT is %08lx", 1868 fetc.cfFormat, format_mime, hres); 1869 if (SUCCEEDED(hres)) { 1870 const size_t bsize = GlobalSize(med.hGlobal); 1871 const void *buffer = (void *)GlobalLock(med.hGlobal); 1872 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT, 1873 ". In Drop File for GlobalLock, format %08x '%s', memory (%lu) %p", 1874 fetc.cfFormat, format_mime, (unsigned long)bsize, buffer); 1875 if (buffer) { 1876 char *text = (char *)SDL_malloc(bsize + sizeof(Uint32)); 1877 SDL_memcpy((Uint8 *)text, buffer, bsize); 1878 SDL_memset((Uint8 *)text + bsize, 0, sizeof(Uint32)); 1879 char *saveptr = NULL; 1880 char *token = SDL_strtok_r(text, "\r\n", &saveptr); 1881 while (token != NULL) { 1882 if (SDL_URIToLocal(token, token) >= 0) { 1883 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT, 1884 ". In Drop File, file (%lu of %lu) '%s'", 1885 (unsigned long)SDL_strlen(token), (unsigned long)bsize, token); 1886 SDL_SendDropFile(target->window, NULL, token); 1887 } 1888 token = SDL_strtok_r(NULL, "\r\n", &saveptr); 1889 } 1890 SDL_free(text); 1891 } 1892 GlobalUnlock(med.hGlobal); 1893 ReleaseStgMedium(&med); 1894 SDL_SendDropComplete(target->window); 1895 return S_OK; 1896 } 1897 } 1898 } 1899 1900 { 1901 FORMATETC fetc; 1902 fetc.cfFormat = target->format_text; 1903 fetc.ptd = NULL; 1904 fetc.dwAspect = DVASPECT_CONTENT; 1905 fetc.lindex = -1; 1906 fetc.tymed = TYMED_HGLOBAL; 1907 const char *format_mime = "text/plain;charset=utf-8"; 1908 if (SUCCEEDED(pDataObject->lpVtbl->QueryGetData(pDataObject, &fetc))) { 1909 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT, 1910 ". In Drop Text for QueryGetData, format %08x '%s', success", 1911 fetc.cfFormat, format_mime); 1912 STGMEDIUM med; 1913 HRESULT hres = pDataObject->lpVtbl->GetData(pDataObject, &fetc, &med); 1914 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT, 1915 ". In Drop Text for GetData, format %08x '%s', HRESULT is %08lx", 1916 fetc.cfFormat, format_mime, hres); 1917 if (SUCCEEDED(hres)) { 1918 const size_t bsize = GlobalSize(med.hGlobal); 1919 const void *buffer = (void *)GlobalLock(med.hGlobal); 1920 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT, 1921 ". In Drop Text for GlobalLock, format %08x '%s', memory (%lu) %p", 1922 fetc.cfFormat, format_mime, (unsigned long)bsize, buffer); 1923 if (buffer) { 1924 char *text = (char *)SDL_malloc(bsize + sizeof(Uint32)); 1925 SDL_memcpy((Uint8 *)text, buffer, bsize); 1926 SDL_memset((Uint8 *)text + bsize, 0, sizeof(Uint32)); 1927 char *saveptr = NULL; 1928 char *token = SDL_strtok_r(text, "\r\n", &saveptr); 1929 while (token != NULL) { 1930 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT, 1931 ". In Drop Text, text (%lu of %lu) '%s'", 1932 (unsigned long)SDL_strlen(token), (unsigned long)bsize, token); 1933 SDL_SendDropText(target->window, (char *)token); 1934 token = SDL_strtok_r(NULL, "\r\n", &saveptr); 1935 } 1936 SDL_free(text); 1937 } 1938 GlobalUnlock(med.hGlobal); 1939 ReleaseStgMedium(&med); 1940 SDL_SendDropComplete(target->window); 1941 return S_OK; 1942 } 1943 } 1944 } 1945 1946 { 1947 FORMATETC fetc; 1948 fetc.cfFormat = CF_UNICODETEXT; 1949 fetc.ptd = NULL; 1950 fetc.dwAspect = DVASPECT_CONTENT; 1951 fetc.lindex = -1; 1952 fetc.tymed = TYMED_HGLOBAL; 1953 const char *format_mime = "CF_UNICODETEXT"; 1954 if (SUCCEEDED(pDataObject->lpVtbl->QueryGetData(pDataObject, &fetc))) { 1955 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT, 1956 ". In Drop Text for QueryGetData, format %08x '%s', success", 1957 fetc.cfFormat, format_mime); 1958 STGMEDIUM med; 1959 HRESULT hres = pDataObject->lpVtbl->GetData(pDataObject, &fetc, &med); 1960 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT, 1961 ". In Drop Text for GetData, format %08x '%s', HRESULT is %08lx", 1962 fetc.cfFormat, format_mime, hres); 1963 if (SUCCEEDED(hres)) { 1964 const size_t bsize = GlobalSize(med.hGlobal); 1965 const void *buffer = (void *)GlobalLock(med.hGlobal); 1966 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT, 1967 ". In Drop Text for GlobalLock, format %08x '%s', memory (%lu) %p", 1968 fetc.cfFormat, format_mime, (unsigned long)bsize, buffer); 1969 if (buffer) { 1970 buffer = WIN_StringToUTF8W((const wchar_t *)buffer); 1971 if (buffer) { 1972 const size_t lbuffer = SDL_strlen((const char *)buffer); 1973 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT, 1974 ". In Drop Text for StringToUTF8, format %08x '%s', memory (%lu) %p", 1975 fetc.cfFormat, format_mime, (unsigned long)lbuffer, buffer); 1976 char *text = (char *)SDL_malloc(lbuffer + sizeof(Uint32)); 1977 SDL_memcpy((Uint8 *)text, buffer, lbuffer); 1978 SDL_memset((Uint8 *)text + lbuffer, 0, sizeof(Uint32)); 1979 char *saveptr = NULL; 1980 char *token = SDL_strtok_r(text, "\r\n", &saveptr); 1981 while (token != NULL) { 1982 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT, 1983 ". In Drop Text, text (%lu of %lu) '%s'", 1984 (unsigned long)SDL_strlen(token), (unsigned long)lbuffer, token); 1985 SDL_SendDropText(target->window, (char *)token); 1986 token = SDL_strtok_r(NULL, "\r\n", &saveptr); 1987 } 1988 SDL_free(text); 1989 SDL_free((void *)buffer); 1990 } 1991 } 1992 GlobalUnlock(med.hGlobal); 1993 ReleaseStgMedium(&med); 1994 SDL_SendDropComplete(target->window); 1995 return S_OK; 1996 } 1997 } 1998 } 1999 2000 { 2001 FORMATETC fetc; 2002 fetc.cfFormat = CF_TEXT; 2003 fetc.ptd = NULL; 2004 fetc.dwAspect = DVASPECT_CONTENT; 2005 fetc.lindex = -1; 2006 fetc.tymed = TYMED_HGLOBAL; 2007 const char *format_mime = "CF_TEXT"; 2008 if (SUCCEEDED(pDataObject->lpVtbl->QueryGetData(pDataObject, &fetc))) { 2009 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT, 2010 ". In Drop Text for QueryGetData, format %08x '%s', success", 2011 fetc.cfFormat, format_mime); 2012 STGMEDIUM med; 2013 HRESULT hres = pDataObject->lpVtbl->GetData(pDataObject, &fetc, &med); 2014 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT, 2015 ". In Drop Text for GetData, format %08x '%s', HRESULT is %08lx", 2016 fetc.cfFormat, format_mime, hres); 2017 if (SUCCEEDED(hres)) { 2018 const size_t bsize = GlobalSize(med.hGlobal); 2019 const void *buffer = (void *)GlobalLock(med.hGlobal); 2020 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT, 2021 ". In Drop Text for GlobalLock, format %08x '%s', memory (%lu) %p", 2022 fetc.cfFormat, format_mime, (unsigned long)bsize, buffer); 2023 if (buffer) { 2024 char *text = (char *)SDL_malloc(bsize + sizeof(Uint32)); 2025 SDL_memcpy((Uint8 *)text, buffer, bsize); 2026 SDL_memset((Uint8 *)text + bsize, 0, sizeof(Uint32)); 2027 char *saveptr = NULL; 2028 char *token = SDL_strtok_r(text, "\r\n", &saveptr); 2029 while (token != NULL) { 2030 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT, 2031 ". In Drop Text, text (%lu of %lu) '%s'", 2032 (unsigned long)SDL_strlen(token), (unsigned long)bsize, token); 2033 SDL_SendDropText(target->window, (char *)token); 2034 token = SDL_strtok_r(NULL, "\r\n", &saveptr); 2035 } 2036 SDL_free(text); 2037 } 2038 GlobalUnlock(med.hGlobal); 2039 ReleaseStgMedium(&med); 2040 SDL_SendDropComplete(target->window); 2041 return S_OK; 2042 } 2043 } 2044 } 2045 2046 { 2047 FORMATETC fetc; 2048 fetc.cfFormat = CF_HDROP; 2049 fetc.ptd = NULL; 2050 fetc.dwAspect = DVASPECT_CONTENT; 2051 fetc.lindex = -1; 2052 fetc.tymed = TYMED_HGLOBAL; 2053 const char *format_mime = "CF_HDROP"; 2054 if (SUCCEEDED(pDataObject->lpVtbl->QueryGetData(pDataObject, &fetc))) { 2055 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT, 2056 ". In Drop File for QueryGetData, format %08x '%s', success", 2057 fetc.cfFormat, format_mime); 2058 STGMEDIUM med; 2059 HRESULT hres = pDataObject->lpVtbl->GetData(pDataObject, &fetc, &med); 2060 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT, 2061 ". In Drop File for GetData, format %08x '%s', HRESULT is %08lx", 2062 fetc.cfFormat, format_mime, hres); 2063 if (SUCCEEDED(hres)) { 2064 const size_t bsize = GlobalSize(med.hGlobal); 2065 HDROP drop = (HDROP)GlobalLock(med.hGlobal); 2066 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT, 2067 ". In Drop File for GlobalLock, format %08x '%s', memory (%lu) %p", 2068 fetc.cfFormat, format_mime, (unsigned long)bsize, drop); 2069 UINT count = DragQueryFile(drop, 0xFFFFFFFF, NULL, 0); 2070 for (UINT i = 0; i < count; ++i) { 2071 UINT size = DragQueryFile(drop, i, NULL, 0) + 1; 2072 LPTSTR buffer = (LPTSTR)SDL_malloc(size * sizeof(TCHAR)); 2073 if (buffer) { 2074 if (DragQueryFile(drop, i, buffer, size)) { 2075 char *file = WIN_StringToUTF8(buffer); 2076 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT, 2077 ". In Drop File, file (%lu of %lu) '%s'", 2078 (unsigned long)SDL_strlen(file), (unsigned long)bsize, file); 2079 SDL_SendDropFile(target->window, NULL, file); 2080 SDL_free(file); 2081 } 2082 SDL_free(buffer); 2083 } 2084 } 2085 GlobalUnlock(med.hGlobal); 2086 ReleaseStgMedium(&med); 2087 SDL_SendDropComplete(target->window); 2088 return S_OK; 2089 } 2090 } 2091 } 2092 2093 SDL_SendDropComplete(target->window); 2094 return S_OK; 2095} 2096 2097static void *vtDropTarget[] = { 2098 (void *)(SDLDropTarget_QueryInterface), 2099 (void *)(SDLDropTarget_AddRef), 2100 (void *)(SDLDropTarget_Release), 2101 (void *)(SDLDropTarget_DragEnter), 2102 (void *)(SDLDropTarget_DragOver), 2103 (void *)(SDLDropTarget_DragLeave), 2104 (void *)(SDLDropTarget_Drop) 2105}; 2106 2107void WIN_AcceptDragAndDrop(SDL_Window *window, bool accept) 2108{ 2109 SDL_WindowData *data = window->internal; 2110 if (data->videodata->oleinitialized) { 2111 if (accept && !data->drop_target) { 2112 SDLDropTarget *drop_target = (SDLDropTarget *)SDL_calloc(1, sizeof(SDLDropTarget)); 2113 if (drop_target != NULL) { 2114 drop_target->lpVtbl = vtDropTarget; 2115 drop_target->window = window; 2116 drop_target->hwnd = data->hwnd; 2117 drop_target->format_file = RegisterClipboardFormatW(L"text/uri-list"); 2118 drop_target->format_text = RegisterClipboardFormatW(L"text/plain;charset=utf-8"); 2119 data->drop_target = drop_target; 2120 SDLDropTarget_AddRef(drop_target); 2121 RegisterDragDrop(data->hwnd, (LPDROPTARGET)drop_target); 2122 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT, 2123 ". In Accept Drag and Drop, window %u, enabled Full OLE IDropTarget", 2124 window->id); 2125 } 2126 } else if (!accept && data->drop_target) { 2127 RevokeDragDrop(data->hwnd); 2128 SDLDropTarget_Release(data->drop_target); 2129 data->drop_target = NULL; 2130 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT, 2131 ". In Accept Drag and Drop, window %u, disabled Full OLE IDropTarget", 2132 window->id); 2133 } 2134 } else { 2135 DragAcceptFiles(data->hwnd, accept ? TRUE : FALSE); 2136 SDL_LogTrace(SDL_LOG_CATEGORY_INPUT, 2137 ". In Accept Drag and Drop, window %u, %s Fallback WM_DROPFILES", 2138 window->id, (accept ? "enabled" : "disabled")); 2139 } 2140} 2141 2142bool WIN_FlashWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_FlashOperation operation) 2143{ 2144 FLASHWINFO desc; 2145 2146 SDL_zero(desc); 2147 desc.cbSize = sizeof(desc); 2148 desc.hwnd = window->internal->hwnd; 2149 switch (operation) { 2150 case SDL_FLASH_CANCEL: 2151 desc.dwFlags = FLASHW_STOP; 2152 break; 2153 case SDL_FLASH_BRIEFLY: 2154 desc.dwFlags = FLASHW_TRAY; 2155 desc.uCount = 1; 2156 break; 2157 case SDL_FLASH_UNTIL_FOCUSED: 2158 desc.dwFlags = (FLASHW_TRAY | FLASHW_TIMERNOFG); 2159 break; 2160 default: 2161 return SDL_Unsupported(); 2162 } 2163 2164 FlashWindowEx(&desc); 2165 2166 return true; 2167} 2168 2169bool WIN_ApplyWindowProgress(SDL_VideoDevice *_this, SDL_Window *window) 2170{ 2171#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) 2172 SDL_WindowData *data = window->internal; 2173 if (!data->taskbar_button_created) { 2174 return true; 2175 } 2176 2177 if (window->progress_state == SDL_PROGRESS_STATE_NONE && !data->videodata->taskbar_list) { 2178 return true; 2179 } 2180 2181 ITaskbarList3 *taskbar_list = GetTaskbarList(window); 2182 if (!taskbar_list) { 2183 return false; 2184 } 2185 2186 TBPFLAG tbpFlags; 2187 switch (window->progress_state) { 2188 case SDL_PROGRESS_STATE_NONE: 2189 tbpFlags = TBPF_NOPROGRESS; 2190 break; 2191 case SDL_PROGRESS_STATE_INDETERMINATE: 2192 tbpFlags = TBPF_INDETERMINATE; 2193 break; 2194 case SDL_PROGRESS_STATE_NORMAL: 2195 tbpFlags = TBPF_NORMAL; 2196 break; 2197 case SDL_PROGRESS_STATE_PAUSED: 2198 tbpFlags = TBPF_PAUSED; 2199 break; 2200 case SDL_PROGRESS_STATE_ERROR: 2201 tbpFlags = TBPF_ERROR; 2202 break; 2203 default: 2204 return SDL_SetError("Parameter 'state' is not supported"); 2205 } 2206 2207 HRESULT ret = taskbar_list->lpVtbl->SetProgressState(taskbar_list, data->hwnd, tbpFlags); 2208 if (FAILED(ret)) { 2209 return WIN_SetErrorFromHRESULT("ITaskbarList3::SetProgressState()", ret); 2210 } 2211 2212 if (window->progress_state >= SDL_PROGRESS_STATE_NORMAL) { 2213 ret = taskbar_list->lpVtbl->SetProgressValue(taskbar_list, data->hwnd, (ULONGLONG)(window->progress_value * 10000.f), 10000); 2214 if (FAILED(ret)) { 2215 return WIN_SetErrorFromHRESULT("ITaskbarList3::SetProgressValue()", ret); 2216 } 2217 } 2218#endif 2219 return true; 2220} 2221 2222void WIN_ShowWindowSystemMenu(SDL_Window *window, int x, int y) 2223{ 2224 const SDL_WindowData *data = window->internal; 2225 POINT pt; 2226 2227 pt.x = x; 2228 pt.y = y; 2229 ClientToScreen(data->hwnd, &pt); 2230 SendMessage(data->hwnd, WM_POPUPSYSTEMMENU, 0, MAKELPARAM(pt.x, pt.y)); 2231} 2232 2233bool WIN_SetWindowFocusable(SDL_VideoDevice *_this, SDL_Window *window, bool focusable) 2234{ 2235 if (!SDL_WINDOW_IS_POPUP(window)) { 2236 SDL_WindowData *data = window->internal; 2237 HWND hwnd = data->hwnd; 2238 const LONG style = GetWindowLong(hwnd, GWL_EXSTYLE); 2239 2240 SDL_assert(style != 0); 2241 2242 if (focusable) { 2243 if (style & WS_EX_NOACTIVATE) { 2244 if (SetWindowLong(hwnd, GWL_EXSTYLE, style & ~WS_EX_NOACTIVATE) == 0) { 2245 return WIN_SetError("SetWindowLong()"); 2246 } 2247 } 2248 } else { 2249 if (!(style & WS_EX_NOACTIVATE)) { 2250 if (SetWindowLong(hwnd, GWL_EXSTYLE, style | WS_EX_NOACTIVATE) == 0) { 2251 return WIN_SetError("SetWindowLong()"); 2252 } 2253 } 2254 } 2255 } else if (window->flags & SDL_WINDOW_POPUP_MENU) { 2256 if (!(window->flags & SDL_WINDOW_HIDDEN)) { 2257 if (!focusable && (window->flags & SDL_WINDOW_INPUT_FOCUS)) { 2258 SDL_Window *new_focus; 2259 const bool set_focus = SDL_ShouldRelinquishPopupFocus(window, &new_focus); 2260 WIN_SetKeyboardFocus(new_focus, set_focus); 2261 } else if (focusable) { 2262 if (SDL_ShouldFocusPopup(window)) { 2263 WIN_SetKeyboardFocus(window, true); 2264 } 2265 } 2266 } 2267 2268 return true; 2269 } 2270 2271 return true; 2272} 2273#endif // !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) 2274 2275bool WIN_SetWindowParent(SDL_VideoDevice *_this, SDL_Window *window, SDL_Window *parent) 2276{ 2277#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) 2278 SDL_WindowData *child_data = window->internal; 2279 const LONG_PTR parent_hwnd = (LONG_PTR)(parent ? parent->internal->hwnd : NULL); 2280 const DWORD style = GetWindowLong(child_data->hwnd, GWL_STYLE); 2281 2282 if (!(style & WS_CHILD)) { 2283 /* Despite the name, this changes the *owner* of a toplevel window, not 2284 * the parent of a child window. 2285 * 2286 * https://devblogs.microsoft.com/oldnewthing/20100315-00/?p=14613 2287 */ 2288 SetWindowLongPtr(child_data->hwnd, GWLP_HWNDPARENT, parent_hwnd); 2289 } else { 2290 SetParent(child_data->hwnd, (HWND)parent_hwnd); 2291 } 2292#endif /*!defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)*/ 2293 2294 return true; 2295} 2296 2297bool WIN_SetWindowModal(SDL_VideoDevice *_this, SDL_Window *window, bool modal) 2298{ 2299#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) 2300 const HWND parent_hwnd = window->parent->internal->hwnd; 2301 2302 if (modal) { 2303 // Disable the parent window. 2304 EnableWindow(parent_hwnd, FALSE); 2305 } else if (!(window->flags & SDL_WINDOW_HIDDEN)) { 2306 // Re-enable the parent window 2307 EnableWindow(parent_hwnd, TRUE); 2308 } 2309#endif // !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) 2310 2311 return true; 2312} 2313 2314#endif // SDL_VIDEO_DRIVER_WINDOWS 2315
[FILE END]
(C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.