Atlas - SDL_windows.c

Home / ext / SDL / src / core / windows Lines: 1 | Size: 24742 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#if defined(SDL_PLATFORM_WINDOWS) 24 25#include "SDL_windows.h" 26 27#include "../../video/SDL_surface_c.h" 28 29#include <shellapi.h> // CommandLineToArgvW() 30 31#include <objbase.h> // for CoInitialize/CoUninitialize (Win32 only) 32#ifdef HAVE_ROAPI_H 33#include <roapi.h> // For RoInitialize/RoUninitialize (Win32 only) 34#else 35typedef enum RO_INIT_TYPE 36{ 37 RO_INIT_SINGLETHREADED = 0, 38 RO_INIT_MULTITHREADED = 1 39} RO_INIT_TYPE; 40#endif 41 42#ifndef LOAD_LIBRARY_SEARCH_SYSTEM32 43#define LOAD_LIBRARY_SEARCH_SYSTEM32 0x00000800 44#endif 45 46#ifndef WC_ERR_INVALID_CHARS 47#define WC_ERR_INVALID_CHARS 0x00000080 48#endif 49 50// Dark mode support 51typedef enum { 52 UXTHEME_APPMODE_DEFAULT, 53 UXTHEME_APPMODE_ALLOW_DARK, 54 UXTHEME_APPMODE_FORCE_DARK, 55 UXTHEME_APPMODE_FORCE_LIGHT, 56 UXTHEME_APPMODE_MAX 57} UxthemePreferredAppMode; 58 59typedef enum { 60 WCA_UNDEFINED = 0, 61 WCA_USEDARKMODECOLORS = 26, 62 WCA_LAST = 27 63} WINDOWCOMPOSITIONATTRIB; 64 65typedef struct { 66 WINDOWCOMPOSITIONATTRIB Attrib; 67 PVOID pvData; 68 SIZE_T cbData; 69} WINDOWCOMPOSITIONATTRIBDATA; 70 71typedef struct { 72 ULONG dwOSVersionInfoSize; 73 ULONG dwMajorVersion; 74 ULONG dwMinorVersion; 75 ULONG dwBuildNumber; 76 ULONG dwPlatformId; 77 WCHAR szCSDVersion[128]; 78} NT_OSVERSIONINFOW; 79 80typedef bool (WINAPI *ShouldAppsUseDarkMode_t)(void); 81typedef void (WINAPI *AllowDarkModeForWindow_t)(HWND, bool); 82typedef void (WINAPI *AllowDarkModeForApp_t)(bool); 83typedef void (WINAPI *RefreshImmersiveColorPolicyState_t)(void); 84typedef UxthemePreferredAppMode (WINAPI *SetPreferredAppMode_t)(UxthemePreferredAppMode); 85typedef BOOL (WINAPI *SetWindowCompositionAttribute_t)(HWND, const WINDOWCOMPOSITIONATTRIBDATA *); 86typedef void (NTAPI *RtlGetVersion_t)(NT_OSVERSIONINFOW *); 87 88// Fake window to help with DirectInput events. 89HWND SDL_HelperWindow = NULL; 90static const TCHAR *SDL_HelperWindowClassName = TEXT("SDLHelperWindowInputCatcher"); 91static const TCHAR *SDL_HelperWindowName = TEXT("SDLHelperWindowInputMsgWindow"); 92static ATOM SDL_HelperWindowClass = 0; 93 94/* 95 * Creates a HelperWindow used for DirectInput. 96 */ 97bool SDL_HelperWindowCreate(void) 98{ 99 HINSTANCE hInstance = GetModuleHandle(NULL); 100 WNDCLASS wce; 101 102 // Make sure window isn't created twice. 103 if (SDL_HelperWindow != NULL) { 104 return true; 105 } 106 107 // Create the class. 108 SDL_zero(wce); 109 wce.lpfnWndProc = DefWindowProc; 110 wce.lpszClassName = SDL_HelperWindowClassName; 111 wce.hInstance = hInstance; 112 113 // Register the class. 114 SDL_HelperWindowClass = RegisterClass(&wce); 115 if (SDL_HelperWindowClass == 0 && GetLastError() != ERROR_CLASS_ALREADY_EXISTS) { 116 return WIN_SetError("Unable to create Helper Window Class"); 117 } 118 119 // Create the window. 120 SDL_HelperWindow = CreateWindowEx(0, SDL_HelperWindowClassName, 121 SDL_HelperWindowName, 122 WS_OVERLAPPED, CW_USEDEFAULT, 123 CW_USEDEFAULT, CW_USEDEFAULT, 124 CW_USEDEFAULT, HWND_MESSAGE, NULL, 125 hInstance, NULL); 126 if (!SDL_HelperWindow) { 127 UnregisterClass(SDL_HelperWindowClassName, hInstance); 128 return WIN_SetError("Unable to create Helper Window"); 129 } 130 131 return true; 132} 133 134/* 135 * Destroys the HelperWindow previously created with SDL_HelperWindowCreate. 136 */ 137void SDL_HelperWindowDestroy(void) 138{ 139 HINSTANCE hInstance = GetModuleHandle(NULL); 140 141 // Destroy the window. 142 if (SDL_HelperWindow != NULL) { 143 if (DestroyWindow(SDL_HelperWindow) == 0) { 144 WIN_SetError("Unable to destroy Helper Window"); 145 return; 146 } 147 SDL_HelperWindow = NULL; 148 } 149 150 // Unregister the class. 151 if (SDL_HelperWindowClass != 0) { 152 if ((UnregisterClass(SDL_HelperWindowClassName, hInstance)) == 0) { 153 WIN_SetError("Unable to destroy Helper Window Class"); 154 return; 155 } 156 SDL_HelperWindowClass = 0; 157 } 158} 159 160// Sets an error message based on an HRESULT 161bool WIN_SetErrorFromHRESULT(const char *prefix, HRESULT hr) 162{ 163 TCHAR buffer[1024]; 164 char *message; 165 TCHAR *p = buffer; 166 DWORD c = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, hr, 0, 167 buffer, SDL_arraysize(buffer), NULL); 168 buffer[c] = 0; 169 // kill CR/LF that FormatMessage() sticks at the end 170 while (*p) { 171 if (*p == '\r') { 172 *p = 0; 173 break; 174 } 175 ++p; 176 } 177 message = WIN_StringToUTF8(buffer); 178 SDL_SetError("%s%s%s", prefix ? prefix : "", prefix ? ": " : "", message); 179 SDL_free(message); 180 return false; 181} 182 183// Sets an error message based on GetLastError() 184bool WIN_SetError(const char *prefix) 185{ 186 return WIN_SetErrorFromHRESULT(prefix, GetLastError()); 187} 188 189HRESULT 190WIN_CoInitialize(void) 191{ 192 /* SDL handles any threading model, so initialize with the default, which 193 is compatible with OLE and if that doesn't work, try multi-threaded mode. 194 195 If you need multi-threaded mode, call CoInitializeEx() before SDL_Init() 196 */ 197#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES) 198 // On Xbox, there's no need to call CoInitializeEx (and it's not implemented) 199 return S_OK; 200#else 201 HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); 202 if (hr == RPC_E_CHANGED_MODE) { 203 hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); 204 } 205 206 // S_FALSE means success, but someone else already initialized. 207 // You still need to call CoUninitialize in this case! 208 if (hr == S_FALSE) { 209 return S_OK; 210 } 211 212 return hr; 213#endif 214} 215 216void WIN_CoUninitialize(void) 217{ 218 CoUninitialize(); 219} 220 221FARPROC WIN_LoadComBaseFunction(const char *name) 222{ 223 static bool s_bLoaded; 224 static HMODULE s_hComBase; 225 226 if (!s_bLoaded) { 227 s_hComBase = LoadLibraryEx(TEXT("combase.dll"), NULL, LOAD_LIBRARY_SEARCH_SYSTEM32); 228 s_bLoaded = true; 229 } 230 if (s_hComBase) { 231 return GetProcAddress(s_hComBase, name); 232 } else { 233 return NULL; 234 } 235} 236 237HRESULT 238WIN_RoInitialize(void) 239{ 240 typedef HRESULT(WINAPI * RoInitialize_t)(RO_INIT_TYPE initType); 241 RoInitialize_t RoInitializeFunc = (RoInitialize_t)WIN_LoadComBaseFunction("RoInitialize"); 242 if (RoInitializeFunc) { 243 // RO_INIT_SINGLETHREADED is equivalent to COINIT_APARTMENTTHREADED 244 HRESULT hr = RoInitializeFunc(RO_INIT_SINGLETHREADED); 245 if (hr == RPC_E_CHANGED_MODE) { 246 hr = RoInitializeFunc(RO_INIT_MULTITHREADED); 247 } 248 249 // S_FALSE means success, but someone else already initialized. 250 // You still need to call RoUninitialize in this case! 251 if (hr == S_FALSE) { 252 return S_OK; 253 } 254 255 return hr; 256 } else { 257 return E_NOINTERFACE; 258 } 259} 260 261void WIN_RoUninitialize(void) 262{ 263 typedef void(WINAPI * RoUninitialize_t)(void); 264 RoUninitialize_t RoUninitializeFunc = (RoUninitialize_t)WIN_LoadComBaseFunction("RoUninitialize"); 265 if (RoUninitializeFunc) { 266 RoUninitializeFunc(); 267 } 268} 269 270#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) 271static BOOL IsWindowsVersionOrGreater(WORD wMajorVersion, WORD wMinorVersion, WORD wServicePackMajor) 272{ 273 OSVERSIONINFOEXW osvi; 274 DWORDLONG const dwlConditionMask = VerSetConditionMask( 275 VerSetConditionMask( 276 VerSetConditionMask( 277 0, VER_MAJORVERSION, VER_GREATER_EQUAL), 278 VER_MINORVERSION, VER_GREATER_EQUAL), 279 VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL); 280 281 SDL_zero(osvi); 282 osvi.dwOSVersionInfoSize = sizeof(osvi); 283 osvi.dwMajorVersion = wMajorVersion; 284 osvi.dwMinorVersion = wMinorVersion; 285 osvi.wServicePackMajor = wServicePackMajor; 286 287 return VerifyVersionInfoW(&osvi, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR, dwlConditionMask) != FALSE; 288} 289 290static DWORD WIN_BuildNumber = 0; 291static BOOL IsWindowsBuildVersionAtLeast(DWORD dwBuildNumber) 292{ 293 if (WIN_BuildNumber != 0) { 294 return (WIN_BuildNumber >= dwBuildNumber); 295 } 296 297 HMODULE ntdll = LoadLibrary(TEXT("ntdll.dll")); 298 if (!ntdll) { 299 return false; 300 } 301 // There is no function to get Windows build number, so let's get it here via RtlGetVersion 302 RtlGetVersion_t RtlGetVersionFunc = (RtlGetVersion_t)GetProcAddress(ntdll, "RtlGetVersion"); 303 NT_OSVERSIONINFOW os_info; 304 os_info.dwOSVersionInfoSize = sizeof(NT_OSVERSIONINFOW); 305 os_info.dwBuildNumber = 0; 306 if (RtlGetVersionFunc) { 307 RtlGetVersionFunc(&os_info); 308 } 309 FreeLibrary(ntdll); 310 311 WIN_BuildNumber = (os_info.dwBuildNumber & ~0xF0000000); 312 return (WIN_BuildNumber >= dwBuildNumber); 313} 314#else 315static BOOL IsWindowsBuildVersionAtLeast(DWORD dwBuildNumber) 316{ 317 return TRUE; 318} 319#endif 320 321// apply some static variables so we only call into the Win32 API once per process for each check. 322#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES) 323 #define CHECKWINVER(notdesktop_platform_result, test) return (notdesktop_platform_result); 324#else 325 #define CHECKWINVER(notdesktop_platform_result, test) \ 326 static bool checked = false; \ 327 static BOOL result = FALSE; \ 328 if (!checked) { \ 329 result = (test); \ 330 checked = true; \ 331 } \ 332 return result; 333#endif 334 335BOOL WIN_IsWine(void) 336{ 337 static bool checked; 338 static bool is_wine; 339 340 if (!checked) { 341 HMODULE ntdll = LoadLibrary(TEXT("ntdll.dll")); 342 if (ntdll) { 343 if (GetProcAddress(ntdll, "wine_get_version") != NULL) { 344 is_wine = true; 345 } 346 FreeLibrary(ntdll); 347 } 348 checked = true; 349 } 350 return is_wine; 351} 352 353// this is the oldest thing we run on (and we may lose support for this in SDL3 at any time!), 354// so there's no "OrGreater" as that would always be TRUE. The other functions are here to 355// ask "can we support a specific feature?" but this function is here to ask "do we need to do 356// something different for an OS version we probably should abandon?" :) 357BOOL WIN_IsWindowsXP(void) 358{ 359 CHECKWINVER(FALSE, !WIN_IsWindowsVistaOrGreater() && IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINXP), LOBYTE(_WIN32_WINNT_WINXP), 0)); 360} 361 362BOOL WIN_IsWindowsVistaOrGreater(void) 363{ 364 CHECKWINVER(TRUE, IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_VISTA), LOBYTE(_WIN32_WINNT_VISTA), 0)); 365} 366 367BOOL WIN_IsWindows7OrGreater(void) 368{ 369 CHECKWINVER(TRUE, IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN7), LOBYTE(_WIN32_WINNT_WIN7), 0)); 370} 371 372BOOL WIN_IsWindows8OrGreater(void) 373{ 374 CHECKWINVER(TRUE, IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN8), LOBYTE(_WIN32_WINNT_WIN8), 0)); 375} 376 377BOOL WIN_IsWindows11OrGreater(void) 378{ 379 return IsWindowsBuildVersionAtLeast(22000); 380} 381 382#undef CHECKWINVER 383 384 385/* 386WAVExxxCAPS gives you 31 bytes for the device name, and just truncates if it's 387longer. However, since WinXP, you can use the WAVExxxCAPS2 structure, which 388will give you a name GUID. The full name is in the Windows Registry under 389that GUID, located here: HKLM\System\CurrentControlSet\Control\MediaCategories 390 391Note that drivers can report GUID_NULL for the name GUID, in which case, 392Windows makes a best effort to fill in those 31 bytes in the usual place. 393This info summarized from MSDN: 394 395http://web.archive.org/web/20131027093034/http://msdn.microsoft.com/en-us/library/windows/hardware/ff536382(v=vs.85).aspx 396 397Always look this up in the registry if possible, because the strings are 398different! At least on Win10, I see "Yeti Stereo Microphone" in the 399Registry, and a unhelpful "Microphone(Yeti Stereo Microph" in winmm. Sigh. 400 401(Also, DirectSound shouldn't be limited to 32 chars, but its device enum 402has the same problem.) 403 404WASAPI doesn't need this. This is just for DirectSound/WinMM. 405*/ 406char *WIN_LookupAudioDeviceName(const WCHAR *name, const GUID *guid) 407{ 408#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES) 409 return WIN_StringToUTF8W(name); // No registry access on Xbox, go with what we've got. 410#else 411 static const GUID nullguid = { 0 }; 412 const unsigned char *ptr; 413 char keystr[128]; 414 WCHAR *strw = NULL; 415 bool rc; 416 HKEY hkey; 417 DWORD len = 0; 418 char *result = NULL; 419 420 if (WIN_IsEqualGUID(guid, &nullguid)) { 421 return WIN_StringToUTF8W(name); // No GUID, go with what we've got. 422 } 423 424 ptr = (const unsigned char *)guid; 425 (void)SDL_snprintf(keystr, sizeof(keystr), 426 "System\\CurrentControlSet\\Control\\MediaCategories\\{%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X}", 427 ptr[3], ptr[2], ptr[1], ptr[0], ptr[5], ptr[4], ptr[7], ptr[6], 428 ptr[8], ptr[9], ptr[10], ptr[11], ptr[12], ptr[13], ptr[14], ptr[15]); 429 430 strw = WIN_UTF8ToStringW(keystr); 431 rc = (RegOpenKeyExW(HKEY_LOCAL_MACHINE, strw, 0, KEY_QUERY_VALUE, &hkey) == ERROR_SUCCESS); 432 SDL_free(strw); 433 if (!rc) { 434 return WIN_StringToUTF8W(name); // oh well. 435 } 436 437 rc = (RegQueryValueExW(hkey, L"Name", NULL, NULL, NULL, &len) == ERROR_SUCCESS); 438 if (!rc) { 439 RegCloseKey(hkey); 440 return WIN_StringToUTF8W(name); // oh well. 441 } 442 443 strw = (WCHAR *)SDL_malloc(len + sizeof(WCHAR)); 444 if (!strw) { 445 RegCloseKey(hkey); 446 return WIN_StringToUTF8W(name); // oh well. 447 } 448 449 rc = (RegQueryValueExW(hkey, L"Name", NULL, NULL, (LPBYTE)strw, &len) == ERROR_SUCCESS); 450 RegCloseKey(hkey); 451 if (!rc) { 452 SDL_free(strw); 453 return WIN_StringToUTF8W(name); // oh well. 454 } 455 456 strw[len / 2] = 0; // make sure it's null-terminated. 457 458 result = WIN_StringToUTF8W(strw); 459 SDL_free(strw); 460 return result ? result : WIN_StringToUTF8W(name); 461#endif 462} 463 464BOOL WIN_IsEqualGUID(const GUID *a, const GUID *b) 465{ 466 return (SDL_memcmp(a, b, sizeof(*a)) == 0); 467} 468 469BOOL WIN_IsEqualIID(REFIID a, REFIID b) 470{ 471 return (SDL_memcmp(a, b, sizeof(*a)) == 0); 472} 473 474void WIN_RECTToRect(const RECT *winrect, SDL_Rect *sdlrect) 475{ 476 sdlrect->x = winrect->left; 477 sdlrect->w = (winrect->right - winrect->left) + 1; 478 sdlrect->y = winrect->top; 479 sdlrect->h = (winrect->bottom - winrect->top) + 1; 480} 481 482void WIN_RectToRECT(const SDL_Rect *sdlrect, RECT *winrect) 483{ 484 winrect->left = sdlrect->x; 485 winrect->right = sdlrect->x + sdlrect->w - 1; 486 winrect->top = sdlrect->y; 487 winrect->bottom = sdlrect->y + sdlrect->h - 1; 488} 489 490bool WIN_WindowRectValid(const RECT *rect) 491{ 492 // A window can be resized to zero height, but not zero width 493 return (rect->right > 0); 494} 495 496void WIN_UpdateDarkModeForHWND(HWND hwnd) 497{ 498#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) 499 if (!IsWindowsBuildVersionAtLeast(17763)) { 500 // Too old to support dark mode 501 return; 502 } 503 HMODULE uxtheme = LoadLibrary(TEXT("uxtheme.dll")); 504 if (!uxtheme) { 505 return; 506 } 507 RefreshImmersiveColorPolicyState_t RefreshImmersiveColorPolicyStateFunc = (RefreshImmersiveColorPolicyState_t)GetProcAddress(uxtheme, MAKEINTRESOURCEA(104)); 508 ShouldAppsUseDarkMode_t ShouldAppsUseDarkModeFunc = (ShouldAppsUseDarkMode_t)GetProcAddress(uxtheme, MAKEINTRESOURCEA(132)); 509 AllowDarkModeForWindow_t AllowDarkModeForWindowFunc = (AllowDarkModeForWindow_t)GetProcAddress(uxtheme, MAKEINTRESOURCEA(133)); 510 if (!IsWindowsBuildVersionAtLeast(18362)) { 511 AllowDarkModeForApp_t AllowDarkModeForAppFunc = (AllowDarkModeForApp_t)GetProcAddress(uxtheme, MAKEINTRESOURCEA(135)); 512 if (AllowDarkModeForAppFunc) { 513 AllowDarkModeForAppFunc(true); 514 } 515 } else { 516 SetPreferredAppMode_t SetPreferredAppModeFunc = (SetPreferredAppMode_t)GetProcAddress(uxtheme, MAKEINTRESOURCEA(135)); 517 if (SetPreferredAppModeFunc) { 518 SetPreferredAppModeFunc(UXTHEME_APPMODE_ALLOW_DARK); 519 } 520 } 521 if (RefreshImmersiveColorPolicyStateFunc) { 522 RefreshImmersiveColorPolicyStateFunc(); 523 } 524 if (AllowDarkModeForWindowFunc) { 525 AllowDarkModeForWindowFunc(hwnd, true); 526 } 527 BOOL value; 528 // Check dark mode using ShouldAppsUseDarkMode, but use SDL_GetSystemTheme as a fallback 529 if (ShouldAppsUseDarkModeFunc) { 530 value = ShouldAppsUseDarkModeFunc() ? TRUE : FALSE; 531 } else { 532 value = (SDL_GetSystemTheme() == SDL_SYSTEM_THEME_DARK) ? TRUE : FALSE; 533 } 534 FreeLibrary(uxtheme); 535 if (!IsWindowsBuildVersionAtLeast(18362)) { 536 SetProp(hwnd, TEXT("UseImmersiveDarkModeColors"), SDL_reinterpret_cast(HANDLE, SDL_static_cast(INT_PTR, value))); 537 } else { 538 HMODULE user32 = GetModuleHandle(TEXT("user32.dll")); 539 if (user32) { 540 SetWindowCompositionAttribute_t SetWindowCompositionAttributeFunc = (SetWindowCompositionAttribute_t)GetProcAddress(user32, "SetWindowCompositionAttribute"); 541 if (SetWindowCompositionAttributeFunc) { 542 WINDOWCOMPOSITIONATTRIBDATA data = { WCA_USEDARKMODECOLORS, &value, sizeof(value) }; 543 SetWindowCompositionAttributeFunc(hwnd, &data); 544 } 545 } 546 } 547#endif 548} 549 550HICON WIN_CreateIconFromSurface(SDL_Surface *surface) 551{ 552#if !(defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES)) 553 SDL_Surface *s = SDL_ConvertSurface(surface, SDL_PIXELFORMAT_ARGB8888); 554 if (!s) { 555 return NULL; 556 } 557 558 /* The dimensions will be needed after s is freed */ 559 const int width = s->w; 560 const int height = s->h; 561 562 BITMAPINFO bmpInfo; 563 SDL_zero(bmpInfo); 564 bmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); 565 bmpInfo.bmiHeader.biWidth = width; 566 bmpInfo.bmiHeader.biHeight = -height; /* Top-down bitmap */ 567 bmpInfo.bmiHeader.biPlanes = 1; 568 bmpInfo.bmiHeader.biBitCount = 32; 569 bmpInfo.bmiHeader.biCompression = BI_RGB; 570 571 HDC hdc = GetDC(NULL); 572 void *pBits = NULL; 573 HBITMAP hBitmap = CreateDIBSection(hdc, &bmpInfo, DIB_RGB_COLORS, &pBits, NULL, 0); 574 if (!hBitmap) { 575 ReleaseDC(NULL, hdc); 576 SDL_DestroySurface(s); 577 return NULL; 578 } 579 580 SDL_memcpy(pBits, s->pixels, width * height * 4); 581 582 SDL_DestroySurface(s); 583 584 HBITMAP hMask = CreateBitmap(width, height, 1, 1, NULL); 585 if (!hMask) { 586 DeleteObject(hBitmap); 587 ReleaseDC(NULL, hdc); 588 return NULL; 589 } 590 591 HDC hdcMem = CreateCompatibleDC(hdc); 592 HGDIOBJ oldBitmap = SelectObject(hdcMem, hMask); 593 594 for (int y = 0; y < height; y++) { 595 for (int x = 0; x < width; x++) { 596 BYTE* pixel = (BYTE*)pBits + (y * width + x) * 4; 597 BYTE alpha = pixel[3]; 598 COLORREF maskColor = (alpha == 0) ? RGB(0, 0, 0) : RGB(255, 255, 255); 599 SetPixel(hdcMem, x, y, maskColor); 600 } 601 } 602 603 ICONINFO iconInfo; 604 iconInfo.fIcon = TRUE; 605 iconInfo.xHotspot = 0; 606 iconInfo.yHotspot = 0; 607 iconInfo.hbmMask = hMask; 608 iconInfo.hbmColor = hBitmap; 609 610 HICON hIcon = CreateIconIndirect(&iconInfo); 611 612 SelectObject(hdcMem, oldBitmap); 613 DeleteDC(hdcMem); 614 DeleteObject(hBitmap); 615 DeleteObject(hMask); 616 ReleaseDC(NULL, hdc); 617 618 return hIcon; 619#else 620 return NULL; 621#endif 622} 623 624// Some GUIDs we need to know without linking to libraries that aren't available before Vista. 625/* *INDENT-OFF* */ // clang-format off 626static const GUID SDL_KSDATAFORMAT_SUBTYPE_PCM = { 0x00000001, 0x0000, 0x0010,{ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } }; 627static const GUID SDL_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = { 0x00000003, 0x0000, 0x0010,{ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } }; 628/* *INDENT-ON* */ // clang-format on 629 630SDL_AudioFormat SDL_WaveFormatExToSDLFormat(WAVEFORMATEX *waveformat) 631{ 632 if ((waveformat->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) && (waveformat->wBitsPerSample == 32)) { 633 return SDL_AUDIO_F32; 634 } else if ((waveformat->wFormatTag == WAVE_FORMAT_PCM) && (waveformat->wBitsPerSample == 16)) { 635 return SDL_AUDIO_S16; 636 } else if ((waveformat->wFormatTag == WAVE_FORMAT_PCM) && (waveformat->wBitsPerSample == 32)) { 637 return SDL_AUDIO_S32; 638 } else if (waveformat->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { 639 const WAVEFORMATEXTENSIBLE *ext = (const WAVEFORMATEXTENSIBLE *)waveformat; 640 if ((SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, sizeof(GUID)) == 0) && (waveformat->wBitsPerSample == 32)) { 641 return SDL_AUDIO_F32; 642 } else if ((SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_PCM, sizeof(GUID)) == 0) && (waveformat->wBitsPerSample == 16)) { 643 return SDL_AUDIO_S16; 644 } else if ((SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_PCM, sizeof(GUID)) == 0) && (waveformat->wBitsPerSample == 32)) { 645 return SDL_AUDIO_S32; 646 } 647 } 648 return SDL_AUDIO_UNKNOWN; 649} 650 651 652int WIN_WideCharToMultiByte(UINT CodePage, DWORD dwFlags, LPCWCH lpWideCharStr, int cchWideChar, LPSTR lpMultiByteStr, int cbMultiByte, LPCCH lpDefaultChar, LPBOOL lpUsedDefaultChar) 653{ 654 if (WIN_IsWindowsXP()) { 655 dwFlags &= ~WC_ERR_INVALID_CHARS; // not supported before Vista. Without this flag, it will just replace bogus chars with U+FFFD. You're on your own, WinXP. 656 } 657 return WideCharToMultiByte(CodePage, dwFlags, lpWideCharStr, cchWideChar, lpMultiByteStr, cbMultiByte, lpDefaultChar, lpUsedDefaultChar); 658} 659 660const char *WIN_CheckDefaultArgcArgv(int *pargc, char ***pargv, void **pallocated) 661{ 662 // If the provided argv is valid, we pass it to the main function as-is, since it's probably what the user wants. 663 // Otherwise, we take a NULL argv as an instruction for SDL to parse the command line into an argv. 664 // On Windows, when SDL provides the main entry point, argv is always NULL. 665 666 const char *out_of_mem_str = "Out of memory - aborting"; 667 const char *proc_err_str = "Error processing command line arguments - aborting"; 668 669 *pallocated = NULL; 670 671 if (*pargv) { 672 return NULL; // just go with what was provided, no error message. 673 } 674 675 // We need to be careful about how we allocate/free memory here. We can't use SDL_alloc()/SDL_free() 676 // because the application might have used SDL_SetMemoryFunctions() to change the allocator. 677 LPWSTR *argvw = NULL; 678 char **argv = NULL; 679 680 const LPWSTR command_line = GetCommandLineW(); 681 682 // Because of how the Windows command line is structured, we know for sure that the buffer size required to 683 // store all argument strings converted to UTF-8 (with null terminators) is guaranteed to be less than or equal 684 // to the size of the original command line string converted to UTF-8. 685 const int argdata_size = WideCharToMultiByte(CP_UTF8, 0, command_line, -1, NULL, 0, NULL, NULL); // Includes the null terminator 686 if (!argdata_size) { 687 return proc_err_str; 688 } 689 690 int argc = -1; 691 argvw = CommandLineToArgvW(command_line, &argc); 692 if (!argvw || argc < 0) { 693 return out_of_mem_str; 694 } 695 696 // Allocate argv followed by the argument string buffer as one contiguous allocation. 697 argv = (char **)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (argc + 1) * sizeof(*argv) + argdata_size); 698 if (!argv) { 699 LocalFree(argvw); 700 return out_of_mem_str; 701 } 702 703 char *argdata = ((char *)argv) + (argc + 1) * sizeof(*argv); 704 int argdata_index = 0; 705 706 for (int i = 0; i < argc; ++i) { 707 const int bytes_written = WideCharToMultiByte(CP_UTF8, 0, argvw[i], -1, argdata + argdata_index, argdata_size - argdata_index, NULL, NULL); 708 if (!bytes_written) { 709 HeapFree(GetProcessHeap(), 0, argv); 710 LocalFree(argvw); 711 return proc_err_str; 712 } 713 argv[i] = argdata + argdata_index; 714 argdata_index += bytes_written; 715 } 716 717 argv[argc] = NULL; 718 719 LocalFree(argvw); 720 721 *pargc = argc; 722 *pallocated = argv; 723 *pargv = argv; 724 725 return NULL; // no error string. 726} 727 728#endif // defined(SDL_PLATFORM_WINDOWS) 729
[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.